From ed99bd04e99b3e5da7a7834546ca827dbf993a21 Mon Sep 17 00:00:00 2001
From: bucyril <bucyril@student.ethz.ch>
Date: Mon, 20 Mar 2017 16:38:22 +0100
Subject: [PATCH] added crazy radio from Dusan

---
 pps_ws/src/d_fall_pps/CMakeLists.txt          |   7 +-
 .../src/d_fall_pps/crazyradio/CrazyRadio.py   | 154 ++++
 .../crazyradio/cfclient/__init__.py           |  44 +
 .../crazyradio/cfclient/__init__.pyc          | Bin 0 -> 526 bytes
 .../crazyradio/cfclient/cfclient.py           | 137 +++
 .../crazyradio/cfclient/cfclient.pyc          | Bin 0 -> 3443 bytes
 .../crazyradio/cfclient/configs/config.json   |  34 +
 .../cfclient/configs/input/Generic_OS_X.json  |  18 +
 .../cfclient/configs/input/Joystick.json      |  95 ++
 .../cfclient/configs/input/PS3_Mode_1.json    |  19 +
 .../cfclient/configs/input/PS3_Mode_2.json    |  18 +
 .../cfclient/configs/input/PS3_Mode_3.json    |  83 ++
 .../cfclient/configs/input/PS4_Mode_1.json    |  87 ++
 .../cfclient/configs/input/PS4_Mode_2.json    |  87 ++
 .../configs/input/PS4_shoulder_btns_yaw.json  |  91 ++
 .../cfclient/configs/input/xbox360_mode1.json |  87 ++
 .../cfclient/configs/log/stabilizer.json      |  11 +
 .../crazyradio/cfclient/icon-256.png          | Bin 0 -> 10000 bytes
 .../crazyradio/cfclient/ui/__init__.py        |  38 +
 .../crazyradio/cfclient/ui/__init__.pyc       | Bin 0 -> 467 bytes
 .../cfclient/ui/dialogs/__init__.py           |   0
 .../cfclient/ui/dialogs/__init__.pyc          | Bin 0 -> 171 bytes
 .../crazyradio/cfclient/ui/dialogs/about.py   | 216 +++++
 .../crazyradio/cfclient/ui/dialogs/about.pyc  | Bin 0 -> 6672 bytes
 .../crazyradio/cfclient/ui/dialogs/about.ui   | 139 +++
 .../cfclient/ui/dialogs/bootloader.py         | 287 ++++++
 .../cfclient/ui/dialogs/bootloader.pyc        | Bin 0 -> 11724 bytes
 .../cfclient/ui/dialogs/bootloader.ui         | 216 +++++
 .../cfclient/ui/dialogs/cf1config.py          | 260 ++++++
 .../cfclient/ui/dialogs/cf1config.pyc         | Bin 0 -> 11202 bytes
 .../cfclient/ui/dialogs/cf1config.ui          | 260 ++++++
 .../cfclient/ui/dialogs/cf2config.py          | 108 +++
 .../cfclient/ui/dialogs/cf2config.pyc         | Bin 0 -> 4257 bytes
 .../cfclient/ui/dialogs/cf2config.ui          | 191 ++++
 .../cfclient/ui/dialogs/connectiondialogue.py | 122 +++
 .../ui/dialogs/connectiondialogue.pyc         | Bin 0 -> 4769 bytes
 .../cfclient/ui/dialogs/connectiondialogue.ui | 102 +++
 .../ui/dialogs/inputconfigdialogue.py         | 423 +++++++++
 .../ui/dialogs/inputconfigdialogue.pyc        | Bin 0 -> 19318 bytes
 .../ui/dialogs/inputconfigdialogue.ui         | 753 +++++++++++++++
 .../cfclient/ui/dialogs/logconfigdialogue.py  | 283 ++++++
 .../cfclient/ui/dialogs/logconfigdialogue.pyc | Bin 0 -> 11145 bytes
 .../cfclient/ui/dialogs/logconfigdialogue.ui  | 284 ++++++
 .../d_fall_pps/crazyradio/cfclient/ui/main.py | 667 ++++++++++++++
 .../crazyradio/cfclient/ui/main.pyc           | Bin 0 -> 22709 bytes
 .../d_fall_pps/crazyradio/cfclient/ui/main.ui | 375 ++++++++
 .../crazyradio/cfclient/ui/pluginhelper.py    |  41 +
 .../crazyradio/cfclient/ui/pluginhelper.pyc   | Bin 0 -> 857 bytes
 .../d_fall_pps/crazyradio/cfclient/ui/tab.py  |  92 ++
 .../d_fall_pps/crazyradio/cfclient/ui/tab.pyc | Bin 0 -> 2814 bytes
 .../crazyradio/cfclient/ui/tabs/ConsoleTab.py |  72 ++
 .../cfclient/ui/tabs/ConsoleTab.pyc           | Bin 0 -> 1935 bytes
 .../crazyradio/cfclient/ui/tabs/ExampleTab.py | 113 +++
 .../cfclient/ui/tabs/ExampleTab.pyc           | Bin 0 -> 4045 bytes
 .../crazyradio/cfclient/ui/tabs/FlightTab.py  | 556 ++++++++++++
 .../crazyradio/cfclient/ui/tabs/FlightTab.pyc | Bin 0 -> 22594 bytes
 .../crazyradio/cfclient/ui/tabs/GpsTab.py     | 295 ++++++
 .../crazyradio/cfclient/ui/tabs/GpsTab.pyc    | Bin 0 -> 8654 bytes
 .../crazyradio/cfclient/ui/tabs/LEDTab.py     | 161 ++++
 .../crazyradio/cfclient/ui/tabs/LEDTab.pyc    | Bin 0 -> 7144 bytes
 .../cfclient/ui/tabs/LogBlockDebugTab.py      | 103 +++
 .../cfclient/ui/tabs/LogBlockDebugTab.pyc     | Bin 0 -> 3746 bytes
 .../cfclient/ui/tabs/LogBlockTab.py           | 345 +++++++
 .../cfclient/ui/tabs/LogBlockTab.pyc          | Bin 0 -> 14996 bytes
 .../crazyradio/cfclient/ui/tabs/LogTab.py     |  97 ++
 .../crazyradio/cfclient/ui/tabs/LogTab.pyc    | Bin 0 -> 2830 bytes
 .../crazyradio/cfclient/ui/tabs/ParamTab.py   | 255 ++++++
 .../crazyradio/cfclient/ui/tabs/ParamTab.pyc  | Bin 0 -> 10768 bytes
 .../crazyradio/cfclient/ui/tabs/PlotTab.py    | 255 ++++++
 .../crazyradio/cfclient/ui/tabs/PlotTab.pyc   | Bin 0 -> 9626 bytes
 .../crazyradio/cfclient/ui/tabs/__init__.py   |  57 ++
 .../crazyradio/cfclient/ui/tabs/__init__.pyc  | Bin 0 -> 1101 bytes
 .../crazyradio/cfclient/ui/tabs/consoleTab.ui |  28 +
 .../crazyradio/cfclient/ui/tabs/exampleTab.ui |  20 +
 .../crazyradio/cfclient/ui/tabs/flightTab.ui  | 678 ++++++++++++++
 .../crazyradio/cfclient/ui/tabs/gpsTab.ui     | 160 ++++
 .../crazyradio/cfclient/ui/tabs/ledTab.ui     | 336 +++++++
 .../cfclient/ui/tabs/logBlockDebugTab.ui      |  75 ++
 .../cfclient/ui/tabs/logBlockTab.ui           |  28 +
 .../crazyradio/cfclient/ui/tabs/logTab.ui     |  60 ++
 .../crazyradio/cfclient/ui/tabs/paramTab.ui   |  24 +
 .../crazyradio/cfclient/ui/tabs/plotTab.ui    |  31 +
 .../cfclient/ui/toolboxes/ConsoleToolbox.py   |  65 ++
 .../cfclient/ui/toolboxes/ConsoleToolbox.pyc  | Bin 0 -> 2448 bytes
 .../cfclient/ui/toolboxes/CrtpSharkToolbox.py | 126 +++
 .../ui/toolboxes/CrtpSharkToolbox.pyc         | Bin 0 -> 5286 bytes
 .../ui/toolboxes/DebugDriverToolbox.py        | 109 +++
 .../ui/toolboxes/DebugDriverToolbox.pyc       | Bin 0 -> 4458 bytes
 .../cfclient/ui/toolboxes/__init__.py         |  59 ++
 .../cfclient/ui/toolboxes/__init__.pyc        | Bin 0 -> 1136 bytes
 .../cfclient/ui/toolboxes/consoleToolbox.ui   |  62 ++
 .../cfclient/ui/toolboxes/crtpSharkToolbox.ui |  92 ++
 .../ui/toolboxes/debugDriverToolbox.ui        |  86 ++
 .../cfclient/ui/widgets/__init__.py           |  30 +
 .../cfclient/ui/widgets/__init__.pyc          | Bin 0 -> 238 bytes
 .../crazyradio/cfclient/ui/widgets/ai.py      | 268 ++++++
 .../crazyradio/cfclient/ui/widgets/ai.pyc     | Bin 0 -> 8343 bytes
 .../cfclient/ui/widgets/hexspinbox.py         |  66 ++
 .../cfclient/ui/widgets/hexspinbox.pyc        | Bin 0 -> 2777 bytes
 .../crazyradio/cfclient/ui/widgets/plotter.ui | 288 ++++++
 .../cfclient/ui/widgets/plotwidget.py         | 291 ++++++
 .../cfclient/ui/widgets/plotwidget.pyc        | Bin 0 -> 10686 bytes
 .../crazyradio/cfclient/utils/__init__.py     |  30 +
 .../crazyradio/cfclient/utils/__init__.pyc    | Bin 0 -> 190 bytes
 .../crazyradio/cfclient/utils/config.py       | 105 +++
 .../crazyradio/cfclient/utils/config.pyc      | Bin 0 -> 3450 bytes
 .../cfclient/utils/config_manager.py          | 121 +++
 .../cfclient/utils/config_manager.pyc         | Bin 0 -> 3562 bytes
 .../crazyradio/cfclient/utils/guiconfig.pyc   | Bin 0 -> 1832 bytes
 .../crazyradio/cfclient/utils/input.pyc       | Bin 0 -> 11733 bytes
 .../cfclient/utils/input/__init__.py          | 412 +++++++++
 .../cfclient/utils/input/__init__.pyc         | Bin 0 -> 13812 bytes
 .../utils/input/inputinterfaces/__init__.py   | 101 +++
 .../utils/input/inputinterfaces/__init__.pyc  | Bin 0 -> 3290 bytes
 .../utils/input/inputinterfaces/leapmotion.py | 149 +++
 .../input/inputinterfaces/leapmotion.pyc      | Bin 0 -> 6071 bytes
 .../utils/input/inputinterfaces/wiimote.py    | 141 +++
 .../utils/input/inputinterfaces/wiimote.pyc   | Bin 0 -> 6087 bytes
 .../utils/input/inputinterfaces/zmqpull.py    | 119 +++
 .../utils/input/inputinterfaces/zmqpull.pyc   | Bin 0 -> 4432 bytes
 .../utils/input/inputreaderinterface.py       | 258 ++++++
 .../utils/input/inputreaderinterface.pyc      | Bin 0 -> 8721 bytes
 .../utils/input/inputreaders/__init__.py      | 146 +++
 .../utils/input/inputreaders/__init__.pyc     | Bin 0 -> 4311 bytes
 .../utils/input/inputreaders/linuxjsdev.py    | 227 +++++
 .../utils/input/inputreaders/linuxjsdev.pyc   | Bin 0 -> 9030 bytes
 .../utils/input/inputreaders/pysdl2.py        | 189 ++++
 .../utils/input/inputreaders/pysdl2.pyc       | Bin 0 -> 8270 bytes
 .../cfclient/utils/input/mux/__init__.py      |  82 ++
 .../cfclient/utils/input/mux/__init__.pyc     | Bin 0 -> 3102 bytes
 .../cfclient/utils/input/mux/nomux.py         |  52 ++
 .../cfclient/utils/input/mux/nomux.pyc        | Bin 0 -> 1267 bytes
 .../cfclient/utils/input/mux/takeovermux.py   |  50 +
 .../cfclient/utils/input/mux/takeovermux.pyc  | Bin 0 -> 1396 bytes
 .../utils/input/mux/takeoverselectivemux.py   |  71 ++
 .../utils/input/mux/takeoverselectivemux.pyc  | Bin 0 -> 2001 bytes
 .../cfclient/utils/joystick/__init__.pyc      | Bin 0 -> 245 bytes
 .../cfclient/utils/joystick/linuxjsdev.pyc    | Bin 0 -> 9305 bytes
 .../cfclient/utils/logconfigreader.py         | 140 +++
 .../cfclient/utils/logconfigreader.pyc        | Bin 0 -> 4251 bytes
 .../cfclient/utils/logdatawriter.py           | 119 +++
 .../cfclient/utils/logdatawriter.pyc          | Bin 0 -> 3823 bytes
 .../cfclient/utils/periodictimer.py           |  85 ++
 .../cfclient/utils/periodictimer.pyc          | Bin 0 -> 2914 bytes
 .../cfclient/utils/pysdl2reader.pyc           | Bin 0 -> 5784 bytes
 .../crazyradio/cfclient/utils/singleton.py    |  44 +
 .../crazyradio/cfclient/utils/singleton.pyc   | Bin 0 -> 899 bytes
 .../cfclient/utils/zmq_led_driver.py          |  99 ++
 .../cfclient/utils/zmq_led_driver.pyc         | Bin 0 -> 3608 bytes
 .../crazyradio/cfclient/utils/zmq_param.py    | 103 +++
 .../crazyradio/cfclient/utils/zmq_param.pyc   | Bin 0 -> 3797 bytes
 .../d_fall_pps/crazyradio/cfconfig/Makefile   |  51 ++
 .../crazyradio/cfconfig/configblock.py        | 109 +++
 .../src/d_fall_pps/crazyradio/cfheadless.py   | 170 ++++
 .../d_fall_pps/crazyradio/cflib/__init__.py   |  56 ++
 .../d_fall_pps/crazyradio/cflib/__init__.pyc  | Bin 0 -> 1182 bytes
 .../crazyradio/cflib/bootloader/__init__.py   | 332 +++++++
 .../crazyradio/cflib/bootloader/__init__.pyc  | Bin 0 -> 8304 bytes
 .../crazyradio/cflib/bootloader/boottypes.py  |  89 ++
 .../crazyradio/cflib/bootloader/boottypes.pyc | Bin 0 -> 2965 bytes
 .../crazyradio/cflib/bootloader/cloader.py    | 420 +++++++++
 .../crazyradio/cflib/bootloader/cloader.pyc   | Bin 0 -> 12420 bytes
 .../crazyradio/cflib/cache/27A2C4BA.json      | 257 ++++++
 .../crazyradio/cflib/cache/81204C68.json      | 317 +++++++
 .../crazyradio/cflib/cache/892049D2.json      |  89 ++
 .../crazyradio/cflib/cache/9E5F2E7D.json      | 355 ++++++++
 .../crazyradio/cflib/cache/E20076B8.json      | 499 ++++++++++
 .../crazyradio/cflib/cache/E8BC7DAD.json      | 499 ++++++++++
 .../crazyradio/cflib/crazyflie/__init__.py    | 391 ++++++++
 .../crazyradio/cflib/crazyflie/__init__.pyc   | Bin 0 -> 15562 bytes
 .../crazyradio/cflib/crazyflie/commander.py   |  78 ++
 .../crazyradio/cflib/crazyflie/commander.pyc  | Bin 0 -> 2173 bytes
 .../crazyradio/cflib/crazyflie/console.py     |  63 ++
 .../crazyradio/cflib/crazyflie/console.pyc    | Bin 0 -> 1678 bytes
 .../crazyradio/cflib/crazyflie/log.py         | 539 +++++++++++
 .../crazyradio/cflib/crazyflie/log.pyc        | Bin 0 -> 19930 bytes
 .../crazyradio/cflib/crazyflie/mem.py         | 782 ++++++++++++++++
 .../crazyradio/cflib/crazyflie/mem.pyc        | Bin 0 -> 27976 bytes
 .../crazyradio/cflib/crazyflie/param.py       | 315 +++++++
 .../crazyradio/cflib/crazyflie/param.pyc      | Bin 0 -> 11522 bytes
 .../cflib/crazyflie/platformservice.py        |  60 ++
 .../cflib/crazyflie/platformservice.pyc       | Bin 0 -> 1580 bytes
 .../crazyradio/cflib/crazyflie/toc.py         | 203 +++++
 .../crazyradio/cflib/crazyflie/toc.pyc        | Bin 0 -> 7071 bytes
 .../crazyradio/cflib/crazyflie/toccache.py    | 126 +++
 .../crazyradio/cflib/crazyflie/toccache.pyc   | Bin 0 -> 3809 bytes
 .../crazyradio/cflib/crtp/__init__.py         |  95 ++
 .../crazyradio/cflib/crtp/__init__.pyc        | Bin 0 -> 2559 bytes
 .../crazyradio/cflib/crtp/crtpdriver.py       |  96 ++
 .../crazyradio/cflib/crtp/crtpdriver.pyc      | Bin 0 -> 3669 bytes
 .../crazyradio/cflib/crtp/crtpstack.py        | 148 +++
 .../crazyradio/cflib/crtp/crtpstack.pyc       | Bin 0 -> 5176 bytes
 .../crazyradio/cflib/crtp/debugdriver.py      | 858 ++++++++++++++++++
 .../crazyradio/cflib/crtp/debugdriver.pyc     | Bin 0 -> 27858 bytes
 .../crazyradio/cflib/crtp/exceptions.py       |  47 +
 .../crazyradio/cflib/crtp/exceptions.pyc      | Bin 0 -> 1069 bytes
 .../crazyradio/cflib/crtp/radiodriver.py      | 424 +++++++++
 .../crazyradio/cflib/crtp/radiodriver.pyc     | Bin 0 -> 12349 bytes
 .../crazyradio/cflib/crtp/serialdriver.py     |  62 ++
 .../crazyradio/cflib/crtp/serialdriver.pyc    | Bin 0 -> 1867 bytes
 .../crazyradio/cflib/crtp/udpdriver.py        | 105 +++
 .../crazyradio/cflib/crtp/udpdriver.pyc       | Bin 0 -> 3552 bytes
 .../crazyradio/cflib/crtp/usbdriver.py        | 254 ++++++
 .../crazyradio/cflib/crtp/usbdriver.pyc       | Bin 0 -> 7434 bytes
 .../crazyradio/cflib/drivers/__init__.py      |  31 +
 .../crazyradio/cflib/drivers/__init__.pyc     | Bin 0 -> 254 bytes
 .../crazyradio/cflib/drivers/cfusb.py         | 196 ++++
 .../crazyradio/cflib/drivers/cfusb.pyc        | Bin 0 -> 5866 bytes
 .../crazyradio/cflib/drivers/crazyradio.py    | 295 ++++++
 .../crazyradio/cflib/drivers/crazyradio.pyc   | Bin 0 -> 9622 bytes
 .../crazyradio/cflib/utils/__init__.py        |  31 +
 .../crazyradio/cflib/utils/__init__.pyc       | Bin 0 -> 241 bytes
 .../crazyradio/cflib/utils/callbacks.py       |  56 ++
 .../crazyradio/cflib/utils/callbacks.pyc      | Bin 0 -> 1737 bytes
 pps_ws/src/d_fall_pps/crazyradio/cfzmq.py     | 366 ++++++++
 .../d_fall_pps/crazyradio/leapsdk/__init__.py |  38 +
 .../crazyradio/leapsdk/__init__.pyc           | Bin 0 -> 651 bytes
 ...rollerOutput.msg => ControlParameters.msg} |   5 +-
 ...conDataNode.cpp => ViconDataPublisher.cpp} |  23 +-
 219 files changed, 22877 insertions(+), 16 deletions(-)
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/CrazyRadio.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/config.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Generic_OS_X.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Joystick.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_1.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_2.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_3.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_1.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_2.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_shoulder_btns_yaw.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/xbox360_mode1.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/log/stabilizer.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/icon-256.png
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/consoleTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/exampleTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/flightTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/gpsTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ledTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockDebugTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/paramTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/plotTab.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/consoleToolbox.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/crtpSharkToolbox.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/debugDriverToolbox.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotter.ui
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/guiconfig.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/joystick/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/joystick/linuxjsdev.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/pysdl2reader.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfconfig/Makefile
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfconfig/configblock.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfheadless.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/cache/27A2C4BA.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/cache/81204C68.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/cache/892049D2.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/cache/9E5F2E7D.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E20076B8.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E8BC7DAD.json
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.py
 create mode 100644 pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.pyc
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/cfzmq.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.py
 create mode 100755 pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.pyc
 rename pps_ws/src/d_fall_pps/msg/{ControllerOutput.msg => ControlParameters.msg} (55%)
 rename pps_ws/src/d_fall_pps/src/{ViconDataNode.cpp => ViconDataPublisher.cpp} (81%)

diff --git a/pps_ws/src/d_fall_pps/CMakeLists.txt b/pps_ws/src/d_fall_pps/CMakeLists.txt
index c2e763df..f7308aef 100644
--- a/pps_ws/src/d_fall_pps/CMakeLists.txt
+++ b/pps_ws/src/d_fall_pps/CMakeLists.txt
@@ -57,6 +57,7 @@ find_package(catkin REQUIRED COMPONENTS
 add_message_files(
   FILES
   ViconData.msg
+  ControlParameters.msg
 )
 
 ## Generate services in the 'srv' folder
@@ -145,7 +146,7 @@ include_directories(
 ## With catkin_make all packages are built within a single CMake context
 ## The recommended prefix ensures that target names across packages don't collide
 # add_executable(${PROJECT_NAME}_node src/d_fall_pps_node.cpp)
-add_executable(ViconDataNode src/ViconDataNode.cpp)
+add_executable(ViconDataPublisher src/ViconDataPublisher.cpp)
 add_executable(PPSClient src/PPSClient.cpp)
 
 ## Rename C++ executable without prefix
@@ -165,8 +166,8 @@ add_executable(PPSClient src/PPSClient.cpp)
 
 find_library(VICON_LIBRARY ViconDataStreamSDK_CPP HINTS lib/vicon)
 
-target_link_libraries(ViconDataNode ${catkin_LIBRARIES})
-target_link_libraries(ViconDataNode ${VICON_LIBRARY})
+target_link_libraries(ViconDataPublisher ${catkin_LIBRARIES})
+target_link_libraries(ViconDataPublisher ${VICON_LIBRARY})
 
 target_link_libraries(PPSClient ${catkin_LIBRARIES})
 
diff --git a/pps_ws/src/d_fall_pps/crazyradio/CrazyRadio.py b/pps_ws/src/d_fall_pps/crazyradio/CrazyRadio.py
new file mode 100755
index 00000000..a3f818b6
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/CrazyRadio.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import roslib
+roslib.load_manifest('d_fall_pps')
+import rospy
+from d_fall_pps.msg import ControlParameters
+
+
+# General import
+import time, sys
+import struct
+import logging
+
+# Add library
+#sys.path.append("lib")
+
+# CrazyFlie client imports
+import cflib
+
+from cflib.crazyflie import Crazyflie
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+
+import cflib.drivers.crazyradio
+
+# Logging import
+from cflib.crazyflie.log import LogConfig
+
+# Logging settings
+logging.basicConfig(level=logging.ERROR)
+
+class SimpleClient:
+    """
+       Example script that runs several threads that read Vicon measurements
+       from a file and send it together with the setpoints to the Crazyflie.
+       It also employs a keyboard event detector that allows the user to
+       manipulate the setpoints with keys.
+    """
+    def __init__(self, link_uri):
+
+        # Setpoints to be sent to the CrazyFlie
+        self.roll = 0.0
+        self.pitch = 0.0
+        self.yaw = 0.0
+        self.motor1cmd = 0.0
+        self.motor2cmd = 0.0
+        self.motor3cmd = 0.0
+        self.motor4cmd = 0.0
+
+        # Initialize the CrazyFlie and add callbacks
+        self._cf = Crazyflie()
+
+        # Add callbacks that get executed depending on the connection status.
+        self._cf.connected.add_callback(self._connected)
+        self._cf.disconnected.add_callback(self._disconnected)
+        self._cf.connection_failed.add_callback(self._connection_failed)
+        self._cf.connection_lost.add_callback(self._connection_lost)
+
+        # Connect to the Crazyflie
+        print "Connecting to %s" % link_uri
+        self._cf.open_link(link_uri)
+
+
+    def _connected(self, link_uri):
+        """
+            This callback is executed as soon as the connection to the
+            quadrotor is established.
+        """
+        #print "Connection to %s successful: " % link_uri
+        rospy.loginfo("Connection to %s successful: " % link_uri)
+
+    def _connection_failed(self, link_uri, msg):
+        """Callback when connection initial connection fails (i.e no Crazyflie
+        at the specified address)"""
+        #print "Connection to %s failed: %s" % (link_uri, msg)
+        rospy.logerr("Connection to %s failed: %s" % (link_uri, msg))
+
+    def _connection_lost(self, link_uri, msg):
+        """Callback when disconnected after a connection has been made (i.e
+        Crazyflie moves out of range)"""
+        #print "Connection to %s lost: %s" % (link_uri, msg)
+        rospy.logerr("Connection to %s lost: %s" % (link_uri, msg))
+
+    def _disconnected(self, link_uri):
+        """Callback when the Crazyflie is disconnected (called in all cases)"""
+        #print "Disconnected from %s" % link_uri
+        rospy.logwarn("Disconnected from %s" % link_uri)
+
+
+    def _send_commands(self,cmd1,cmd2,cmd3,cmd4):
+
+            # Send setpoints at the given frequency.
+                # Fill the CRTP packet with the setpoints and send it to the stabilizer
+                pk = CRTPPacket()
+                pk.port = CRTPPort.STABILIZER
+                pk.data = struct.pack('<ffff', cmd1,cmd2,cmd3,cmd4)
+                self._cf.send_packet(pk)
+                #print "Motor commands: %f, %f, %f, %f" % (cmd1,cmd2,cmd3,cmd4)
+
+    def _send_to_commander(self,roll, pitch, yaw, thrust,cmd1,cmd2,cmd3,cmd4,mode):
+        pk = CRTPPacket()
+        pk.port = CRTPPort.COMMANDER
+        pk.data = struct.pack('<fffHHHHHH', roll, pitch, yaw, thrust,cmd1,cmd2,cmd3,cmd4,mode)
+        self._cf.send_packet(pk)
+
+        # self._cf.commander.send_setpoint (roll, pitch, yaw, thrust,cmd1,cmd2,cmd3,cmd4,mode)
+        # print "Motor commands: %f, %f, %f, %f" % (cmd1,cmd2,cmd3,cmd4)
+
+def subscriberControllerOutputCallback(data):
+    #cf_client._send_commands(data.cmd1,data.cmd2,data.cmd3,data.cmd4)
+    cf_client._send_to_commander(data.roll,data.pitch,data.yaw,data.thrust,data.motorCmd1, data.motorCmd2, data.motorCmd3, data.motorCmd4, data.onboardControllerType)
+    #rospy.loginfo(data.onboardControllerType);
+    #rospy.loginfo(data.motorCmd1);
+    #rospy.logdebug_throttle(2,"sending motor commands to crazyflie: ")
+
+
+
+
+if __name__ == '__main__':
+    rospy.init_node('CrazyRadio', anonymous=True)
+    cflib.crtp.init_drivers(enable_debug_driver=False)
+
+    while not rospy.is_shutdown():
+
+        # Initialize the low-level drivers (don't list the debug drivers)
+
+        # Scan for Crazyflies and use the first one found
+        rospy.loginfo("Scanning interfaces for Crazyflies...")
+        available=[]
+        available = cflib.crtp.scan_interfaces()
+        rospy.loginfo("Crazyflies found:")
+        for i in available:
+            print i[0]
+        if len(available) > 0:
+            # uri would can be specified directly, as for example: radio://0/70/250K
+            # instead of available[0][0]
+            global cf_client
+            cf_client = SimpleClient('radio://0/80/250K')
+            #cf_client = SimpleClient(available[0][0])
+            time.sleep(5.0)
+            rospy.Subscriber("FlightControl/topicControllerOutput", ControllerOutputPackage, subscriberControllerOutputCallback)
+
+            rospy.spin()
+        else:
+            rospy.logerr("No Crazyflies found, cannot run example")
+
+        #inp=raw_input('press any key');
+
+
+        time.sleep(0.5)
+
+    cf_client._cf.close_link()
+
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.py
new file mode 100755
index 00000000..e3208939
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+from .cfclient import main
+
+try:
+    from .version import VERSION
+except:
+    try:
+        import subprocess
+        VERSION = subprocess.check_output(["git", "describe"])
+    except:
+        VERSION = "dev"
+
+    try:
+        import subprocess
+        ret = subprocess.call(["git", "diff", "--quiet", "HEAD"])
+        if ret > 0:
+            VERSION += "+"
+    except:
+        VERSION += "+"
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..8c79f1b6984af0d16b764ba41f46662cedd7fbf9
GIT binary patch
literal 526
zcmZWk-AV#M7(L^zu9-rCHz9Nv2%=tg6H%a0HwFqSd$Z`yXp{S+JF~)G_X<5huhB)X
z6!ZY~%~*8N{G8#OnRC8xoX<w>qvKCQ{ERZ4H?-9|0jI?f0WpKmz>21oN{C8`9rzB|
zEO`NAthfMoz+FTxd>6vSQyH9A1?v)?$?jm5G|W@(8Imx}i@`rr#hR+%N8mNAU2F9q
zJn&;|Tx72hRHu$hlL5?jZzl6kBR)6T)XJpYpg%}P&^Dl5f_Nd51M`SYhvUip!$^M7
z7-@%;wbZ&wCl@Nr<Wg9Xi@h~-xmqdX>vng!6xxb+!`p!*4cZjxw2j+FgsU);BGF@e
zX>5CuDVZi?+f9uGl`pqh8VaR!ooM(d!cCCodYd1z!$lO?4l<$JCgu-Ri)gRckL0rd
tTd5xeGLbq6dfURba22OKkHocktIi2dN)Br>hi09f5!Q&;S&Q~5v2TZ$VWa>6

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.py
new file mode 100755
index 00000000..81b3594b
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""Initialization of the PC Client GUI."""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['']
+
+import sys
+import os
+import argparse
+import datetime
+
+import logging
+
+
+def main():
+
+    """
+    Check starting conditions and start GUI.
+
+    First, check command line arguments and start loggers. Set log levels. Try
+    all imports and exit verbosely if a library is not found. Disable outputs
+    to stdout and start the GUI.
+    """
+
+    # Set ERROR level for PyQt4 logger
+    qtlogger = logging.getLogger('PyQt4')
+    qtlogger.setLevel(logging.ERROR)
+
+    parser = argparse.ArgumentParser(description="cfclient - "
+                                     "Crazyflie graphical control client")
+    parser.add_argument('--debug', '-d', nargs=1, default='info', type=str,
+                        help="set debug level "
+                        "[minimal, info, debug, debugfile]")
+    args = parser.parse_args()
+    globals().update(vars(args))
+
+    cflogger = logging.getLogger('')
+
+    # Set correct logging fuctionality according to commandline
+    if ("debugfile" in debug):
+        logging.basicConfig(level=logging.DEBUG)
+        # Add extra format options for file logger (thread and time)
+        formatter = logging.Formatter('%(asctime)s:%(threadName)s:%(name)'
+                                      's:%(levelname)s:%(message)s')
+        filename = "debug-%s.log" % datetime.datetime.now()
+        filehandler = logging.FileHandler(filename)
+        filehandler.setLevel(logging.DEBUG)
+        filehandler.setFormatter(formatter)
+        cflogger.addHandler(filehandler)
+    elif ("debug" in debug):
+        logging.basicConfig(level=logging.DEBUG)
+    elif ("minimal" in debug):
+        logging.basicConfig(level=logging.WARNING)
+    elif ("info" in debug):
+        logging.basicConfig(level=logging.INFO)
+
+    logger = logging.getLogger(__name__)
+    
+    logger.debug("Using config path {}".format(sys.path[1]))
+    logger.debug("sys.path={}".format(sys.path))
+
+    # Try all the imports used in the project here to control what happens....
+    try:
+        import usb
+    except ImportError:
+        logger.critical("No pyusb installation found, exiting!")
+        sys.exit(1)
+
+    if not sys.platform.startswith('linux'):
+        try:
+            import sdl2
+        except ImportError:
+            logger.critical("No pysdl2 installation found, exiting!")
+            sys.exit(1)
+
+    try:
+        import PyQt4
+    except ImportError:
+        logger.critical("No PyQT4 installation found, exiting!")
+        sys.exit(1)
+
+    # Disable printouts from STL
+    if os.name == 'posix':
+        stdout = os.dup(1)
+        os.dup2(os.open('/dev/null', os.O_WRONLY), 1)
+        sys.stdout = os.fdopen(stdout, 'w')
+        logger.info("Disabling STL printouts")
+    
+    if os.name == 'nt':
+        stdout = os.dup(1)
+        os.dup2(os.open('NUL', os.O_WRONLY), 1)
+        sys.stdout = os.fdopen(stdout, 'w')
+        logger.info("Disabling STL printouts")
+
+    # Start up the main user-interface
+    from ui.main import MainUI
+    from PyQt4.QtGui import QApplication, QIcon
+    app = QApplication(sys.argv)
+
+    app.setWindowIcon(QIcon(sys.path[0] + "/cfclient/icon-256.png"))
+    # Make sure the right icon is set in Windows 7+ taskbar
+    if os.name == 'nt':
+        import ctypes
+        try:
+            myappid = 'mycompany.myproduct.subproduct.version'
+            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
+        except Exception:
+            pass
+    
+    main_window = MainUI()
+    main_window.show()
+    sys.exit(app.exec_())
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/cfclient.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..6054b91a78b93015ac313504f2b88b0f3a1b2edb
GIT binary patch
literal 3443
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/config.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/config.json
new file mode 100755
index 00000000..c78d5b0e
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/config.json
@@ -0,0 +1,34 @@
+{
+  "writable" : {
+    "input_device": "", 
+    "link_uri": "", 
+    "flightmode": "Normal", 
+    "open_tabs": "Flight Control", 
+    "trim_pitch": 0.0, 
+    "slew_limit": 45, 
+    "slew_rate": 30, 
+    "trim_roll": 0.0, 
+    "max_thrust": 80, 
+    "min_thrust": 25, 
+    "max_yaw": 200, 
+    "max_rp": 30,
+    "client_side_xmode": false,
+    "auto_reconnect": false,
+    "device_config_mapping": {},
+    "enable_debug_driver": false,
+    "input_device_blacklist": "(VirtualBox|VMware)",
+    "ui_update_period": 100,
+    "enable_zmq_input": false
+  },
+  "read-only" : {
+    "normal_slew_limit": 45, 
+    "normal_slew_rate": 30, 
+    "normal_max_thrust": 80, 
+    "normal_min_thrust": 25, 
+    "normal_max_yaw": 200, 
+    "normal_max_rp": 30,
+    "default_cf_channel": 10,
+    "default_cf_speed": 0,
+    "default_cf_trim": 0
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Generic_OS_X.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Generic_OS_X.json
new file mode 100755
index 00000000..da3c935f
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Generic_OS_X.json
@@ -0,0 +1,18 @@
+{
+  "inputconfig": {
+    "inputdevice":
+    {"name": "Generic 2-axis gamepad on OS X", "updateperiod":10,
+     "axis": [
+          {"name":"Roll", "type":"Input.AXIS", "id":3, "scale":1.0, "key":"roll"},
+          {"name":"Pitch", "type":"Input.AXIS", "id":4, "scale":-1.0, "key":"pitch"},
+          {"name":"Yaw", "type":"Input.AXIS", "id":0, "scale":1.0, "key":"yaw"},
+          {"name":"Thrust", "type":"Input.AXIS", "id":1, "scale":-1.0, "key":"thrust"},
+          {"name":"Pitch Cal+", "type":"Input.BUTTON", "id":6, "scale":-1.0, "key":"pitchNeg"},
+          {"name":"Pitch Cal-", "type":"Input.BUTTON", "id":4, "scale":1.0, "key":"pitchPos"},
+          {"name":"Roll Cal +", "type":"Input.BUTTON", "id":5, "scale":1.0, "key":"rollPos"},
+          {"name":"Roll Cal -", "type":"Input.BUTTON", "id":7, "scale":-1.0, "key":"rollNeg"},
+          {"name":"Killswtich", "type":"Input.BUTTON", "id":14, "scale":1.0, "key":"estop"},
+          {"name":"Exit", "type":"Input.BUTTON", "id":12, "scale":1.0, "key":"exit"}
+     ]}
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Joystick.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Joystick.json
new file mode 100755
index 00000000..00e1a5ed
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/Joystick.json
@@ -0,0 +1,95 @@
+{
+  "inputconfig": {
+    "inputdevice": {
+      "updateperiod": 10, 
+      "springythrottle": false,
+      "name": "Joystick", 
+      "axis": [
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 3, 
+          "key": "thrust", 
+          "name": "thrust"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 2, 
+          "key": "yaw", 
+          "name": "yaw"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 0, 
+          "key": "roll", 
+          "name": "roll"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 1, 
+          "key": "pitch", 
+          "name": "pitch"
+        }, 
+        {
+          "scale": 1.0,
+          "type": "Input.HAT",
+          "id": 0,
+          "key": "trim",
+          "name": "trim"
+        },
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 4, 
+          "key": "pitchNeg",
+          "name": "pitchNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 5, 
+          "key": "pitchPos",
+          "name": "pitchPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 1, 
+          "key": "estop", 
+          "name": "killswitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 2, 
+          "key": "rollNeg",
+          "name": "rollNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 3, 
+          "key": "rollPos",
+          "name": "rollPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 0, 
+          "key": "althold", 
+          "name": "althold"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 6, 
+          "key": "exit", 
+          "name": "exitapp"
+        }
+      ]
+    }
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_1.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_1.json
new file mode 100755
index 00000000..ae5cc3df
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_1.json
@@ -0,0 +1,19 @@
+{
+  "inputconfig": {
+    "inputdevice":
+    {"name": "Playstation 3 Mode 1", "updateperiod":10,
+     "axis": [
+          {"name":"Roll", "type":"Input.AXIS", "id":0, "scale":1.0, "key":"roll"},
+          {"name":"Pitch", "type":"Input.AXIS", "id":1, "scale":-1.0, "key":"pitch"},
+          {"name":"Yaw", "type":"Input.AXIS", "id":2, "scale":1.0, "key":"yaw"},
+          {"name":"Thrust", "type":"Input.AXIS", "id":3, "scale":-1.0, "key":"thrust"},
+          {"name":"Pitch Cal+", "type":"Input.BUTTON", "id":6, "scale":-1.0, "key":"pitchNeg"},
+          {"name":"Pitch Cal-", "type":"Input.BUTTON", "id":4, "scale":1.0, "key":"pitchPos"},
+          {"name":"Roll Cal +", "type":"Input.BUTTON", "id":5, "scale":1.0, "key":"rollPos"},
+          {"name":"Roll Cal -", "type":"Input.BUTTON", "id":7, "scale":-1.0, "key":"rollNeg"},
+          {"name":"Killswtich", "type":"Input.BUTTON", "id":14, "scale":1.0, "key":"estop"},
+          {"name":"Altitude hold", "type":"Input.BUTTON", "id":10, "scale":1.0, "key":"althold"},
+          {"name":"Exit", "type":"Input.BUTTON", "id":12, "scale":1.0, "key":"exit"}
+     ]}
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_2.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_2.json
new file mode 100755
index 00000000..4c7225de
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_2.json
@@ -0,0 +1,18 @@
+{
+  "inputconfig": {
+    "inputdevice":
+    {"name": "Playstation 3 Mode 2", "updateperiod":10,
+     "axis": [
+          {"name":"Roll", "type":"Input.AXIS", "id":2, "scale":1.0, "key":"roll"},
+          {"name":"Pitch", "type":"Input.AXIS", "id":3, "scale":-1.0, "key":"pitch"},
+          {"name":"Yaw", "type":"Input.AXIS", "id":0, "scale":1.0, "key":"yaw"},
+          {"name":"Thrust", "type":"Input.AXIS", "id":1, "scale":-1.0, "key":"thrust"},
+          {"name":"Pitch Cal+", "type":"Input.BUTTON", "id":6, "scale":-1.0, "key":"pitchNeg"},
+          {"name":"Pitch Cal-", "type":"Input.BUTTON", "id":4, "scale":1.0, "key":"pitchPos"},
+          {"name":"Roll Cal +", "type":"Input.BUTTON", "id":5, "scale":1.0, "key":"rollPos"},
+          {"name":"Roll Cal -", "type":"Input.BUTTON", "id":7, "scale":-1.0, "key":"rollNeg"},
+          {"name":"Killswtich", "type":"Input.BUTTON", "id":14, "scale":1.0, "key":"estop"},
+          {"name":"Exit", "type":"Input.BUTTON", "id":12, "scale":1.0, "key":"exit"}
+     ]}
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_3.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_3.json
new file mode 100755
index 00000000..18fea844
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS3_Mode_3.json
@@ -0,0 +1,83 @@
+{
+  "inputconfig": {
+    "inputdevice": {
+      "updateperiod": 10, 
+      "name": "PS3_Mode_3", 
+      "axis": [
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 1, 
+          "key": "thrust", 
+          "name": "thrust"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "ids": [
+            12, 
+            13
+          ], 
+          "key": "yaw", 
+          "name": "yaw"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 2, 
+          "key": "roll", 
+          "name": "roll"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 3, 
+          "key": "pitch", 
+          "name": "pitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 6, 
+          "key": "pitchNeg",
+          "name": "pitchNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 4, 
+          "key": "pitchPos",
+          "name": "pitchPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 14, 
+          "key": "estop", 
+          "name": "killswitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 7, 
+          "key": "rollNeg",
+          "name": "rollNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 5, 
+          "key": "rollPos",
+          "name": "rollPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 12, 
+          "key": "exit", 
+          "name": "exitapp"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_1.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_1.json
new file mode 100755
index 00000000..15e0010d
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_1.json
@@ -0,0 +1,87 @@
+{
+  "inputconfig": {
+    "inputdevice": {
+      "updateperiod": 10, 
+      "name": "PS4_Mode_1", 
+      "axis": [
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 5, 
+          "key": "thrust", 
+          "name": "thrust"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 2, 
+          "key": "yaw", 
+          "name": "yaw"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 0, 
+          "key": "roll", 
+          "name": "roll"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 1, 
+          "key": "pitch", 
+          "name": "pitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 1, 
+          "key": "pitchNeg",
+          "name": "pitchNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 3, 
+          "key": "pitchPos",
+          "name": "pitchPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 12, 
+          "key": "estop", 
+          "name": "killswitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 0, 
+          "key": "rollNeg",
+          "name": "rollNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 2, 
+          "key": "rollPos",
+          "name": "rollPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 5, 
+          "key": "althold", 
+          "name": "althold"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 8, 
+          "key": "exit", 
+          "name": "exitapp"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_2.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_2.json
new file mode 100755
index 00000000..f369ebdc
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_Mode_2.json
@@ -0,0 +1,87 @@
+{
+  "inputconfig": {
+    "inputdevice": {
+      "updateperiod": 10, 
+      "name": "PS4_Mode_2", 
+      "axis": [
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 1, 
+          "key": "thrust", 
+          "name": "thrust"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 0, 
+          "key": "yaw", 
+          "name": "yaw"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 2, 
+          "key": "roll", 
+          "name": "roll"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 5, 
+          "key": "pitch", 
+          "name": "pitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 1, 
+          "key": "pitchNeg",
+          "name": "pitchNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 3, 
+          "key": "pitchPos",
+          "name": "pitchPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 7, 
+          "key": "estop", 
+          "name": "killswitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": 0, 
+          "key": "rollNeg",
+          "name": "rollNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 2, 
+          "key": "rollPos",
+          "name": "rollPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 4, 
+          "key": "althold", 
+          "name": "althold"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 8, 
+          "key": "exit", 
+          "name": "exitapp"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_shoulder_btns_yaw.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_shoulder_btns_yaw.json
new file mode 100755
index 00000000..6a14488d
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/PS4_shoulder_btns_yaw.json
@@ -0,0 +1,91 @@
+{
+  "inputconfig": {
+    "inputdevice": {
+      "updateperiod": 10,
+      "name": "PS4_shoulder_btns_yaw",
+      "axis": [
+        {
+          "scale": -1.0,
+          "type": "Input.AXIS",
+          "id": 5,
+          "key": "thrust",
+          "name": "thrust"
+        },
+        {
+          "offset": 1.0,
+          "scale": 0.5,
+          "type": "Input.AXIS",
+          "ids": [
+            3,
+            4
+          ],
+          "key": "yaw",
+          "name": "yaw"
+        },
+        {
+          "scale": 1.0,
+          "type": "Input.AXIS",
+          "id": 0,
+          "key": "roll",
+          "name": "roll"
+        },
+        {
+          "scale": -1.0,
+          "type": "Input.AXIS",
+          "id": 1,
+          "key": "pitch",
+          "name": "pitch"
+        },
+        {
+          "scale": -1.0,
+          "type": "Input.BUTTON",
+          "id": 22,
+          "key": "pitchNeg",
+          "name": "pitchNeg"
+        },
+        {
+          "scale": 1.0,
+          "type": "Input.BUTTON",
+          "id": 21,
+          "key": "pitchPos",
+          "name": "pitchPos"
+        },
+        {
+          "scale": 1.0,
+          "type": "Input.BUTTON",
+          "id": -1,
+          "key": "estop",
+          "name": "killswitch"
+        },
+        {
+          "scale": -1.0,
+          "type": "Input.BUTTON",
+          "id": 23,
+          "key": "rollNeg",
+          "name": "rollNeg"
+        },
+        {
+          "scale": 1.0,
+          "type": "Input.BUTTON",
+          "id": 24,
+          "key": "rollPos",
+          "name": "rollPos"
+        },
+        {
+          "scale": 1.0,
+          "type": "Input.BUTTON",
+          "id": 5,
+          "key": "althold",
+          "name": "althold"
+        },
+        {
+          "scale": 1.0,
+          "type": "Input.BUTTON",
+          "id": -1,
+          "key": "exit",
+          "name": "exitapp"
+        }
+      ]
+    }
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/xbox360_mode1.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/xbox360_mode1.json
new file mode 100755
index 00000000..0ca5d032
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/input/xbox360_mode1.json
@@ -0,0 +1,87 @@
+{
+  "inputconfig": {
+    "inputdevice": {
+      "updateperiod": 10, 
+      "name": "xbox360_mode1_linux", 
+      "axis": [
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 4, 
+          "key": "thrust", 
+          "name": "thrust"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 3, 
+          "key": "yaw", 
+          "name": "yaw"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.AXIS", 
+          "id": 0, 
+          "key": "roll", 
+          "name": "roll"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.AXIS", 
+          "id": 1, 
+          "key": "pitch", 
+          "name": "pitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": -1, 
+          "key": "pitchNeg",
+          "name": "pitchNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": -1, 
+          "key": "pitchPos",
+          "name": "pitchPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": -1, 
+          "key": "estop", 
+          "name": "killswitch"
+        }, 
+        {
+          "scale": -1.0, 
+          "type": "Input.BUTTON", 
+          "id": -1, 
+          "key": "rollNeg",
+          "name": "rollNeg"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": -1, 
+          "key": "rollPos",
+          "name": "rollPos"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": 5, 
+          "key": "althold", 
+          "name": "althold"
+        }, 
+        {
+          "scale": 1.0, 
+          "type": "Input.BUTTON", 
+          "id": -1, 
+          "key": "exit", 
+          "name": "exitapp"
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/log/stabilizer.json b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/log/stabilizer.json
new file mode 100755
index 00000000..c131b817
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/configs/log/stabilizer.json
@@ -0,0 +1,11 @@
+{
+  "logconfig": {
+    "logblock":
+    {"name": "Stabilizer", "period":20,
+     "variables": [
+          {"name":"stabilizer.roll", "type":"TOC", "stored_as":"float", "fetch_as":"float"},
+          {"name":"stabilizer.pitch", "type":"TOC", "stored_as":"float", "fetch_as":"float"},
+          {"name":"stabilizer.yaw", "type":"TOC", "stored_as":"float", "fetch_as":"float"}
+     ]}
+  }
+}
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/icon-256.png b/pps_ws/src/d_fall_pps/crazyradio/cfclient/icon-256.png
new file mode 100755
index 0000000000000000000000000000000000000000..8c9fc927c683fa24956eb0bce233a808fe6d7f15
GIT binary patch
literal 10000
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
TPlUmZYJl1!P2~zjRQUe@nR}}j

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.py
new file mode 100755
index 00000000..51d3a306
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Create a plugin helper that is passed to all the tabs and toolboxes for easy #
+access to objects that are needed.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = []
+
+from cfclient.ui.pluginhelper import PluginHelper
+
+pluginhelper = PluginHelper()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..c104e7ddfe707c932c60f7d3a900cc8a2627a688
GIT binary patch
literal 467
zcmYjMF;2uV5Oi|60|k&M=~#&-&BY%;2#$sVAp}&{SdP7SF>>N4b|j9Lj;HY*9suhE
z1eQG+&p5lYv(KZv`FMX)>}RF)y`<wgfo5z?n6P!iw!~AWY0sFQu_j~dj3t<{H!|i-
zXY8?<#~(dGe%+%C2ogFwY)uO_+75dNwG3c9bkcj&5FAKrNks^<@*vx&Ia|3G^ssR~
zp!7R9%cWB2eSGh#XH<b`tR#Dc7Ez-vbALd6S0<=lz9L+$%onX9Au${h+>beK#u#_S
zBFcLNLTIOi2>Hn65Ng-cb+RZM@e{JtMp=VxC<jwc6HNm>kT=!;uNG1L1Aj|bUb_Z)
z^^=cxy&LD?G45_d#g|hDzwCAtqUF|9{I@M13>U(*CJ0fIzfT3v8>a`07pKI16c*_s
IS)|A5H!JmkbpQYW

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/__init__.py
new file mode 100755
index 00000000..e69de29b
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..5f70eceb5bdffb7217e4898413ba04e8b69a74cb
GIT binary patch
literal 171
zcmZSn%*&N~sw*U!0SXv_v;z<qvjB+{28Lh_kcgiKkYGR~ibH^6`WgATsrpHoCCNpJ
zRjK*~Mfq8&$tA`5AZ}$^PG+iZGLXnCDb_8hEXl~v)6dCF(oar<2<n$+>ZfET=H#ar
h>&M4u=4F<|$Lj&raR3dr$<0qG%}KQbSzipq3;<T1D-Qqw

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.py
new file mode 100755
index 00000000..b89b6176
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The about dialog.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['AboutDialog']
+
+import sys
+
+from PyQt4 import Qt, QtCore, QtGui, uic
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from PyQt4.Qt import *
+
+import cfclient
+
+import cflib.crtp
+
+(about_widget_class,
+about_widget_base_class) = (uic.loadUiType(sys.path[0] +
+                                           '/cfclient/ui/dialogs/about.ui'))
+
+DEBUG_INFO_FORMAT = """
+<b>Cfclient</b><br>
+Cfclient version: {version}<br>
+System: {system}<br>
+<br>
+<b>Interface status</b><br>
+{interface_status}
+<br>
+<b>Input readers</b><br>
+{input_readers}
+<br>
+<b>Input devices</b><br>
+{input_devices}
+<br>
+<b>Crazyflie</b><br>
+Connected: {uri}<br>
+Firmware: {firmware}<br>
+<b>Sensors found</b><br>
+{imu_sensors}
+<b>Sensors tests</b><br>
+{imu_sensor_tests}
+"""
+
+INTERFACE_FORMAT = "{}: {}<br>"
+INPUT_READER_FORMAT = "{} ({} devices connected)<br>"
+DEVICE_FORMAT = "{}: ({}) {}<br>"
+IMU_SENSORS_FORMAT = "{}: {}<br>"
+SENSOR_TESTS_FORMAT = "{}: {}<br>"
+FIRMWARE_FORMAT = "{:x}{:x} ({})"
+
+CREDITS_FORMAT = U"""
+<b>Contributions</b><br>
+{contribs}
+<br><br>
+<b>Used libraries</b><br>
+<a href="http://qt-project.org/">QT</a><br>
+<a href="http://www.riverbankcomputing.co.uk/software/pyqt/intro">PyQT</a><br>
+<a href="http://pysdl2.readthedocs.org">PySDL2</a><br>
+<a href="http://www.pyqtgraph.org/">PyQtGraph</a><br>
+<a href="http://marble.kde.org/">KDE Marble</a><br>
+<a href="http://sourceforge.net/projects/pyusb/">PyUSB</a><br>
+<a href="http://www.python.org/">Python</a><br>
+"""
+
+class AboutDialog(QtGui.QWidget, about_widget_class):
+
+    _disconnected_signal = pyqtSignal(str)
+
+    """Crazyflie client About box for debugging and information"""
+    def __init__(self, helper, *args):
+        super(AboutDialog, self).__init__(*args)
+        self.setupUi(self)
+        self._close_button.clicked.connect(self.close)
+        self._name_label.setText(
+                             self._name_label.text().replace('#version#',
+                                                             cfclient.VERSION))
+
+        self._interface_text = ""
+        self._imu_sensors_text = ""
+        self._imu_sensor_test_text = ""
+        self._uri = None 
+        self._fw_rev0 = None
+        self._fw_rev1 = None
+        self._fw_modified = None
+        self._helper = helper
+
+        helper.cf.param.add_update_callback(group="imu_sensors",
+                                            cb=self._imu_sensors_update)
+        helper.cf.param.add_update_callback(group="imu_tests",
+                                            cb=self._imu_sensor_tests_update)
+        helper.cf.param.add_update_callback(group="firmware",
+                                            cb=self._firmware_update)
+        helper.cf.connected.add_callback(self._connected)
+
+        self._disconnected_signal.connect(self._disconnected)
+        helper.cf.disconnected.add_callback(self._disconnected_signal.emit)
+
+        # Open the Credits file and show it in the UI
+        credits = U""
+        try:
+            with open("CREDITS.txt", 'r') as f:
+                for line in f:
+                    credits += U"{}<br>".format(line.decode("UTF-8"))
+        except IOError:
+            credits = U""
+
+        self._credits.setHtml(
+            CREDITS_FORMAT.format(contribs=credits)
+        )
+
+    def showEvent(self, event):
+        """Event when the about box is shown"""
+        self._interface_text = ""
+        interface_status = cflib.crtp.get_interfaces_status()
+        for key in interface_status.keys():
+            self._interface_text += INTERFACE_FORMAT.format(key,
+                                                    interface_status[key])
+        firmware = None
+
+        self._device_text = ""
+        devs = self._helper.inputDeviceReader.available_devices()
+        for d in devs:
+            self._device_text += DEVICE_FORMAT.format(d.reader_name,
+                                                      d.id,
+                                                      d.name)
+        if len(self._device_text) == 0:
+            self._device_text = "None<br>"
+
+        self._input_readers_text = ""
+        #readers = self._helper.inputDeviceReader.getAvailableDevices()
+        for reader in cfclient.utils.input.inputreaders.initialized_readers:
+            self._input_readers_text += INPUT_READER_FORMAT.format(reader.name,
+                                                                   len(reader.devices()))
+        if len(self._input_readers_text) == 0:
+            self._input_readers_text = "None<br>"
+
+        if self._uri:
+            firmware = FIRMWARE_FORMAT.format(self._fw_rev0, self._fw_rev1,
+                                "MODIFIED" if self._fw_modified else "CLEAN")
+        self._debug_out.setHtml(
+                DEBUG_INFO_FORMAT.format(version=cfclient.VERSION,
+                                         system=sys.platform,
+                                         interface_status=self._interface_text,
+                                         input_devices=self._device_text,
+                                         input_readers=self._input_readers_text,
+                                         uri = self._uri,
+                                         firmware = firmware,
+                                         imu_sensors=self._imu_sensors_text,
+                                         imu_sensor_tests=
+                                            self._imu_sensor_test_text))
+
+    def _connected(self, uri):
+        """Callback when Crazyflie is connected"""
+        self._uri = uri
+
+    def _firmware_update(self, name, value):
+        """Callback for firmware parameters"""
+        if "revision0" in name:
+            self._fw_rev0 = eval(value)
+        if "revision1" in name:
+            self._fw_rev1 = eval(value)
+        if "modified" in name:
+            self._fw_modified = eval(value)
+
+    def _imu_sensors_update(self, name, value):
+        """Callback for sensor found parameters"""
+        param = name[name.index('.') + 1:]
+        if not param in self._imu_sensors_text:
+            self._imu_sensors_text += IMU_SENSORS_FORMAT.format(param,
+                                                                eval(value))
+
+    def _imu_sensor_tests_update(self, name, value):
+        """Callback for sensor test parameters"""
+        param = name[name.index('.') + 1:]
+        if not param in self._imu_sensor_test_text:
+            self._imu_sensor_test_text += SENSOR_TESTS_FORMAT.format(param,
+                                                                 eval(value))
+
+    def _disconnected(self, uri):
+        """Callback for Crazyflie disconnected"""
+        self._interface_text = ""
+        self._imu_sensors_text = ""
+        self._imu_sensor_test_text = ""
+        self._uri = None
+        self._fw_rev1 = None
+        self._fw_rev0 = None
+        self._fw_modified = None
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..ffef3f1194f284714b174e6d7a4759edd5e8822f
GIT binary patch
literal 6672
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
MbIc$9P8D+h1&7@*+W-In

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.ui
new file mode 100755
index 00000000..9fd190e5
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/about.ui
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>511</width>
+    <height>338</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>About Crazyflie Client</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_4">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QTabWidget" name="tabWidget">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="currentIndex">
+        <number>0</number>
+       </property>
+       <widget class="QWidget" name="About">
+        <attribute name="title">
+         <string>About</string>
+        </attribute>
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_3">
+           <item>
+            <layout class="QGridLayout" name="gridLayout_2">
+             <item row="1" column="0">
+              <widget class="QLabel" name="label_2">
+               <property name="text">
+                <string>Copyright (c) 2011-2013, Bitcraze AB</string>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="0">
+              <widget class="QLabel" name="label">
+               <property name="text">
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;The Crazyflie Nano Quadcopter client is a multi-platform client&lt;br/&gt;for controlling, bootloading and logging the Crazyflie Nano&lt;br/&gt;Quadcopter. For more info visit our homepage.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://www.bitcraze.se&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Bitcraze Homepage&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;a href=&quot;http://wiki.bitcraze.se&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Bitcraze Wiki&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;a href=&quot;http://forum.bitcraze.se&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Bitcraze Forum&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+               </property>
+              </widget>
+             </item>
+             <item row="3" column="0">
+              <spacer name="verticalSpacer">
+               <property name="orientation">
+                <enum>Qt::Vertical</enum>
+               </property>
+               <property name="sizeHint" stdset="0">
+                <size>
+                 <width>20</width>
+                 <height>40</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+             <item row="0" column="0">
+              <widget class="QLabel" name="_name_label">
+               <property name="text">
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Cfclient #version#&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QWidget" name="Credits">
+        <attribute name="title">
+         <string>Credits</string>
+        </attribute>
+        <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <item>
+          <layout class="QGridLayout" name="gridLayout_3">
+           <item row="0" column="0">
+            <widget class="QTextEdit" name="_credits"/>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+       <widget class="QWidget" name="Debug">
+        <attribute name="title">
+         <string>Debug</string>
+        </attribute>
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_2">
+           <item>
+            <widget class="QTextBrowser" name="_debug_out"/>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="1" column="0">
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="1">
+        <widget class="QPushButton" name="_close_button">
+         <property name="text">
+          <string>Close</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.py
new file mode 100755
index 00000000..b593e11d
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.py
@@ -0,0 +1,287 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The bootloader dialog is used to update the Crazyflie firmware and to
+read/write the configuration block in the Crazyflie flash.
+"""
+from cflib.bootloader import Bootloader
+
+__author__ = 'Bitcraze AB'
+__all__ = ['BootloaderDialog']
+
+import struct
+import sys
+import time
+
+import logging
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+from cfclient.ui.tab import Tab
+
+import cflib.crtp
+
+from cflib.bootloader.cloader import Cloader
+
+service_dialog_class = uic.loadUiType(sys.path[0] +
+                                      "/cfclient/ui/dialogs/bootloader.ui")[0]
+
+
+class UIState:
+    DISCONNECTED = 0
+    CONNECTING = 5
+    CONNECT_FAILED = 1
+    COLD_CONNECT = 2
+    FLASHING = 3
+    RESET = 4
+
+
+class BootloaderDialog(QtGui.QWidget, service_dialog_class):
+    """Tab for update the Crazyflie firmware and for reading/writing the config
+    block in flash"""
+    def __init__(self, helper, *args):
+        super(BootloaderDialog, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Service"
+        self.menuName = "Service"
+
+        # self.tabWidget = tabWidget
+        self.helper = helper
+
+        # self.cf = crazyflie
+        self.clt = CrazyloadThread()
+
+        # Connecting GUI signals (a pity to do that manually...)
+        self.imagePathBrowseButton.clicked.connect(self.pathBrowse)
+        self.programButton.clicked.connect(self.programAction)
+        self.verifyButton.clicked.connect(self.verifyAction)
+        self.coldBootButton.clicked.connect(self.initiateColdboot)
+        self.resetButton.clicked.connect(self.resetCopter)
+        self._cancel_bootloading.clicked.connect(self.close)
+
+        # connecting other signals
+        self.clt.programmed.connect(self.programDone)
+        self.clt.verified.connect(self.verifyDone)
+        self.clt.statusChanged.connect(self.statusUpdate)
+        # self.clt.updateBootloaderStatusSignal.connect(
+        #                                         self.updateBootloaderStatus)
+        self.clt.connectingSignal.connect(lambda:
+                                          self.setUiState(UIState.CONNECTING))
+        self.clt.connectedSignal.connect(lambda:
+                                         self.setUiState(UIState.COLD_CONNECT))
+        self.clt.failed_signal.connect(lambda m: self._ui_connection_fail(m))
+        self.clt.disconnectedSignal.connect(lambda:
+                                        self.setUiState(UIState.DISCONNECTED))
+
+        self.clt.start()
+
+    def _ui_connection_fail(self, message):
+        self.setStatusLabel(message)
+        self.coldBootButton.setEnabled(True)
+
+    def setUiState(self, state):
+        if (state == UIState.DISCONNECTED):
+            self.resetButton.setEnabled(False)
+            self.programButton.setEnabled(False)
+            self.setStatusLabel("Not connected")
+            self.coldBootButton.setEnabled(True)
+            self.progressBar.setTextVisible(False)
+            self.progressBar.setValue(0)
+            self.statusLabel.setText('Status: <b>IDLE</b>')
+            self.imagePathLine.setText("")
+        elif (state == UIState.CONNECTING):
+            self.resetButton.setEnabled(False)
+            self.programButton.setEnabled(False)
+            self.setStatusLabel("Trying to connect cold bootloader, restart "
+                                "the Crazyflie to connect")
+            self.coldBootButton.setEnabled(False)
+        elif (state == UIState.CONNECT_FAILED):
+            self.setStatusLabel("Connecting to bootloader failed")
+            self.coldBootButton.setEnabled(True)
+        elif (state == UIState.COLD_CONNECT):
+            self.resetButton.setEnabled(True)
+            self.programButton.setEnabled(True)
+            self.setStatusLabel("Connected to bootloader")
+            self.coldBootButton.setEnabled(False)
+        elif (state == UIState.RESET):
+            self.setStatusLabel("Resetting to firmware, disconnected")
+            self.resetButton.setEnabled(False)
+            self.programButton.setEnabled(False)
+            self.coldBootButton.setEnabled(False)
+            self.imagePathLine.setText("")
+
+    def setStatusLabel(self, text):
+        self.connectionStatus.setText("Status: <b>%s</b>" % text)
+
+    def connected(self):
+        self.setUiState(UIState.COLD_CONNECT)
+
+    def connectionFailed(self):
+        self.setUiState(UIState.CONNECT_FAILED)
+
+    def resetCopter(self):
+        self.clt.resetCopterSignal.emit()
+        self.setUiState(UIState.RESET)
+
+    def updateConfig(self, channel, speed, rollTrim, pitchTrim):
+        self.rollTrim.setValue(rollTrim)
+        self.pitchTrim.setValue(pitchTrim)
+        self.radioChannel.setValue(channel)
+        self.radioSpeed.setCurrentIndex(speed)
+
+    def closeEvent(self, event):
+        self.setUiState(UIState.RESET)
+        self.clt.resetCopterSignal.emit()
+
+    @pyqtSlot()
+    def pathBrowse(self):
+        filename = ""
+        # Fix for crash in X on Ubuntu 14.04
+        filename = QtGui.QFileDialog.getOpenFileName()
+        if filename != "":
+            self.imagePathLine.setText(filename)
+        pass
+
+    @pyqtSlot()
+    def programAction(self):
+        # self.setStatusLabel("Initiate programming")
+        self.resetButton.setEnabled(False)
+        self.programButton.setEnabled(False)
+        self.imagePathBrowseButton.setEnabled(False)
+        if self.imagePathLine.text() != "":
+            self.clt.program.emit(self.imagePathLine.text(),
+                                  self.verifyCheckBox.isChecked())
+        else:
+            msgBox = QtGui.QMessageBox()
+            msgBox.setText("Please choose an image file to program.")
+
+            msgBox.exec_()
+
+    @pyqtSlot()
+    def verifyAction(self):
+        self.statusLabel.setText('Status: <b>Initiate verification</b>')
+        pass
+
+    @pyqtSlot(bool)
+    def programDone(self, success):
+        if success:
+            self.statusLabel.setText('Status: <b>Programing complete!</b>')
+        else:
+            self.statusLabel.setText('Status: <b>Programing failed!</b>')
+
+        self.resetButton.setEnabled(True)
+        self.programButton.setEnabled(True)
+        self.imagePathBrowseButton.setEnabled(True)
+
+    @pyqtSlot()
+    def verifyDone(self):
+        self.statusLabel.setText('Status: <b>Verification complete</b>')
+        pass
+
+    @pyqtSlot(str, int)
+    def statusUpdate(self, status, progress):
+        logger.debug("Status: [%s] | %d", status, progress)
+        self.statusLabel.setText('Status: <b>' + status + '</b>')
+        if progress >= 0:
+            self.progressBar.setValue(progress)
+
+    def initiateColdboot(self):
+        self.clt.initiateColdBootSignal.emit("radio://0/100")
+
+
+# No run method specified here as the default run implementation is running the
+# event loop which is what we want
+class CrazyloadThread(QThread):
+    # Input signals declaration (not sure it should be used like that...)
+    program = pyqtSignal(str, bool)
+    verify = pyqtSignal()
+    initiateColdBootSignal = pyqtSignal(str)
+    resetCopterSignal = pyqtSignal()
+    writeConfigSignal = pyqtSignal(int, int, float, float)
+    # Output signals declaration
+    programmed = pyqtSignal(bool)
+    verified = pyqtSignal()
+    statusChanged = pyqtSignal(str, int)
+    connectedSignal = pyqtSignal()
+    connectingSignal = pyqtSignal()
+    failed_signal = pyqtSignal(str)
+    disconnectedSignal = pyqtSignal()
+    updateConfigSignal = pyqtSignal(int, int, float, float)
+    updateCpuIdSignal = pyqtSignal(str)
+
+    radioSpeedPos = 2
+
+    def __init__(self):
+        super(CrazyloadThread, self).__init__()
+
+        self._bl = Bootloader()
+        self._bl.progress_cb = self.statusChanged.emit
+
+        # Make sure that the signals are handled by this thread event loop
+        self.moveToThread(self)
+
+        self.program.connect(self.programAction)
+        self.initiateColdBootSignal.connect(self.initiateColdBoot)
+        self.resetCopterSignal.connect(self.resetCopter)
+
+    def __del__(self):
+        self.quit()
+        self.wait()
+
+    def initiateColdBoot(self, linkURI):
+        self.connectingSignal.emit()
+
+        try:
+            success = self._bl.start_bootloader(warm_boot=False)
+            if not success:
+                self.failed_signal.emit("Could not connect to bootloader")
+            else:
+                self.connectedSignal.emit()
+        except Exception as e:
+            self.failed_signal.emit("{}".format(e))
+
+    def programAction(self, filename, verify):
+        targets = {}
+        if str(filename).endswith("bin"):
+            targets["stm32"] = ("fw", )
+        try:
+            self._bl.flash(str(filename), targets)
+            self.programmed.emit(True)
+        except Exception:
+            self.programmed.emit(False)
+
+    def resetCopter(self):
+        try:
+            self._bl.reset_to_firmware()
+        except Exception:
+            pass
+        self._bl.close()
+        self.disconnectedSignal.emit()
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..4acfd27a8b9a4ed140627350fa9c21415047d163
GIT binary patch
literal 11724
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
ZtbkvHvv`2ef{Q;TZvIUDWaGlG{t0}=4><q;

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.ui
new file mode 100755
index 00000000..1254c297
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/bootloader.ui
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>825</width>
+    <height>338</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Crazyflie Service</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="0" column="0">
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
+        <string>Crazyflie connection</string>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <widget class="QPushButton" name="coldBootButton">
+            <property name="text">
+             <string>Initiate bootloader cold boot</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="pushButton_2">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string>Restart in bootloader mode</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="resetButton">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string>Restart in firmware mode</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="_cancel_bootloading">
+            <property name="text">
+             <string>Cancel bootloading</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QLabel" name="connectionStatus">
+          <property name="text">
+           <string>Status: &lt;b&gt;Not connected&lt;/b&gt;</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="horizontalSpacing">
+        <number>6</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QGroupBox" name="groupBox_2">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="title">
+          <string>Crazyflie firmware update</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <layout class="QGridLayout" name="gridLayout_4">
+            <item row="1" column="2">
+             <widget class="QPushButton" name="imagePathBrowseButton">
+              <property name="text">
+               <string>Browse</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="0">
+             <widget class="QCheckBox" name="verifyCheckBox">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Verify flashing</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="label_3">
+              <property name="text">
+               <string>Select package to flash:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLineEdit" name="imagePathLine"/>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="horizontalLayout_3">
+            <item>
+             <spacer name="horizontalSpacer_2">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item>
+             <widget class="QPushButton" name="verifyButton">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Verify</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QPushButton" name="programButton">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Program</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <spacer name="verticalSpacer_3">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>40</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <widget class="QProgressBar" name="progressBar">
+         <property name="value">
+          <number>0</number>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="statusLabel">
+         <property name="text">
+          <string>Status: &lt;b&gt;IDLE&lt;/b&gt;</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.py
new file mode 100755
index 00000000..c7635676
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.py
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The bootloader dialog is used to update the Crazyflie firmware and to
+read/write the configuration block in the Crazyflie flash.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Cf1ConfigDialog']
+
+import struct
+import sys
+from cflib.bootloader import Bootloader
+
+import logging
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+from cfclient.utils.config import Config
+
+service_dialog_class = uic.loadUiType(sys.path[0] +
+                                      "/cfclient/ui/dialogs/cf1config.ui")[0]
+
+class UIState:
+    DISCONNECTED = 0
+    CONNECTING = 5
+    CONNECT_FAILED = 1
+    COLD_CONNECT = 2
+    FLASHING = 3
+    RESET = 4
+
+
+class Cf1ConfigDialog(QtGui.QWidget, service_dialog_class):
+    """Tab for update the Crazyflie firmware and for reading/writing the config
+    block in flash"""
+    def __init__(self, helper, *args):
+        super(Cf1ConfigDialog, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "CF1 Config"
+        self.menuName = "CF1 Config"
+
+        # self.tabWidget = tabWidget
+        self.helper = helper
+
+        # self.cf = crazyflie
+        self.clt = CrazyloadThread()
+
+        # Connecting GUI signals (a pity to do that manually...)
+        self.coldBootButton.clicked.connect(self.initiateColdboot)
+        self.resetButton.clicked.connect(self.resetCopter)
+        self.saveConfigblock.clicked.connect(self.writeConfig)
+        self._cancel_bootloading.clicked.connect(self.close)
+
+        self.clt.statusChanged.connect(self.statusUpdate)
+        self.clt.connectingSignal.connect(lambda:
+                                          self.setUiState(UIState.CONNECTING))
+        self.clt.connectedSignal.connect(lambda:
+                                         self.setUiState(UIState.COLD_CONNECT))
+        self.clt.failed_signal.connect(lambda m: self._ui_connection_fail(m))
+        self.clt.disconnectedSignal.connect(lambda:
+                                        self.setUiState(UIState.DISCONNECTED))
+        self.clt.updateConfigSignal.connect(self.updateConfig)
+
+        self.clt.start()
+
+    def _ui_connection_fail(self, message):
+        self.setStatusLabel(message)
+        self.coldBootButton.setEnabled(True)
+
+    def setUiState(self, state):
+        if (state == UIState.DISCONNECTED):
+            self.resetButton.setEnabled(False)
+            self.setStatusLabel("Not connected")
+            self.coldBootButton.setEnabled(True)
+            self.progressBar.setTextVisible(False)
+            self.progressBar.setValue(0)
+            self.statusLabel.setText('Status: <b>IDLE</b>')
+            self.saveConfigblock.setEnabled(False)
+        elif (state == UIState.CONNECTING):
+            self.resetButton.setEnabled(False)
+            self.setStatusLabel("Trying to connect cold bootloader, restart "
+                                "the Crazyflie to connect")
+            self.coldBootButton.setEnabled(False)
+        elif (state == UIState.CONNECT_FAILED):
+            self.setStatusLabel("Connecting to bootloader failed")
+            self.coldBootButton.setEnabled(True)
+        elif (state == UIState.COLD_CONNECT):
+            self.resetButton.setEnabled(True)
+            self.saveConfigblock.setEnabled(True)
+            self.setStatusLabel("Connected to bootloader")
+            self.coldBootButton.setEnabled(False)
+        elif (state == UIState.RESET):
+            self.setStatusLabel("Resetting to firmware, disconnected")
+            self.resetButton.setEnabled(False)
+            self.coldBootButton.setEnabled(False)
+            self.rollTrim.setValue(0)
+            self.pitchTrim.setValue(0)
+            self.radioChannel.setValue(0)
+            self.radioSpeed.setCurrentIndex(0)
+
+    def setStatusLabel(self, text):
+        self.connectionStatus.setText("Status: <b>%s</b>" % text)
+
+    def connected(self):
+        self.setUiState(UIState.COLD_CONNECT)
+
+    def connectionFailed(self):
+        self.setUiState(UIState.CONNECT_FAILED)
+
+    def resetCopter(self):
+        self.clt.resetCopterSignal.emit()
+        self.setUiState(UIState.RESET)
+
+    def updateConfig(self, channel, speed, rollTrim, pitchTrim):
+        self.rollTrim.setValue(rollTrim)
+        self.pitchTrim.setValue(pitchTrim)
+        self.radioChannel.setValue(channel)
+        self.radioSpeed.setCurrentIndex(speed)
+
+    def closeEvent(self, event):
+        self.setUiState(UIState.RESET)
+        self.clt.resetCopterSignal.emit()
+
+    @pyqtSlot(str, int)
+    def statusUpdate(self, status, progress):
+        logger.debug("Status: [%s] | %d", status, progress)
+        self.statusLabel.setText('Status: <b>' + status + '</b>')
+        if progress >= 0 and progress < 100:
+            self.progressBar.setTextVisible(True)
+            self.progressBar.setValue(progress)
+        else:
+            self.progressBar.setTextVisible(False)
+            self.progressBar.setValue(100)
+        if progress >= 100:
+            self.resetButton.setEnabled(True)
+
+    def initiateColdboot(self):
+        self.clt.initiateColdBootSignal.emit("radio://0/100")
+
+    def writeConfig(self):
+        pitchTrim = self.pitchTrim.value()
+        rollTrim = self.rollTrim.value()
+        channel = self.radioChannel.value()
+        speed = self.radioSpeed.currentIndex()
+
+        self.clt.writeConfigSignal.emit(channel, speed, rollTrim, pitchTrim)
+
+
+# No run method specified here as the default run implementation is running the
+# event loop which is what we want
+class CrazyloadThread(QThread):
+    # Input signals declaration (not sure it should be used like that...)
+    initiateColdBootSignal = pyqtSignal(str)
+    resetCopterSignal = pyqtSignal()
+    writeConfigSignal = pyqtSignal(int, int, float, float)
+    # Output signals declaration
+    statusChanged = pyqtSignal(str, int)
+    connectedSignal = pyqtSignal()
+    connectingSignal = pyqtSignal()
+    failed_signal = pyqtSignal(str)
+    disconnectedSignal = pyqtSignal()
+    updateConfigSignal = pyqtSignal(int, int, float, float)
+
+    def __init__(self):
+        super(CrazyloadThread, self).__init__()
+
+        # Make sure that the signals are handled by this thread event loop
+        self.moveToThread(self)
+        self._bl = Bootloader()
+        self._bl.progress_cb = self.statusChanged.emit
+
+        self.writeConfigSignal.connect(self.writeConfigAction)
+        self.initiateColdBootSignal.connect(self.initiateColdBoot)
+        self.resetCopterSignal.connect(self.resetCopter)
+
+    def __del__(self):
+        self.quit()
+        self.wait()
+
+    def initiateColdBoot(self, linkURI):
+        self.connectingSignal.emit()
+
+        try:
+            success = self._bl.start_bootloader(warm_boot=False)
+            if not success:
+                self.failed_signal.emit("Could not connect to bootloader")
+            else:
+                self.connectedSignal.emit()
+                self.readConfigAction()
+        except Exception as e:
+            self.failed_signal.emit("{}".format(e))
+
+    def checksum256(self, st):
+        return reduce(lambda x, y: x + y, map(ord, st)) % 256
+
+    def writeConfigAction(self, channel, speed, rollTrim, pitchTrim):
+        data = (0x00, channel, speed, pitchTrim, rollTrim)
+        image = struct.pack("<BBBff", *data)
+        # Adding some magic:
+        image = "0xBC" + image
+        image += struct.pack("B", 256 - self.checksum256(image))
+
+        self._bl.write_cf1_config(image)
+
+    def readConfigAction(self):
+        self.statusChanged.emit("Reading config block...", 0)
+        data = self._bl.read_cf1_config()
+        if (data is not None):
+            if data[0:4] == "0xBC":
+                # Skip 0xBC and version at the beginning
+                [channel,
+                 speed,
+                 pitchTrim,
+                 rollTrim] = struct.unpack("<BBff", data[5:15])
+                self.statusChanged.emit("Reading config block...done!", 100)
+            else:
+                channel = Config().get("default_cf_channel")
+                speed = Config().get("default_cf_speed")
+                pitchTrim = Config().get("default_cf_trim")
+                rollTrim = Config().get("default_cf_trim")
+                self.statusChanged.emit("Could not find config block, showing defaults", 100)
+            self.updateConfigSignal.emit(channel, speed, rollTrim, pitchTrim)
+        else:
+            self.statusChanged.emit("Reading config block failed!", 0)
+
+    def resetCopter(self):
+        try:
+            self._bl.reset_to_firmware()
+        except Exception:
+            pass
+        self._bl.close()
+        self.disconnectedSignal.emit()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..ab6c2681b1fd88b2195c184b6971087ffc8feb73
GIT binary patch
literal 11202
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
W8`FrV@M7PrOyf3dvNBgcefOVH0f6NI

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.ui
new file mode 100755
index 00000000..4d7804bb
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf1config.ui
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>825</width>
+    <height>395</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Crazyflie 1.0 Config</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="0" column="0">
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
+        <string>Crazyflie connection</string>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <widget class="QPushButton" name="coldBootButton">
+            <property name="text">
+             <string>Initiate bootloader cold boot</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="pushButton_2">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string>Restart in bootloader mode</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="resetButton">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string>Restart in firmware mode</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="_cancel_bootloading">
+            <property name="text">
+             <string>Cancel bootloading</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QLabel" name="connectionStatus">
+          <property name="text">
+           <string>Status: &lt;b&gt;Not connected&lt;/b&gt;</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="horizontalSpacing">
+        <number>6</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QGroupBox" name="configBlockGroup">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="title">
+          <string>Configuration block</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_2">
+          <item row="6" column="0">
+           <widget class="QLabel" name="label_9">
+            <property name="text">
+             <string>Radio bandwidth:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="6" column="2">
+           <widget class="QComboBox" name="radioSpeed">
+            <property name="editable">
+             <bool>false</bool>
+            </property>
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <item>
+             <property name="text">
+              <string>250 Kbit/s</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>1 MBit/s</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string> 2 MBit/s</string>
+             </property>
+            </item>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_4">
+            <property name="text">
+             <string>Pitch trim:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_6">
+            <property name="text">
+             <string>Roll trim:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="8" column="2">
+           <widget class="QPushButton" name="saveConfigblock">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string>Program</string>
+            </property>
+           </widget>
+          </item>
+          <item row="5" column="0">
+           <widget class="QLabel" name="label_8">
+            <property name="text">
+             <string>Radio channel:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="7" column="0">
+           <spacer name="verticalSpacer_2">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>0</width>
+              <height>10</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item row="5" column="2">
+           <widget class="QSpinBox" name="radioChannel">
+            <property name="prefix">
+             <string>Ch. </string>
+            </property>
+            <property name="maximum">
+             <number>127</number>
+            </property>
+           </widget>
+          </item>
+          <item row="9" column="2">
+           <spacer name="verticalSpacer">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>40</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item row="1" column="2">
+           <widget class="QDoubleSpinBox" name="pitchTrim">
+            <property name="decimals">
+             <number>1</number>
+            </property>
+            <property name="minimum">
+             <double>-10.000000000000000</double>
+            </property>
+            <property name="maximum">
+             <double>10.000000000000000</double>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="2">
+           <widget class="QDoubleSpinBox" name="rollTrim">
+            <property name="decimals">
+             <number>1</number>
+            </property>
+            <property name="minimum">
+             <double>-10.000000000000000</double>
+            </property>
+            <property name="maximum">
+             <double>10.000000000000000</double>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <widget class="QProgressBar" name="progressBar">
+         <property name="value">
+          <number>0</number>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="statusLabel">
+         <property name="text">
+          <string>Status: &lt;b&gt;IDLE&lt;/b&gt;</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.py
new file mode 100755
index 00000000..236fb245
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The bootloader dialog is used to update the Crazyflie firmware and to
+read/write the configuration block in the Crazyflie flash.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['CfConfig']
+
+import sys
+import logging
+
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+from cflib.crazyflie.mem import MemoryElement
+
+service_dialog_class = uic.loadUiType(sys.path[0] +
+                                      "/cfclient/ui/dialogs/cf2config.ui")[0]
+
+class Cf2ConfigDialog(QtGui.QWidget, service_dialog_class):
+    """Tab for update the Crazyflie firmware and for reading/writing the config
+    block in flash"""
+
+    connected_signal = pyqtSignal(str)
+    disconnected_signal = pyqtSignal(str)
+
+    def __init__(self, helper, *args):
+        super(Cf2ConfigDialog, self).__init__(*args)
+        self.setupUi(self)
+
+        self._cf = helper.cf
+
+        self.disconnected_signal.connect(self._set_ui_disconnected)
+        self.connected_signal.connect(self._set_ui_connected)
+        self._cf.disconnected.add_callback(self.disconnected_signal.emit)
+        self._cf.connected.add_callback(self.connected_signal.emit)
+
+        self._exit_btn.clicked.connect(self.hide)
+        self._write_data_btn.clicked.connect(self._write_data)
+
+    def _write_done(self, mem, addr):
+        self._cf.mem.get_mems(MemoryElement.TYPE_I2C)[0].update(self._data_updated)
+
+    def _data_updated(self, mem):
+        self._roll_trim.setValue(mem.elements["roll_trim"])
+        self._pitch_trim.setValue(mem.elements["pitch_trim"])
+        self._radio_channel.setValue(mem.elements["radio_channel"])
+        self._radio_speed.setCurrentIndex(mem.elements["radio_speed"])
+        if "radio_address" in mem.elements:
+            self._radio_address.setValue(mem.elements["radio_address"])
+            self._radio_address.setEnabled(True)
+        else:
+            self._radio_address.setValue(int("0xE7E7E7E7E7", 0))
+            self._radio_address.setEnabled(False)
+        self._write_data_btn.setEnabled(True)
+
+    def _set_ui_connected(self, link_uri):
+        mems = self._cf.mem.get_mems(MemoryElement.TYPE_I2C)
+        if len(mems) > 0:
+            mems[0].update(self._data_updated)
+
+    def _set_ui_disconnected(self, link_uri):
+        self._write_data_btn.setEnabled(False)
+        self._roll_trim.setValue(0)
+        self._pitch_trim.setValue(0)
+        self._radio_channel.setValue(0)
+        self._radio_speed.setCurrentIndex(0)
+        self._radio_address.setValue(0)
+        self._radio_address.setEnabled(False)
+
+    def _write_data(self):
+        self._write_data_btn.setEnabled(False)
+        mem = self._cf.mem.get_mems(MemoryElement.TYPE_I2C)[0]
+        mem.elements["pitch_trim"] = self._pitch_trim.value()
+        mem.elements["roll_trim"] = self._roll_trim.value()
+        mem.elements["radio_channel"] = self._radio_channel.value()
+        mem.elements["radio_speed"] = self._radio_speed.currentIndex()
+        if "radio_address" in mem.elements:
+            mem.elements["radio_address"] = self._radio_address.value()
+        mem.write_data(self._write_done)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..ad60a1c0724d08baa3898ba4db38cf7d0467ec8c
GIT binary patch
literal 4257
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
Lci<m*d;Wg_Nz<k{

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.ui
new file mode 100755
index 00000000..3dfa261e
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/cf2config.ui
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>431</width>
+    <height>258</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Crazyflie 2.0 config</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_3">
+   <item row="0" column="0">
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="horizontalSpacing">
+        <number>6</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QGroupBox" name="configBlockGroup">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="title">
+          <string>Restart needed for changes to take effect</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_2">
+          <item row="6" column="0">
+           <widget class="QLabel" name="label_9">
+            <property name="text">
+             <string>Radio bandwith:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="5" column="0">
+           <widget class="QLabel" name="label_8">
+            <property name="text">
+             <string>Radio channel:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="5" column="1">
+           <widget class="QSpinBox" name="_radio_channel">
+            <property name="prefix">
+             <string>Ch. </string>
+            </property>
+            <property name="maximum">
+             <number>127</number>
+            </property>
+           </widget>
+          </item>
+          <item row="11" column="1">
+           <spacer name="verticalSpacer">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>40</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item row="2" column="1">
+           <widget class="QDoubleSpinBox" name="_roll_trim">
+            <property name="decimals">
+             <number>1</number>
+            </property>
+            <property name="minimum">
+             <double>-10.000000000000000</double>
+            </property>
+            <property name="maximum">
+             <double>10.000000000000000</double>
+            </property>
+           </widget>
+          </item>
+          <item row="8" column="1">
+           <layout class="QGridLayout" name="gridLayout_4">
+            <item row="0" column="1">
+             <widget class="QPushButton" name="_exit_btn">
+              <property name="text">
+               <string>Exit</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QPushButton" name="_write_data_btn">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Write</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item row="1" column="1">
+           <widget class="QDoubleSpinBox" name="_pitch_trim">
+            <property name="decimals">
+             <number>1</number>
+            </property>
+            <property name="minimum">
+             <double>-10.000000000000000</double>
+            </property>
+            <property name="maximum">
+             <double>10.000000000000000</double>
+            </property>
+           </widget>
+          </item>
+          <item row="6" column="1">
+           <widget class="QComboBox" name="_radio_speed">
+            <property name="editable">
+             <bool>false</bool>
+            </property>
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <item>
+             <property name="text">
+              <string>250 Kbit/s</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>1 MBit/s</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string> 2 MBit/s</string>
+             </property>
+            </item>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_4">
+            <property name="text">
+             <string>Pitch trim:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_6">
+            <property name="text">
+             <string>Roll trim:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="7" column="0">
+           <widget class="QLabel" name="label_10">
+            <property name="text">
+             <string>Radio Address:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="7" column="1">
+           <widget class="HexSpinBox" name="_radio_address">
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>HexSpinBox</class>
+   <extends>QSpinBox</extends>
+   <header>cfclient.ui.widgets.hexspinbox</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.py
new file mode 100755
index 00000000..dff76f7b
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Dialogue that lists available Crazyflies, lets user choose which to connect to.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['ConnectionDialogue']
+
+import sys
+
+from PyQt4 import QtGui, uic
+from PyQt4.QtCore import pyqtSignal, pyqtSlot, QThread
+
+import cflib.crtp
+
+(connect_widget_class,
+connect_widget_base_class) = (uic.loadUiType(sys.path[0] +
+                                 '/cfclient/ui/dialogs/connectiondialogue.ui'))
+
+
+class ConnectDialogue(QtGui.QWidget, connect_widget_class):
+
+    # Outgoing signal for connecting a Crazyflie
+    requestConnectionSignal = pyqtSignal(str)
+
+    def __init__(self, *args):
+        super(ConnectDialogue, self).__init__(*args)
+        self.setupUi(self)
+
+        self.scanner = ScannerThread()
+        self.scanner.start()
+
+        # Connect signals to slots
+        self.connectButton.clicked.connect(self.openConnection)
+        self.scanButton.clicked.connect(self.rescan)
+        self.cancelButton.clicked.connect(self.cancel)
+        self.interfaceList.itemDoubleClicked.connect(self.interfaceSelected)
+        self.scanner.interfaceFoundSignal.connect(self.foundInterfaces)
+        self.box = None
+        self.address.setValue(0xE7E7E7E7E7)
+
+        self.available_interfaces = []
+
+    def rescan(self):
+        """Disable all buttons and scan signals from Crazyflies."""
+        self.interfaceList.clear()
+        self.interfaceList.addItem("Scanning...")
+        self.scanButton.setEnabled(False)
+        self.cancelButton.setEnabled(False)
+        self.connectButton.setEnabled(False)
+        self.scanner.scanSignal.emit(self.address.value())
+
+    def foundInterfaces(self, interfaces):
+        """
+        Add found interfaces to list and enable buttons in UI.
+        """
+        self.interfaceList.clear()
+        self.available_interfaces = interfaces
+        for i in interfaces:
+            if (len(i[1]) > 0):
+                self.interfaceList.addItem("%s - %s" % (i[0], i[1]))
+            else:
+                self.interfaceList.addItem(i[0])
+        if len(interfaces) > 0:
+            self.interfaceList.setCurrentRow(0)
+            self.connectButton.setEnabled(True)
+        self.cancelButton.setEnabled(True)
+        self.scanButton.setEnabled(True)
+
+    def interfaceSelected(self, listItem):
+        self.requestConnectionSignal.emit(
+                self.available_interfaces[self.interfaceList.currentRow()][0])
+        self.close()
+
+    def openConnection(self):
+        self.interfaceSelected(self.interfaceList.currentItem())
+
+    def cancel(self):
+        self.close()
+
+    def showEvent(self, ev):
+        self.rescan()
+
+
+class ScannerThread(QThread):
+
+    scanSignal = pyqtSignal(object)
+    interfaceFoundSignal = pyqtSignal(object)
+
+    def __init__(self):
+        QThread.__init__(self)
+        self.moveToThread(self)
+        self.scanSignal.connect(self.scan)
+
+    def scan(self, address):
+        self.interfaceFoundSignal.emit(cflib.crtp.scan_interfaces(address))
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..cbacbabb4c9977ebb812ac2b985d07e1c9071eaf
GIT binary patch
literal 4769
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.ui
new file mode 100755
index 00000000..3e1f25aa
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/connectiondialogue.ui
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>551</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Connect Crazyflie</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="font">
+        <font>
+         <pointsize>16</pointsize>
+        </font>
+       </property>
+       <property name="text">
+        <string>Connect to Crazyflie</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignHCenter|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QListWidget" name="interfaceList"/>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <item>
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Address:</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="HexSpinBox" name="address">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="1" column="0">
+        <widget class="QPushButton" name="scanButton">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="text">
+          <string>Scan</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <widget class="QPushButton" name="connectButton">
+         <property name="text">
+          <string>Connect</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QPushButton" name="cancelButton">
+         <property name="text">
+          <string>Cancel</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>HexSpinBox</class>
+   <extends>QSpinBox</extends>
+   <header>cfclient.ui.widgets.hexspinbox</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.py
new file mode 100755
index 00000000..ec8ed5ac
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.py
@@ -0,0 +1,423 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Dialogue used to select and configure an inputdevice. This includes mapping buttons and
+axis to match controls for the Crazyflie.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['InputConfigDialogue']
+
+import sys
+import json
+import logging
+
+logger = logging.getLogger(__name__)
+
+from cfclient.utils.config_manager import ConfigManager
+from cflib.crtp.exceptions import CommunicationException
+
+from PyQt4 import Qt, QtCore, QtGui, uic
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from PyQt4.Qt import *
+
+from cfclient.utils.input import JoystickReader
+
+(inputconfig_widget_class,
+connect_widget_base_class) = (uic.loadUiType(sys.path[0] +
+                             '/cfclient/ui/dialogs/inputconfigdialogue.ui'))
+
+
+class InputConfigDialogue(QtGui.QWidget, inputconfig_widget_class):
+
+    def __init__(self, joystickReader, *args):
+        super(InputConfigDialogue, self).__init__(*args)
+        self.setupUi(self)
+        self._input = joystickReader
+
+        self._input_device_reader = DeviceReader(self._input)
+        self._input_device_reader.start()
+
+        self._input_device_reader.raw_axis_data_signal.connect(self._detect_axis)
+        self._input_device_reader.raw_button_data_signal.connect(self._detect_button)
+        self._input_device_reader.mapped_values_signal.connect(self._update_mapped_values)
+
+        self.cancelButton.clicked.connect(self.close)
+        self.saveButton.clicked.connect(self._save_config)
+        
+        self.detectPitch.clicked.connect(lambda : self._axis_detect("pitch", "Pitch axis",
+                                                 "Center the pitch axis then do max %s pitch", ["forward", "backward"]))
+        self.detectRoll.clicked.connect(lambda : self._axis_detect("roll", "Roll axis",
+                                                 "Center the roll axis and then do max %s roll", ["right", "left"]))
+        self.detectYaw.clicked.connect(lambda : self._axis_detect("yaw", "Yaw axis",
+                                                "Center the yaw axis and then do max %s yaw", ["right", "left"]))
+        self.detectThrust.clicked.connect(lambda : self._axis_detect("thrust", "Thrust axis",
+                                                   "Center the thrust axis, and then do max thrust (also used to adjust target altitude in altitude hold mode)"))
+        self.detectPitchPos.clicked.connect(lambda : self._button_detect("pitchPos", "Pitch Cal Positive",
+                                                  "Press the button for Pitch postive calibration"))
+        self.detectPitchNeg.clicked.connect(lambda : self._button_detect("pitchNeg", "Pitch Cal Negative",
+                                                     "Press the button for Pitch negative calibration"))
+        self.detectRollPos.clicked.connect(lambda : self._button_detect("rollPos", "Roll Cal Positive",
+                                                    "Press the button for Roll positive calibration"))
+        self.detectRollNeg.clicked.connect(lambda : self._button_detect("rollNeg", "Roll Cal Negative",
+                                                    "Press the button for Roll negative calibration"))
+        self.detectKillswitch.clicked.connect(lambda : self._button_detect("killswitch", "Killswitch",
+                                                       "Press the button for the killswitch (will disable motors)"))
+        self.detectAlt1.clicked.connect(lambda : self._button_detect("alt1", "Alternative function 1",
+                                                       "The alternative function 1 that will do a callback"))
+        self.detectAlt2.clicked.connect(lambda : self._button_detect("alt2", "Alternative function 2",
+                                                       "The alternative function 2 that will do a callback"))
+        self.detectExitapp.clicked.connect(lambda : self._button_detect("exitapp", "Exit application",
+                                                    "Press the button for exiting the application"))
+        self.detectAltHold.clicked.connect(lambda : self._button_detect("althold", "Altitude hold",
+                                                    "Press the button for altitude hold mode activation (releasing returns to manual mode)"))        
+        self.detectMuxswitch.clicked.connect(lambda: self._button_detect("muxswitch", "Mux Switch",
+                                                     "Press the button for mux switching"))
+
+
+        self.configButton.clicked.connect(self._start_configuration)
+        self.loadButton.clicked.connect(self._load_config_from_file)
+        self.deleteButton.clicked.connect(self._delete_configuration)
+
+        self._popup = None
+        self._combined_button = None
+        self._detection_buttons = [self.detectPitch, self.detectRoll,
+                              self.detectYaw, self.detectThrust,
+                              self.detectPitchPos, self.detectPitchNeg,
+                              self.detectRollPos, self.detectRollNeg,
+                              self.detectKillswitch, self.detectExitapp,
+                              self.detectAltHold, self.detectAlt1,
+                              self.detectAlt2, self.detectMuxswitch]
+
+        self._button_to_detect = ""
+        self._axis_to_detect = ""
+        self.combinedDetection = 0
+        self._prev_combined_id = None
+
+        self._maxed_axis = []
+        self._mined_axis = []
+
+        self._buttonindicators = {}
+        self._axisindicators = {}
+        self._reset_mapping()
+
+        for d in self._input.available_devices():
+            if d.supports_mapping:
+                self.inputDeviceSelector.addItem(d.name, d.id)
+
+        if len(self._input.available_devices()) > 0:
+            self.configButton.setEnabled(True)
+
+        self._map = {}
+        self._saved_open_device = None
+
+    @staticmethod
+    def _scale(max_value, value):
+        return (value/max_value) * 100
+
+    def _reset_mapping(self):
+        self._buttonindicators= {
+            "pitchPos": self.pitchPos,
+            "pitchNeg": self.pitchNeg,
+            "rollPos": self.rollPos,
+            "rollNeg": self.rollNeg,
+            "killswitch": self.killswitch,
+            "alt1": self.alt1,
+            "alt2": self.alt2,
+            "exitapp": self.exitapp,
+            "althold": self.althold,
+            "muxswitch": self.muxswitch,
+            }
+
+        self._axisindicators = {
+            "pitch": self.pitchAxisValue,
+            "roll": self.rollAxisValue,
+            "yaw": self.yawAxisValue,
+            "thrust": self.thrustAxisValue,
+            }
+
+    def _cancel_config_popup(self, button):
+        self._axis_to_detect = ""
+        self._button_to_detect = ""
+
+    def _show_config_popup(self, caption, message, directions=[]):
+        self._maxed_axis = []
+        self._mined_axis = []
+        self._popup = QMessageBox()
+        self._popup.directions = directions
+        self._combined_button = QtGui.QPushButton('Combined Axis Detection')
+        self.cancelButton = QtGui.QPushButton('Cancel')
+        self._popup.addButton(self.cancelButton, QMessageBox.DestructiveRole)
+        self._popup.setWindowTitle(caption)
+        self._popup.setWindowFlags(Qt.Dialog|Qt.MSWindowsFixedSizeDialogHint)
+        if len(directions) > 1:
+            self._popup.originalMessage = message
+            message = self._popup.originalMessage % directions[0]
+            self._combined_button.setCheckable(True)
+            self._combined_button.blockSignals(True)
+            self._popup.addButton(self._combined_button, QMessageBox.ActionRole)
+        self._popup.setText(message)
+        self._popup.show()
+
+    def _start_configuration(self):
+        self._input.enableRawReading(str(self.inputDeviceSelector.currentText()))
+        self._input_device_reader.start_reading()
+        self._populate_config_dropdown()
+        self.profileCombo.setEnabled(True)
+        for b in self._detection_buttons:
+            b.setEnabled(True)
+
+    def _detect_axis(self, data):
+        if (len(self._axis_to_detect) > 0):
+            if self._combined_button and self._combined_button.isChecked() and self.combinedDetection == 0:
+                self._combined_button.setDisabled(True)
+                self.combinedDetection = 1
+            for a in data:
+                # Axis must go low and high before it's accepted as selected
+                # otherwise maxed out axis (like gyro/acc) in some controllers
+                # will always be selected. Not enforcing negative values makes it
+                # possible to detect split axis (like bumpers on PS3 controller)
+                if a not in self._maxed_axis and abs(data[a]) > 0.8:
+                    self._maxed_axis.append(a)
+                if a not in self._mined_axis and abs(data[a]) < 0.1:
+                    self._mined_axis.append(a)
+                if a in self._maxed_axis and a in self._mined_axis and len(self._axis_to_detect) > 0:
+                    if self.combinedDetection == 0:
+                        if data[a] >= 0:
+                            self._map_axis(self._axis_to_detect, a, 1.0)
+                        else:
+                            self._map_axis(self._axis_to_detect, a, -1.0)
+                        self._axis_to_detect = ""
+                        self._check_and_enable_saving()
+                        if self._popup != None:
+                            self.cancelButton.click()
+                    elif self.combinedDetection == 2: #finished detection
+                        if self._prev_combined_id != a: # not the same axis again ...
+                            self._map_axis(self._axis_to_detect, a, -1.0)
+                            self._axis_to_detect = ""
+                            self._check_and_enable_saving()
+                            if (self._popup != None):
+                                self.cancelButton.click()
+                            self.combinedDetection = 0
+                    elif self.combinedDetection == 1:
+                        self._map_axis(self._axis_to_detect, a, 1.0)
+                        self._prev_combined_id = a
+                        self.combinedDetection = 2
+                        message = self._popup.originalMessage % self._popup.directions[1]
+                        self._popup.setText(message)
+
+    def _update_mapped_values(self, mapped_data):
+        for v in mapped_data.get_all_indicators():
+            if v in self._buttonindicators:
+                if mapped_data.get(v):
+                    self._buttonindicators[v].setChecked(True)
+                else:
+                    self._buttonindicators[v].setChecked(False)
+            if v in self._axisindicators:
+                # The sliders used are set to 0-100 and the values from the
+                # input-layer is scaled according to the max settings in
+                # the input-layer. So scale the value and place 0 in the middle.
+                scaled_value = mapped_data.get(v)
+                if v == "thrust":
+                    scaled_value = InputConfigDialogue._scale(
+                        self._input.max_thrust, scaled_value
+                    )
+                if v == "roll" or v == "pitch":
+                    scaled_value = InputConfigDialogue._scale(
+                        self._input.max_rp_angle, scaled_value
+                    )
+                if v == "yaw":
+                    scaled_value = InputConfigDialogue._scale(
+                        self._input.max_yaw_rate, scaled_value
+                    )
+                self._axisindicators[v].setValue(scaled_value)
+
+    def _map_axis(self, function, key_id, scale):
+        self._map["Input.AXIS-{}".format(key_id)] = {}
+        self._map["Input.AXIS-{}".format(key_id)]["id"] = key_id
+        self._map["Input.AXIS-{}".format(key_id)]["key"] = function
+        self._map["Input.AXIS-{}".format(key_id)]["scale"] = scale
+        self._map["Input.AXIS-{}".format(key_id)]["offset"] = 0.0
+        self._map["Input.AXIS-{}".format(key_id)]["type"] = "Input.AXIS"
+        self._input.set_raw_input_map(self._map)
+
+    def _map_button(self, function, key_id):
+        # Duplicate buttons is not allowed, remove if there's already one mapped
+        prev_button = None
+        for m in self._map:
+            if "key" in self._map[m] and self._map[m]["key"] == function:
+                prev_button = m
+        if prev_button:
+            del self._map[prev_button]
+
+        self._map["Input.BUTTON-{}".format(key_id)] = {}
+        self._map["Input.BUTTON-{}".format(key_id)]["id"] = key_id
+        self._map["Input.BUTTON-{}".format(key_id)]["key"] = function
+        self._map["Input.BUTTON-{}".format(key_id)]["scale"] = 1.0
+        self._map["Input.BUTTON-{}".format(key_id)]["type"] = "Input.BUTTON"
+        self._input.set_raw_input_map(self._map)
+
+    def _detect_button(self, data):
+        if len(self._button_to_detect) > 0:
+            for b in data:
+                if data[b] > 0:
+                    self._map_button(self._button_to_detect, b)
+                    self._button_to_detect = ""
+                    self._check_and_enable_saving()
+                    if self._popup != None:
+                        self._popup.close()
+
+    def _check_and_enable_saving(self):
+        needed_funcs = ["thrust", "yaw", "roll", "pitch"]
+
+        for m in self._map:
+            if self._map[m]["key"] in needed_funcs:
+                needed_funcs.remove(self._map[m]["key"])
+
+        if len(needed_funcs) == 0:
+            self.saveButton.setEnabled(True)
+
+    def _populate_config_dropdown(self):
+        configs = ConfigManager().get_list_of_configs()
+        if len(configs):
+            self.loadButton.setEnabled(True)
+        for c in configs:
+            self.profileCombo.addItem(c)
+
+    def _axis_detect(self, varname, caption, message, directions=[]):
+        self._axis_to_detect = varname
+        self._show_config_popup(caption, message, directions)
+
+    def _button_detect(self, varname, caption, message):
+        self._button_to_detect = varname
+        self._show_config_popup(caption, message)
+
+    def _show_error(self, caption, message):
+        QMessageBox.critical(self, caption, message)
+
+    def _load_config_from_file(self):
+        loaded_map = ConfigManager().get_config(self.profileCombo.currentText())
+        if loaded_map:
+            self._input.set_raw_input_map(loaded_map)
+            self._map = loaded_map
+        else:
+            logger.warning("Could not load configfile [%s]",
+                           self.profileCombo.currentText())
+            self._show_error("Could not load config",
+                           "Could not load config [%s]" %
+                           self.profileCombo.currentText())
+        self._check_and_enable_saving()
+
+    def _delete_configuration(self):
+        logger.warning("deleteConfig not implemented")
+
+    def _save_config(self):
+        configName = str(self.profileCombo.currentText())
+
+        mapping = {'inputconfig': {'inputdevice': {'axis': []}}}
+
+        # Create intermediate structure for the configuration file
+        funcs = {}
+        for m in self._map:
+            key = self._map[m]["key"]
+            if not key in funcs:
+                funcs[key] = []
+            funcs[key].append(self._map[m])
+
+        # Create a mapping for each axis, take care to handle
+        # split axis configurations
+        for a in funcs:
+            func = funcs[a]
+            axis = {}
+            # Check for split axis
+            if len(func) > 1:
+                axis["ids"] = [func[0]["id"], func[1]["id"]]
+                axis["scale"] = func[1]["scale"]
+            else:
+                axis["id"] = func[0]["id"]
+                axis["scale"] = func[0]["scale"]
+            axis["key"] = func[0]["key"]
+            axis["name"] = func[0]["key"] # Name isn't used...
+            axis["type"] =func[0]["type"]
+            mapping["inputconfig"]["inputdevice"]["axis"].append(axis)
+
+        mapping["inputconfig"]['inputdevice']['name'] = configName
+        mapping["inputconfig"]['inputdevice']['updateperiod'] = 10
+
+        config_name = self.profileCombo.currentText()
+        filename = ConfigManager().configs_dir + "/%s.json" % config_name
+        logger.info("Saving config to [%s]", filename)
+        json_data = open(filename, 'w')
+        json_data.write(json.dumps(mapping, indent=2))
+        json_data.close()
+
+        ConfigManager().conf_needs_reload.call(config_name)
+        self.close()
+
+    def showEvent(self, event):
+        """Called when dialog is opened"""
+        #self._saved_open_device = self._input.get_device_name()
+        #self._input.stop_input()
+        self._input.pause_input()
+
+    def closeEvent(self, event):
+        """Called when dialog is closed"""
+        self._input.stop_raw_reading()
+        self._input_device_reader.stop_reading()
+        #self._input.start_input(self._saved_open_device)
+        self._input.resume_input()
+
+class DeviceReader(QThread):
+    """Used for polling data from the Input layer during configuration"""
+    raw_axis_data_signal = pyqtSignal(object)
+    raw_button_data_signal = pyqtSignal(object)
+    mapped_values_signal = pyqtSignal(object)
+
+    def __init__(self, input):
+        QThread.__init__(self)
+
+        self._input = input
+        self._read_timer = QTimer()
+        self._read_timer.setInterval(25)
+
+        self.connect(self._read_timer, SIGNAL("timeout()"), self._read_input)
+
+    def stop_reading(self):
+        """Stop polling data"""
+        self._read_timer.stop()
+
+    def start_reading(self):
+        """Start polling data"""
+        self._read_timer.start()
+
+    def _read_input(self):
+        [rawaxis, rawbuttons, mapped_values] = self._input.read_raw_values()
+        self.raw_axis_data_signal.emit(rawaxis)
+        self.raw_button_data_signal.emit(rawbuttons)
+        self.mapped_values_signal.emit(mapped_values)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..0b7a51788ba0f7690c7604f11af4892f6bb744af
GIT binary patch
literal 19318
zcmd5^TW}o5b?w<*Jaz#PAVGoxUs@}a1-*cXwiPR;ERmpW(w1Oy0Esdzc|6z|00Zoc
zGqVJ!kZ}~riDNsdRPq(O94E2!kyOelf2PV+`AJohO6B27<?)sORDPUF<u}ebr)PI(
zA<!jLRUiRuqq}c+PxtNn>U(>(_&*bs%eBwGRx{a85&vJrCpqL86Bx6K)G@1$2?}Nd
znSu$5CMcPD(X1k4^Ch!d>gI>c>W~SC%+|08N6b6uGAhuJsh3ToVpc20VYgvZA2W?{
zvpR0eBc?uK8i&m4A=8*NtCOZNWmcz5<FHvhY+H<)`Vq6jzQ|4Yaz|~hZ0g5MW5%q`
z7$-bzwvL;yXto|QLB-4lW9B$!@<zd|&YIN|TuV4*woaOG)~sVi<K|ss-ZAF$tEWsb
zVZzg99Sc6xt3AUNFV9U9FKA{emm<I3+USJUP7(&yv{g;QdRR-Velw`nTFv!nqZ5ZH
zRikFRlLq0<s20vwS2m*rHMM#t2$O2VZ?~i7Ms=-|rmbeez7>BLEihcePivbTDUDn8
zq`KaUtLbJ~U5x#=Zmrj&aK4g^VICKww1%p%df{R^jq(*fZBftf&M^83ewOC2nI@e;
zVxh12O@AYdZS{<*8;wpgs`+WuYQD5v3){@hmDsI-<XV~z>)&E44$~oJKi!E^l1nG5
z*@5_wCv5f*vY%<)O46vd?S_63#t~{v@+6Xl+IkIZXr>FDXdw_LlLaBlqRv)z!g&nL
zPW<5<F5;8?2uTRC#QqrW4+s+b3=-uIg4jTZ1=SCc>eyh|Y=DMK=3PKXOfaNM(B!Z(
zWfP1jQz2Qg{g?^L%8ZkCSTkWjK+GJ<NCni%F()+UR7NQv(_m7~j+kIdnQ0RoR_3S)
zjwo}?1k=jQnBb@~$Bnx@cMPklag8N>h`SMdlD`IaQ~WvRt5{sYY!%Gyg87Cq4$50a
z)jGzxEx1=Q*cSpyL<BP4u!_TG3w)S$LSfWwl?AA<orXf~ShsdOUpvvQJ=Ck6G+SUD
zJHp{!?U7vVbg%YkR{QHl4CR<#(<bSe#|B}AJZxqVR?NeW55h`$*h7P`p*(DM5H_5L
zofw3T<Y6ZVVWWB2sX<sd4?8^wtK?y424Q1)*u#Uc@jR?L2%E^mJ~RkBl!rZ%!AKoH
z*V~=5x!Om2wI9yaKBn5hh?*WJ$C0d_5cM2YdQ9nz(&I`WQaY>jgwm5rPbqy^X;tZI
zrDv3W$ZQ;9#(R=X#LQFO%+us1*35S&ut4r(&Cho;&lrdo)|~5RKEmSGM@9I%=T#_~
zt&g#M>CGq2ouau7_3*5TSBh--3uf!%s(DW7FOuDy8!JWpT~9wT&>c*J32m25`~!6b
za?hLed7-Lly3hY)cd{=qvpjbg487D7XOyKB*vTm*e7>|SBo|~>01-B1HVWn)>KwLQ
zlE7_4*l#8kl&>;ZrLIVxKyeYGG_(prZ3LmdXjTKNlHKZ~NmY=PVg<@%$B%<_6!|s3
zw#|YS+T*D4III_hDb(2<%Ep2ym!RX;Rh4)sEo`<L;lf&`ffm|vYYSbH1u7)zsHd%v
zPo8eyN;g~0g?hC1z9m1paC+zKeq$~0KlvFxo#Juaxn~q|1yH8-q>PM<j78v6DCH2y
z78ZY0<$G{Qptd58Ha63gBi6(9R2XuJygtHQ>e>TCmP?iKGCm&^Nkxot%il?e{LlM4
z`_dTTcf}B3?iBjEB=-jdFH4xpUc7MY#D7e_I>>+T6v1|Euq2Zw-Hba)DsyW^S&I~K
zY@TjxD}kPp6__WT3^?c4lUC1S^n)#oTTT6VBZT2tPoorOA<V;mezR2%s*P3<KK_89
z@+ua6kEkR?Nd{eQCCL#KtPorD>s8c5X>>D8=2;zw2~1!ZyVfq2?Q7e%TQDbYhSi!6
zQ4-5iwZe(i@7d<>kzDtI5nW8@gJNkZ+}ML9)L_ZL(!x*2QZsA!9yGZ$G9{8MH66T!
zTDf<`P;pWGl6ojxB*5Pv@u(?iM>O^TV&%0DjuleN09Ja$a#xlTRvutldHvq8LKA5_
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.ui
new file mode 100755
index 00000000..a8032bbd
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/inputconfigdialogue.ui
@@ -0,0 +1,753 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>957</width>
+    <height>512</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Input device configuration</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>40</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>16</pointsize>
+        </font>
+       </property>
+       <property name="text">
+        <string>Configure input device</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignHCenter|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_4">
+       <item row="0" column="0">
+        <widget class="QLabel" name="label_2">
+         <property name="font">
+          <font>
+           <pointsize>14</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>Input device</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QComboBox" name="inputDeviceSelector">
+         <property name="minimumSize">
+          <size>
+           <width>700</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="duplicatesEnabled">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QPushButton" name="configButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>80</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>Configure</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="inputDetectionLayout">
+       <item>
+        <widget class="QGroupBox" name="inputDetectionGroup">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>350</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>500</height>
+          </size>
+         </property>
+         <property name="title">
+          <string>Input response</string>
+         </property>
+         <widget class="QWidget" name="gridLayoutWidget">
+          <property name="geometry">
+           <rect>
+            <x>-1</x>
+            <y>19</y>
+            <width>301</width>
+            <height>141</height>
+           </rect>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_3">
+           <item row="0" column="1">
+            <widget class="QSlider" name="rollAxisValue">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>0</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>16777215</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="minimum">
+              <number>-100</number>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="sliderPosition">
+              <number>5</number>
+             </property>
+             <property name="orientation">
+              <enum>Qt::Horizontal</enum>
+             </property>
+             <property name="invertedAppearance">
+              <bool>false</bool>
+             </property>
+             <property name="invertedControls">
+              <bool>false</bool>
+             </property>
+             <property name="tickPosition">
+              <enum>QSlider::NoTicks</enum>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_3">
+             <property name="font">
+              <font>
+               <pointsize>14</pointsize>
+              </font>
+             </property>
+             <property name="text">
+              <string>Pitch</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_5">
+             <property name="font">
+              <font>
+               <pointsize>14</pointsize>
+              </font>
+             </property>
+             <property name="text">
+              <string>Roll</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QSlider" name="pitchAxisValue">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>0</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>16777215</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="layoutDirection">
+              <enum>Qt::LeftToRight</enum>
+             </property>
+             <property name="minimum">
+              <number>-100</number>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="singleStep">
+              <number>1</number>
+             </property>
+             <property name="sliderPosition">
+              <number>0</number>
+             </property>
+             <property name="tracking">
+              <bool>false</bool>
+             </property>
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="invertedAppearance">
+              <bool>false</bool>
+             </property>
+             <property name="invertedControls">
+              <bool>false</bool>
+             </property>
+             <property name="tickPosition">
+              <enum>QSlider::NoTicks</enum>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="2">
+            <widget class="QPushButton" name="detectRoll">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="2">
+            <widget class="QPushButton" name="detectPitch">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+         <widget class="QWidget" name="gridLayoutWidget_4">
+          <property name="geometry">
+           <rect>
+            <x>310</x>
+            <y>20</y>
+            <width>301</width>
+            <height>141</height>
+           </rect>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_6">
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_7">
+             <property name="font">
+              <font>
+               <pointsize>14</pointsize>
+              </font>
+             </property>
+             <property name="text">
+              <string>Thrust</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QSlider" name="thrustAxisValue">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>0</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>16777215</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="minimum">
+              <number>0</number>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="singleStep">
+              <number>0</number>
+             </property>
+             <property name="sliderPosition">
+              <number>0</number>
+             </property>
+             <property name="tracking">
+              <bool>true</bool>
+             </property>
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="invertedAppearance">
+              <bool>false</bool>
+             </property>
+             <property name="invertedControls">
+              <bool>false</bool>
+             </property>
+             <property name="tickPosition">
+              <enum>QSlider::NoTicks</enum>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QSlider" name="yawAxisValue">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>0</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>16777215</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="minimum">
+              <number>-100</number>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="singleStep">
+              <number>0</number>
+             </property>
+             <property name="sliderPosition">
+              <number>1</number>
+             </property>
+             <property name="tracking">
+              <bool>true</bool>
+             </property>
+             <property name="orientation">
+              <enum>Qt::Horizontal</enum>
+             </property>
+             <property name="invertedAppearance">
+              <bool>false</bool>
+             </property>
+             <property name="invertedControls">
+              <bool>false</bool>
+             </property>
+             <property name="tickPosition">
+              <enum>QSlider::NoTicks</enum>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="2">
+            <widget class="QPushButton" name="detectThrust">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_4">
+             <property name="font">
+              <font>
+               <pointsize>14</pointsize>
+              </font>
+             </property>
+             <property name="text">
+              <string>Yaw</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="2">
+            <widget class="QPushButton" name="detectYaw">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+         <widget class="QWidget" name="gridLayoutWidget_5">
+          <property name="geometry">
+           <rect>
+            <x>10</x>
+            <y>170</y>
+            <width>518</width>
+            <height>151</height>
+           </rect>
+          </property>
+          <layout class="QGridLayout" name="gridLayout">
+           <item row="2" column="1">
+            <widget class="QCheckBox" name="rollNeg">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Roll -</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="2">
+            <widget class="QCheckBox" name="pitchNeg">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Pitch -</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="2">
+            <widget class="QPushButton" name="detectPitchNeg">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="3">
+            <widget class="QCheckBox" name="rollPos">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Roll +</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="4">
+            <widget class="QPushButton" name="detectRollPos">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="2">
+            <widget class="QPushButton" name="detectPitchPos">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QPushButton" name="detectRollNeg">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="2">
+            <widget class="QCheckBox" name="pitchPos">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Pitch +</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+         <widget class="QWidget" name="gridLayoutWidget_2">
+          <property name="geometry">
+           <rect>
+            <x>600</x>
+            <y>158</y>
+            <width>292</width>
+            <height>194</height>
+           </rect>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_5">
+           <item row="5" column="1">
+            <widget class="QPushButton" name="detectAlt2">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0">
+            <widget class="QCheckBox" name="alt1">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Alt 1</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QPushButton" name="detectExitapp">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QPushButton" name="detectKillswitch">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QCheckBox" name="killswitch">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Killswitch</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QCheckBox" name="exitapp">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Exit app</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QCheckBox" name="althold">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Altitude hold</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QPushButton" name="detectAltHold">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="0">
+            <widget class="QCheckBox" name="alt2">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Alt 2</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="1">
+            <widget class="QPushButton" name="detectAlt1">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="0">
+            <widget class="QCheckBox" name="muxswitch">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Mux switch</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="1">
+            <widget class="QPushButton" name="detectMuxswitch">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Detect</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="0" column="1">
+        <widget class="QLabel" name="label_16">
+         <property name="text">
+          <string>Profile name</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <spacer name="horizontalSpacer_9">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="4">
+        <widget class="QPushButton" name="deleteButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Delete</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="6">
+        <widget class="QPushButton" name="cancelButton">
+         <property name="text">
+          <string>Cancel</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QComboBox" name="profileCombo">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>400</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="editable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="7">
+        <widget class="QPushButton" name="saveButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Save</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="5">
+        <widget class="QPushButton" name="loadButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Load</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.py
new file mode 100755
index 00000000..31ac80bd
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+This dialogue is used to configure different log configurations that is used to
+enable logging of data from the Crazyflie. These can then be used in different
+views in the UI.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LogConfigDialogue']
+
+import sys
+import os
+import logging
+
+logger = logging.getLogger(__name__)
+
+from PyQt4 import Qt, QtCore, QtGui, uic
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from PyQt4.Qt import *
+
+from cflib.crazyflie.log import Log, LogVariable, LogConfig
+
+(logconfig_widget_class,
+connect_widget_base_class) = (uic.loadUiType(sys.path[0] +
+                                 '/cfclient/ui/dialogs/logconfigdialogue.ui'))
+
+NAME_FIELD = 0
+ID_FIELD = 1
+PTYPE_FIELD = 2
+CTYPE_FIELD = 3
+
+
+class LogConfigDialogue(QtGui.QWidget, logconfig_widget_class):
+
+    def __init__(self, helper, *args):
+        super(LogConfigDialogue, self).__init__(*args)
+        self.setupUi(self)
+        self.helper = helper
+
+        self.logTree.setHeaderLabels(['Name', 'ID', 'Unpack', 'Storage'])
+        self.varTree.setHeaderLabels(['Name', 'ID', 'Unpack', 'Storage'])
+
+        self.addButton.clicked.connect(lambda: self.moveNode(self.logTree,
+                                                             self.varTree))
+        self.removeButton.clicked.connect(lambda: self.moveNode(self.varTree,
+                                                                self.logTree))
+        self.cancelButton.clicked.connect(self.close)
+        self.loadButton.clicked.connect(self.loadConfig)
+        self.saveButton.clicked.connect(self.saveConfig)
+
+        self.loggingPeriod.textChanged.connect(self.periodChanged)
+
+        self.packetSize.setMaximum(30)
+        self.currentSize = 0
+        self.packetSize.setValue(0)
+        self.period = 0
+
+    def decodeSize(self, s):
+        size = 0
+        if ("16" in s):
+            size = 2
+        if ("float" in s):
+            size = 4
+        if ("8" in s):
+            size = 1
+        if ("FP16" in s):
+            size = 2
+        if ("32" in s):
+            size = 4
+        return size
+
+    def sortTrees(self):
+        self.varTree.invisibleRootItem().sortChildren(NAME_FIELD,
+                                                      Qt.AscendingOrder)
+        for node in self.getNodeChildren(self.varTree.invisibleRootItem()):
+            node.sortChildren(NAME_FIELD, Qt.AscendingOrder)
+        self.logTree.invisibleRootItem().sortChildren(NAME_FIELD,
+                                                      Qt.AscendingOrder)
+        for node in self.getNodeChildren(self.logTree.invisibleRootItem()):
+            node.sortChildren(NAME_FIELD, Qt.AscendingOrder)
+
+    def getNodeChildren(self, treeNode):
+        children = []
+        for i in range(treeNode.childCount()):
+            children.append(treeNode.child(i))
+        return children
+
+    def updatePacketSizeBar(self):
+        self.currentSize = 0
+        for node in self.getNodeChildren(self.varTree.invisibleRootItem()):
+            for leaf in self.getNodeChildren(node):
+                self.currentSize = (self.currentSize +
+                                    self.decodeSize(leaf.text(CTYPE_FIELD)))
+        self.packetSize.setValue(self.currentSize)
+
+    def addNewVar(self, logTreeItem, target):
+        parentName = logTreeItem.parent().text(NAME_FIELD)
+        varParent = target.findItems(parentName, Qt.MatchExactly, NAME_FIELD)
+
+        item = logTreeItem.clone()
+
+        if (len(varParent) == 0):
+            newParent = QtGui.QTreeWidgetItem()
+            newParent.setData(0, Qt.DisplayRole, parentName)
+            newParent.addChild(item)
+            target.addTopLevelItem(newParent)
+            target.expandItem(newParent)
+        else:
+            parent = varParent[0]
+            parent.addChild(item)
+
+    def moveNodeItem(self, source, target, item):
+        if (item.parent() == None):
+            children = self.getNodeChildren(item)
+            for c in children:
+                self.addNewVar(c, target)
+            source.takeTopLevelItem(source.indexOfTopLevelItem(item))
+        elif (item.parent().childCount() > 1):
+            self.addNewVar(item, target)
+            item.parent().removeChild(item)
+        else:
+            self.addNewVar(item, target)
+            # item.parent().removeChild(item)
+            source.takeTopLevelItem(source.indexOfTopLevelItem(item.parent()))
+        self.updatePacketSizeBar()
+        self.sortTrees()
+        self.checkAndEnableSaveButton()
+
+    def checkAndEnableSaveButton(self):
+        if (self.currentSize > 0 and self.period > 0):
+            self.saveButton.setEnabled(True)
+        else:
+            self.saveButton.setEnabled(False)
+
+    def moveNode(self, source, target):
+        self.moveNodeItem(source, target, source.currentItem())
+
+    def moveNodeByName(self, source, target, parentName, itemName):
+        parents = source.findItems(parentName, Qt.MatchExactly, NAME_FIELD)
+        node = None
+        if (len(parents) > 0):
+            parent = parents[0]
+            for n in range(parent.childCount()):
+                if (parent.child(n).text(NAME_FIELD) == itemName):
+                    node = parent.child(n)
+                    break
+        if (node != None):
+            self.moveNodeItem(source, target, node)
+            return True
+        return False
+
+    def showEvent(self, event):
+        self.updateToc()
+        self.populateDropDown()
+        toc = self.helper.cf.log.toc
+        if (len(toc.toc.keys()) > 0):
+            self.configNameCombo.setEnabled(True)
+        else:
+            self.configNameCombo.setEnabled(False)
+
+    def resetTrees(self):
+        self.varTree.clear()
+        self.updateToc()
+
+    def periodChanged(self, value):
+        try:
+            self.period = int(value)
+            self.checkAndEnableSaveButton()
+        except:
+            self.period = 0
+
+    def showErrorPopup(self, caption, message):
+        self.box = QMessageBox()
+        self.box.setWindowTitle(caption)
+        self.box.setText(message)
+        # self.box.setButtonText(1, "Ok")
+        self.box.setWindowFlags(Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)
+        self.box.show()
+
+    def updateToc(self):
+        self.logTree.clear()
+
+        toc = self.helper.cf.log.toc
+
+        for group in toc.toc.keys():
+            groupItem = QtGui.QTreeWidgetItem()
+            groupItem.setData(NAME_FIELD, Qt.DisplayRole, group)
+            for param in toc.toc[group].keys():
+                item = QtGui.QTreeWidgetItem()
+                item.setData(NAME_FIELD, Qt.DisplayRole, param)
+                item.setData(ID_FIELD, Qt.DisplayRole,
+                             toc.toc[group][param].ident)
+                item.setData(PTYPE_FIELD, Qt.DisplayRole,
+                             toc.toc[group][param].pytype)
+                item.setData(CTYPE_FIELD, Qt.DisplayRole,
+                             toc.toc[group][param].ctype)
+                groupItem.addChild(item)
+
+            self.logTree.addTopLevelItem(groupItem)
+            self.logTree.expandItem(groupItem)
+        self.sortTrees()
+
+    def populateDropDown(self):
+        self.configNameCombo.clear()
+        toc = self.helper.logConfigReader.getLogConfigs()
+        for d in toc:
+            self.configNameCombo.addItem(d.name)
+        if (len(toc) > 0):
+            self.loadButton.setEnabled(True)
+
+    def loadConfig(self):
+        cText = self.configNameCombo.currentText()
+        config = None
+        for d in self.helper.logConfigReader.getLogConfigs():
+            if (d.name == cText):
+                config = d
+        if (config == None):
+            logger.warning("Could not load config")
+        else:
+            self.resetTrees()
+            self.loggingPeriod.setText("%d" % config.period_in_ms)
+            self.period = config.period_in_ms
+            for v in config.variables:
+                if (v.is_toc_variable()):
+                    parts = v.name.split(".")
+                    varParent = parts[0]
+                    varName = parts[1]
+                    if self.moveNodeByName(self.logTree,
+                                            self.varTree,
+                                            varParent,
+                                            varName) == False:
+                        logger.warning("Could not find node %s.%s!!", varParent, varName)
+                else:
+                    logger.warning("Error: Mem vars not supported!")
+
+    def saveConfig(self):
+        updatedConfig = self.createConfigFromSelection()
+        try:
+            self.helper.logConfigReader.saveLogConfigFile(updatedConfig)
+            self.close()
+        except Exception as e:
+            self.showErrorPopup("Error when saving file", "Error: %s" % e)
+        self.helper.cf.log.add_config(updatedConfig)
+
+    def createConfigFromSelection(self):
+        logconfig = LogConfig(str(self.configNameCombo.currentText()),
+                              self.period)
+        for node in self.getNodeChildren(self.varTree.invisibleRootItem()):
+            parentName = node.text(NAME_FIELD)
+            for leaf in self.getNodeChildren(node):
+                varName = leaf.text(NAME_FIELD)
+                varType = str(leaf.text(CTYPE_FIELD))
+                completeName = "%s.%s" % (parentName, varName)
+                logconfig.add_variable(completeName, varType)
+        return logconfig
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..b40dc18a485fa00619560986b8b0610fc644e4df
GIT binary patch
literal 11145
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.ui
new file mode 100755
index 00000000..d0cc7228
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/dialogs/logconfigdialogue.ui
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>925</width>
+    <height>447</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Log configuration</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>40</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>16</pointsize>
+        </font>
+       </property>
+       <property name="text">
+        <string>Create and modify log configurations</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignHCenter|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="1" column="0">
+        <widget class="QTreeWidget" name="logTree">
+         <property name="columnCount">
+          <number>4</number>
+         </property>
+         <attribute name="headerDefaultSectionSize">
+          <number>100</number>
+         </attribute>
+         <column>
+          <property name="text">
+           <string notr="true">1</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string notr="true">2</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string notr="true">3</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string notr="true">4</string>
+          </property>
+         </column>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <layout class="QVBoxLayout" name="verticalLayout_2">
+         <item>
+          <widget class="QPushButton" name="addButton">
+           <property name="text">
+            <string>--&gt;</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="removeButton">
+           <property name="text">
+            <string>&lt;--</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="2">
+        <widget class="QTreeWidget" name="varTree">
+         <property name="columnCount">
+          <number>4</number>
+         </property>
+         <attribute name="headerDefaultSectionSize">
+          <number>100</number>
+         </attribute>
+         <column>
+          <property name="text">
+           <string notr="true">1</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string notr="true">2</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string notr="true">3</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string notr="true">4</string>
+          </property>
+         </column>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <item>
+        <spacer name="horizontalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Fixed</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item alignment="Qt::AlignTop">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Logging period</string>
+         </property>
+        </widget>
+       </item>
+       <item alignment="Qt::AlignTop">
+        <widget class="QLineEdit" name="loggingPeriod">
+         <property name="maximumSize">
+          <size>
+           <width>100</width>
+           <height>16777215</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item alignment="Qt::AlignTop">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>ms</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="QProgressBar" name="packetSize">
+       <property name="value">
+        <number>24</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="0" column="1">
+        <widget class="QLabel" name="label_16">
+         <property name="text">
+          <string>Config name</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <spacer name="horizontalSpacer_9">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="3">
+        <widget class="QPushButton" name="deleteButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Delete</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="5">
+        <widget class="QPushButton" name="cancelButton">
+         <property name="text">
+          <string>Cancel</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QComboBox" name="configNameCombo">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>400</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="editable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="6">
+        <widget class="QPushButton" name="saveButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Save</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="4">
+        <widget class="QPushButton" name="loadButton">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Load</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.py
new file mode 100755
index 00000000..0c3b59b2
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.py
@@ -0,0 +1,667 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The main file for the Crazyflie control application.
+"""
+__author__ = 'Bitcraze AB'
+__all__ = ['MainUI']
+
+import sys
+import logging
+
+
+logger = logging.getLogger(__name__)
+
+import PyQt4
+from PyQt4 import QtGui, uic
+from PyQt4.QtCore import pyqtSignal, Qt, pyqtSlot, QDir, QUrl
+from PyQt4.QtGui import QLabel, QActionGroup, QMessageBox, QAction, QDesktopServices, QMenu
+
+from dialogs.connectiondialogue import ConnectDialogue
+from dialogs.inputconfigdialogue import InputConfigDialogue
+from dialogs.cf2config import Cf2ConfigDialog
+from dialogs.cf1config import Cf1ConfigDialog
+from cflib.crazyflie import Crazyflie
+from dialogs.logconfigdialogue import LogConfigDialogue
+
+from cfclient.utils.input import JoystickReader
+from cfclient.utils.zmq_param import ZMQParamAccess
+from cfclient.utils.zmq_led_driver import ZMQLEDDriver
+from cfclient.utils.config import Config
+from cfclient.utils.logconfigreader import LogConfigReader
+from cfclient.utils.config_manager import ConfigManager
+
+import cfclient.ui.toolboxes
+import cfclient.ui.tabs
+import cflib.crtp
+
+from cflib.crazyflie.log import Log, LogVariable, LogConfig
+
+from cfclient.ui.dialogs.bootloader import BootloaderDialog
+from cfclient.ui.dialogs.about import AboutDialog
+
+from cflib.crazyflie.mem import MemoryElement
+
+(main_window_class,
+main_windows_base_class) = (uic.loadUiType(sys.path[0] +
+                                           '/cfclient/ui/main.ui'))
+
+
+class MyDockWidget(QtGui.QDockWidget):
+    closed = pyqtSignal()
+
+    def closeEvent(self, event):
+        super(MyDockWidget, self).closeEvent(event)
+        self.closed.emit()
+
+
+class UIState:
+    DISCONNECTED = 0
+    CONNECTING = 1
+    CONNECTED = 2
+
+
+class MainUI(QtGui.QMainWindow, main_window_class):
+
+    connectionLostSignal = pyqtSignal(str, str)
+    connectionInitiatedSignal = pyqtSignal(str)
+    batteryUpdatedSignal = pyqtSignal(int, object, object)
+    connectionDoneSignal = pyqtSignal(str)
+    connectionFailedSignal = pyqtSignal(str, str)
+    disconnectedSignal = pyqtSignal(str)
+    linkQualitySignal = pyqtSignal(int)
+
+    _input_device_error_signal = pyqtSignal(str)
+    _input_discovery_signal = pyqtSignal(object)
+    _log_error_signal = pyqtSignal(object, str)
+
+    def __init__(self, *args):
+        super(MainUI, self).__init__(*args)
+        self.setupUi(self)
+        
+        ######################################################
+        ### By lxrocks
+        ### 'Skinny Progress Bar' tweak for Yosemite
+        ### Tweak progress bar - artistic I am not - so pick your own colors !!!
+        ### Only apply to Yosemite
+        ######################################################
+        import platform
+        if platform.system() == 'Darwin':
+        
+            (Version,junk,machine) =  platform.mac_ver()
+            logger.info("This is a MAC - checking if we can apply Progress Bar Stylesheet for Yosemite Skinny Bars ")
+            yosemite = (10,10,0)
+            tVersion = tuple(map(int, (Version.split("."))))
+            
+            if tVersion >= yosemite:
+                logger.info( "Found Yosemite:")
+        
+                tcss = """
+                    QProgressBar {
+                    border: 2px solid grey;
+                    border-radius: 5px;
+                    text-align: center;
+                }
+                QProgressBar::chunk {
+                     background-color: #05B8CC;
+                 }
+                 """
+                self.setStyleSheet(tcss)
+                
+            else:
+                logger.info( "Pre-Yosemite")
+        
+        ######################################################
+        
+        self.cf = Crazyflie(ro_cache=sys.path[0] + "/cflib/cache",
+                            rw_cache=sys.path[1] + "/cache")
+
+        cflib.crtp.init_drivers(enable_debug_driver=Config()
+                                                .get("enable_debug_driver"))
+
+        zmq_params = ZMQParamAccess(self.cf)
+        zmq_params.start()
+
+        zmq_leds = ZMQLEDDriver(self.cf)
+        zmq_leds.start()
+
+        # Create the connection dialogue
+        self.connectDialogue = ConnectDialogue()
+
+        # Create and start the Input Reader
+        self._statusbar_label = QLabel("No input-device found, insert one to"
+                                       " fly.")
+        self.statusBar().addWidget(self._statusbar_label)
+
+        self.joystickReader = JoystickReader()
+        self._active_device = ""
+        #self.configGroup = QActionGroup(self._menu_mappings, exclusive=True)
+
+        self._mux_group = QActionGroup(self._menu_inputdevice, exclusive=True)
+
+        # TODO: Need to reload configs
+        #ConfigManager().conf_needs_reload.add_callback(self._reload_configs)
+
+        # Connections for the Connect Dialogue
+        self.connectDialogue.requestConnectionSignal.connect(self.cf.open_link)
+
+        self.cf.connection_failed.add_callback(self.connectionFailedSignal.emit)
+        self.connectionFailedSignal.connect(self._connection_failed)
+        
+        
+        self._input_device_error_signal.connect(self._display_input_device_error)
+        self.joystickReader.device_error.add_callback(
+                        self._input_device_error_signal.emit)
+        self._input_discovery_signal.connect(self.device_discovery)
+        self.joystickReader.device_discovery.add_callback(
+                        self._input_discovery_signal.emit)
+
+        # Connect UI signals
+        self.menuItemConnect.triggered.connect(self._connect)
+        self.logConfigAction.triggered.connect(self._show_connect_dialog)
+        self.connectButton.clicked.connect(self._connect)
+        self.quickConnectButton.clicked.connect(self._quick_connect)
+        self.menuItemQuickConnect.triggered.connect(self._quick_connect)
+        self.menuItemConfInputDevice.triggered.connect(self._show_input_device_config_dialog)
+        self.menuItemExit.triggered.connect(self.closeAppRequest)
+        self.batteryUpdatedSignal.connect(self._update_vbatt)
+        self._menuitem_rescandevices.triggered.connect(self._rescan_devices)
+        self._menuItem_openconfigfolder.triggered.connect(self._open_config_folder)
+
+        self._auto_reconnect_enabled = Config().get("auto_reconnect")
+        self.autoReconnectCheckBox.toggled.connect(
+                                              self._auto_reconnect_changed)
+        self.autoReconnectCheckBox.setChecked(Config().get("auto_reconnect"))
+        
+        self.joystickReader.input_updated.add_callback(
+                                         self.cf.commander.send_setpoint)
+
+        # Connection callbacks and signal wrappers for UI protection
+        self.cf.connected.add_callback(self.connectionDoneSignal.emit)
+        self.connectionDoneSignal.connect(self._connected)
+        self.cf.disconnected.add_callback(self.disconnectedSignal.emit)
+        self.disconnectedSignal.connect(
+                        lambda linkURI: self._update_ui_state(UIState.DISCONNECTED,
+                                                        linkURI))
+        self.cf.connection_lost.add_callback(self.connectionLostSignal.emit)
+        self.connectionLostSignal.connect(self._connection_lost)
+        self.cf.connection_requested.add_callback(
+                                         self.connectionInitiatedSignal.emit)
+        self.connectionInitiatedSignal.connect(
+                           lambda linkURI: self._update_ui_state(UIState.CONNECTING,
+                                                           linkURI))
+        self._log_error_signal.connect(self._logging_error)
+
+        # Connect link quality feedback
+        self.cf.link_quality_updated.add_callback(self.linkQualitySignal.emit)
+        self.linkQualitySignal.connect(
+                   lambda percentage: self.linkQualityBar.setValue(percentage))
+
+        # Set UI state in disconnected buy default
+        self._update_ui_state(UIState.DISCONNECTED)
+
+        # Parse the log configuration files
+        self.logConfigReader = LogConfigReader(self.cf)
+
+        self._current_input_config = None
+        self._active_config = None
+        self._active_config = None
+
+        self.inputConfig = None
+
+        # Add things to helper so tabs can access it
+        cfclient.ui.pluginhelper.cf = self.cf
+        cfclient.ui.pluginhelper.inputDeviceReader = self.joystickReader
+        cfclient.ui.pluginhelper.logConfigReader = self.logConfigReader
+
+        self.logConfigDialogue = LogConfigDialogue(cfclient.ui.pluginhelper)
+        self._bootloader_dialog = BootloaderDialog(cfclient.ui.pluginhelper)
+        self._cf2config_dialog = Cf2ConfigDialog(cfclient.ui.pluginhelper)
+        self._cf1config_dialog = Cf1ConfigDialog(cfclient.ui.pluginhelper)
+        self.menuItemBootloader.triggered.connect(self._bootloader_dialog.show)
+        self._about_dialog = AboutDialog(cfclient.ui.pluginhelper)
+        self.menuItemAbout.triggered.connect(self._about_dialog.show)
+        self._menu_cf2_config.triggered.connect(self._cf2config_dialog.show)
+        self._menu_cf1_config.triggered.connect(self._cf1config_dialog.show)
+
+        # Loading toolboxes (A bit of magic for a lot of automatic)
+        self.toolboxes = []
+        self.toolboxesMenuItem.setMenu(QtGui.QMenu())
+        for t_class in cfclient.ui.toolboxes.toolboxes:
+            toolbox = t_class(cfclient.ui.pluginhelper)
+            dockToolbox = MyDockWidget(toolbox.getName())
+            dockToolbox.setWidget(toolbox)
+            self.toolboxes += [dockToolbox, ]
+
+            # Add menu item for the toolbox
+            item = QtGui.QAction(toolbox.getName(), self)
+            item.setCheckable(True)
+            item.triggered.connect(self.toggleToolbox)
+            self.toolboxesMenuItem.menu().addAction(item)
+
+            dockToolbox.closed.connect(lambda: self.toggleToolbox(False))
+
+            # Setup some introspection
+            item.dockToolbox = dockToolbox
+            item.menuItem = item
+            dockToolbox.dockToolbox = dockToolbox
+            dockToolbox.menuItem = item
+
+        # Load and connect tabs
+        self.tabsMenuItem.setMenu(QtGui.QMenu())
+        tabItems = {}
+        self.loadedTabs = []
+        for tabClass in cfclient.ui.tabs.available:
+            tab = tabClass(self.tabs, cfclient.ui.pluginhelper)
+            item = QtGui.QAction(tab.getMenuName(), self)
+            item.setCheckable(True)
+            item.toggled.connect(tab.toggleVisibility)
+            self.tabsMenuItem.menu().addAction(item)
+            tabItems[tab.getTabName()] = item
+            self.loadedTabs.append(tab)
+            if not tab.enabled:
+                item.setEnabled(False)
+
+        # First instantiate all tabs and then open them in the correct order
+        try:
+            for tName in Config().get("open_tabs").split(","):
+                t = tabItems[tName]
+                if (t != None and t.isEnabled()):
+                    # Toggle though menu so it's also marked as open there
+                    t.toggle()
+        except Exception as e:
+            logger.warning("Exception while opening tabs [{}]".format(e))
+
+        # References to all the device sub-menus in the "Input device" menu
+        self._all_role_menus = ()
+        # Used to filter what new devices to add default mapping to
+        self._available_devices = ()
+        # Keep track of mux nodes so we can enable according to how many
+        # devices we have
+        self._all_mux_nodes = ()
+
+        # Check which Input muxes are available
+        self._mux_group = QActionGroup(self._menu_inputdevice, exclusive=True)
+        for m in self.joystickReader.available_mux():
+            node = QAction(m.name,
+                           self._menu_inputdevice,
+                           checkable=True,
+                           enabled=False)
+            node.toggled.connect(self._mux_selected)
+            self._mux_group.addAction(node)
+            self._menu_inputdevice.addAction(node)
+            self._all_mux_nodes += (node, )
+            mux_subnodes = ()
+            for name in m.supported_roles():
+                sub_node = QMenu("    {}".format(name),
+                                   self._menu_inputdevice,
+                                   enabled=False)
+                self._menu_inputdevice.addMenu(sub_node)
+                mux_subnodes += (sub_node, )
+                self._all_role_menus += ({"muxmenu": node,
+                                          "rolemenu": sub_node}, )
+            node.setData((m, mux_subnodes))
+
+        self._mapping_support = True
+
+    def _update_ui_state(self, newState, linkURI=""):
+        self.uiState = newState
+        if newState == UIState.DISCONNECTED:
+            self.setWindowTitle("Not connected")
+            self.menuItemConnect.setText("Connect to Crazyflie")
+            self.connectButton.setText("Connect")
+            self.menuItemQuickConnect.setEnabled(True)
+            self.batteryBar.setValue(3000)
+            self._menu_cf2_config.setEnabled(False)
+            self._menu_cf1_config.setEnabled(True)
+            self.linkQualityBar.setValue(0)
+            self.menuItemBootloader.setEnabled(True)
+            self.logConfigAction.setEnabled(False)
+            if len(Config().get("link_uri")) > 0:
+                self.quickConnectButton.setEnabled(True)
+        if newState == UIState.CONNECTED:
+            s = "Connected on %s" % linkURI
+            self.setWindowTitle(s)
+            self.menuItemConnect.setText("Disconnect")
+            self.connectButton.setText("Disconnect")
+            self.logConfigAction.setEnabled(True)
+            # Find out if there's an I2C EEPROM, otherwise don't show the
+            # dialog.
+            if len(self.cf.mem.get_mems(MemoryElement.TYPE_I2C)) > 0:
+                self._menu_cf2_config.setEnabled(True)
+            self._menu_cf1_config.setEnabled(False)
+        if newState == UIState.CONNECTING:
+            s = "Connecting to {} ...".format(linkURI)
+            self.setWindowTitle(s)
+            self.menuItemConnect.setText("Cancel")
+            self.connectButton.setText("Cancel")
+            self.quickConnectButton.setEnabled(False)
+            self.menuItemBootloader.setEnabled(False)
+            self.menuItemQuickConnect.setEnabled(False)
+
+    @pyqtSlot(bool)
+    def toggleToolbox(self, display):
+        menuItem = self.sender().menuItem
+        dockToolbox = self.sender().dockToolbox
+
+        if display and not dockToolbox.isVisible():
+            dockToolbox.widget().enable()
+            self.addDockWidget(dockToolbox.widget().preferedDockArea(),
+                               dockToolbox)
+            dockToolbox.show()
+        elif not display:
+            dockToolbox.widget().disable()
+            self.removeDockWidget(dockToolbox)
+            dockToolbox.hide()
+            menuItem.setChecked(False)
+
+    def _rescan_devices(self):
+        self._statusbar_label.setText("No inputdevice connected!")
+        self._menu_devices.clear()
+        self._active_device = ""
+        self.joystickReader.stop_input()
+
+        #for c in self._menu_mappings.actions():
+        #    c.setEnabled(False)
+        #devs = self.joystickReader.available_devices()
+        #if (len(devs) > 0):
+        #    self.device_discovery(devs)
+
+    def _show_input_device_config_dialog(self):
+        self.inputConfig = InputConfigDialogue(self.joystickReader)
+        self.inputConfig.show()
+        
+    def _auto_reconnect_changed(self, checked):
+        self._auto_reconnect_enabled = checked 
+        Config().set("auto_reconnect", checked)
+        logger.info("Auto reconnect enabled: {}".format(checked))
+
+    def _show_connect_dialog(self):
+        self.logConfigDialogue.show()
+
+    def _update_vbatt(self, timestamp, data, logconf):
+        self.batteryBar.setValue(int(data["pm.vbat"] * 1000))
+
+    def _connected(self, linkURI):
+        self._update_ui_state(UIState.CONNECTED, linkURI)
+
+        Config().set("link_uri", str(linkURI))
+
+        lg = LogConfig("Battery", 1000)
+        lg.add_variable("pm.vbat", "float")
+        try:
+            self.cf.log.add_config(lg)
+            lg.data_received_cb.add_callback(self.batteryUpdatedSignal.emit)
+            lg.error_cb.add_callback(self._log_error_signal.emit)
+            lg.start()
+        except KeyError as e:
+            logger.warning(str(e))
+
+        mem = self.cf.mem.get_mems(MemoryElement.TYPE_DRIVER_LED)[0]
+        mem.write_data(self._led_write_done)
+
+        #self._led_write_test = 0
+
+        #mem.leds[self._led_write_test] = [10, 20, 30]
+        #mem.write_data(self._led_write_done)
+
+    def _led_write_done(self, mem, addr):
+        logger.info("LED write done callback")
+        #self._led_write_test += 1
+        #mem.leds[self._led_write_test] = [10, 20, 30]
+        #mem.write_data(self._led_write_done)
+
+    def _logging_error(self, log_conf, msg):
+        QMessageBox.about(self, "Log error", "Error when starting log config"
+                " [{}]: {}".format(log_conf.name, msg))
+
+    def _connection_lost(self, linkURI, msg):
+        if not self._auto_reconnect_enabled:
+            if self.isActiveWindow():
+                warningCaption = "Communication failure"
+                error = "Connection lost to {}: {}".format(linkURI, msg)
+                QMessageBox.critical(self, warningCaption, error)
+                self._update_ui_state(UIState.DISCONNECTED, linkURI)
+        else:
+            self._quick_connect()
+
+    def _connection_failed(self, linkURI, error):
+        if not self._auto_reconnect_enabled:
+            msg = "Failed to connect on {}: {}".format(linkURI, error)
+            warningCaption = "Communication failure"
+            QMessageBox.critical(self, warningCaption, msg)
+            self._update_ui_state(UIState.DISCONNECTED, linkURI)
+        else:
+            self._quick_connect()
+
+    def closeEvent(self, event):
+        self.hide()
+        self.cf.close_link()
+        Config().save_file()
+
+    def _connect(self):
+        if self.uiState == UIState.CONNECTED:
+            self.cf.close_link()
+        elif self.uiState == UIState.CONNECTING:
+            self.cf.close_link()
+            self._update_ui_state(UIState.DISCONNECTED)
+        else:
+            self.connectDialogue.show()
+
+    def _display_input_device_error(self, error):
+        self.cf.close_link()
+        QMessageBox.critical(self, "Input device error", error)
+
+    def _mux_selected(self, checked):
+        """Called when a new mux is selected. The menu item contains a
+        reference to the raw mux object as well as to the associated device
+        sub-nodes"""
+        if not checked:
+            (mux, sub_nodes) = self.sender().data().toPyObject()
+            for s in sub_nodes:
+                s.setEnabled(False)
+        else:
+            (mux, sub_nodes) = self.sender().data().toPyObject()
+            for s in sub_nodes:
+                s.setEnabled(True)
+            self.joystickReader.set_mux(mux=mux)
+
+            # Go though the tree and select devices/mapping that was
+            # selected before it was disabled.
+            for role_node in sub_nodes:
+                for dev_node in role_node.children():
+                    if type(dev_node) is QAction and dev_node.isChecked():
+                        dev_node.toggled.emit(True)
+
+            self._update_input_device_footer()
+
+    def _get_dev_status(self, device):
+        msg = "{}".format(device.name)
+        if device.supports_mapping:
+            map_name = "N/A"
+            if device.input_map:
+                map_name = device.input_map_name
+            msg += " ({})".format(map_name)
+        return msg
+
+    def _update_input_device_footer(self):
+        """Update the footer in the bottom of the UI with status for the
+        input device and its mapping"""
+
+        msg = ""
+
+        if len(self.joystickReader.available_devices()) > 0:
+            mux = self.joystickReader._selected_mux
+            msg = "Using {} mux with ".format(mux.name)
+            for key in mux._devs.keys()[:-1]:
+                if mux._devs[key]:
+                    msg += "{}, ".format(self._get_dev_status(mux._devs[key]))
+                else:
+                    msg += "N/A, "
+            # Last item
+            key = mux._devs.keys()[-1]
+            if mux._devs[key]:
+                msg += "{}".format(self._get_dev_status(mux._devs[key]))
+            else:
+                msg += "N/A"
+        else:
+            msg = "No input device found"
+        self._statusbar_label.setText(msg)
+
+    def _inputdevice_selected(self, checked):
+        """Called when a new input device has been selected from the menu. The
+        data in the menu object is the associated map menu (directly under the
+        item in the menu) and the raw device"""
+        (map_menu, device, mux_menu) = self.sender().data().toPyObject()
+        if not checked:
+            if map_menu:
+                map_menu.setEnabled(False)
+            # Do not close the device, since we don't know exactly
+            # how many devices the mux can have open. When selecting a
+            # new mux the old one will take care of this.
+        else:
+            if map_menu:
+                map_menu.setEnabled(True)
+
+            (mux, sub_nodes) = mux_menu.data().toPyObject()
+            for role_node in sub_nodes:
+                for dev_node in role_node.children():
+                    if type(dev_node) is QAction and dev_node.isChecked():
+                        if device.id == dev_node.data().toPyObject()[1].id \
+                                and dev_node is not self.sender():
+                            dev_node.setChecked(False)
+
+            role_in_mux = str(self.sender().parent().title()).strip()
+            logger.info("Role of {} is {}".format(device.name,
+                                                  role_in_mux))
+
+            Config().set("input_device", str(device.name))
+
+            self._mapping_support = self.joystickReader.start_input(device.name,
+                                                                    role_in_mux)
+        self._update_input_device_footer()
+
+    def _inputconfig_selected(self, checked):
+        """Called when a new configuration has been selected from the menu. The
+        data in the menu object is a referance to the device QAction in parent
+        menu. This contains a referance to the raw device."""
+        if not checked:
+            return
+
+        selected_mapping = str(self.sender().text())
+        device = self.sender().data().toPyObject().data().toPyObject()[1]
+        self.joystickReader.set_input_map(device.name, selected_mapping)
+        self._update_input_device_footer()
+
+    def device_discovery(self, devs):
+        """Called when new devices have been added"""
+        for menu in self._all_role_menus:
+            role_menu = menu["rolemenu"]
+            mux_menu = menu["muxmenu"]
+            dev_group = QActionGroup(role_menu, exclusive=True)
+            for d in devs:
+                dev_node = QAction(d.name, role_menu, checkable=True,
+                                   enabled=True)
+                role_menu.addAction(dev_node)
+                dev_group.addAction(dev_node)
+                dev_node.toggled.connect(self._inputdevice_selected)
+
+                map_node = None
+                if d.supports_mapping:
+                    map_node = QMenu("    Input map", role_menu, enabled=False)
+                    map_group = QActionGroup(role_menu, exclusive=True)
+                    # Connect device node to map node for easy
+                    # enabling/disabling when selection changes and device
+                    # to easily enable it
+                    dev_node.setData((map_node, d))
+                    for c in ConfigManager().get_list_of_configs():
+                        node = QAction(c, map_node, checkable=True,
+                                       enabled=True)
+                        node.toggled.connect(self._inputconfig_selected)
+                        map_node.addAction(node)
+                        # Connect all the map nodes back to the device
+                        # action node where we can access the raw device
+                        node.setData(dev_node)
+                        map_group.addAction(node)
+                        # If this device hasn't been found before, then
+                        # select the default mapping for it.
+                        if d not in self._available_devices:
+                            last_map = Config().get("device_config_mapping")
+                            if last_map.has_key(d.name) and last_map[d.name] == c:
+                                node.setChecked(True)
+                    role_menu.addMenu(map_node)
+                dev_node.setData((map_node, d, mux_menu))
+
+        # Update the list of what devices we found
+        # to avoid selecting default mapping for all devices when
+        # a new one is inserted
+        self._available_devices = ()
+        for d in devs:
+            self._available_devices += (d, )
+
+        # Only enable MUX nodes if we have enough devies to cover
+        # the roles
+        for mux_node in self._all_mux_nodes:
+            (mux, sub_nodes) = mux_node.data().toPyObject()
+            if len(mux.supported_roles()) <= len(self._available_devices):
+                mux_node.setEnabled(True)
+
+        # TODO: Currently only supports selecting default mux
+        if self._all_mux_nodes[0].isEnabled():
+            self._all_mux_nodes[0].setChecked(True)
+
+        # If the previous length of the available devies was 0, then select
+        # the default on. If that's not available then select the first
+        # on in the list.
+        # TODO: This will only work for the "Normal" mux so this will be
+        #       selected by default
+        if Config().get("input_device") in [d.name for d in devs]:
+            for dev_menu in self._all_role_menus[0]["rolemenu"].actions():
+                if dev_menu.text() == Config().get("input_device"):
+                    dev_menu.setChecked(True)
+        else:
+            # Select the first device in the first mux (will always be "Normal"
+            # mux)
+            self._all_role_menus[0]["rolemenu"].actions()[0].setChecked(True)
+            logger.info("Select first device")
+
+        self._update_input_device_footer()
+
+    def _quick_connect(self):
+        try:
+            self.cf.open_link(Config().get("link_uri"))
+        except KeyError:
+            self.cf.open_link("")
+
+    def _open_config_folder(self):
+        QDesktopServices.openUrl(QUrl("file:///" +
+                                      QDir.toNativeSeparators(sys.path[1])))
+
+    def closeAppRequest(self):
+        self.close()
+        sys.exit(0)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..354b7bcf1879459e213634c9b237de32a70a699e
GIT binary patch
literal 22709
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
Ye;eYII)iYSpfrc4HGF*2H`@EZ0V+4lQ2+n{

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.ui
new file mode 100755
index 00000000..25cd15f6
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/main.ui
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>872</width>
+    <height>641</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Crazyflie Nano Quadcopter Control</string>
+  </property>
+  <property name="statusTip">
+   <string/>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <layout class="QGridLayout" name="gridLayout_2">
+        <item row="0" column="1">
+         <widget class="QPushButton" name="quickConnectButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="toolTip">
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Quickconnect using the last connection parameters&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          </property>
+          <property name="text">
+           <string>Quick Connect</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="3">
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="0" column="5">
+         <widget class="QProgressBar" name="linkQualityBar">
+          <property name="minimumSize">
+           <size>
+            <width>150</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="maximumSize">
+           <size>
+            <width>100</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="maximum">
+           <number>100</number>
+          </property>
+          <property name="value">
+           <number>0</number>
+          </property>
+          <property name="textVisible">
+           <bool>true</bool>
+          </property>
+          <property name="format">
+           <string>Link quality %p%</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="0">
+         <widget class="QPushButton" name="connectButton">
+          <property name="toolTip">
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Connect to a Crazyflie&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          </property>
+          <property name="text">
+           <string>Connect</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="4">
+         <widget class="QProgressBar" name="batteryBar">
+          <property name="minimumSize">
+           <size>
+            <width>150</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="maximumSize">
+           <size>
+            <width>150</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="minimum">
+           <number>3000</number>
+          </property>
+          <property name="maximum">
+           <number>4200</number>
+          </property>
+          <property name="value">
+           <number>3000</number>
+          </property>
+          <property name="textVisible">
+           <bool>true</bool>
+          </property>
+          <property name="invertedAppearance">
+           <bool>false</bool>
+          </property>
+          <property name="textDirection">
+           <enum>QProgressBar::TopToBottom</enum>
+          </property>
+          <property name="format">
+           <string>Battery %v mV</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="2">
+         <widget class="QCheckBox" name="autoReconnectCheckBox">
+          <property name="mouseTracking">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>Auto Reconnect</string>
+          </property>
+          <property name="checked">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QTabWidget" name="tabs">
+        <property name="currentIndex">
+         <number>-1</number>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>872</width>
+     <height>25</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="separator"/>
+    <addaction name="menuItemExit"/>
+   </widget>
+   <widget class="QMenu" name="menuView">
+    <property name="title">
+     <string>View</string>
+    </property>
+    <addaction name="tabsMenuItem"/>
+    <addaction name="toolboxesMenuItem"/>
+   </widget>
+   <widget class="QMenu" name="menuHelp">
+    <property name="title">
+     <string>Help</string>
+    </property>
+    <addaction name="menuItemAbout"/>
+   </widget>
+   <widget class="QMenu" name="menuSettings">
+    <property name="title">
+     <string>Settings</string>
+    </property>
+    <addaction name="logConfigAction"/>
+    <addaction name="separator"/>
+    <addaction name="_menuItem_openconfigfolder"/>
+   </widget>
+   <widget class="QMenu" name="menuCrazyflie">
+    <property name="title">
+     <string>Crazyflie</string>
+    </property>
+    <addaction name="menuItemConnect"/>
+    <addaction name="menuItemQuickConnect"/>
+    <addaction name="menuItemBootloader"/>
+    <addaction name="_menu_cf2_config"/>
+    <addaction name="_menu_cf1_config"/>
+   </widget>
+   <widget class="QMenu" name="_menu_inputdevice">
+    <property name="title">
+     <string>Input device</string>
+    </property>
+    <addaction name="menuItemConfInputDevice"/>
+    <addaction name="_menuitem_rescandevices"/>
+    <addaction name="separator"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuCrazyflie"/>
+   <addaction name="_menu_inputdevice"/>
+   <addaction name="menuSettings"/>
+   <addaction name="menuView"/>
+   <addaction name="menuHelp"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="menuItemConnect">
+   <property name="text">
+    <string>Connect</string>
+   </property>
+  </action>
+  <action name="menuItemExit">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+  <action name="menuItemRegulation">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Regulation</string>
+   </property>
+  </action>
+  <action name="menuItemPlot">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Plotting</string>
+   </property>
+  </action>
+  <action name="menuItemFlightdata">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Flight data</string>
+   </property>
+  </action>
+  <action name="menuItemService">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Service</string>
+   </property>
+  </action>
+  <action name="menuItemAbout">
+   <property name="text">
+    <string>About</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+  <action name="menuItemQuickConnect">
+   <property name="text">
+    <string>Quick connect</string>
+   </property>
+  </action>
+  <action name="menuItemConfInputDevice">
+   <property name="text">
+    <string>Configure device mapping</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+  <action name="actionSelect_input_device">
+   <property name="text">
+    <string>Select input device</string>
+   </property>
+  </action>
+  <action name="menuItemConsole">
+   <property name="text">
+    <string>Console</string>
+   </property>
+  </action>
+  <action name="toolboxesMenuItem">
+   <property name="text">
+    <string>Toolboxes</string>
+   </property>
+  </action>
+  <action name="tabsMenuItem">
+   <property name="text">
+    <string>Tabs</string>
+   </property>
+  </action>
+  <action name="logConfigAction">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Logging configurations</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+  <action name="actionOptions">
+   <property name="text">
+    <string>Options</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+  <action name="menuItemBootloader">
+   <property name="text">
+    <string>Bootloader</string>
+   </property>
+  </action>
+  <action name="actionAsdfasdf">
+   <property name="text">
+    <string>asdfasdf</string>
+   </property>
+  </action>
+  <action name="actionSdfasdf">
+   <property name="text">
+    <string>sdfasdf</string>
+   </property>
+  </action>
+  <action name="_menuitem_rescandevices">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Rescan devices</string>
+   </property>
+  </action>
+  <action name="_menuItem_openconfigfolder">
+   <property name="text">
+    <string>Open config folder</string>
+   </property>
+  </action>
+  <action name="_menu_cf2_config">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Configure 2.0</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+  <action name="_menu_cf_config_2">
+   <property name="text">
+    <string>Configure 1.0</string>
+   </property>
+  </action>
+  <action name="_menu_cf1_config">
+   <property name="text">
+    <string>Configure 1.0</string>
+   </property>
+   <property name="menuRole">
+    <enum>QAction::NoRole</enum>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.py
new file mode 100755
index 00000000..36cdde03
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Used for passing objects to tabs and toolboxes.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['PluginHelper']
+
+
+class PluginHelper():
+    """Used for passing objects to tabs and toolboxes"""
+    def __init__(self):
+        self.cf = None
+        self.menu = None
+        self.logConfigReader = None
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/pluginhelper.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..06a84b6b0cc14e0ab6ae757a2f890ea9d224a53a
GIT binary patch
literal 857
zcmcgpv2N5r5FMX`;SLZbUE(5&u1L<JM+lOJ>J&Lp<eHW3UHcZTcP;N)B8UzuK8@eW
z5AbGvN@@zQHP18aH#5)A>HW)9|NGach7MPX=X-=d2S}nFFri&SazP!E1t;<e$&`K)
z{UEyCr6iB&W}R`@8^G$bS2Azim=Bfr+IG3Czo;hoJh(hmwa+UnkvmhnugY&${yDlX
zb!f)wo60|2h8L)>&D3f8QJF!F59$&oBK&KBf;#8|LQ%qUAf>!nFPN|a{GE=5hjVcM
z6~dnYF#bD{h<iA}s`&(8P55eV*shO&fnn>c3Ot}`W8Et|g;%JJ>#m$_byuoNsxkIO
zhF7LtgFq4dHL_ywdR5d1LyBQ^(Y}KHxoeG97Y)D$e=*#Jy|abUb<wo*VKM1qh~Dj^
zcN=h?VW<$=>LA1!a?k&Yk|pk0`b@A@uY?Gz$nKq-43|%1DP1E($$gJv|DECzd*~G5
v+0-n_lI^^f%W?@gsH`R!Fg~cPjDbqL{x2${uWx6g-W_9U^fg;#$uj)|`F7C(

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.py
new file mode 100755
index 00000000..3913e341
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Superclass for all tabs that implements common functions.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Tab']
+
+import logging
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+from cfclient.utils.config import Config
+
+
+class Tab(QtGui.QWidget):
+    """Superclass for all tabs that implements common functions."""
+
+    def __init__(self):
+        super(Tab, self).__init__()
+        self.tabName = "N/A"
+        self.menuName = "N/A"
+        self.enabled = True
+
+    @pyqtSlot(bool)
+    def toggleVisibility(self, checked):
+        """Show or hide the tab."""
+        if checked:
+            self.tabWidget.addTab(self, self.getTabName())
+            s = ""
+            try:
+                s = Config().get("open_tabs")
+                if (len(s) > 0):
+                    s += ","
+            except Exception as e:
+                logger.warning("Exception while adding tab to config and "
+                               "reading tab config")
+            # Check this since tabs in config are opened when app is started
+            if (self.tabName not in s):
+                s += "%s" % self.tabName
+                Config().set("open_tabs", str(s))
+
+        if not checked:
+            self.tabWidget.removeTab(self.tabWidget.indexOf(self))
+            try:
+                parts = Config().get("open_tabs").split(",")
+            except Exception as e:
+                logger.warning("Exception while removing tab from config and "
+                               "reading tab config")
+                parts = []
+            s = ""
+            for p in parts:
+                if (self.tabName != p):
+                    s += "%s," % p
+            s = s[0:len(s) - 1]  # Remove last comma
+            Config().set("open_tabs", str(s))
+
+    def getMenuName(self):
+        """Return the name of the tab that will be shown in the menu"""
+        return self.menuName
+
+    def getTabName(self):
+        """Return the name of the tab that will be shown in the tab"""
+        return self.tabName
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..a18916cffa5e79aa6b00b247f4326c0ec69183ce
GIT binary patch
literal 2814
zcmcIm>yFz*6h32Tvq`$?Zd+O|s;K*;B|_~+q98z^Lc5@XXcy|NS{eyOjy*{x>r3jH
zunpS&Di6R9UV!)LTk!z!o#VJv`~Xz3j%PA+=JuWMT*m(I-Og9>HxFW3KR*6HN3p-5
zN{EK26%7?79!=5kDDf$2Q0mjrC+*X`L3)FpVW=rX8<cKR)}mpHvNjFdlyzv>A;rs@
zl%Aq&i-ud2b!ph8>@*EeQ?^aRZOV3NxKqvBr1T7BXK8pAeUEgT=I3Yv3@!SZ=o!(Y
z;cE<H&}%c)Pf&FR%Y`oEG_p3B6lD;lY2c!<4cshpfyoxB&UEf<5Eofi<iTW_$IcYF
z-R;;@Sby8NxQu?%!L8elIXH;M<_|pkJ?^<Cs{3xQD7D*==AEUHc}o-bSmHbpyB7Wy
ztH*AT7S46*j+y3B>Na`G!K~C#A{!3AzO#SpZcjlh{k<Zem}!izJiOy=6nhhuhABV*
zYFI!q4NQN{JxF3=Y9sLICKJSs7KH<WXg|h2uS`?SFu5=vdnoojR1UAAW1Q{L++))y
zphJHIb61T9fijlBKX87J)!Mhb%YOe>k2w)aE!!_dkA%a~$mGV2MnW14xgTX(24Kad
zbQ+j?P%gF1(0MdYby9K3BUY!A9`7cVy@OVNR%E(AUK_f<D2us{o$a&TSCiD}>oF?$
z$o0j_&5FFAnsGm#RKot!^Z{dcu?pE;`Y4udP3oL#;}^49>?Q0BLeH-t>sV3woRy#B
z6*{;rJb^H#>ZwPo4+yRV>JUmWB&-M_b0+P?r^5!7kGKnqeu%N0=wTjE15R_;q4Jp2
zQ=b%gn71gASX8;9@A;@UFFV{66j<LBEN!~L+pG`m@S*P1`Z$L5_&FZ|?DiM6#=cc6
z(d7mp;#I3l^&U0w`Dw<;8Z-d$>I;Zh)XNYzW%ZPJ76Ywlc(O~!4f^7TtMpX-g|TnY
zp-<%v#k#k+E5IM$P<S7*(0_@jHobBApVWd*Ne2$d_Os_v4Q9nrfLt*%iAJ74fq~)C
za6!&1#X{#Jh^!G2`~Ee_HD5l7^@5W}a5OWi4x%J6`IJ`&u0Vp}C=Q}L2{_X0kxFdY
zO|Dq`8UFshO{ufu$9nptEV6&uibLp%y=EBLes3GKzy^$mCYfp{IR)4bqOoKmh|uKN
z30XZ!Up$TXN#XY_e1S<8r^R%tO9`-}sLX*a+^SdF^-AkXSt;l>pT!|<@<c!Rc2Y(5
zA~mkZu@Ua@Y95bgIzH41atCZ7b(L5bQR%Aw;$))N2?Xes>IcS}u>m#&AW(2RBfqKM
zR88fpv-q7++v<#uqAqz&brI4_kaWEcW^~ayuU=O%Yfu-7dseTCnt`MvNxPwT%Q6=y
z%VB_^nA8qeT~J2`_tjVj7A~0wrkcsAPPPcU*t2Re{1ak^;{yJ0Z=GNtpeyc5-hWW?
zBg}p!CE>1>X6#uftz&orhv1W@R!qJh*7ptD1@{f#*`rZbB+Hb$Y|YUqDdN#c5`QR{
zJ>M|nqDaH5XoT-@#XJ9H7-{{lw+^<aYbcfhHdWJWH_u;gH+r1N!go=vkw;71UF8ep
z_zqgPIhYZ_609)uU5V|GfrfAKa#rlS)qVGV$nG3+q6#@RI5vz7>{eeuA!{!@gc8(D
zQP|>{jHSLXcbCqjcDG7<Rl?zk_p96_7ySv+B{u!Evf|CRajxaSX(Ei<-j>?-E~=il
G<Npc0m~_|x

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.py
new file mode 100755
index 00000000..a90e1bed
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The console tab is used as a console for printouts from the Crazyflie.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['ConsoleTab']
+
+import time
+import sys
+
+import logging
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import pyqtSlot, pyqtSignal
+
+from cfclient.ui.tab import Tab
+
+console_tab_class = uic.loadUiType(sys.path[0] +
+                                   "/cfclient/ui/tabs/consoleTab.ui")[0]
+
+
+class ConsoleTab(Tab, console_tab_class):
+    """Console tab for showing printouts from Crazyflie"""
+    update = pyqtSignal(str)
+
+    def __init__(self, tabWidget, helper, *args):
+        super(ConsoleTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Console"
+        self.menuName = "Console"
+
+        self.tabWidget = tabWidget
+        self.helper = helper
+
+        self.update.connect(self.printText)
+
+        self.helper.cf.console.receivedChar.add_callback(self.update.emit)
+
+    def printText(self, text):
+        # Make sure we get printouts from the Crazyflie into the log (such as
+        # build version and test ok/fail)
+        logger.debug("[%s]", text)
+        self.console.insertPlainText(text)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ConsoleTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..979bf106dc4e463400fc56259c980787123275fa
GIT binary patch
literal 1935
zcmcgsOONA35H35fNhYshmRVNfkpoCMuoHw3Cxl>vv>ecA*U1WDge=GIByIe{-3Ee&
z({e)mEiU{WegJ$`P6i3iOO&ou^`lbteP4I$&(R?J<JWaW$4?8tpJ15ZVPYa5)*;`a
z*rgpDm*N)1ZAx3@!)g3B`R$Y6A-_YaLrIrZk6z%&nJjfE?Nc@&e?Zwe`R9}k$sdx#
zDY}%7C>xVMrffp~q?zqeI;HG_{0nl_kdjwOwJEuz_>3OKeR_!8?D`M5Hg!Sj0c}y<
zfPNu*LG+dXDi^w%pL08(!wlAYHH(Ve6seloa5K|pRvQ)1LNg2R7q&$;D=VGbqPAwX
zt%_`Bac)_KKOMHIR@Vc=8E>?WFr{Xn-q-=+Ws`RuZuDRH+|C(E+l6^%mqn#)N8Bg1
zw!Bj7XzuckGnn%5on57cl|&NgT^^=$hd1zU&Way0Z^100ZG<{<yQuX7ZJ5QeC+xaL
zUf#$bPJe@8-i1-15_rJ^s}>{4)O46!z~y%tLw0)Pt)z7G9!?#b60jH-voC(o`R?EN
z++!1Qv26_geS%?rhOziMbO%;kN?f{Y(QiZ!;-n?B4)~V!04i#n4&60Qm+o+aDCn_G
z&M4`NGvN9qM~{+oN}!JsCF9eki7cbtm7siUd#K~s&3wu=F&(BZRpr5HXb1wGYa0Zz
z*(h6=&$ajf`8LdyaFwaNmY~na!p)aD-YF|Y?^TL?;?`vxS|x`;POc(rdB5mtt$wtY
z$%(e74Xj8z#8jmsrN3A4avxSQ5yo*4g=xA8qi-!Et1@lp>_}u(y7fkgyzyx&tafIO
zfW(*;yv4rA)M9f~?4qoS1QkIyCrv+Y1Z5tV2fHuwMXERdRoM|fR#|!QxSbC%45LZT
zl{0etPG82Q8*zI)SO-fC^EM20kU)bV)?q0Kdr$;KfS5&aPnY2azkbJj({#$WC@pq7
zfXlSSYE$pLOU^if(Ye9G(=^mMLo0-P4|!E!;wc0#8IBA5!)F*$Ikl$FoG-R0CJ1tf
z2eRU4A_%e~u2T+Kl0gs`Q4o0CmfUn>E03EJd_B(kPw_98Q2Z6Wk72m*uG4jfZht(#
zLg4YMbF4nBA&~06dMFdV;H+^$m~L}0(i#(nw?cG}8}fzl=e@!C9&tWy`RU=A{lMdA
z&SSNBd|qTP3)jud>2dl7Ax7r(`r)a&#{KhH98z3bgz<B|K9oudDnq-s3?MEb->(3h
tgD4G+ksJR%!72*)sNwwMH&Kmw4W{dMaaV?bc<4;r*PSbO>Q0?;>u)c4xyS$j

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.py
new file mode 100755
index 00000000..e34870a9
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+#  02110-1301, USA.
+
+"""
+An example template for a tab in the Crazyflie Client. It comes pre-configured
+with the necessary QT Signals to wrap Crazyflie API callbacks and also
+connects the connected/disconnected callbacks.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['ExampleTab']
+
+import logging
+import sys
+
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import pyqtSlot, pyqtSignal, QThread, Qt
+from PyQt4.QtGui import QMessageBox
+
+from cfclient.ui.tab import Tab
+
+from cflib.crazyflie.log import LogConfig, Log
+from cflib.crazyflie.param import Param
+
+example_tab_class = uic.loadUiType(sys.path[0] +
+                                "/cfclient/ui/tabs/exampleTab.ui")[0]
+
+class ExampleTab(Tab, example_tab_class):
+    """Tab for plotting logging data"""
+
+    _connected_signal = pyqtSignal(str)
+    _disconnected_signal = pyqtSignal(str)
+    _log_data_signal = pyqtSignal(int, object, object)
+    _log_error_signal = pyqtSignal(object, str)
+    _param_updated_signal = pyqtSignal(str, str)
+
+    def __init__(self, tabWidget, helper, *args):
+        super(ExampleTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Example"
+        self.menuName = "Example Tab"
+        self.tabWidget = tabWidget
+
+        self._helper = helper
+
+        # Always wrap callbacks from Crazyflie API though QT Signal/Slots
+        # to avoid manipulating the UI when rendering it
+        self._connected_signal.connect(self._connected)
+        self._disconnected_signal.connect(self._disconnected)
+        self._log_data_signal.connect(self._log_data_received)
+        self._param_updated_signal.connect(self._param_updated)
+
+        # Connect the Crazyflie API callbacks to the signals
+        self._helper.cf.connected.add_callback(
+            self._connected_signal.emit)
+
+        self._helper.cf.disconnected.add_callback(
+            self._disconnected_signal.emit)
+
+    def _connected(self, link_uri):
+        """Callback when the Crazyflie has been connected"""
+
+        logger.debug("Crazyflie connected to {}".format(link_uri))
+
+    def _disconnected(self, link_uri):
+        """Callback for when the Crazyflie has been disconnected"""
+
+        logger.debug("Crazyflie disconnected from {}".format(link_uri))
+
+    def _param_updated(self, name, value):
+        """Callback when the registered parameter get's updated"""
+
+        logger.debug("Updated {0} to {1}".format(name, value))
+
+    def _log_data_received(self, timestamp, data, log_conf):
+        """Callback when the log layer receives new data"""
+
+        logger.debug("{0}:{1}:{2}".format(timestamp, log_conf.name, data))
+
+    def _logging_error(self, log_conf, msg):
+        """Callback from the log layer when an error occurs"""
+
+        QMessageBox.about(self, "Example error",
+                          "Error when using log config"
+                          " [{0}]: {1}".format(log_conf.name, msg))
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ExampleTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..8ef24d306d402aa0303d10941dfbb52747162bff
GIT binary patch
literal 4045
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.py
new file mode 100755
index 00000000..8995debb
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.py
@@ -0,0 +1,556 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+The flight control tab shows telemetry data and flight settings.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['FlightTab']
+
+import sys
+
+import logging
+logger = logging.getLogger(__name__)
+
+from time import time
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QVariant
+from PyQt4.QtGui import QMessageBox
+
+from cflib.crazyflie import Crazyflie
+
+from cfclient.ui.widgets.ai import AttitudeIndicator
+
+from cfclient.utils.config import Config
+from cflib.crazyflie.log import Log, LogVariable, LogConfig
+
+from cfclient.ui.tab import Tab
+
+from cflib.crazyflie.mem import MemoryElement
+
+flight_tab_class = uic.loadUiType(sys.path[0] +
+                                  "/cfclient/ui/tabs/flightTab.ui")[0]
+
+MAX_THRUST = 65365.0
+
+
+class FlightTab(Tab, flight_tab_class):
+
+    uiSetupReadySignal = pyqtSignal()
+
+    _motor_data_signal = pyqtSignal(int, object, object)
+    _imu_data_signal = pyqtSignal(int, object, object)
+    _althold_data_signal = pyqtSignal(int, object, object)
+    _baro_data_signal = pyqtSignal(int, object, object)
+
+    _input_updated_signal = pyqtSignal(float, float, float, float)
+    _rp_trim_updated_signal = pyqtSignal(float, float)
+    _emergency_stop_updated_signal = pyqtSignal(bool)
+
+    _log_error_signal = pyqtSignal(object, str)
+
+    #UI_DATA_UPDATE_FPS = 10
+
+    connectionFinishedSignal = pyqtSignal(str)
+    disconnectedSignal = pyqtSignal(str)
+
+    _limiting_updated = pyqtSignal(bool, bool, bool)
+
+    def __init__(self, tabWidget, helper, *args):
+        super(FlightTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Flight Control"
+        self.menuName = "Flight Control"
+
+        self.tabWidget = tabWidget
+        self.helper = helper
+
+        self.disconnectedSignal.connect(self.disconnected)
+        self.connectionFinishedSignal.connect(self.connected)
+        # Incomming signals
+        self.helper.cf.connected.add_callback(
+            self.connectionFinishedSignal.emit)
+        self.helper.cf.disconnected.add_callback(self.disconnectedSignal.emit)
+
+        self._input_updated_signal.connect(self.updateInputControl)
+        self.helper.inputDeviceReader.input_updated.add_callback(
+                                     self._input_updated_signal.emit)
+        self._rp_trim_updated_signal.connect(self.calUpdateFromInput)
+        self.helper.inputDeviceReader.rp_trim_updated.add_callback(
+                                     self._rp_trim_updated_signal.emit)
+        self._emergency_stop_updated_signal.connect(self.updateEmergencyStop)
+        self.helper.inputDeviceReader.emergency_stop_updated.add_callback(
+                                     self._emergency_stop_updated_signal.emit)
+        
+        self.helper.inputDeviceReader.althold_updated.add_callback(
+                    lambda enabled: self.helper.cf.param.set_value("flightmode.althold", enabled))
+
+        self._imu_data_signal.connect(self._imu_data_received)
+        self._baro_data_signal.connect(self._baro_data_received)
+        self._althold_data_signal.connect(self._althold_data_received)
+        self._motor_data_signal.connect(self._motor_data_received)
+
+        self._log_error_signal.connect(self._logging_error)
+
+        # Connect UI signals that are in this tab
+        self.flightModeCombo.currentIndexChanged.connect(self.flightmodeChange)
+        self.minThrust.valueChanged.connect(self.minMaxThrustChanged)
+        self.maxThrust.valueChanged.connect(self.minMaxThrustChanged)
+        self.thrustLoweringSlewRateLimit.valueChanged.connect(
+                                      self.thrustLoweringSlewRateLimitChanged)
+        self.slewEnableLimit.valueChanged.connect(
+                                      self.thrustLoweringSlewRateLimitChanged)
+        self.targetCalRoll.valueChanged.connect(self._trim_roll_changed)
+        self.targetCalPitch.valueChanged.connect(self._trim_pitch_changed)
+        self.maxAngle.valueChanged.connect(self.maxAngleChanged)
+        self.maxYawRate.valueChanged.connect(self.maxYawRateChanged)
+        self.uiSetupReadySignal.connect(self.uiSetupReady)
+        self.clientXModeCheckbox.toggled.connect(self.changeXmode)
+        self.isInCrazyFlightmode = False
+        self.uiSetupReady()
+
+        self.clientXModeCheckbox.setChecked(Config().get("client_side_xmode"))
+
+        self.crazyflieXModeCheckbox.clicked.connect(
+                             lambda enabled:
+                             self.helper.cf.param.set_value("flightmode.x",
+                                                            str(enabled)))
+        self.helper.cf.param.add_update_callback(
+                        group="flightmode", name="xmode",
+                        cb=( lambda name, checked:
+                        self.crazyflieXModeCheckbox.setChecked(eval(checked))))
+
+        self.ratePidRadioButton.clicked.connect(
+                    lambda enabled:
+                    self.helper.cf.param.set_value("flightmode.ratepid",
+                                                   str(enabled)))
+
+        self.angularPidRadioButton.clicked.connect(
+                    lambda enabled:
+                    self.helper.cf.param.set_value("flightmode.ratepid",
+                                                   str(not enabled)))
+
+        self._led_ring_headlight.clicked.connect(
+                    lambda enabled:
+                    self.helper.cf.param.set_value("ring.headlightEnable",
+                                                   str(enabled)))
+
+        self.helper.cf.param.add_update_callback(
+                    group="flightmode", name="ratepid",
+                    cb=(lambda name, checked:
+                    self.ratePidRadioButton.setChecked(eval(checked))))
+
+        self.helper.cf.param.add_update_callback(
+                    group="cpu", name="flash",
+                    cb=self._set_enable_client_xmode)
+
+        self.helper.cf.param.add_update_callback(
+                    group="ring", name="headlightEnable",
+                    cb=(lambda name, checked:
+                    self._led_ring_headlight.setChecked(eval(checked))))
+
+        self.helper.cf.param.add_update_callback(
+                    group="flightmode", name="althold",
+                    cb=(lambda name, enabled:
+                    self.helper.inputDeviceReader.enable_alt_hold(eval(enabled))))
+
+        self._ledring_nbr_effects = 0
+
+        self.helper.cf.param.add_update_callback(
+                        group="ring",
+                        name="neffect",
+                        cb=(lambda name, value: self._set_neffect(eval(value))))
+
+        self.helper.cf.param.add_update_callback(
+                        group="imu_sensors",
+                        cb=self._set_available_sensors)
+
+        self.helper.cf.param.all_updated.add_callback(self._ring_populate_dropdown)
+
+        self.logBaro = None
+        self.logAltHold = None
+
+        self.ai = AttitudeIndicator()
+        self.verticalLayout_4.addWidget(self.ai)
+        self.splitter.setSizes([1000,1])
+
+        self.targetCalPitch.setValue(Config().get("trim_pitch"))
+        self.targetCalRoll.setValue(Config().get("trim_roll"))
+
+        self.helper.inputDeviceReader.alt1_updated.add_callback(self.alt1_updated)
+        self.helper.inputDeviceReader.alt2_updated.add_callback(self.alt2_updated)
+        self._tf_state = 0
+        self._ring_effect = 0
+
+        # Connect callbacks for input device limiting of rpöö/pitch/yaw/thust
+        self.helper.inputDeviceReader.limiting_updated.add_callback(
+            self._limiting_updated.emit)
+        self._limiting_updated.connect(self._set_limiting_enabled)
+
+    def _set_enable_client_xmode(self, name, value):
+        if eval(value) <= 128:
+            self.clientXModeCheckbox.setEnabled(True)
+        else:
+            self.clientXModeCheckbox.setEnabled(False)
+            self.clientXModeCheckbox.setChecked(False)
+
+    def _set_limiting_enabled(self, rp_limiting_enabled,
+                                    yaw_limiting_enabled,
+                                    thrust_limiting_enabled):
+        self.maxAngle.setEnabled(rp_limiting_enabled)
+        self.targetCalRoll.setEnabled(rp_limiting_enabled)
+        self.targetCalPitch.setEnabled(rp_limiting_enabled)
+        self.maxYawRate.setEnabled(yaw_limiting_enabled)
+        self.maxThrust.setEnabled(thrust_limiting_enabled)
+        self.minThrust.setEnabled(thrust_limiting_enabled)
+        self.slewEnableLimit.setEnabled(thrust_limiting_enabled)
+        self.thrustLoweringSlewRateLimit.setEnabled(thrust_limiting_enabled)
+
+    def _set_neffect(self, n):
+        self._ledring_nbr_effects = n
+
+    def thrustToPercentage(self, thrust):
+        return ((thrust / MAX_THRUST) * 100.0)
+
+    def uiSetupReady(self):
+        flightComboIndex = self.flightModeCombo.findText(
+                             Config().get("flightmode"), Qt.MatchFixedString)
+        if (flightComboIndex < 0):
+            self.flightModeCombo.setCurrentIndex(0)
+            self.flightModeCombo.currentIndexChanged.emit(0)
+        else:
+            self.flightModeCombo.setCurrentIndex(flightComboIndex)
+            self.flightModeCombo.currentIndexChanged.emit(flightComboIndex)
+
+    def _logging_error(self, log_conf, msg):
+        QMessageBox.about(self, "Log error", "Error when starting log config"
+                " [%s]: %s" % (log_conf.name, msg))
+
+    def _motor_data_received(self, timestamp, data, logconf):
+        if self.isVisible():
+            self.actualM1.setValue(data["motor.m1"])
+            self.actualM2.setValue(data["motor.m2"])
+            self.actualM3.setValue(data["motor.m3"])
+            self.actualM4.setValue(data["motor.m4"])
+        
+    def _baro_data_received(self, timestamp, data, logconf):
+        if self.isVisible():
+            self.actualASL.setText(("%.2f" % data["baro.aslLong"]))
+            self.ai.setBaro(data["baro.aslLong"])
+        
+    def _althold_data_received(self, timestamp, data, logconf):
+        if self.isVisible():
+            target = data["altHold.target"]
+            if target>0:
+                if not self.targetASL.isEnabled():
+                    self.targetASL.setEnabled(True) 
+                self.targetASL.setText(("%.2f" % target))
+                self.ai.setHover(target)    
+            elif self.targetASL.isEnabled():
+                self.targetASL.setEnabled(False)
+                self.targetASL.setText("Not set")   
+                self.ai.setHover(0)    
+        
+    def _imu_data_received(self, timestamp, data, logconf):
+        if self.isVisible():
+            self.actualRoll.setText(("%.2f" % data["stabilizer.roll"]))
+            self.actualPitch.setText(("%.2f" % data["stabilizer.pitch"]))
+            self.actualYaw.setText(("%.2f" % data["stabilizer.yaw"]))
+            self.actualThrust.setText("%.2f%%" %
+                                      self.thrustToPercentage(
+                                                      data["stabilizer.thrust"]))
+    
+            self.ai.setRollPitch(-data["stabilizer.roll"],
+                                 data["stabilizer.pitch"])
+
+    def connected(self, linkURI):
+        # IMU & THRUST
+        lg = LogConfig("Stabilizer", Config().get("ui_update_period"))
+        lg.add_variable("stabilizer.roll", "float")
+        lg.add_variable("stabilizer.pitch", "float")
+        lg.add_variable("stabilizer.yaw", "float")
+        lg.add_variable("stabilizer.thrust", "uint16_t")
+
+        try:
+            self.helper.cf.log.add_config(lg)
+            lg.data_received_cb.add_callback(self._imu_data_signal.emit)
+            lg.error_cb.add_callback(self._log_error_signal.emit)
+            lg.start()
+        except KeyError as e:
+            logger.warning(str(e))
+        except AttributeError as e:
+            logger.warning(str(e))
+
+        # MOTOR
+        lg = LogConfig("Motors", Config().get("ui_update_period"))
+        lg.add_variable("motor.m1")
+        lg.add_variable("motor.m2")
+        lg.add_variable("motor.m3")
+        lg.add_variable("motor.m4")
+
+        try:
+            self.helper.cf.log.add_config(lg)
+            lg.data_received_cb.add_callback(self._motor_data_signal.emit)
+            lg.error_cb.add_callback(self._log_error_signal.emit)
+            lg.start()
+        except KeyError as e:
+            logger.warning(str(e))
+        except AttributeError as e:
+            logger.warning(str(e))
+
+        if self.helper.cf.mem.ow_search(vid=0xBC, pid=0x01):
+            self._led_ring_effect.setEnabled(True)
+            self._led_ring_headlight.setEnabled(True)
+
+    def _set_available_sensors(self, name, available):
+        logger.info("[%s]: %s", name, available)
+        available = eval(available)
+        if ("HMC5883L" in name):
+            if (not available):
+                self.actualASL.setText("N/A")
+                self.actualASL.setEnabled(False)
+            else:
+                self.actualASL.setEnabled(True)
+                self.helper.inputDeviceReader.set_alt_hold_available(available)
+                if (not self.logBaro and not self.logAltHold):
+                    # The sensor is available, set up the logging
+                    self.logBaro = LogConfig("Baro", 200)
+                    self.logBaro.add_variable("baro.aslLong", "float")
+
+                    try:
+                        self.helper.cf.log.add_config(self.logBaro)
+                        self.logBaro.data_received_cb.add_callback(
+                                self._baro_data_signal.emit)
+                        self.logBaro.error_cb.add_callback(
+                                self._log_error_signal.emit)
+                        self.logBaro.start()
+                    except KeyError as e:
+                        logger.warning(str(e))
+                    except AttributeError as e:
+                        logger.warning(str(e))
+                    self.logAltHold = LogConfig("AltHold", 200)
+                    self.logAltHold.add_variable("altHold.target", "float")
+
+                    try:
+                        self.helper.cf.log.add_config(self.logAltHold)
+                        self.logAltHold.data_received_cb.add_callback(
+                            self._althold_data_signal.emit)
+                        self.logAltHold.error_cb.add_callback(
+                            self._log_error_signal.emit)
+                        self.logAltHold.start()
+                    except KeyError as e:
+                        logger.warning(str(e))
+                    except AttributeError:
+                        logger.warning(str(e))
+
+    def disconnected(self, linkURI):
+        self.ai.setRollPitch(0, 0)
+        self.actualM1.setValue(0)
+        self.actualM2.setValue(0)
+        self.actualM3.setValue(0)
+        self.actualM4.setValue(0)
+        self.actualRoll.setText("")
+        self.actualPitch.setText("")
+        self.actualYaw.setText("")
+        self.actualThrust.setText("")
+        self.actualASL.setText("")
+        self.targetASL.setText("Not Set")
+        self.targetASL.setEnabled(False)
+        self.actualASL.setEnabled(False)
+        self.clientXModeCheckbox.setEnabled(False)
+        self.logBaro = None
+        self.logAltHold = None
+        self._led_ring_effect.setEnabled(False)
+        self._led_ring_headlight.setEnabled(False)
+
+
+    def minMaxThrustChanged(self):
+        self.helper.inputDeviceReader.min_thrust = self.minThrust.value()
+        self.helper.inputDeviceReader.max_thrust = self.maxThrust.value()
+        if (self.isInCrazyFlightmode == True):
+            Config().set("min_thrust", self.minThrust.value())
+            Config().set("max_thrust", self.maxThrust.value())
+
+    def thrustLoweringSlewRateLimitChanged(self):
+        self.helper.inputDeviceReader.thrust_slew_rate = self.thrustLoweringSlewRateLimit.value()
+        self.helper.inputDeviceReader.thrust_slew_limit = self.slewEnableLimit.value()
+        if (self.isInCrazyFlightmode == True):
+            Config().set("slew_limit", self.slewEnableLimit.value())
+            Config().set("slew_rate", self.thrustLoweringSlewRateLimit.value())
+
+    def maxYawRateChanged(self):
+        logger.debug("MaxYawrate changed to %d", self.maxYawRate.value())
+        self.helper.inputDeviceReader.max_yaw_rate = self.maxYawRate.value()
+        if (self.isInCrazyFlightmode == True):
+            Config().set("max_yaw", self.maxYawRate.value())
+
+    def maxAngleChanged(self):
+        logger.debug("MaxAngle changed to %d", self.maxAngle.value())
+        self.helper.inputDeviceReader.max_rp_angle = self.maxAngle.value()
+        if (self.isInCrazyFlightmode == True):
+            Config().set("max_rp", self.maxAngle.value())
+
+    def _trim_pitch_changed(self, value):
+        logger.debug("Pitch trim updated to [%f]" % value)
+        self.helper.inputDeviceReader.trim_pitch = value
+        Config().set("trim_pitch", value)
+
+    def _trim_roll_changed(self, value):
+        logger.debug("Roll trim updated to [%f]" % value)
+        self.helper.inputDeviceReader.trim_roll = value
+        Config().set("trim_roll", value)
+
+    def calUpdateFromInput(self, rollCal, pitchCal):
+        logger.debug("Trim changed on joystick: roll=%.2f, pitch=%.2f",
+                     rollCal, pitchCal)
+        self.targetCalRoll.setValue(rollCal)
+        self.targetCalPitch.setValue(pitchCal)
+
+    def updateInputControl(self, roll, pitch, yaw, thrust):
+        self.targetRoll.setText(("%0.2f" % roll))
+        self.targetPitch.setText(("%0.2f" % pitch))
+        self.targetYaw.setText(("%0.2f" % yaw))
+        self.targetThrust.setText(("%0.2f %%" %
+                                   self.thrustToPercentage(thrust)))
+        self.thrustProgress.setValue(thrust)
+
+    def setMotorLabelsEnabled(self, enabled):
+        self.M1label.setEnabled(enabled)
+        self.M2label.setEnabled(enabled)
+        self.M3label.setEnabled(enabled)
+        self.M4label.setEnabled(enabled)
+
+    def emergencyStopStringWithText(self, text):
+        return ("<html><head/><body><p>"
+                "<span style='font-weight:600; color:#7b0005;'>{}</span>"
+                "</p></body></html>".format(text))
+
+    def updateEmergencyStop(self, emergencyStop):
+        if emergencyStop:
+            self.setMotorLabelsEnabled(False)
+            self.emergency_stop_label.setText(
+                      self.emergencyStopStringWithText("Kill switch active"))
+        else:
+            self.setMotorLabelsEnabled(True)
+            self.emergency_stop_label.setText("")
+
+    def flightmodeChange(self, item):
+        Config().set("flightmode", str(self.flightModeCombo.itemText(item)))
+        logger.debug("Changed flightmode to %s",
+                    self.flightModeCombo.itemText(item))
+        self.isInCrazyFlightmode = False
+        if (item == 0):  # Normal
+            self.maxAngle.setValue(Config().get("normal_max_rp"))
+            self.maxThrust.setValue(Config().get("normal_max_thrust"))
+            self.minThrust.setValue(Config().get("normal_min_thrust"))
+            self.slewEnableLimit.setValue(Config().get("normal_slew_limit"))
+            self.thrustLoweringSlewRateLimit.setValue(
+                                              Config().get("normal_slew_rate"))
+            self.maxYawRate.setValue(Config().get("normal_max_yaw"))
+        if (item == 1):  # Advanced
+            self.maxAngle.setValue(Config().get("max_rp"))
+            self.maxThrust.setValue(Config().get("max_thrust"))
+            self.minThrust.setValue(Config().get("min_thrust"))
+            self.slewEnableLimit.setValue(Config().get("slew_limit"))
+            self.thrustLoweringSlewRateLimit.setValue(
+                                                  Config().get("slew_rate"))
+            self.maxYawRate.setValue(Config().get("max_yaw"))
+            self.isInCrazyFlightmode = True
+
+        if (item == 0):
+            newState = False
+        else:
+            newState = True
+        self.maxThrust.setEnabled(newState)
+        self.maxAngle.setEnabled(newState)
+        self.minThrust.setEnabled(newState)
+        self.thrustLoweringSlewRateLimit.setEnabled(newState)
+        self.slewEnableLimit.setEnabled(newState)
+        self.maxYawRate.setEnabled(newState)
+
+    @pyqtSlot(bool)
+    def changeXmode(self, checked):
+        self.helper.cf.commander.set_client_xmode(checked)
+        Config().set("client_side_xmode", checked)
+        logger.info("Clientside X-mode enabled: %s", checked)
+
+    def alt1_updated(self, state):
+        if state:
+            self._ring_effect += 1
+            if self._ring_effect > self._ledring_nbr_effects:
+                self._ring_effect = 0
+            self.helper.cf.param.set_value("ring.effect", str(self._ring_effect))
+
+    def alt2_updated(self, state):
+        self.helper.cf.param.set_value("ring.headlightEnable", str(state))
+
+    def _ring_populate_dropdown(self):
+        try:
+            nbr = int(self.helper.cf.param.values["ring"]["neffect"])
+            current = int(self.helper.cf.param.values["ring"]["effect"])
+        except KeyError:
+            return
+
+        hardcoded_names = {0: "Off",
+                           1: "White spinner",
+                           2: "Color spinner",
+                           3: "Tilt effect",
+                           4: "Brightness effect",
+                           5: "Color spinner 2",
+                           6: "Double spinner",
+                           7: "Solid color effect",
+                           8: "Factory test",
+                           9: "Battery status",
+                           10: "Boat lights",
+                           11: "Alert",
+                           12: "Gravity"}
+
+        for i in range(nbr+1):
+            name = "{}: ".format(i)
+            if i in hardcoded_names:
+                name += hardcoded_names[i]
+            else:
+                name += "N/A"
+            self._led_ring_effect.addItem(name, QVariant(i))
+
+        self._led_ring_effect.setCurrentIndex(current)
+        self._led_ring_effect.currentIndexChanged.connect(self._ring_effect_changed)
+        self.helper.cf.param.add_update_callback(group="ring",
+                                         name="effect",
+                                         cb=self._ring_effect_updated)
+
+    def _ring_effect_changed(self, index):
+        i = self._led_ring_effect.itemData(index).toInt()[0]
+        logger.info("Changed effect to {}".format(i))
+        if i != self.helper.cf.param.values["ring"]["effect"]:
+            self.helper.cf.param.set_value("ring.effect", str(i))
+
+    def _ring_effect_updated(self, name, value):
+        self._led_ring_effect.setCurrentIndex(int(value))
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/FlightTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..286da7cc9555d72815377df2eb0c6ba072978286
GIT binary patch
literal 22594
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.py
new file mode 100755
index 00000000..613350a1
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+This tab plots different logging data defined by configurations that has been
+pre-configured.
+"""
+import math
+
+__author__ = 'Bitcraze AB'
+__all__ = ['GpsTab']
+
+import glob
+import json
+import logging
+import os
+import sys
+
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from pprint import pprint
+import datetime
+
+#from cfclient.ui.widgets.plotwidget import PlotWidget
+
+from cflib.crazyflie.log import Log, LogVariable, LogConfig
+
+from cfclient.ui.tab import Tab
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+try:
+    from PyKDE4.marble import *
+    should_enable_tab = True
+except:
+    should_enable_tab = False
+
+import sys
+
+gps_tab_class = uic.loadUiType(sys.path[0] +
+                                "/cfclient/ui/tabs/gpsTab.ui")[0]
+
+class GpsTab(Tab, gps_tab_class):
+    """Tab for plotting logging data"""
+
+    _log_data_signal = pyqtSignal(int, object, object)
+    _log_error_signal = pyqtSignal(object, str)
+
+    _disconnected_signal = pyqtSignal(str)
+    _connected_signal = pyqtSignal(str)
+    _console_signal = pyqtSignal(str)
+
+
+    def __init__(self, tabWidget, helper, *args):
+        super(GpsTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "GPS"
+        self.menuName = "GPS"
+
+        self.tabWidget = tabWidget
+        self.helper = helper
+        self._cf = helper.cf
+        self._got_home_point = False
+        self._line = ""
+
+        if not should_enable_tab:
+            self.enabled = False
+
+        if self.enabled:
+           # create the marble widget
+            #self._marble = Marble.MarbleWidget()
+            self._marble = FancyMarbleWidget()
+
+            # Load the OpenStreetMap map
+            self._marble.setMapThemeId("earth/openstreetmap/openstreetmap.dgml")
+
+            # Enable the cloud cover and enable the country borders
+            self._marble.setShowClouds(True)
+            self._marble.setShowBorders(True)
+
+            # Hide the FloatItems: Compass and StatusBar
+            self._marble.setShowOverviewMap(False)
+            self._marble.setShowScaleBar(False)
+            self._marble.setShowCompass(False)
+
+            self._marble.setShowGrid(False)
+            self._marble.setProjection(Marble.Mercator)
+
+            # Change the map to center on Australia
+
+            self._marble.zoomView(10)
+
+            # create the slider
+            self.zoomSlider = QSlider(Qt.Horizontal)
+
+            self._reset_max_btn.clicked.connect(self._reset_max)
+
+            # add all the components
+            #self.gpslayout.addWidget(self._marble)
+            self.map_layout.addWidget(self._marble)
+            # Connect the signals
+            self._log_data_signal.connect(self._log_data_received)
+            self._log_error_signal.connect(self._logging_error)
+            self._connected_signal.connect(self._connected)
+            self._disconnected_signal.connect(self._disconnected)
+
+            # Connect the callbacks from the Crazyflie API
+            self.helper.cf.disconnected.add_callback(
+                self._disconnected_signal.emit)
+            self.helper.cf.connected.add_callback(
+                self._connected_signal.emit)
+
+        else:
+            logger.warning("GPS tab not enabled since no Python"
+                           "bindings for Marble was found")
+
+        self._max_speed = 0.0
+
+        self._fix_types = {
+            0: "No fix",
+            1: "Dead reckoning only",
+            2: "2D-fix",
+            3: "3D-fix",
+            4: "GNSS+dead",
+            5: "Time only fix"
+        }
+
+    def _connected(self, link_uri):
+        lg = LogConfig("GPS", 100)
+        lg.add_variable("gps.lat")
+        lg.add_variable("gps.lon")
+        lg.add_variable("gps.hMSL")
+        lg.add_variable("gps.heading")
+        lg.add_variable("gps.gSpeed")
+        lg.add_variable("gps.hAcc")
+        lg.add_variable("gps.fixType")
+        try:
+            self._cf.log.add_config(lg)
+            lg.data_received_cb.add_callback(self._log_data_signal.emit)
+            lg.error_cb.add_callback(self._log_error_signal.emit)
+            lg.start()
+        except KeyError as e:
+            logger.warning(str(e))
+        except AttributeError as e:
+            logger.warning(str(e))
+        self._max_speed = 0.0
+
+    def _disconnected(self, link_uri):
+        """Callback for when the Crazyflie has been disconnected"""
+        self._got_home_point = False
+        return
+
+    def _logging_error(self, log_conf, msg):
+        """Callback from the log layer when an error occurs"""
+        QMessageBox.about(self, "Plot error", "Error when starting log config"
+                " [%s]: %s" % (log_conf.name, msg))
+
+    def _reset_max(self):
+        """Callback from reset button"""
+        self._max_speed = 0.0
+        self._speed_max.setText(str(self._max_speed))
+        self._marble.clear_data()
+
+        self._long.setText("")
+        self._lat.setText("")
+        self._height.setText("")
+
+        self._speed.setText("")
+        self._heading.setText("")
+        self._accuracy.setText("")
+
+        self._fix_type.setText("")
+
+
+    def _log_data_received(self, timestamp, data, logconf):
+        """Callback when the log layer receives new data"""
+
+        long = float(data["gps.lon"])/10000000.0
+        lat = float(data["gps.lat"])/10000000.0
+        alt = float(data["gps.hMSL"])/1000.0
+        speed = float(data["gps.gSpeed"])/1000.0
+        accuracy = float(data["gps.hAcc"])/1000.0
+        fix_type = float(data["gps.fixType"])
+        heading = float(data["gps.heading"])
+
+        self._long.setText(str(long))
+        self._lat.setText(str(lat))
+        self._height.setText(str(alt))
+
+        self._speed.setText(str(speed))
+        self._heading.setText(str(heading))
+        self._accuracy.setText(str(accuracy))
+        if speed > self._max_speed:
+            self._max_speed = speed
+        self._speed_max.setText(str(self._max_speed))
+
+        self._fix_type.setText(self._fix_types[fix_type])
+
+
+        point = Marble.GeoDataCoordinates(long, lat, alt,
+                                             Marble.GeoDataCoordinates.Degree)
+        if not self._got_home_point:
+            self._got_home_point = True
+
+            self._marble.centerOn(point, True)
+            self._marble.zoomView(4000, Marble.Jump)
+
+        self._marble.add_data(long, lat, alt, accuracy,
+                              True if fix_type == 3 else False)
+
+# If Marble is not installed then do not create MarbleWidget subclass
+if should_enable_tab:
+    class FancyMarbleWidget(Marble.MarbleWidget):
+        def __init__(self):
+            Marble.MarbleWidget.__init__(self)
+            self._points = []
+            self._lat = None
+            self._long = None
+            self._height = None
+            self._accu =  None
+
+        def clear_data(self):
+            self._points = []
+            self._lat = None
+            self._long = None
+            self._height = None
+            self._accu =  None
+
+        def add_data(self, long, lat, height, accu, locked):
+            self._points.append([long, lat, height, accu, locked])
+            self._lat = lat
+            self._long = long
+            self._height = height
+            self._accu =  accu
+            self.update()
+
+        def customPaint(self, painter):
+            if self._lat:
+                current = Marble.GeoDataCoordinates(self._long,
+                                                    self._lat,
+                                                    self._height,
+                                                    Marble.GeoDataCoordinates.Degree)
+
+                #Paint data points
+                for p in self._points:
+                    pos = Marble.GeoDataCoordinates(p[0],
+                                                    p[1],
+                                                    p[2],
+                                                    Marble.GeoDataCoordinates.Degree)
+                    if p[4]:
+                        painter.setPen(Qt.green)
+                    else:
+                        painter.setPen(Qt.red)
+                    painter.drawEllipse(pos, 1, 1)
+
+                # Paint accuracy
+                painter.setPen(Qt.blue)
+                painter.setBrush(QtGui.QBrush(QtGui.QColor(0, 0, 255, 64)))
+                pixel_per_meter = self.radiusFromDistance(self.distance())/(6371.0*1000)
+                painter.drawEllipse(current, self._accu*pixel_per_meter, self._accu*pixel_per_meter, False)
+
+                #Paint Crazyflie
+                painter.setPen(Qt.black)
+                painter.setBrush(Qt.NoBrush)
+                painter.drawText(current, "Crazyflie")
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/GpsTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..b3788bae74fe8f0f52ba0fc1fd4c7819e244ff81
GIT binary patch
literal 8654
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
WsjOAlt`g&*t~7vOscipr-uYhv3_p_q

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.py
new file mode 100755
index 00000000..b51292e6
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+#  02110-1301, USA.
+
+"""
+Basic tab to be able to set (and test) colors in the LED-ring.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LEDTab']
+
+import logging
+import sys
+
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtGui, uic
+from PyQt4.QtCore import pyqtSignal
+from PyQt4.QtGui import QColorDialog
+
+from cfclient.ui.tab import Tab
+
+from cflib.crazyflie.mem import MemoryElement
+
+led_tab_class = uic.loadUiType(sys.path[0] +
+                                "/cfclient/ui/tabs/ledTab.ui")[0]
+
+class LEDTab(Tab, led_tab_class):
+    """Tab for plotting logging data"""
+
+    _connected_signal = pyqtSignal(str)
+    _disconnected_signal = pyqtSignal(str)
+
+    def __init__(self, tabWidget, helper, *args):
+        super(LEDTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "LED"
+        self.menuName = "LED tab"
+        self.tabWidget = tabWidget
+
+        self._helper = helper
+
+        # Always wrap callbacks from Crazyflie API though QT Signal/Slots
+        # to avoid manipulating the UI when rendering it
+        self._connected_signal.connect(self._connected)
+        self._disconnected_signal.connect(self._disconnected)
+
+        # Connect the Crazyflie API callbacks to the signals
+        self._helper.cf.connected.add_callback(
+            self._connected_signal.emit)
+
+        self._helper.cf.disconnected.add_callback(
+            self._disconnected_signal.emit)
+
+        self._btns = [self._u1,
+                      self._u2,
+                      self._u3,
+                      self._u4,
+                      self._u5,
+                      self._u6,
+                      self._u7,
+                      self._u8,
+                      self._u9,
+                      self._u10,
+                      self._u11,
+                      self._u12]
+
+        self._intensity = 1
+
+        self._u1.clicked.connect(lambda: self._select(0))
+        self._u2.clicked.connect(lambda: self._select(1))
+        self._u3.clicked.connect(lambda: self._select(2))
+        self._u4.clicked.connect(lambda: self._select(3))
+        self._u5.clicked.connect(lambda: self._select(4))
+        self._u6.clicked.connect(lambda: self._select(5))
+        self._u7.clicked.connect(lambda: self._select(6))
+        self._u8.clicked.connect(lambda: self._select(7))
+        self._u9.clicked.connect(lambda: self._select(8))
+        self._u10.clicked.connect(lambda: self._select(9))
+        self._u11.clicked.connect(lambda: self._select(10))
+        self._u12.clicked.connect(lambda: self._select(11))
+
+        self._mem = None
+
+        self._intensity_slider.valueChanged.connect(self._intensity_change)
+        self._intensity_slider.valueChanged.connect(
+            self._intensity_spin.setValue)
+        self._intensity_spin.valueChanged.connect(
+            self._intensity_slider.setValue)
+
+    def _select(self, nbr):
+        col = QColorDialog()
+        col = QtGui.QColorDialog.getColor()
+        if col.isValid() and self._mem:
+            logger.info(col.red())
+            self._mem.leds[nbr].set(r=col.red(), g=col.green(), b=col.blue())
+            self.sender().setStyleSheet("background-color: rgb({},{},{})"
+                                        .format(col.red(), col.green(),
+                                                col.blue()))
+            self._write_led_output()
+
+    def _intensity_change(self, value):
+        self._intensity = value
+        self._write_led_output()
+
+    def _write_led_output(self):
+        if self._mem:
+            for led in self._mem.leds:
+                led.intensity = self._intensity
+            self._mem.write_data(self._led_write_done)
+        else:
+            logger.info("No LED-ring memory found!")
+
+    def _led_write_done(self, mem, addr):
+        logger.info("LED write done callback")
+
+    def _connected(self, link_uri):
+        """Callback when the Crazyflie has been connected"""
+        self._mem = self._helper.cf.mem.get_mems(
+            MemoryElement.TYPE_DRIVER_LED)[0]
+        logger.info(self._mem)
+        if self._mem:
+            for btn in self._btns:
+                btn.setEnabled(True)
+                btn.setStyleSheet("background-color: black")
+                self._intensity_slider.setEnabled(True)
+                self._intensity_spin.setEnabled(True)
+
+    def _disconnected(self, link_uri):
+        """Callback for when the Crazyflie has been disconnected"""
+        for btn in self._btns:
+            btn.setEnabled(False)
+            btn.setStyleSheet("background-color: none")
+            self._intensity_slider.setEnabled(False)
+            self._intensity_spin.setEnabled(False)
+            self._intensity_slider.setValue(100)
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LEDTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..6687928d80f5b2fa0155ed7409e097da94404bfe
GIT binary patch
literal 7144
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.py
new file mode 100755
index 00000000..7437823c
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Shows all the parameters available in the Crazyflie and also gives the ability
+to edit them.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LogBlockTab']
+
+import time
+import sys
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+from cfclient.ui.tab import Tab
+
+logblock_tab_class = uic.loadUiType(sys.path[0] +
+                                 "/cfclient/ui/tabs/logBlockDebugTab.ui")[0]
+
+class LogBlockDebugTab(Tab, logblock_tab_class):
+    """
+    Used to show debug-information about log status.
+    """
+
+    _blocks_updated_signal = pyqtSignal(object, bool)
+    _disconnected_signal = pyqtSignal(str)
+
+    def __init__(self, tabWidget, helper, *args):
+        super(LogBlockDebugTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Log Blocks Debugging"
+        self.menuName = "Log Blocks Debugging"
+
+        self._helper = helper
+        self.tabWidget = tabWidget
+
+        self._helper.cf.log.block_added_cb.add_callback(self._block_added)
+        self._disconnected_signal.connect(self._disconnected)
+        self._helper.cf.disconnected.add_callback(self._disconnected_signal.emit)
+        self._blocks_updated_signal.connect(self._update_tree)
+
+        self._block_tree.setHeaderLabels(['Id', 'Name', 'Period (ms)', 'Added', 'Started', 'Error', 'Contents'])
+        self._block_tree.sortItems(0, QtCore.Qt.AscendingOrder)
+
+    def _block_added(self, block):
+        """Callback when a new logblock has been created"""
+        block.added_cb.add_callback(self._blocks_updated_signal.emit)
+        block.started_cb.add_callback(self._blocks_updated_signal.emit)
+
+    def _update_tree(self, conf, value):
+        """Update the block tree"""
+        self._block_tree.clear()
+        for block in self._helper.cf.log.log_blocks:
+            item = QtGui.QTreeWidgetItem()
+            item.setFlags(Qt.ItemIsEnabled |
+                          Qt.ItemIsSelectable)
+            item.setData(0, Qt.DisplayRole, block.id)
+            item.setData(1, Qt.EditRole, block.name)
+            item.setData(2, Qt.DisplayRole, (block.period_in_ms))
+            item.setData(3, Qt.DisplayRole, block.added)
+            item.setData(4, Qt.EditRole, block.started)
+            item.setData(5, Qt.EditRole, block.err_no)
+            for var in block.variables:
+                subItem = QtGui.QTreeWidgetItem()
+                subItem.setFlags(Qt.ItemIsEnabled |
+                              Qt.ItemIsSelectable)
+                subItem.setData(6, Qt.EditRole, var.name)
+                item.addChild(subItem)                
+
+            self._block_tree.addTopLevelItem(item)
+            self._block_tree.expandItem(item)
+
+    def _disconnected(self, link_uri):
+        """Callback when the Crazyflie is disconnected"""
+        self._block_tree.clear()
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockDebugTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..072ba320bb399515f2283559891157209c168abd
GIT binary patch
literal 3746
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
NH=K^!Mk;N%{{XvRS2zFw

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.py
new file mode 100755
index 00000000..98eb6ce9
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.py
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+This tab shows all log blocks that are registered and can be used to start the
+logging and also to write the logging data to file.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LogBlockTab']
+
+import sys
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+from cfclient.ui.tab import Tab
+
+logblock_tab_class = uic.loadUiType(sys.path[0] +
+                                 "/cfclient/ui/tabs/logBlockTab.ui")[0]
+
+import logging
+logger = logging.getLogger(__name__)
+
+from PyQt4.QtGui import QApplication, QStyledItemDelegate, QAbstractItemView
+from PyQt4.QtGui import QStyleOptionButton, QStyle
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import pyqtSlot, pyqtSignal, QThread
+from PyQt4.QtCore import QAbstractItemModel, QModelIndex, QString, QVariant
+
+from cfclient.utils.logdatawriter import LogWriter
+
+
+class LogBlockChildItem(object):
+    """Class that acts as a child in the tree and represents one variable in
+    a log block"""
+    def __init__(self, parent, name):
+        """Initialize the node"""
+        self.parent = parent
+        self.name = name
+
+    def child_count(self):
+        """Return the number of children this node has"""
+        return 0
+
+
+class LogBlockItem(object):
+    """Class that acts as a parent in the tree view and represents a complete
+    log block"""
+
+    def __init__(self, block, model, connected_ts):
+        """Initialize the parent node"""
+        super(LogBlockItem, self).__init__()
+        self._block = block
+        self.parent = None
+        self.children = []
+        self.name = block.name
+        self.id = block.id
+        self.period = block.period_in_ms
+        self._model = model
+        self._log_file_writer = LogWriter(block, connected_ts)
+
+        self._block.started_cb.add_callback(self._set_started)
+        self._block.added_cb.add_callback(self._set_added)
+        self._block.error_cb.add_callback(self._log_error)
+
+        self._var_list = ""
+
+        for b in block.variables:
+            self.children.append(LogBlockChildItem(self, b.name))
+            self._var_list += "%s/" % b.name
+        self._var_list = self._var_list[:-1]
+
+        self._block_started = False
+        self._doing_transaction = False
+
+    def _log_error(self, logconfig, msg):
+        """Callback when there's an error starting the block in the Crazyflie"""
+        # Do nothing here, a pop-up will notify the user that the
+        # starting failed
+        self._doing_transaction = False
+
+    def _set_started(self, conf, started):
+        """Callback when a block has been started in the Crazyflie"""
+        logger.debug("%s started: %s", self.name, started)
+        if started:
+             self._block_started = True
+        else:
+             self._block_started = False
+        self._doing_transaction = False
+        self._model.refresh()
+
+    def logging_started(self):
+        """Return True if the block has been started, otherwise False"""
+        return self._block_started
+
+    def writing_to_file(self):
+        """Return True if the block is being logged to file, otherwise False"""
+        return self._log_file_writer.writing()
+
+    def start_writing_to_file(self):
+        """Start logging to file for this block"""
+        self._log_file_writer.start()
+
+    def stop_writing_to_file(self):
+        """Stop logging to file for this block"""
+        self._log_file_writer.stop()
+
+    def start(self):
+        """Start the logging of this block"""
+        self._doing_transaction = True
+        self._block.start()
+
+    def stop(self):
+        """Stop the logging of this block"""
+        self._doing_transaction = True
+        self._block.delete()
+
+    def doing_transaction(self):
+        """Return True if a block is being added or started, False when it's
+        been added/started/failed"""
+        return self._doing_transaction
+
+    def _set_added(self, conf, started):
+        """Callback when a block has been added to the Crazyflie"""
+        logger.debug("%s added: %s", self.name, started)
+
+    def var_list(self):
+        """Return a string containing all the variable names of the children"""
+        return self._var_list
+
+    def child_count(self):
+        """Return the number of children this node has"""
+        return len(self.children)
+
+    def get_child(self, index):
+        return self.children[index]
+
+
+class LogBlockModel(QAbstractItemModel):
+    def __init__(self, view, parent=None):
+        super(LogBlockModel, self).__init__(parent)
+        self._nodes = []
+        self._column_headers = ['Id', 'Name', 'Period (ms)', 'Start',
+                                'Write to file', 'Contents']
+        self._view = view
+        self._nodes_written_to_file = []
+
+    def add_block(self, block, connected_ts):
+        self._nodes.append(LogBlockItem(block, self, connected_ts))
+        self.layoutChanged.emit()
+
+    def refresh(self):
+        """Force a refresh of the view though the model"""
+        self.layoutChanged.emit()
+
+    def clicked(self, index):
+        """Callback when a cell has been clicked (mouse down/up on same cell)"""
+        node = index.internalPointer()
+        if not node.parent and index.column() == 3:
+            if node.logging_started():
+                node.stop()
+            else:
+                node.start()
+        if not node.parent and index.column() == 4:
+            if node.writing_to_file():
+                node.stop_writing_to_file()
+            else:
+                node.start_writing_to_file()
+        self.layoutChanged.emit()
+
+    def parent(self, index):
+        """Re-implemented method to get the parent of the given index"""
+        if not index.isValid():
+            return QModelIndex()
+
+        node = index.internalPointer()
+        if node.parent is None:
+            return QModelIndex()
+        else:
+            return self.createIndex(self._nodes.index(node.parent), 0,
+                                    node.parent)
+
+    def remove_block(self, block):
+        """Remove a block from the view"""
+        raise NotImplementedError()
+
+    def columnCount(self, parent):
+        """Re-implemented method to get the number of columns"""
+        return len(self._column_headers)
+
+    def headerData(self, section, orientation, role):
+        """Re-implemented method to get the headers"""
+        if role == Qt.DisplayRole:
+            return QString(self._column_headers[section])
+
+    def rowCount(self, parent):
+        """Re-implemented method to get the number of rows for a given index"""
+        parent_item = parent.internalPointer()
+        if parent.isValid():
+            parent_item = parent.internalPointer()
+            return parent_item.child_count()
+        else:
+            return len(self._nodes)
+
+    def index(self, row, column, parent):
+        """Re-implemented method to get the index for a specified
+        row/column/parent combination"""
+        if not self._nodes:
+            return QModelIndex()
+        node = parent.internalPointer()
+        if not node:
+            index = self.createIndex(row, column, self._nodes[row])
+            self._nodes[row].index = index
+            return index
+        else:
+            return self.createIndex(row, column, node.get_child(row))
+
+    def data(self, index, role):
+        """Re-implemented method to get the data for a given index and role"""
+        node = index.internalPointer()
+        parent = node.parent
+        if parent:
+            if role == Qt.DisplayRole and index.column() == 5:
+                return node.name
+        elif not parent and role == Qt.DisplayRole and index.column() == 5:
+            return node.var_list()
+        elif not parent and role == Qt.DisplayRole:
+            if index.column() == 0:
+                return node.id
+            if index.column() == 1:
+                return node.name
+            if index.column() == 2:
+                return str(node.period)
+        if role == Qt.TextAlignmentRole and \
+                (index.column() == 4 or index.column() == 3):
+            return Qt.AlignHCenter | Qt.AlignVCenter
+
+        return QVariant()
+
+    def reset(self):
+        """Reset the model"""
+        # Stop the logging to file
+        for node in self._nodes:
+            if node.writing_to_file():
+                node.stop_writing_to_file()
+        self._nodes = []
+        self.layoutChanged.emit()
+
+
+class CheckboxDelegate(QStyledItemDelegate):
+    """Custom delegate for rending checkboxes in the table"""
+
+    def paint(self, painter, option, index):
+        """Re-implemented paint method"""
+        item = index.internalPointer()
+        col = index.column()
+        if not item.parent and (col == 3 or col == 4):
+            s = QStyleOptionButton()
+            checkbox_rect = QApplication.style().\
+                subElementRect(QStyle.SE_CheckBoxIndicator, option)
+            s.rect = option.rect
+            center_offset = s.rect.width() / 2 - checkbox_rect.width() / 2
+            s.rect.adjust(center_offset, 0, 0, 0)
+
+            if col == 3:
+                if not item.doing_transaction():
+                    s.state = QStyle.State_Enabled
+                if item.logging_started():
+                    s.state |= QStyle.State_On
+
+            if col == 4:
+                s.state = QStyle.State_Enabled
+                if item.writing_to_file():
+                    s.state |= QStyle.State_On
+
+            QApplication.style().drawControl(
+                QStyle.CE_CheckBox, s, painter)
+
+        else:
+            super(CheckboxDelegate, self).paint(painter, option, index)
+
+
+class LogBlockTab(Tab, logblock_tab_class):
+    """
+    Used to show debug-information about logblock status.
+    """
+
+    _blocks_updated_signal = pyqtSignal(bool)
+    _disconnected_signal = pyqtSignal(str)
+
+    def __init__(self, tabWidget, helper, *args):
+        """Initialize the tab"""
+        super(LogBlockTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Log Blocks"
+        self.menuName = "Log Blocks"
+
+        self._helper = helper
+        self.tabWidget = tabWidget
+
+        self._helper.cf.log.block_added_cb.add_callback(self._block_added)
+        self._disconnected_signal.connect(self._disconnected)
+        self._helper.cf.disconnected.add_callback(
+            self._disconnected_signal.emit)
+
+        self._model = LogBlockModel(self._block_tree)
+        self._block_tree.setModel(self._model)
+        self._block_tree.clicked.connect(self._model.clicked)
+        self._block_tree.setItemDelegate(CheckboxDelegate())
+        self._block_tree.setSelectionMode(QAbstractItemView.NoSelection)
+
+    def _block_added(self, block):
+        """Callback from logging layer when a new block is added"""
+        self._model.add_block(block, self._helper.cf.connected_ts)
+
+    def _disconnected(self, link_uri):
+        """Callback when the Crazyflie is disconnected"""
+        self._model.reset()
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogBlockTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..c71e399d654417ad5df9fb3c9cbc87ec96f9b9b3
GIT binary patch
literal 14996
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.py
new file mode 100755
index 00000000..629b452f
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Shows the Log TOC of available variables in the Crazyflie.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LogTab']
+
+import time
+import sys
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+from cflib.crazyflie import Crazyflie
+
+from cfclient.ui.tab import Tab
+
+param_tab_class = uic.loadUiType(sys.path[0] +
+                                 "/cfclient/ui/tabs/logTab.ui")[0]
+
+
+class LogTab(Tab, param_tab_class):
+    connectedSignal = pyqtSignal(str)
+    disconnectedSignal = pyqtSignal(str)
+
+    def __init__(self, tabWidget, helper, *args):
+        super(LogTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Log TOC"
+        self.menuName = "Log TOC"
+
+        self.helper = helper
+        self.tabWidget = tabWidget
+
+        self.cf = helper.cf
+
+        # Init the tree widget
+        self.logTree.setHeaderLabels(['Name', 'ID', 'Unpack', 'Storage'])
+
+        self.cf.connected.add_callback(self.connectedSignal.emit)
+        self.connectedSignal.connect(self.connected)
+
+        # Clear the log TOC list when the Crazyflie is disconnected
+        self.cf.disconnected.add_callback(self.disconnectedSignal.emit)
+        self.disconnectedSignal.connect(self.disconnected)
+
+    @pyqtSlot('QString')
+    def disconnected(self, linkname):
+        self.logTree.clear()
+
+    @pyqtSlot(str)
+    def connected(self, linkURI):
+        self.logTree.clear()
+
+        toc = self.cf.log.toc
+
+        for group in toc.toc.keys():
+            groupItem = QtGui.QTreeWidgetItem()
+            groupItem.setData(0, Qt.DisplayRole, group)
+            for param in toc.toc[group].keys():
+                item = QtGui.QTreeWidgetItem()
+                item.setData(0, Qt.DisplayRole, param)
+                item.setData(1, Qt.DisplayRole, toc.toc[group][param].ident)
+                item.setData(2, Qt.DisplayRole, toc.toc[group][param].pytype)
+                item.setData(3, Qt.DisplayRole, toc.toc[group][param].ctype)
+                groupItem.addChild(item)
+
+            self.logTree.addTopLevelItem(groupItem)
+            self.logTree.expandItem(groupItem)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/LogTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..e9ffc7219c6f8354eb9c3150ad53016588c9790b
GIT binary patch
literal 2830
zcmcguUvC>l5TCpA|2T=0HYtG$EP;?8Xe=c9Rw3F%YNJLeIVV({gihzXjlJ%j&%Irj
z8YNHVkuSlQ;7h=_;RC>L<{T#oUW@G=@6OK7&Y$1Ro%iQ@>skE#yO@?A5C7j_m_I-g
zq5#yPz@fyY5hgAr9wik@Jqj=>=M@SnGOtovrL0Cljj}ogb;=qPG{|AO8l_FjS`@S>
zTcco&vNi>6%GN1Zr)-0QjZ&^o=_X|z3OePwLFpD{+Z1e*qZ%~1L#jfPyOcC(FKN-e
zcd!l9V24dq+oX2r0#>fk&qVKtz7KZU-a)U;UOfV74aWJ8rfbKldz_EDXD<%A`9(K+
z6X`S>rmFiUDl|__S5HOipoo52T%=m<x6B$8Jkd4=OLZSVu{Err<t!TNzwkNfaR7yT
zY7g>4*(%nj_UT+(-kIyT#~kYl+cxkwTfDY|G`F^Oz0jj+l-fFroQ(?=C8BU}^z`KM
zanAwUHbGWql`K}zX5f#^M<D(9B8JgZ+n?*cjfSS5ilO^3f`$0QW<0?#0}zFDJERar
zj}TSl^k!aVFd(AHtg164IP9*(7?KTUmZy+v4m_Am@&~;N^u}x&PuTOP80L2ni=RW6
zkacO|(xpeg5II;+JmES>zwC#=RynECB?O7W8eNv0I&&rsVG5lzY0_fLN@ylk8dcaD
zQ4I8~(WK4NlXaSGtTwRN6z<xJ1=-HE?AC2rEK1pq$R-4AF$zu$Cv1r*%kG{;nUX{w
z9ZCYvr?V)2C3zj#yog4sx6Ogzu$cKw6+R{;m<U5X)iw-8*eE-nooksRkD``=ld0)k
z7JU6!rO+V2fXqvsjFgqj;|n>+xi6Hmn~;UVXF!!Ij-#PUjU0;e=~Ts5C6)^%N|G>+
z(salM^O4o|paduTr!s9t_i|HCCfclIExUJ{Q63ghNQ8_^FZ>+{`FBBt6%`}X!-9mE
z7nt?Od8YcqW!3d(MLvP0rq4wvb@`ygxp^>K*l|AXr+WBr@RwD(KU?@6=zNG_J^&$S
z*I9R)PE$tPyYKEfcf37E96KG@LQh9AmkAH9y#oxh1;Qx852b(&fdOxy^jOfp2O^1$
zQxz2@ugg46T&kz9rU1u#_VZ(0^&G>nMdXSXj8Puq#X}hU4=;Fudh%#ep(~f(ZaZ9F
zXtz~%^6&@8x$?NXP3pY9s?a6&8eE>h5b6#^{LUli?Qc?+*!F1e`s}ZCRlRi<B@8{c
z&*Bc8U2@%PjdDNhxvWAL>hnehrj)n-Q>J}ert;r1>!l2=-(WkqLA)N*%VjW7-#1MI
zA?aMNUw=U_CDUB8C(>#)CtY)dwFlB;WJvb?PkFWrBAp-gC;v8&1?R8S!uWhl%IlPC
zvn<*pt1<x}s`N0jk<X=O`MGnb%`A-;ex8<(6`i2DOJ@P@7qgNX%ZyQm8a^27G?5Fi
zILl|p>Wxa}0)E!0x3g%P@Uq8{@Vg*ouK+&J{UiT#X0WTHBA?HsO~_0%Fn<;mQ6}za
z)&kCZEZaS3=XPN3^F8jmyXo#aZIthaLO*l(yXAJA9y_;eqg-Zaqs$4zEKlYsmOeL3
z$%e6o&(BK#6DHgkeD=%d(((Di8=qwMUiuNPp)WBETUmoMZMV6*Q|U<$;mm|#lE*7A
zqB)vev3xd!A-Xi?0uqhq4TI1ujHK<`#i{+uXMhUQeBP5LEgSpg8;ano^qRxs3ltbX
zW#7kp&v1X$X?=e2SZ<U}7jF~$c;NUFb0%pXCFlB#@yJHaB0H7?f|U@WVVp+B$g}Q$
gh_2w^QJML#Ijx3EvgX#%@j24>ou1p_ue0I(4VbN2DF6Tf

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.py
new file mode 100755
index 00000000..7ac7fada
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Shows all the parameters available in the Crazyflie and also gives the ability
+to edit them.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['ParamTab']
+
+import sys
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+from PyQt4.QtCore import QAbstractItemModel, QModelIndex, QString, QVariant
+from PyQt4.QtGui import QApplication, QStyledItemDelegate, QAbstractItemView, QBrush, QColor
+from PyQt4.QtGui import QSortFilterProxyModel
+
+from cfclient.ui.tab import Tab
+
+param_tab_class = uic.loadUiType(sys.path[0] +
+                                 "/cfclient/ui/tabs/paramTab.ui")[0]
+
+import logging
+logger = logging.getLogger(__name__)
+
+class ParamChildItem(object):
+    """Represents a leaf-node in the tree-view (one parameter)"""
+    def __init__(self, parent, name, crazyflie):
+        """Initialize the node"""
+        self.parent = parent
+        self.name = name
+        self.ctype = None
+        self.access = None
+        self.value = ""
+        self._cf = crazyflie
+        self.is_updating = True
+
+    def updated(self, name, value):
+        """Callback from the param layer when a parameter has been updated"""
+        self.value = value
+        self.is_updating = False
+        self.parent.model.refresh()
+
+    def set_value(self, value):
+        """Send the update value to the Crazyflie. It will automatically be
+        read again after sending and then the updated callback will be
+        called"""
+        complete_name = "%s.%s" % (self.parent.name, self.name)
+        self._cf.param.set_value(complete_name, value)
+        self.is_updating = True
+
+    def child_count(self):
+        """Return the number of children this node has"""
+        return 0
+
+
+class ParamGroupItem(object):
+    """Represents a parameter group in the tree-view"""
+    def __init__(self, name, model):
+        """Initialize the parent node"""
+        super(ParamGroupItem, self).__init__()
+        self.parent = None
+        self.children = []
+        self.name = name
+        self.model = model
+
+    def child_count(self):
+        """Return the number of children this node has"""
+        return len(self.children)
+
+class ParamBlockModel(QAbstractItemModel):
+    """Model for handling the parameters in the tree-view"""
+    def __init__(self, parent):
+        """Create the empty model"""
+        super(ParamBlockModel, self).__init__(parent)
+        self._nodes = []
+        self._column_headers = ['Name', 'Type', 'Access', 'Value']
+        self._red_brush = QBrush(QColor("red"))
+
+    def set_toc(self, toc, crazyflie):
+        """Populate the model with data from the param TOC"""
+
+        # No luck using proxy sorting, so do it here instead...
+        for group in sorted(toc.keys()):
+            new_group = ParamGroupItem(group, self)
+            for param in sorted(toc[group].keys()):
+                new_param = ParamChildItem(new_group, param, crazyflie)
+                new_param.ctype = toc[group][param].ctype
+                new_param.access = toc[group][param].get_readable_access()
+                crazyflie.param.add_update_callback(
+                    group=group, name=param, cb=new_param.updated)
+                new_group.children.append(new_param)
+            self._nodes.append(new_group)
+
+        self.layoutChanged.emit()
+
+    def refresh(self):
+        """Force a refresh of the view though the model"""
+        self.layoutChanged.emit()
+
+    def parent(self, index):
+        """Re-implemented method to get the parent of the given index"""
+        if not index.isValid():
+            return QModelIndex()
+
+        node = index.internalPointer()
+        if node.parent is None:
+            return QModelIndex()
+        else:
+            return self.createIndex(self._nodes.index(node.parent), 0,
+                                    node.parent)
+
+    def columnCount(self, parent):
+        """Re-implemented method to get the number of columns"""
+        return len(self._column_headers)
+
+    def headerData(self, section, orientation, role):
+        """Re-implemented method to get the headers"""
+        if role == Qt.DisplayRole:
+            return QString(self._column_headers[section])
+
+    def rowCount(self, parent):
+        """Re-implemented method to get the number of rows for a given index"""
+        parent_item = parent.internalPointer()
+        if parent.isValid():
+            parent_item = parent.internalPointer()
+            return parent_item.child_count()
+        else:
+            return len(self._nodes)
+
+    def index(self, row, column, parent):
+        """Re-implemented method to get the index for a specified
+        row/column/parent combination"""
+        if not self._nodes:
+            return QModelIndex()
+        node = parent.internalPointer()
+        if not node:
+            index = self.createIndex(row, column, self._nodes[row])
+            self._nodes[row].index = index
+            return index
+        else:
+            return self.createIndex(row, column, node.children[row])
+
+    def data(self, index, role):
+        """Re-implemented method to get the data for a given index and role"""
+        node = index.internalPointer()
+        parent = node.parent
+        if not parent:
+            if role == Qt.DisplayRole and index.column() == 0:
+                return node.name
+        elif role == Qt.DisplayRole:
+            if index.column() == 0:
+                return node.name
+            if index.column() == 1:
+                return node.ctype
+            if index.column() == 2:
+                return node.access
+            if index.column() == 3:
+                return node.value
+        elif role == Qt.EditRole and index.column() == 3:
+            return node.value
+        elif (role == Qt.BackgroundRole and index.column() == 3
+                and node.is_updating):
+            return self._red_brush
+
+        return QVariant()
+
+    def setData(self, index, value, role):
+        """Re-implemented function called when a value has been edited"""
+        node = index.internalPointer()
+        if role == Qt.EditRole:
+            new_val = str(value.toString())
+            # This will not update the value, only trigger a setting and
+            # reading of the parameter from the Crazyflie
+            node.set_value(new_val)
+            return True
+        return False
+
+
+    def flags(self, index):
+        """Re-implemented function for getting the flags for a certain index"""
+        flag = super(ParamBlockModel, self).flags(index)
+        node = index.internalPointer()
+        if index.column() == 3 and node.parent and node.access=="RW":
+            flag |= Qt.ItemIsEditable
+        return flag
+
+
+    def reset(self):
+        """Reset the model"""
+        self._nodes = []
+        super(ParamBlockModel, self).reset()
+        self.layoutChanged.emit()
+
+
+class ParamTab(Tab, param_tab_class):
+    """
+    Show all the parameters in the TOC and give the user the ability to edit
+    them
+    """
+    _expand_all_signal = pyqtSignal()
+    _connected_signal = pyqtSignal(str)
+    _disconnected_signal = pyqtSignal(str)
+
+    def __init__(self, tabWidget, helper, *args):
+        """Create the parameter tab"""
+        super(ParamTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Parameters"
+        self.menuName = "Parameters"
+
+        self.helper = helper
+        self.tabWidget = tabWidget
+        self.cf = helper.cf
+
+        self.cf.connected.add_callback(self._connected_signal.emit)
+        self._connected_signal.connect(self._connected)
+        self.cf.disconnected.add_callback(self._disconnected_signal.emit)
+        self._disconnected_signal.connect(self._disconnected)
+
+        self._model = ParamBlockModel(None)
+        self.paramTree.setModel(self._model)
+
+    def _connected(self, link_uri):
+        self._model.set_toc(self.cf.param.toc.toc, self.helper.cf)
+        self.paramTree.expandAll()
+
+    def _disconnected(self, link_uri):
+        self._model.reset()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ParamTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..68c20f7541dca74cae5f9e4f4c2fd3595f495658
GIT binary patch
literal 10768
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
Xj`J3-jT-(r>-5fR+iz^IZ$JG%ulRQ^

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.py
new file mode 100755
index 00000000..b6cd3e19
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+This tab plots different logging data defined by configurations that has been
+pre-configured.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['PlotTab']
+
+import glob
+import json
+import logging
+import os
+import sys
+
+logger = logging.getLogger(__name__)
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import pyqtSlot, pyqtSignal, QThread, Qt
+from PyQt4.QtGui import QMessageBox
+from PyQt4.QtGui import QApplication, QStyledItemDelegate, QAbstractItemView
+from PyQt4.QtCore import QAbstractItemModel, QModelIndex, QString, QVariant
+
+from pprint import pprint
+import datetime
+
+from cfclient.ui.widgets.plotwidget import PlotWidget
+
+from cflib.crazyflie.log import Log
+
+from cfclient.ui.tab import Tab
+
+plot_tab_class = uic.loadUiType(sys.path[0] +
+                                "/cfclient/ui/tabs/plotTab.ui")[0]
+
+class LogConfigModel(QAbstractItemModel):
+    """Model for log configurations in the ComboBox"""
+    def __init__(self, parent=None):
+        super(LogConfigModel, self).__init__(parent)
+        self._nodes = []
+
+    def add_block(self, block):
+        self._nodes.append(block)
+        self.layoutChanged.emit()
+
+    def parent(self, index):
+        """Re-implemented method to get the parent of the given index"""
+        return QModelIndex()
+
+    def remove_block(self, block):
+        """Remove a block from the view"""
+        raise NotImplementedError()
+
+    def columnCount(self, parent):
+        """Re-implemented method to get the number of columns"""
+        return 1
+
+    def rowCount(self, parent):
+        """Re-implemented method to get the number of rows for a given index"""
+        parent_item = parent.internalPointer()
+        if parent.isValid():
+            parent_item = parent.internalPointer()
+            return 0
+        else:
+            return len(self._nodes)
+
+    def index(self, row, column, parent):
+        """Re-implemented method to get the index for a specified
+        row/column/parent combination"""
+        if not self._nodes:
+            return QModelIndex()
+        node = parent.internalPointer()
+        if not node:
+            index = self.createIndex(row, column, self._nodes[row])
+            return index
+        else:
+            return self.createIndex(row, column, node.get_child(row))
+
+    def data(self, index, role):
+        """Re-implemented method to get the data for a given index and role"""
+        node = index.internalPointer()
+        if not index.isValid() or not 0 <= index.row() < len(self._nodes): 
+            return QVariant() 
+        if role == Qt.DisplayRole:
+            return self._nodes[index.row()].name
+        return QVariant()
+
+    def reset(self):
+        """Reset the model"""
+        self._nodes = []
+        self.layoutChanged.emit()
+
+    def get_config(self, i):
+        return self._nodes[i]
+
+class PlotTab(Tab, plot_tab_class):
+    """Tab for plotting logging data"""
+
+    _log_data_signal = pyqtSignal(int, object, object)
+    _log_error_signal = pyqtSignal(object, str)
+    _disconnected_signal = pyqtSignal(str)
+    _connected_signal = pyqtSignal(str)
+
+    colors = ['g', 'b', 'm', 'r', 'y', 'c']
+
+    def __init__(self, tabWidget, helper, *args):
+        super(PlotTab, self).__init__(*args)
+        self.setupUi(self)
+
+        self.tabName = "Plotter"
+        self.menuName = "Plotter"
+
+        self._log_error_signal.connect(self._logging_error)
+
+        self._plot = PlotWidget(fps=30)
+        # Check if we could find the PyQtImport. If not, then
+        # set this tab as disabled
+        self.enabled = self._plot.can_enable
+
+        self._model = LogConfigModel()
+        self.dataSelector.setModel(self._model)
+        self._log_data_signal.connect(self._log_data_received)
+        self.tabWidget = tabWidget
+        self.helper = helper
+        self.plotLayout.addWidget(self._plot)
+
+        # Connect external signals if we can use the tab
+        if self.enabled:
+            self._disconnected_signal.connect(self._disconnected)
+            self.helper.cf.disconnected.add_callback(
+                self._disconnected_signal.emit)
+
+            self._connected_signal.connect(self._connected)
+            self.helper.cf.connected.add_callback(
+                self._connected_signal.emit)
+
+            self.helper.cf.log.block_added_cb.add_callback(self._config_added)
+            self.dataSelector.currentIndexChanged.connect(
+                self._selection_changed)
+
+        self._previous_config = None
+        self._started_previous = False
+
+    def _connected(self, link_uri):
+        """Callback when the Crazyflie has been connected"""
+        self._plot.removeAllDatasets()
+        self._plot.set_title("")
+
+    def _disconnected(self, link_uri):
+        """Callback for when the Crazyflie has been disconnected"""
+        self._model.reset()
+        self.dataSelector.setCurrentIndex(-1)
+        self._previous_config = None
+        self._started_previous = False
+
+    def _log_data_signal_wrapper(self, ts, data, logconf):
+        """Wrapper for signal"""
+
+        # For some reason the *.emit functions are not
+        # the same over time (?!) so they cannot be registered and then
+        # removed as callbacks.
+        self._log_data_signal.emit(ts, data, logconf)
+
+    def _log_error_signal_wrapper(self, config, msg):
+        """Wrapper for signal"""
+
+        # For some reason the *.emit functions are not
+        # the same over time (?!) so they cannot be registered and then
+        # removed as callbacks.
+        self._log_error_signal.emit(config, msg)
+
+    def _selection_changed(self, i):
+        """Callback from ComboBox when a new item has been selected"""
+
+        # Check if we have disconnected
+        if i < 0:
+            return
+        # First check if we need to stop the old block
+        if self._started_previous and self._previous_config:
+            logger.debug("Should stop config [%s], stopping!",
+                        self._previous_config.name)
+            self._previous_config.delete()
+
+        # Remove our callback for the previous config
+        if self._previous_config:
+            self._previous_config.data_received_cb.remove_callback(
+                self._log_data_signal_wrapper)
+            self._previous_config.error_cb.remove_callback(
+                self._log_error_signal_wrapper)
+
+        lg = self._model.get_config(i)
+        if not lg.started:
+            logger.debug("Config [%s] not started, starting!", lg.name)
+            self._started_previous = True
+            lg.start()
+        else:
+            self._started_previous = False
+        self._plot.removeAllDatasets()
+        color_selector = 0
+
+        self._plot.set_title(lg.name)
+
+        for d in lg.variables:
+            self._plot.add_curve(d.name,
+                                self.colors[color_selector % len(self.colors)])
+            color_selector += 1
+        lg.data_received_cb.add_callback(self._log_data_signal_wrapper)
+        lg.error_cb.add_callback(self._log_error_signal_wrapper)
+
+        self._previous_config = lg
+
+    def _config_added(self, logconfig):
+        """Callback from the log layer when a new config has been added"""
+        logger.debug("Callback for new config [%s]", logconfig.name)
+        self._model.add_block(logconfig)
+
+    def _logging_error(self, log_conf, msg):
+        """Callback from the log layer when an error occurs"""
+        QMessageBox.about(self, "Plot error", "Error when starting log config"
+                " [%s]: %s" % (log_conf.name, msg))
+
+    def _log_data_received(self, timestamp, data, logconf):
+        """Callback when the log layer receives new data"""
+
+        # Check so that the incoming data belongs to what we are currently
+        # logging
+        if self._previous_config:
+            if self._previous_config.name == logconf.name:
+                self._plot.add_data(data, timestamp)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/PlotTab.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..6177d2525e46ab08df538999f586baf6b8b331ad
GIT binary patch
literal 9626
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.py
new file mode 100755
index 00000000..e9bfba63
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Find all the available tabs so they can be loaded.
+
+Dropping a new .py file into this directory will automatically list and load
+it into the UI when it is started.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = []
+
+import os
+import glob
+import logging
+
+logger = logging.getLogger(__name__)
+
+found_tabs = [os.path.splitext(os.path.basename(f))[0] for
+             f in glob.glob(os.path.dirname(__file__) + "/[A-Za-z]*Tab.py")]
+if len(found_tabs) == 0:
+    found_tabs = [os.path.splitext(os.path.basename(f))[0] for
+                 f in glob.glob(os.path.dirname(__file__) +
+                                "/[A-Za-z]*Tab.pyc")]
+
+logger.debug("Found tabs: %s", found_tabs)
+
+available = []
+
+for tab in found_tabs:
+    tabModule = __import__(tab, globals(), locals(), [tab], -1)
+    available.append(getattr(tabModule, tab))
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..229ceb2b231e511d54ef38e37ed32371bd1adde2
GIT binary patch
literal 1101
zcmb7CO>fgc5S_J?v`xQTN`ccB2`NHKeFg!dRjGvdkPt;6qhhV?NwUGQUGIiAz2?UM
z;HPlm?;vr7w{8L#IN-#yGjC>g-pttlzS<sr`+Snn>{-D17_WIJh*F{uQcx(U58a~_
zf1fg+LVvC|C~VC2CWTGnCJkG}3p7RIqD$lk4I9Kh4VP%xCSG=~v_ZQ#j!ruiuFRdQ
zb7hTq#YMTmCf)qt(HT>?PP{?9K0`lxRQ>q({jLAJ|Es${2irv&Zc@5X7xGBB&bR2|
ziL;MRA5yqYe2o}#ho%U>UdwIjgQ`bU%wU&zi-tFdZxTb_rYUei;%r;I#rR%#7Ttuu
zK(vo_o=TNv(hgXvPfBMh&RAL%8)*uMPGq8T8MDj^m2$e@ZXZ=eS?atmmCX5E?w1qU
z(@4^}bs5^oR9BqXqMFEajl0#@79(YKf<hCSX=9~AS$APu+j$<#H&5mHfOF|El((v~
zuAo^4(?e~ON?o!%IMhG!yfhm?gZBsBP<1a)@1Cd_L^Lomn}6{nW)0|ZF-Bq6-F<n-
zbZ!B%?vsn6v`C`Jwrd@H2So+*$_3WYJ-7;E8#ww|5!b$~==U)QyW~K&&tY<9-E^ZU
zS0hGR3x*2=?ovx-2X!h&<RWIlZhi>HH&4A9GG(SMU)b7+mEk(r(d)SuBG-cT-QAqq
zCeD=O@fDHZTde&EoOPu~Wl`B#SFQz>nHfDS#?(z9`bCkBGp-$~EIChWy68?>TXj&0
z{s{95W-us59K^Hz1Z7nWv3wJ_bxnGi=57L!ThlEkm}?$nIu4TF6&Q?l;Ccz7Naq^t
tu*ACO2cw$DqdinGw;@_yOSHTdfuFbT`Qn+_6290G_q+|S<*kZ6@e4O62crN0

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/consoleTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/consoleTab.ui
new file mode 100755
index 00000000..39f514a8
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/consoleTab.ui
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>693</width>
+    <height>463</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QTextEdit" name="console"/>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/exampleTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/exampleTab.ui
new file mode 100755
index 00000000..a53e2ffa
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/exampleTab.ui
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>765</width>
+    <height>384</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Plot</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/flightTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/flightTab.ui
new file mode 100755
index 00000000..5653fb73
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/flightTab.ui
@@ -0,0 +1,678 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1228</width>
+    <height>916</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QSplitter" name="splitter_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <widget class="QWidget" name="layoutWidget">
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="sizeConstraint">
+        <enum>QLayout::SetMaximumSize</enum>
+       </property>
+       <item>
+        <widget class="QGroupBox" name="groupBox_2">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>320</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="title">
+          <string>Basic Flight Control</string>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout_3">
+          <item>
+           <layout class="QGridLayout" name="gridLayout_3">
+            <item row="1" column="1">
+             <widget class="QComboBox" name="flightModeCombo">
+              <property name="minimumSize">
+               <size>
+                <width>150</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="maximumSize">
+               <size>
+                <width>150</width>
+                <height>16777215</height>
+               </size>
+              </property>
+              <property name="toolTip">
+               <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select what flightmode to use:&lt;/p&gt;&lt;p&gt; * Safe prevents crashing&lt;/p&gt;&lt;p&gt; * Crazy does not prevent crashing :)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+              </property>
+              <property name="editable">
+               <bool>false</bool>
+              </property>
+              <property name="currentIndex">
+               <number>0</number>
+              </property>
+              <item>
+               <property name="text">
+                <string>Normal</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>Advanced</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <spacer name="verticalSpacer_3">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeType">
+               <enum>QSizePolicy::Maximum</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>0</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="2" column="0">
+             <widget class="QLabel" name="label_5">
+              <property name="text">
+               <string>Thrust mode</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QComboBox" name="thrustModeCombo">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <item>
+               <property name="text">
+                <string>Linear</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>Quadratic</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="label_2">
+              <property name="text">
+               <string>Roll Trim</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <widget class="QDoubleSpinBox" name="targetCalRoll">
+              <property name="minimum">
+               <double>-20.000000000000000</double>
+              </property>
+              <property name="maximum">
+               <double>20.000000000000000</double>
+              </property>
+              <property name="singleStep">
+               <double>0.200000000000000</double>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="1">
+             <widget class="QDoubleSpinBox" name="targetCalPitch">
+              <property name="minimum">
+               <double>-20.000000000000000</double>
+              </property>
+              <property name="maximum">
+               <double>20.000000000000000</double>
+              </property>
+              <property name="singleStep">
+               <double>0.200000000000000</double>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="0">
+             <widget class="QLabel" name="label_3">
+              <property name="text">
+               <string>Pitch Trim</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="label_4">
+              <property name="minimumSize">
+               <size>
+                <width>100</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="text">
+               <string>Flight mode</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="1">
+             <widget class="QCheckBox" name="crazyflieXModeCheckbox">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Crazyflie X-mode</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="0">
+             <widget class="QCheckBox" name="clientXModeCheckbox">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Client X-mode</string>
+              </property>
+             </widget>
+            </item>
+            <item row="6" column="0">
+             <widget class="QRadioButton" name="angularPidRadioButton">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Attitude control</string>
+              </property>
+              <property name="checked">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="6" column="1">
+             <widget class="QRadioButton" name="ratePidRadioButton">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Rate control</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox_3">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>320</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="title">
+          <string>Advanced Flight Control</string>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout_4">
+          <item>
+           <layout class="QGridLayout" name="gridLayout_4">
+            <item row="2" column="0">
+             <widget class="QLabel" name="label_9">
+              <property name="text">
+               <string>Max thrust (%)</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="label_7">
+              <property name="layoutDirection">
+               <enum>Qt::LeftToRight</enum>
+              </property>
+              <property name="text">
+               <string>Max angle/rate</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="label">
+              <property name="text">
+               <string>Min thrust (%)</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="0">
+             <widget class="QLabel" name="label_26">
+              <property name="text">
+               <string>Thrust lowering slewrate (%/sec)</string>
+              </property>
+              <property name="wordWrap">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="0">
+             <widget class="QLabel" name="label_31">
+              <property name="text">
+               <string>SlewLimit (%)</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="label_32">
+              <property name="text">
+               <string>Max Yaw angle/rate</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QDoubleSpinBox" name="maxThrust"/>
+            </item>
+            <item row="3" column="1">
+             <widget class="QDoubleSpinBox" name="minThrust"/>
+            </item>
+            <item row="4" column="1">
+             <widget class="QDoubleSpinBox" name="slewEnableLimit"/>
+            </item>
+            <item row="5" column="1">
+             <widget class="QDoubleSpinBox" name="thrustLoweringSlewRateLimit"/>
+            </item>
+            <item row="1" column="1">
+             <widget class="QSpinBox" name="maxYawRate">
+              <property name="minimum">
+               <number>0</number>
+              </property>
+              <property name="maximum">
+               <number>500</number>
+              </property>
+              <property name="value">
+               <number>0</number>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QSpinBox" name="maxAngle">
+              <property name="minimum">
+               <number>0</number>
+              </property>
+              <property name="maximum">
+               <number>500</number>
+              </property>
+              <property name="value">
+               <number>0</number>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox_4">
+         <property name="minimumSize">
+          <size>
+           <width>320</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="title">
+          <string>Expansion boards</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_6">
+          <item row="0" column="0">
+           <layout class="QGridLayout" name="gridLayout_2">
+            <item row="0" column="1">
+             <widget class="QComboBox" name="_led_ring_effect">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="label_8">
+              <property name="text">
+               <string>LED-ring effect</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QCheckBox" name="_led_ring_headlight">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>LED-ring headlight</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="layoutWidget">
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <property name="sizeConstraint">
+        <enum>QLayout::SetDefaultConstraint</enum>
+       </property>
+       <item>
+        <widget class="QGroupBox" name="groupBox">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>2</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="title">
+          <string>Flight Data</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout">
+          <item row="0" column="0">
+           <widget class="QSplitter" name="splitter">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <widget class="QWidget" name="layoutWidget">
+             <layout class="QVBoxLayout" name="verticalLayout_4"/>
+            </widget>
+            <widget class="QWidget" name="layoutWidget">
+             <layout class="QGridLayout" name="gridLayout_5" rowstretch="0,3,3,3,3,3" rowminimumheight="1,1,1,1,1,1">
+              <property name="sizeConstraint">
+               <enum>QLayout::SetMinimumSize</enum>
+              </property>
+              <property name="horizontalSpacing">
+               <number>6</number>
+              </property>
+              <property name="verticalSpacing">
+               <number>0</number>
+              </property>
+              <item row="5" column="0">
+               <widget class="QLabel" name="label_17">
+                <property name="text">
+                 <string>ASL</string>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="4">
+               <widget class="QLineEdit" name="actualPitch"/>
+              </item>
+              <item row="1" column="8" rowspan="5">
+               <widget class="QProgressBar" name="actualM1">
+                <property name="maximum">
+                 <number>65535</number>
+                </property>
+                <property name="value">
+                 <number>0</number>
+                </property>
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="10" rowspan="5">
+               <widget class="QProgressBar" name="actualM3">
+                <property name="maximum">
+                 <number>65535</number>
+                </property>
+                <property name="value">
+                 <number>0</number>
+                </property>
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="6">
+               <widget class="QLabel" name="label_6">
+                <property name="text">
+                 <string>Thrust</string>
+                </property>
+                <property name="alignment">
+                 <set>Qt::AlignCenter</set>
+                </property>
+               </widget>
+              </item>
+              <item row="5" column="4">
+               <widget class="QLineEdit" name="actualASL">
+                <property name="enabled">
+                 <bool>false</bool>
+                </property>
+                <property name="text">
+                 <string/>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="2">
+               <widget class="QLabel" name="label_14">
+                <property name="text">
+                 <string>Target</string>
+                </property>
+                <property name="alignment">
+                 <set>Qt::AlignCenter</set>
+                </property>
+               </widget>
+              </item>
+              <item row="3" column="2">
+               <widget class="QLineEdit" name="targetRoll"/>
+              </item>
+              <item row="1" column="6" rowspan="5">
+               <widget class="QProgressBar" name="thrustProgress">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="Maximum" vsizetype="Expanding">
+                  <horstretch>0</horstretch>
+                  <verstretch>0</verstretch>
+                 </sizepolicy>
+                </property>
+                <property name="sizeIncrement">
+                 <size>
+                  <width>0</width>
+                  <height>0</height>
+                 </size>
+                </property>
+                <property name="maximum">
+                 <number>65600</number>
+                </property>
+                <property name="value">
+                 <number>24</number>
+                </property>
+                <property name="alignment">
+                 <set>Qt::AlignCenter</set>
+                </property>
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+                <property name="invertedAppearance">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="label_16">
+                <property name="text">
+                 <string>Thrust</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="8">
+               <widget class="QLabel" name="M1label">
+                <property name="text">
+                 <string>M1</string>
+                </property>
+               </widget>
+              </item>
+              <item row="3" column="0">
+               <widget class="QLabel" name="label_12">
+                <property name="text">
+                 <string>Roll</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="10">
+               <widget class="QLabel" name="M3label">
+                <property name="text">
+                 <string>M3</string>
+                </property>
+               </widget>
+              </item>
+              <item row="4" column="2">
+               <widget class="QLineEdit" name="targetYaw"/>
+              </item>
+              <item row="1" column="9" rowspan="5">
+               <widget class="QProgressBar" name="actualM2">
+                <property name="maximum">
+                 <number>65535</number>
+                </property>
+                <property name="value">
+                 <number>0</number>
+                </property>
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="2">
+               <widget class="QLineEdit" name="targetPitch"/>
+              </item>
+              <item row="2" column="0">
+               <widget class="QLabel" name="label_11">
+                <property name="text">
+                 <string>Pitch</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="4">
+               <widget class="QLabel" name="label_15">
+                <property name="text">
+                 <string>Actual</string>
+                </property>
+                <property name="alignment">
+                 <set>Qt::AlignCenter</set>
+                </property>
+               </widget>
+              </item>
+              <item row="4" column="4">
+               <widget class="QLineEdit" name="actualYaw"/>
+              </item>
+              <item row="0" column="9">
+               <widget class="QLabel" name="M2label">
+                <property name="text">
+                 <string>M2</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="11" rowspan="5">
+               <widget class="QProgressBar" name="actualM4">
+                <property name="maximum">
+                 <number>65535</number>
+                </property>
+                <property name="value">
+                 <number>0</number>
+                </property>
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="2">
+               <widget class="QLineEdit" name="targetThrust"/>
+              </item>
+              <item row="0" column="11">
+               <widget class="QLabel" name="M4label">
+                <property name="text">
+                 <string>M4</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="4">
+               <widget class="QLineEdit" name="actualThrust"/>
+              </item>
+              <item row="0" column="12" rowspan="6">
+               <layout class="QGridLayout" name="gridLayout_7"/>
+              </item>
+              <item row="4" column="0">
+               <widget class="QLabel" name="label_13">
+                <property name="text">
+                 <string>Yaw</string>
+                </property>
+               </widget>
+              </item>
+              <item row="3" column="4">
+               <widget class="QLineEdit" name="actualRoll"/>
+              </item>
+              <item row="5" column="2">
+               <widget class="QLineEdit" name="targetASL">
+                <property name="enabled">
+                 <bool>false</bool>
+                </property>
+                <property name="text">
+                 <string/>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/gpsTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/gpsTab.ui
new file mode 100755
index 00000000..da2c503d
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/gpsTab.ui
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>765</width>
+    <height>384</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Ignored">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Plot</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="gpslayout" stretch="0,0">
+     <property name="sizeConstraint">
+      <enum>QLayout::SetDefaultConstraint</enum>
+     </property>
+     <item>
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="sizeConstraint">
+        <enum>QLayout::SetDefaultConstraint</enum>
+       </property>
+       <item row="0" column="5">
+        <widget class="QLabel" name="label_6">
+         <property name="text">
+          <string>Accuracy</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLineEdit" name="_lat">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>Lat</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Long</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QLineEdit" name="_speed_max">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="5">
+        <widget class="QLineEdit" name="_accuracy"/>
+       </item>
+       <item row="2" column="2">
+        <widget class="QLineEdit" name="_fix_type">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="3">
+        <widget class="QLabel" name="label_4">
+         <property name="text">
+          <string>Ground speed</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="4">
+        <widget class="QLineEdit" name="_height">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLineEdit" name="_speed">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <widget class="QLineEdit" name="_heading">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="4">
+        <widget class="QLabel" name="label_5">
+         <property name="text">
+          <string>Height (MSL)</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLineEdit" name="_long">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>Heading</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QPushButton" name="_reset_max_btn">
+         <property name="text">
+          <string>Reset all</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <widget class="QWidget" name="widget" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="0" column="0">
+         <layout class="QGridLayout" name="map_layout"/>
+        </item>
+       </layout>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ledTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ledTab.ui
new file mode 100755
index 00000000..83dc3692
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/ledTab.ui
@@ -0,0 +1,336 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>765</width>
+    <height>392</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Plot</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <spacer name="horizontalSpacer">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>40</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="3" column="0">
+      <widget class="QPushButton" name="_u4">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="autoFillBackground">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>U4</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="6">
+      <widget class="QPushButton" name="_u10">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U10</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QPushButton" name="_u1">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U1</string>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="3">
+      <widget class="QPushButton" name="_u7">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U7</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QPushButton" name="_u3">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U3</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QPushButton" name="_u2">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U2</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="4">
+      <widget class="QPushButton" name="_u12">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U12</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="5">
+      <widget class="QPushButton" name="_u11">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U11</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QPushButton" name="_u5">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U5</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="2">
+      <widget class="QPushButton" name="_u6">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U6</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="4">
+      <widget class="QPushButton" name="_u8">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U8</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="5">
+      <widget class="QPushButton" name="_u9">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>5</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>U9</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout_2">
+     <item row="0" column="0">
+      <widget class="QSlider" name="_intensity_slider">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="minimum">
+        <number>1</number>
+       </property>
+       <property name="maximum">
+        <number>100</number>
+       </property>
+       <property name="value">
+        <number>100</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="invertedAppearance">
+        <bool>false</bool>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::NoTicks</enum>
+       </property>
+       <property name="tickInterval">
+        <number>10</number>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <layout class="QGridLayout" name="gridLayout_3">
+       <item row="1" column="0">
+        <widget class="QSpinBox" name="_intensity_spin">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="suffix">
+          <string>%</string>
+         </property>
+         <property name="prefix">
+          <string/>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+         <property name="value">
+          <number>100</number>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>Intensity</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="horizontalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>40</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockDebugTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockDebugTab.ui
new file mode 100755
index 00000000..8c993df1
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockDebugTab.ui
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>668</width>
+    <height>528</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeWidget" name="_block_tree">
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+     <property name="columnCount">
+      <number>7</number>
+     </property>
+     <attribute name="headerDefaultSectionSize">
+      <number>100</number>
+     </attribute>
+     <attribute name="headerHighlightSections">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="headerShowSortIndicator" stdset="0">
+      <bool>true</bool>
+     </attribute>
+     <column>
+      <property name="text">
+       <string notr="true">1</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">2</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">3</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">4</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>5</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>6</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>7</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockTab.ui
new file mode 100755
index 00000000..a058deb6
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logBlockTab.ui
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>668</width>
+    <height>528</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeView" name="_block_tree">
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logTab.ui
new file mode 100755
index 00000000..08a68ca0
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/logTab.ui
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>668</width>
+    <height>528</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeWidget" name="logTree">
+     <property name="columnCount">
+      <number>4</number>
+     </property>
+     <attribute name="headerDefaultSectionSize">
+      <number>100</number>
+     </attribute>
+     <attribute name="headerHighlightSections">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="headerShowSortIndicator" stdset="0">
+      <bool>false</bool>
+     </attribute>
+     <column>
+      <property name="text">
+       <string notr="true">1</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">2</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">3</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">4</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/paramTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/paramTab.ui
new file mode 100755
index 00000000..6b732b40
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/paramTab.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>668</width>
+    <height>528</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeView" name="paramTree"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/plotTab.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/plotTab.ui
new file mode 100755
index 00000000..c5e28c02
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/tabs/plotTab.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>765</width>
+    <height>384</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Plot</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QComboBox" name="dataSelector"/>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="plotLayout"/>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.py
new file mode 100755
index 00000000..79c8f010
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+A detachable toolbox for showing console printouts from the Crazyflie
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['ConsoleToolbox']
+
+import sys, time
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal
+  
+console_class = uic.loadUiType(sys.path[0] + "/cfclient/ui/toolboxes/consoleToolbox.ui")[0]
+
+class ConsoleToolbox(QtGui.QWidget, console_class):
+    """Console toolbox for showing printouts from the Crazyflie"""
+    update = pyqtSignal(str)
+
+    def __init__(self, helper, *args):
+        super(ConsoleToolbox, self).__init__(*args)
+        self.setupUi(self)
+        
+        self.update.connect(self.console.insertPlainText)
+        
+        self.helper = helper
+
+    def getName(self):
+        return 'Console'
+    
+    def enable(self):
+        self.helper.cf.console.receivedChar.add_callback(self.update.emit)
+    
+    def disable(self):
+        self.helper.cf.console.receivedChar.remove_callback(self.update.emit)
+    
+    def preferedDockArea(self):
+        return Qt.BottomDockWidgetArea
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/ConsoleToolbox.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..0635198045bcde4805aa3c02761f9db148a5ce56
GIT binary patch
literal 2448
zcmc&#+m75s5G{M|v%A@F(T0d98i|)tr0l#P1X2X-4kB->tal@Xg=Bf`o|$%Rw{f>a
z2JKsTLi`1P!|(6`P^at-1OZQk^-PzmFRrdSr@FfL>%p}6<;Tl}x~IqQ&$-#3S)@cE
zOOHa2QlB=g_>}f2?Ni>Pkd;>NQ`i@MK>2`*A%#OKMih>y7*jYV59@}MPpFtuIHh8b
z!aXYXDcmPdjVU`I)uZedr6anVj_J-z-jCJr5ZkH|Ww%Kk(waA)&<{i}iM|fsf*Tj}
zDV%-GVtO)5m5Y;Yyvo(g8I!Nfi`m-LGrKk4>2fnmOlb|zRkbdiX`G#{Yg5eJR?U`m
z{QYj7Yc;idZ0l6Jgh$lu<ka2b{<8h@vNfuI<L7J+sN9gnxm%iAxq+yU8|^UD=%iiY
z%di{stJ;0*E^^}>oR*H>lyR=PM|RF)k*pJTqI8Q!FS-zvU389j+aI%Oe1QkXPq_&K
zis@qDSTblmQhj0^!vP3n(3lej6Uqo&K^X%=C=*HtqD(2cm?I|pF^ewjzXkK(Kuq8k
zKtKaG_DdFy{~ld2eLiJAUHSAAk;nb4C!=25=~Fg9KO0guLImEGd=;^`#Dsfn>TEuN
zK_G6MO4R`?BoIfDF13pyH^Q89P4!d@Nllf;PRTICS*pZc>&OXr$jfzURqdYSu`Vyw
z3n!m#RnGQXCi`MCUk6ADhhAK7gf{|tWWQmt*qTBuR$T%us@i0{7jt->VGmn|?Zay4
zwx(R<di4*qzn*o+)h<919b+db<Q;hj{*j*mk?Xd^EL>yKFXrIkOycZpiJt;tfQZ{J
zD{tO+@nWOgSzM@(upxXS@0IiDit`_^0{Z#yAx}6vvc8NAkP}FVKji^vZux+LWF74D
z%R$sCQTn+`m)p3OfjCX0B+m0yoIDF~0);|VXm>M!;Fzfr)#?-0{%bsfci5<)5xk37
zL3gbR^IW|SLU6<rHv<5;OSJ_8k68UH0!V4e2(07c!Rc^)`oVR8EO&_Qlq;$!zA(wN
zuXM^Oc2cYOW+J-H-c+?(t6HV-2cCS!V_j%-+$~+1D)}66nL-{$QDM?1M;D<+QEHMX
z3XmUil~`9xzRK|r-sa)p4#ZuE_gVaLj759%b%^(*2iz<w;m{j;`~GA+>ESmQ|L*g6
z7hBwLnX6yL#s`q~9zNb#Ie|_W?dADockVt6KzkqrW09O(nK*8*S)n~Z<!=E3fVqj&
zr}}bNDKS{ZZtD(soC`gQdz9p{wSw(=n;~*v{NJbv@sC=nKLtQ7dj8Ox`0x4mynFtA
F|95dnMO**?

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.py
new file mode 100755
index 00000000..37476512
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Toolbox for showing packets that is sent via the communication link when debugging.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['CrtpSharkBoolbox']
+
+import sys, time
+import os
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+from time import time
+
+param_tab_class = uic.loadUiType(sys.path[0] +
+                                "/cfclient/ui/toolboxes/crtpSharkToolbox.ui")[0]
+
+class CrtpSharkToolbox(QtGui.QWidget, param_tab_class):
+    """Show packets that is sent vie the communication link"""
+    nameModified = pyqtSignal()
+    _incoming_packet_signal = pyqtSignal(object)
+    _outgoing_packet_signal = pyqtSignal(object)
+    
+    def __init__(self, helper, *args):
+        super(CrtpSharkToolbox, self).__init__(*args)
+        self.setupUi(self)
+
+        self.helper = helper
+        
+        #Init the tree widget
+        self.logTree.setHeaderLabels(['ms', 'Direction', 'Port/Chan', 'Data'])
+        
+        #Connect GUI signals
+        self.clearButton.clicked.connect(self.clearLog)
+        self.saveButton.clicked.connect(self._save_data)
+
+        self._incoming_packet_signal.connect(lambda p: self._packet("IN", p))
+        self._outgoing_packet_signal.connect(lambda p: self._packet("OUT", p))
+        self._ms_offset = int(round(time() * 1000))
+
+        self._data = []
+
+    def _packet(self, dir, pk):
+        if self.masterCheck.isChecked():
+            line = QtGui.QTreeWidgetItem()
+
+            ms_diff = int(round(time()*1000))-self._ms_offset
+            line.setData(0, Qt.DisplayRole, "%d" % ms_diff)
+            line.setData(1, Qt.DisplayRole, "%s" % dir)
+            line.setData(2, Qt.DisplayRole, "%d/%d" % (pk.port, pk.channel))
+            line.setData(3, Qt.DisplayRole, pk.datal.__str__())
+
+            s = "%d, %s, %d/%d, %s" % (ms_diff, dir, pk.port, pk.channel,
+                                      pk.datal.__str__())
+            self._data.append(s)
+
+            self.logTree.addTopLevelItem(line)
+            self.logTree.scrollToItem(line)
+    
+    @pyqtSlot()
+    def clearLog(self):
+        self.logTree.clear()
+        self._data = []
+    
+    def getName(self):
+        return 'Crtp sniffer'
+    
+    def getTabName(self):
+        return 'Crtp sniffer'
+    
+    def enable(self):
+        self.helper.cf.packet_received.add_callback(
+            self._incoming_packet_signal.emit)
+        self.helper.cf.packet_sent.add_callback(
+            self._outgoing_packet_signal.emit)
+
+    def disable(self):
+        self.helper.cf.packet_received.remove_callback(
+            self._incoming_packet_signal.emit)
+        self.helper.cf.packet_sent.remove_callback(
+            self._outgoing_packet_signal.emit)
+
+    def preferedDockArea(self):
+        return Qt.RightDockWidgetArea
+
+    def _save_data(self):
+        dir = os.path.join(sys.path[1], "logdata")
+        fname = os.path.join(dir, "shark_data.csv")
+        if not os.path.exists(dir):
+            os.makedirs(dir)
+        f = open(fname, 'w')
+        for s in self._data:
+            f.write("%s\n" % s)
+        f.close()
+
+
+
+
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/CrtpSharkToolbox.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..15e2f34b5bcbf4cd9cc2eb10b5e248280ac71b27
GIT binary patch
literal 5286
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
W>ieisfkrDgq0!r3M^nMo%D({$oTxVd

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.py
new file mode 100755
index 00000000..402b9cbb
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Toolbox used to interact with the DebugDriver using a designated port. It's
+intended to be used for debugging.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['DebugDriverToolbox']
+
+import time
+import sys
+
+import struct
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL
+
+debugdriver_tab_class = uic.loadUiType(
+                           sys.path[0] +
+                           "/cfclient/ui/toolboxes/debugDriverToolbox.ui")[0]
+
+
+class DebugDriverToolbox(QtGui.QWidget, debugdriver_tab_class):
+    """Used to interact with the DebugDriver toolbox"""
+    connectionDoneSignal = pyqtSignal(str)
+    disconnectedSignal = pyqtSignal(str)
+
+    def __init__(self, helper, *args):
+        super(DebugDriverToolbox, self).__init__(*args)
+        self.setupUi(self)
+
+        self.helper = helper
+
+        # Connected / disconnected signals
+        self.helper.cf.connected.add_callback(
+                                             self.connectionDoneSignal.emit)
+        self.connectionDoneSignal.connect(self.connectionDone)
+        self.helper.cf.disconnected.add_callback(self.disconnectedSignal.emit)
+        self.disconnectedSignal.connect(self.disconnected)
+
+        self.linkQuality.valueChanged.connect(self.linkQualityChanged)
+        self.forceDisconnect.pressed.connect(self.forceDisconnecPressed)
+
+    def forceDisconnecPressed(self):
+        if (self.helper.cf.link != None):
+            p = CRTPPacket()
+            p.set_header(CRTPPort.DEBUGDRIVER, 0)
+            p.data = struct.pack('<B', 1)  # Force disconnect
+            self.helper.cf.send_packet(p)
+
+    def linkQualityChanged(self, value):
+        if (self.helper.cf.link != None):
+            p = CRTPPacket()
+            p.set_header(CRTPPort.DEBUGDRIVER, 0)
+            p.data = struct.pack('<BB', 0, value)  # Set link quality
+            self.helper.cf.send_packet(p)
+
+    def disconnected(self, linkURI):
+        if ("debug" in linkURI):
+            self.linkQuality.setEnabled(False)
+            self.forceDisconnect.setEnabled(False)
+
+    def connectionDone(self, linkURI):
+        if ("debug" in linkURI):
+            self.linkQuality.setEnabled(True)
+            self.forceDisconnect.setEnabled(True)
+
+    def getName(self):
+        return 'Debug driver'
+
+    def getTabName(self):
+        return 'Debug driver'
+
+    def enable(self):
+        return
+
+    def disable(self):
+        return
+
+    def preferedDockArea(self):
+        return Qt.RightDockWidgetArea
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/DebugDriverToolbox.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..8973d533d1112b1d954e0e5d7bcc9435755a2b54
GIT binary patch
literal 4458
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
GrTQCVMgn30

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.py
new file mode 100755
index 00000000..0ab24d41
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+List all the available toolboxes so they can be used by the UI.
+
+Dropping a new .py file into this directory will automatically list and load
+it into the UI when it is started.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = []
+
+import os
+import glob
+import logging
+
+logger = logging.getLogger(__name__)
+
+foundToolboxes = [os.path.splitext(os.path.basename(f))[0] for f in
+                  glob.glob(os.path.dirname(__file__) +
+                            "/[A-Za-z]*Toolbox.py")]
+if len(foundToolboxes) == 0:
+    foundToolboxes = [os.path.splitext(os.path.basename(f))[0] for f in
+                      glob.glob(os.path.dirname(__file__) +
+                                "/[A-Za-z]*Toolbox.pyc")]
+
+
+logger.debug("Found toolboxes: %s", foundToolboxes)
+
+toolboxes = []
+
+for tb in foundToolboxes:
+    tbModule = __import__(tb, globals(), locals(), [tb], -1)
+    toolboxes.append(getattr(tbModule, tb))
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..a2c0023e92b9b4013d7f43721d6c8aa271234f13
GIT binary patch
literal 1136
zcmb7D&2G~`5FXn}+Pc3qEv1~c5=aqJ>KhUW5UmPQrNV_4kWsPL_9WS`@w(m(ZF<d(
z$Kf5g@H$9bVRqw`Qx7=F?94ZR-^@C}uhnq$^V?|#^|L_dOZw@L9zYHdlk_0=pijCF
zIei0=0mQ*vZ$R9b>rIH8fK3>-02g3NiHkN78!&7D1~6QLVF<WvU3mj`=r}s>K)f<{
zuFjP;z!e*11Dmk>*@p{+cpY#9aJ@#q_)z`6y?*OI>;JIz3u3zn!#j{KxJEvBcJfWQ
zdS&gS^XCw^0k;52Zo`zq?>gCrK2h~yN;}vA+=Sshz+FJn+b|{E1Mi~keW3Nd>nz$0
ze<u;12yGabl9>TA{*?>KQ;EzdB~x{UnrUTS6PEFUrO3t_bCynA_}TG(7#>xsEJe|0
zoE3P<_R9(D3929pV>5+jxu`HRs+zD%L5=v>s1Y|JqplMsUH>9yQt><#W}b)a?3i5+
zuwWJ=8irQ}^ZigS6YWD`vWj0LJ2(`7=y|KR2#r1+bYtGVK7VvNqfcZ6;h?u~@n*VB
zp4Vzj1a3xt${y;@eZq}>8IvSeS(2F0=|p~@Dl%s-8i915TuPe;9et@%=aZ`6r>&SJ
z3!*t86Dwo)o+JeyA*Hp*uwjJT)RLQlOQn6;h&tFVlY;5r06hokQVN4t#u+Ks=z`a*
z-_B8wj@ksaJ{G1)XO8K3M&@QrN86<JU}99N%G86|DY(=%L%JyKnwj*C%EuBN&dU;u
z+!3bvxG`0S`oKS--;)^))Ci-rzOJaO)R5i<9ogHP^rXOUMxrpfTTW=RMI=QUWxW{~
zjYV`rH%byw2x3T=>exIRIWjNysEvL=qUE=|mcQcB$6xmYZ`*t9ZF=^65^VS_f7RRb
F{sJa%6}A8X

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/consoleToolbox.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/consoleToolbox.ui
new file mode 100755
index 00000000..8b349b68
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/consoleToolbox.ui
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>693</width>
+    <height>463</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QTextEdit" name="console">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="contextMenuPolicy">
+        <enum>Qt::DefaultContextMenu</enum>
+       </property>
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QPushButton" name="clearButton">
+       <property name="text">
+        <string>Clear</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>clearButton</sender>
+   <signal>clicked()</signal>
+   <receiver>console</receiver>
+   <slot>clear()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>346</x>
+     <y>437</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>346</x>
+     <y>212</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/crtpSharkToolbox.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/crtpSharkToolbox.ui
new file mode 100755
index 00000000..e2f01706
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/crtpSharkToolbox.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1187</width>
+    <height>751</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeWidget" name="logTree">
+     <property name="columnCount">
+      <number>4</number>
+     </property>
+     <attribute name="headerDefaultSectionSize">
+      <number>100</number>
+     </attribute>
+     <column>
+      <property name="text">
+       <string notr="true">1</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>2</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>3</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string notr="true">4</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="clearButton">
+       <property name="text">
+        <string>Clear</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="saveButton">
+       <property name="text">
+        <string>Save</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="masterCheck">
+       <property name="text">
+        <string>Enable</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="checkGrid"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/debugDriverToolbox.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/debugDriverToolbox.ui
new file mode 100755
index 00000000..915e3c35
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/toolboxes/debugDriverToolbox.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_2">
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>DebugDriver Controls</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="checkGrid">
+       <item row="1" column="1">
+        <widget class="QPushButton" name="forceDisconnect">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Force disconnect</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QSlider" name="linkQuality">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+         <property name="value">
+          <number>0</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Set Link Quality</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.py
new file mode 100755
index 00000000..8de7dc26
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Custom widgets used by the user interface.
+"""
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..8563898fea878cd9191ad2659176eee65bf93907
GIT binary patch
literal 238
zcmYL@K?=e^3`M89Pzqi_X4!>pE+8VfcVky>hIS^kBb`p^q)0u2NAo&fz!VDx^78*D
zge*Us*u7u57IQ^BkK7?<7$L6ANL-S|l<gStX$a(<d{}cs3UUZ&WI0NzanilCEn#03
z8ngJEOy_9DP|A3%RKimH5puRrdxub3(tYtn=z4!gO#vo5R*l6}Gg=C1H&X3eXlx0(
f`h_r9m|p><Y->rWjQ7@jc<;O!8a!-S2X65Nq#{3r

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.py
new file mode 100755
index 00000000..39a975fe
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.py
@@ -0,0 +1,268 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Attitude indicator widget.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['AttitudeIndicator']
+
+import sys
+from PyQt4 import QtGui, QtCore
+
+
+class AttitudeIndicator(QtGui.QWidget):
+    """Widget for showing attitude"""
+    def __init__(self):
+        super(AttitudeIndicator, self).__init__()
+
+        self.roll = 0
+        self.pitch = 0
+        self.hover = False
+        self.hoverASL = 0.0
+        self.hoverTargetASL = 0.0
+
+        self.setMinimumSize(30, 30)
+        # self.setMaximumSize(240,240)
+
+    def setRoll(self, roll):
+        self.roll = roll
+        self.repaint()
+
+    def setPitch(self, pitch):
+        self.pitch = pitch
+        self.repaint()
+        
+    def setHover(self, target):        
+        self.hoverTargetASL = target
+        self.hover = target>0
+        self.repaint()
+        
+    def setBaro(self, asl):
+        self.hoverASL = asl;
+        self.repaint()
+
+    def setRollPitch(self, roll, pitch):
+        self.roll = roll
+        self.pitch = pitch
+        self.repaint()
+
+    def paintEvent(self, e):
+        qp = QtGui.QPainter()
+        qp.begin(self)
+        self.drawWidget(qp)
+        qp.end()
+
+    def drawWidget(self, qp):
+        size = self.size()
+        w = size.width()
+        h = size.height()
+
+        qp.translate(w / 2, h / 2)
+        qp.rotate(self.roll)
+        qp.translate(0, (self.pitch * h) / 50)
+        qp.translate(-w / 2, -h / 2)
+        qp.setRenderHint(qp.Antialiasing)
+
+        font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
+        qp.setFont(font)
+
+        # Draw the blue
+        qp.setPen(QtGui.QColor(0, 61, 144))
+        qp.setBrush(QtGui.QColor(0, 61, 144))
+        qp.drawRect(-w, h / 2, 3 * w, -3 * h)
+
+        # Draw the marron
+        qp.setPen(QtGui.QColor(59, 41, 39))
+        qp.setBrush(QtGui.QColor(59, 41, 39))
+        qp.drawRect(-w, h / 2, 3 * w, 3 * h)
+
+        pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 1.5,
+            QtCore.Qt.SolidLine)
+        qp.setPen(pen)
+        qp.drawLine(-w, h / 2, 3 * w, h / 2)
+
+        # Drawing pitch lines
+        for ofset in [-180, 0, 180]:
+            for i in range(-900, 900, 25):
+                pos = (((i / 10.0) + 25 + ofset) * h / 50.0)
+                if i % 100 == 0:
+                    length = 0.35 * w
+                    if i != 0:
+                        if ofset == 0:
+                            qp.drawText((w / 2) + (length / 2) + (w * 0.06),
+                                        pos, "{}".format(-i / 10))
+                            qp.drawText((w / 2) - (length / 2) - (w * 0.08),
+                                        pos, "{}".format(-i / 10))
+                        else:
+                            qp.drawText((w / 2) + (length / 2) + (w * 0.06),
+                                        pos, "{}".format(i / 10))
+                            qp.drawText((w / 2) - (length / 2) - (w * 0.08),
+                                        pos, "{}".format(i / 10))
+                elif i % 50 == 0:
+                    length = 0.2 * w
+                else:
+                    length = 0.1 * w
+
+                qp.drawLine((w / 2) - (length / 2), pos,
+                            (w / 2) + (length / 2), pos)
+
+        qp.setWorldMatrixEnabled(False)
+
+        pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2,
+            QtCore.Qt.SolidLine)
+        qp.setBrush(QtGui.QColor(0, 0, 0))
+        qp.setPen(pen)
+        qp.drawLine(0, h / 2, w, h / 2)
+        
+        
+        
+        # Draw Hover vs Target
+        
+        qp.setWorldMatrixEnabled(False)
+        
+        pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 2,
+                         QtCore.Qt.SolidLine)
+        qp.setBrush(QtGui.QColor(255, 255, 255))
+        qp.setPen(pen)
+        fh = max(7,h/50)
+        font = QtGui.QFont('Sans', fh, QtGui.QFont.Light)
+        qp.setFont(font)
+        qp.resetTransform()
+      
+        
+
+        
+        qp.translate(0,h/2)      
+        if not self.hover:  
+            qp.drawText(w-fh*10, fh/2, str(round(self.hoverASL,2)))  # asl
+               
+        
+        if self.hover:
+            qp.drawText(w-fh*10, fh/2, str(round(self.hoverTargetASL,2)))  # target asl (center)    
+            diff = round(self.hoverASL-self.hoverTargetASL,2)
+            pos_y = -h/6*diff
+            
+            # cap to +- 2.8m
+            if diff<-2.8:
+                pos_y = -h/6*-2.8
+            elif diff>2.8:
+                pos_y= -h/6*2.8
+            else:
+                pos_y = -h/6*diff
+            qp.drawText(w-fh*3.8, pos_y+fh/2, str(diff)) # difference from target (moves up and down +- 2.8m)        
+            qp.drawLine(w-fh*4.5,0,w-fh*4.5,pos_y) # vertical line     
+            qp.drawLine(w-fh*4.7,0,w-fh*4.5,0) # left horizontal line
+            qp.drawLine(w-fh*4.2,pos_y,w-fh*4.5,pos_y) #right horizontal line
+        
+        
+        
+
+
+if __name__ == "__main__":
+    class Example(QtGui.QWidget):
+
+        def __init__(self):
+            super(Example, self).__init__()
+
+            self.initUI()
+
+        def updatePitch(self, pitch):
+            self.wid.setPitch(pitch - 90)
+
+        def updateRoll(self, roll):
+            self.wid.setRoll((roll / 10.0) - 180.0)
+        
+        def updateTarget(self, target):
+            self.wid.setHover(500+target/10.)
+        def updateBaro(self, asl):
+            self.wid.setBaro(500+asl/10.)           
+        
+        
+        def initUI(self):
+
+            vbox = QtGui.QVBoxLayout()
+
+            sld = QtGui.QSlider(QtCore.Qt.Horizontal, self)
+            sld.setFocusPolicy(QtCore.Qt.NoFocus)
+            sld.setRange(0, 3600)
+            sld.setValue(1800)
+            vbox.addWidget(sld)
+            
+            
+            self.wid = AttitudeIndicator()
+
+            sld.valueChanged[int].connect(self.updateRoll)
+            vbox.addWidget(self.wid)
+
+            hbox = QtGui.QHBoxLayout()
+            hbox.addLayout(vbox)
+
+            sldPitch = QtGui.QSlider(QtCore.Qt.Vertical, self)
+            sldPitch.setFocusPolicy(QtCore.Qt.NoFocus)
+            sldPitch.setRange(0, 180)
+            sldPitch.setValue(90)
+            sldPitch.valueChanged[int].connect(self.updatePitch)
+            hbox.addWidget(sldPitch)
+            
+            sldASL = QtGui.QSlider(QtCore.Qt.Vertical, self)
+            sldASL.setFocusPolicy(QtCore.Qt.NoFocus)
+            sldASL.setRange(-200, 200)
+            sldASL.setValue(0)
+            sldASL.valueChanged[int].connect(self.updateBaro)
+            
+            sldT = QtGui.QSlider(QtCore.Qt.Vertical, self)
+            sldT.setFocusPolicy(QtCore.Qt.NoFocus)
+            sldT.setRange(-200, 200)
+            sldT.setValue(0)
+            sldT.valueChanged[int].connect(self.updateTarget)
+            
+            hbox.addWidget(sldT)  
+            hbox.addWidget(sldASL)
+                      
+
+            self.setLayout(hbox)
+
+            self.setGeometry(50, 50, 510, 510)
+            self.setWindowTitle('Attitude Indicator')
+            self.show()
+
+        def changeValue(self, value):
+
+            self.c.updateBW.emit(value)
+            self.wid.repaint()
+
+    def main():
+
+        app = QtGui.QApplication(sys.argv)
+        ex = Example()
+        sys.exit(app.exec_())
+
+
+    if __name__ == '__main__':
+        main()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/ai.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..7214cb8692aab93825ea2ad0850495e247bce2a7
GIT binary patch
literal 8343
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
XqGTf{!fu`^%zt;pn!hpFKVtnCB?^+^

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.py
new file mode 100755
index 00000000..82eab2e9
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+This class provides a spin box with hexadecimal numbers and arbitrarily length (i.e. not limited by 32 bit).
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['HexSpinBox']
+
+from PyQt4 import QtGui, QtCore
+from PyQt4.QtGui import QAbstractSpinBox
+
+class HexSpinBox(QAbstractSpinBox):
+    def __init__(self, *args):
+        QAbstractSpinBox.__init__(self, *args)
+        regexp = QtCore.QRegExp('^0x[0-9A-Fa-f]{1,10}$')
+        self.validator = QtGui.QRegExpValidator(regexp)
+        self._value = 0
+
+    def validate(self, text, pos):
+        return self.validator.validate(text, pos)
+
+    def textFromValue(self, value):
+        return "0x%X" % value
+
+    def valueFromText(self, text):
+        return int(str(text), 0)
+
+    def setValue(self, value):
+        self._value = value
+        self.lineEdit().setText(self.textFromValue(value))
+
+    def value(self):
+        self._value = self.valueFromText(self.lineEdit().text())
+        return self._value
+
+    def stepBy(self, steps):
+        self.setValue(self._value + steps)
+
+    def stepEnabled(self):
+        return QAbstractSpinBox.StepUpEnabled | QAbstractSpinBox.StepDownEnabled
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/hexspinbox.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..fe4095fa3bfd81eede39080a259b2554c64d7b83
GIT binary patch
literal 2777
zcmcguU2hvj6urCtNF6AR_y|Zy)jm*gB^;*|A%RduT+&uTikeLuff20MyAx-k^{%x$
zZfr=O$|L`l9{}gvb(~VfW9($E?|gXgoOAA-*~VWtH=iUw94E5;H1Pc$TJ;<z6&b>M
zGV~<%Wemrcv>|CzvW85WGHlAECBv3@9BoO~mdS<;H_-RRY{&_I*Oos-UWxoPydmj^
z9CdH-@j1-qvr}6INfuXCFfEH0HZ@fc2i4T(!Kj!A=hmGDr)D0fCb5$^3-Z}yWJ;{b
z(;zNK)|GK-vqg}Ze2k@CyJL2Oyl_EgC)SxX7%hVPUj$fqcW1MD3n2&AC1w1Z3HAnV
z6a6P<euQv?Vs8J&r|V<PwO|h2<C%4B^&S?b>3W>mVt;Q`A#mcBKU`7A6$7;DDU1Pi
z051ej(mZuQlCMsaG;*p%GU>F*ADxXQWi?$K=}1_!mM{brVtb5MJ%Di{XP#V$TzK-Q
zC!X`=41qj(<;hE5TvN_ka^bU!MG*q|>L8Fn4nFIk`T+LV-TBYEy|4FrkK*3RFTdaW
z{NC=%Pb?-xH4C}YQN1X#xph$#DqR~GISkEse?ASbSgHq(<IJY9D@xbFsu!ysiH*>m
z8I6xU0*uK{)QQV+rPP;ZZ06H0=0r_(46lD$OiUk|s*rso&Y4ME)n|8cl3CMBU~*UW
zrVDpk<o(Q!`pHQ>*q_<{xlP9gKR{+N)sSgB(?!S~e1=vLpm<x}E$>|~xg?w44>|iD
z3|Ks?IR~TQ)d!0NexT_Sk`JY63ActAk^qJ0%-lIXa9ULB!S*(S_0THnC0@d>%ciJ^
z83}^~+CEYc2Nn6)o!|Mf-XT}l2v{7oaB!|&7IzatNZ6yYm>e@-b}{fKj(@3xa&QU-
z`OYGsI^6z2oyl~SeVf-RpqOjjC{W0FYY|0}DUpQs3=r=l^#3Bei%H1C0<IGf$dH3t
z$*6Wr0WSVMt?4=&DPc%yH5)UVoBh-}$Na2}W5|#P+H2@<v5R*7x^i3Cgi87qeQs8~
zj+b!nY!ph9^A4Q4<Y+H(13lo951_|*V^xF-n{^t#PeVnO1+7s{;<Z2wN#-|bOK7*R
z>TZ><A7T1+Xpr&4H$>FR+g@SNt{x>U#LAiJdd?6%j~Q;T;GJ`sr~eV_9Zc3260I+T
z%cGH)>l=iE9N|gNr~7$4%1r9W6i2=<&hzEin#FQ_IUu}c_&s*XwRl~=CoHeMJf4^+
z(l>`FniT0QLszekkY}uxQ>dw@iV;^xjl+*&uCh+O>%7bmO+H1dSl?~0)9y6%ui0sK
z8V6m?A07iylorVfP&~tZS)yA{#aRZQY4&t+=)Tmuy}t2DmX7V{Ym&+uVr-2NRgiCM
T2_D===!!D6eSr11y)FMA2OEBA

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotter.ui b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotter.ui
new file mode 100755
index 00000000..bfd23dd1
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotter.ui
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>926</width>
+    <height>499</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Plot</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="mainLayout">
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <item>
+        <layout class="QVBoxLayout" name="plotLayout"/>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="lowerLayout">
+       <item>
+        <layout class="QGridLayout" name="options">
+         <item row="3" column="0">
+          <layout class="QGridLayout" name="gridLayout">
+           <item row="1" column="0">
+            <layout class="QGridLayout" name="gridLayout_5">
+             <item row="0" column="1">
+              <widget class="QLineEdit" name="_range_x_min">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>0</string>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="2">
+              <widget class="QLabel" name="label_2">
+               <property name="text">
+                <string>-</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QRadioButton" name="_enable_samples_x">
+               <property name="enabled">
+                <bool>true</bool>
+               </property>
+               <property name="text">
+                <string>Samples</string>
+               </property>
+               <property name="checked">
+                <bool>true</bool>
+               </property>
+               <property name="autoExclusive">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="1">
+              <widget class="QSpinBox" name="_nbr_of_samples_x">
+               <property name="suffix">
+                <string/>
+               </property>
+               <property name="maximum">
+                <number>2000</number>
+               </property>
+               <property name="singleStep">
+                <number>100</number>
+               </property>
+               <property name="value">
+                <number>500</number>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="3">
+              <widget class="QLineEdit" name="_range_x_max">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>1000</string>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="0">
+              <widget class="QRadioButton" name="_enable_range_x">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>Range</string>
+               </property>
+               <property name="autoExclusive">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item row="3" column="0">
+              <widget class="QRadioButton" name="_enable_manual_x">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>Manual</string>
+               </property>
+               <property name="autoExclusive">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="0">
+              <widget class="QRadioButton" name="_enable_seconds_x">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>Seconds</string>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="1">
+              <widget class="QSpinBox" name="_nbr_of_seconds_x">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="suffix">
+                <string/>
+               </property>
+               <property name="maximum">
+                <number>59</number>
+               </property>
+               <property name="value">
+                <number>1</number>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="1" column="1">
+            <layout class="QGridLayout" name="gridLayout_6">
+             <item row="2" column="2">
+              <widget class="QLabel" name="label_3">
+               <property name="text">
+                <string>-</string>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="0">
+              <widget class="QRadioButton" name="_enable_range_y">
+               <property name="enabled">
+                <bool>true</bool>
+               </property>
+               <property name="text">
+                <string>Range</string>
+               </property>
+               <property name="autoExclusive">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QRadioButton" name="_enable_auto_y">
+               <property name="text">
+                <string>Auto</string>
+               </property>
+               <property name="checked">
+                <bool>true</bool>
+               </property>
+               <property name="autoExclusive">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="5">
+              <spacer name="horizontalSpacer_2">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" stdset="0">
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+             <item row="2" column="1">
+              <widget class="QDoubleSpinBox" name="_range_y_min">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="minimum">
+                <double>-65000.000000000000000</double>
+               </property>
+               <property name="maximum">
+                <double>65000.000000000000000</double>
+               </property>
+               <property name="singleStep">
+                <double>1.000000000000000</double>
+               </property>
+               <property name="value">
+                <double>-1.000000000000000</double>
+               </property>
+              </widget>
+             </item>
+             <item row="2" column="4">
+              <widget class="QDoubleSpinBox" name="_range_y_max">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="minimum">
+                <double>-65000.000000000000000</double>
+               </property>
+               <property name="maximum">
+                <double>65000.000000000000000</double>
+               </property>
+               <property name="singleStep">
+                <double>1.000000000000000</double>
+               </property>
+               <property name="value">
+                <double>1.000000000000000</double>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="2" column="1">
+            <layout class="QGridLayout" name="gridLayout_2">
+             <item row="0" column="0">
+              <spacer name="horizontalSpacer">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" stdset="0">
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+             <item row="0" column="1">
+              <widget class="QCheckBox" name="_auto_redraw">
+               <property name="text">
+                <string>Auto update graph</string>
+               </property>
+               <property name="checked">
+                <bool>true</bool>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>X Axis</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_4">
+             <property name="text">
+              <string>Y Axis</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.py
new file mode 100755
index 00000000..459be6f6
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.py
@@ -0,0 +1,291 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Container for the simple plot with functionality for data legend, saving data
+and manipulating the plot.
+
+For more advanced plotting save the data and use an external application.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['PlotWidget']
+
+from PyQt4 import QtCore, QtGui, uic
+from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, QLine, QPoint, QPointF, QSize, QRectF
+
+from time import time
+import math
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+import sys
+
+from PyQt4 import Qt, QtCore, QtGui, uic
+from PyQt4.QtGui import QButtonGroup
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from PyQt4.Qt import *
+from time import time
+
+(plot_widget_class,
+connect_widget_base_class) = (uic.loadUiType(
+                             sys.path[0] + '/cfclient/ui/widgets/plotter.ui'))
+
+# Try the imports for PyQtGraph to see if it is installed
+try:
+    import pyqtgraph as pg
+    from pyqtgraph import ViewBox
+    from pyqtgraph.Qt import QtCore, QtGui
+    import pyqtgraph.console
+    import numpy as np
+    
+    _pyqtgraph_found = True
+except Exception:
+    import traceback
+    logger.warning("PyQtGraph (or dependency) failed to import:\n%s",
+                   traceback.format_exc())
+    _pyqtgraph_found = False
+
+# This is required to force py2exe to pull in the correct dependencies on
+# Windows. But for Linux this is not required and might not be installed with
+# the PyQtGraph package.
+try:
+    from scipy.stats import futil
+    from scipy.sparse.csgraph import _validation
+    from scipy.special import _ufuncs_cxx
+except Exception:
+    pass
+
+class PlotItemWrapper:
+    """Wrapper for PlotDataItem to handle what data is shown"""
+    def __init__(self, curve):
+        """Initialize"""
+        self.data = []
+        self.ts = []
+        self.curve = curve
+
+    def add_point(self, p, ts):
+        """
+        Add a point to the curve.
+
+        p - point
+        ts - timestamp in ms
+        """
+        self.data.append(p)
+        self.ts.append(ts)
+
+    def show_data(self, start, stop):
+        """Set what data should be shown from the curve. This is done to keep performance when many
+        points have been added."""
+        limit = min(stop, len(self.data))
+        self.curve.setData(y=self.data[start:limit], x=self.ts[start:limit])
+        return [self.ts[start], self.ts[limit-1]]
+
+class PlotWidget(QtGui.QWidget, plot_widget_class):
+    """Wrapper widget for PyQtGraph adding some extra buttons"""
+
+    def __init__(self, parent=None, fps=100, title="", *args):
+        super(PlotWidget, self).__init__(*args)
+        self.setupUi(self)
+
+        # Limit the plot update to 10Hz
+        self._ts = time()
+        self._delay = 0.1
+
+        # Check if we could import PyQtGraph, if not then stop here
+        if not _pyqtgraph_found:
+            self.can_enable = False
+            return
+        else:
+            self.can_enable = True
+
+        self._items = {}
+        self._last_item = 0
+
+        self.setSizePolicy(QtGui.QSizePolicy(
+                                         QtGui.QSizePolicy.MinimumExpanding,
+                                         QtGui.QSizePolicy.MinimumExpanding))
+
+        self.setMinimumSize(self.minimumSizeHint())
+        self.parent = parent
+
+        pg.setConfigOption('background', 'w')
+        pg.setConfigOption('foreground', 'k')
+        self._plot_widget = pg.PlotWidget()
+        self._plot_widget.hideButtons()
+        self._plot_widget.setLabel('bottom', "Time", "ms")
+        self._plot_widget.addLegend()
+        self._plot_widget.getViewBox().disableAutoRange(ViewBox.XAxis)
+        self._plot_widget.getViewBox().sigRangeChangedManually.connect(self._manual_range_change)
+        self._plot_widget.getViewBox().setMouseEnabled(x=False, y=True)
+        self._plot_widget.getViewBox().setMouseMode(ViewBox.PanMode)
+
+        self.plotLayout.addWidget(self._plot_widget)
+
+        #self.saveToFile.clicked.connect(self.saveToFileSignal)
+        self._x_min = 0
+        self._x_max = 500
+        self._enable_auto_y.setChecked(True)
+        self._enable_samples_x.setChecked(True)
+        self._last_ts = None
+        self._dtime = None
+
+        self._x_range = (float(self._range_x_min.text()), float(self._range_x_max.text()))
+        self._nbr_samples = int(self._nbr_of_samples_x.text())
+
+        self._nbr_of_samples_x.valueChanged.connect(self._nbr_samples_changed)
+        self._range_y_min.valueChanged.connect(self._y_range_changed)
+        self._range_y_max.valueChanged.connect(self._y_range_changed)
+
+        self._y_btn_group = QButtonGroup()
+        self._y_btn_group.addButton(self._enable_auto_y)
+        self._y_btn_group.addButton(self._enable_range_y)
+        self._y_btn_group.setExclusive(True)
+        self._y_btn_group.buttonClicked.connect(self._y_mode_change)
+
+        self._x_btn_group = QButtonGroup()
+        self._x_btn_group.addButton(self._enable_range_x)
+        self._x_btn_group.addButton(self._enable_samples_x)
+        self._x_btn_group.addButton(self._enable_seconds_x)
+        self._x_btn_group.addButton(self._enable_manual_x)
+        self._x_btn_group.setExclusive(True)
+        self._x_btn_group.buttonClicked.connect(self._x_mode_change)
+
+        self._draw_graph = True
+        self._auto_redraw.stateChanged.connect(self._auto_redraw_change)
+
+    def _auto_redraw_change(self, state):
+        """Callback from the auto redraw checkbox"""
+        if state == 0:
+            self._draw_graph = False
+        else:
+            self._draw_graph = True
+
+    def _x_mode_change(self, box):
+        """Callback when user changes the X-axis mode"""
+        if box == self._enable_range_x:
+            logger.info("Enable range x")
+            self._x_range = (float(self._range_x_min.text()), float(self._range_x_max.text()))
+        else:
+            self._range_x_min.setEnabled(False)
+            self._range_x_max.setEnabled(False)
+
+    def _y_mode_change(self, box):
+        """Callback when user changes the Y-axis mode"""
+        if box == self._enable_range_y:
+            self._range_y_min.setEnabled(True)
+            self._range_y_max.setEnabled(True)
+            y_range = (float(self._range_y_min.value()), float(self._range_y_max.value()))
+            self._plot_widget.getViewBox().setRange(yRange=y_range)
+        else:
+            self._range_y_min.setEnabled(False)
+            self._range_y_max.setEnabled(False)
+
+        if box == self._enable_auto_y:
+            self._plot_widget.getViewBox().enableAutoRange(ViewBox.YAxis)
+
+    def _manual_range_change(self, obj):
+        """Callback from pyqtplot when users changes the range of the plot using the mouse"""
+        [[x_min,x_max],[y_min,y_max]] = self._plot_widget.getViewBox().viewRange()
+        self._range_y_min.setValue(y_min)
+        self._range_y_max.setValue(y_max)
+        self._range_y_min.setEnabled(True)
+        self._range_y_max.setEnabled(True)
+        self._enable_range_y.setChecked(True)
+
+    def _y_range_changed(self, val):
+        """Callback when user changes Y range manually"""
+        _y_range = (float(self._range_y_min.value()), float(self._range_y_max.value()))
+        self._plot_widget.getViewBox().setRange(yRange=_y_range, padding=0)
+
+    def _nbr_samples_changed(self, val):
+        """Callback when user changes the number of samples to be shown"""
+        self._nbr_samples = val
+
+    def set_title(self, title):
+        """
+        Set the title of the plot.
+
+        title - the new title
+        """
+        self._plot_widget.setTitle(title)
+
+    def add_curve(self, title, pen='r'):
+        """
+        Add a new curve to the plot.
+
+        title - the name of the data
+        pen - color of curve (using r for red and so on..)
+        """
+        self._items[title] = PlotItemWrapper(self._plot_widget.plot(name=title, pen=pen))
+
+    def add_data(self, data, ts):
+        """
+        Add new data to the plot.
+
+        data - dictionary sent from logging layer containing variable/value pairs
+        ts - timestamp of the data in ms
+        """
+        if not self._last_ts:
+            self._last_ts = ts
+        elif not self._last_ts:
+            self._dtime = ts - self._last_ts
+            self._last_ts = ts
+
+        x_min_limit = 0
+        x_max_limit = 0
+        # We are adding new datasets, calculate what we should show.
+        if self._enable_samples_x.isChecked():
+            x_min_limit = max(0, self._last_item-self._nbr_samples)
+            x_max_limit = max(self._last_item, self._nbr_samples)
+
+        for name in self._items:
+            self._items[name].add_point(data[name], ts)
+            if self._draw_graph and time() > self._ts + self._delay:
+                [self._x_min, self._x_max] = self._items[name].show_data(x_min_limit, x_max_limit)
+        if time() > self._ts + self._delay:
+            self._ts = time()
+        if self._enable_samples_x.isChecked() and self._dtime and self._last_item < self._nbr_samples:
+            self._x_max = self._x_min + self._nbr_samples * self._dtime
+
+        self._last_item = self._last_item + 1
+        self._plot_widget.getViewBox().setRange(xRange=(self._x_min, self._x_max))
+
+    def removeAllDatasets(self):
+        """Reset the plot by removing all the datasets"""
+        for item in self._items:
+            self._plot_widget.removeItem(self._items[item])
+        self._plot_widget.plotItem.legend.items = []
+        self._items = {}
+        self._last_item = 0
+        self._last_ts = None
+        self._dtime = None
+        self._plot_widget.clear()
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/ui/widgets/plotwidget.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..a6cc6be362d25fd29aa4af50696e22e375fe8247
GIT binary patch
literal 10686
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
IwWXE+1JLb)L;wH)

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.py
new file mode 100755
index 00000000..8a252d5b
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Various utilities used by the user interface.
+"""
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..5859123c9c6fa6c4519f23a61276a26911af398d
GIT binary patch
literal 190
zcmYL>!3x4K42C;BC<R|Z>TMvjFCgL*coBBkC2Vbmfo_vFJM6i=c`^mT!2jh-2-&_5
z`g3j@uYOfYze%zd1f^PGr5^RB?3_S1U7~r(W?_oNgkl+k=~u%~ES?NajA`gRT79p}
yN8Skl!t4QRQT0bY$!M*M)H^@;NI0=8zq13Ni5S4XR^H3iWt_tj@p=@`67>agpez;u

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.py
new file mode 100755
index 00000000..e7e09816
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Gives access for reading and writing application configuration parameters
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Config']
+
+import sys
+import json
+import logging
+from .singleton import Singleton
+
+logger = logging.getLogger(__name__)
+
+class Config():
+    """ Singleton class for accessing application configuration """
+    __metaclass__ = Singleton
+    def __init__(self):
+        """ Initializes the singleton and reads the config files """
+        self._dist_config = sys.path[0] + "/cfclient/configs/config.json"
+        self._config = sys.path[1] + "/config.json"
+
+        [self._readonly, self._data] = self._read_distfile()
+
+        user_config = self._read_config()
+        if (user_config):
+            self._data.update(user_config)
+
+    def _read_distfile(self):
+        """ Read the distribution config file containing the defaults """
+        f = open(self._dist_config, 'r')
+        data = json.load(f)
+        f.close()
+        logger.info("Dist config read from %s" % self._dist_config)
+
+        return [data["read-only"], data["writable"]]
+
+    def set(self, key, value):
+        """ Set the value of a config parameter """
+        try:
+            self._data[key] = value
+        except KeyError:
+            raise KeyError("Could not set the parameter [%s]" % key)
+
+    def get(self, key):
+        """ Get the value of a config parameter """
+        value = None
+        if (key in self._data):
+            value = self._data[key]
+        elif (key in self._readonly):
+            value = self._readonly[key]
+        else:
+            raise KeyError("Could not get the parameter [%s]" % key)
+
+        if (isinstance(value, unicode)):
+            value = str(value)
+
+        return value
+
+    def save_file(self):
+        """ Save the user config to file """
+        json_data = open(self._config, 'w')
+        json_data.write(json.dumps(self._data, indent=2))
+        json_data.close()
+        logger.info("Config file saved to [%s]" % self._config)
+
+    def _read_config(self):
+        """ Read the user config from file """
+        try:
+            json_data = open(self._config)
+            data = json.load(json_data)
+            json_data.close()
+            logger.info("Config file read from [%s]" % self._config)
+        except Exception:
+            return None
+
+        return data
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..484e51ab008a24f203c54cb006c29fea6489473f
GIT binary patch
literal 3450
zcmcImYi}Dx6rEYWYNsV>D74|#<wKAKYW)QvAu3dwR;3gou|~8tpw(t~>}<N(wRXlS
zuG%kzf6NbnbME+&ikFZ`+PS{-*qOQK+{ZM2yVUzKefWK<7Jm)=-bS+(M5fdTQmc_x
zS)e9R0+lsX)>K79ji7kFsYcDE-cqBM%J9FfincoLs8L60(^5wll<BHdY|-Oh*#(t#
z)UQgND)nTv!7YdVE)RYP(Yv3Y7#k*OYOD>%RUOtQ$?|d%CS?{rt8*v%bXw$T;_|8t
z)2bZjlUeP1)1*$0jWf0F*$wP>CwFO`JU8JNcU&9&Jz1Uqj=%j}W1vsrc>WMO7sgfP
z3KCA;L9<^$7(fCrAOmH90qoW^gGL9Uw21^qIy8z3Ol^tMB`O^0sc6{maL-R6!qu5!
zS|kgDz3u;OX3DDtXk={<&E^nJsUuubs~z0v>I<dfU^&*nSksR+6lRW^#Q(>DdfXD(
zmhle9UuyM2tJ=s&n+&2026YLiXn`+6X+MDoAC#aZDe~uF%^jN1E>RT1NS+_}G=<~5
zz(QVD*>@oZ={PNNQ@Vj)U>CnPk8D*6LH4376W(NWU%bW9ayz%KiQj4B4jtFUS#DkI
zw{nDeslP^@w~-aRg>WuIRawkkOTJEA;`foIi+yP9Y?>v`^a;6~XiYJ8bg-E<_2Q5|
zx=Muo2HN1TIyQrYg#!+zb#-J?2Ls`c^D&IR;Vri}rgL{#m4hNbSi>=Md9eZm3oc;C
zIL?7~9N&a4u&CgQz7kvuF6y=xmmnx?dtC7?1YCk_i&vZmB-RD$2reLez6Aez;R_lN
z9KnUfCGf$U<okt0{SJScYG)FFdIDuXg9szQDV!7ZIzPabSMexBGd5j<J0pDQ8<`m=
zv%<k4=)SeR26-3XE@D_hJsj87aky(OLy|1q5PE1nqdz$)jKq(yP)$u4(E+4Kml36?
zl1vCqi^>{_`=XjmOf9o{Ij;HyDI$Mzm9b2K;p99yjOa)Vd)tFh!M5JiJ@A(<StGQ<
z92d<mE5J_jK%rK*9+mH_CT{z*r|OTD)*3oO9aCiL7p+RI9)rE1*k*UJML0B0fImr!
znF*_Lm@J`Q<;l>7Jhhq?Sy)ys1YYvVdchC7_NRUE{D>@wCq6Xudv#saXK<tuPmK>G
zIqy87^wDpeWM|$=1r2>!U()F^De^jk<p~tP;$7Xx!qc{@cNcL1{B|%`4u~YD1I6jP
z3w=4grEugcaKmN558Mo4Ro4n$hAx=kW?<}9BJTfpA|`)B1hbmB>wZ-lVI<;E#9(rC
ztQ;xWx};2vWWZUOr&VS=V(tuM(GEX*3*y|Ff-Mhlj@^b(K^uO*#TIM@n|d>l%thW-
z{y#+L&zTEcfE^fu;zg#U@S^0NYAv}6(`{BS4ZBgYkg+`7lF8O(>pg594wDlje$M>8
z3<_8Iv?V#=ndFVU%ut+i-p&%QJ*h)}nZ+JRfK})tgb6F!qM#j;f0(gMba~m$X2(<O
zDSMsku28%I;iF5^5qDuCi_be7N&TY<F{|NY9BX|bZR_huOF{~RXhq5sbmq6$ywKPG
zy`1=<T9lJV<u-CyM{?MA%wfz|kJqsh;)r!wIZKUH1zt3?^w_gBut@l-@mD+u`Vk#3
zdKGiA|K883nX+s}?_k9`n?+2Y(N&J2omWwhNOp{7={8JZ2nS$)4T-E3^u-^@NgS7W
z)W)%dcN`yA*{on!9yoDUrEwg|Gxm}!#``ys=Ol(XB6TNvn_5Wm0R@j<+a}uo|7wYF
zi24Xk3~b|-($?LLZnxX%ZgxBVcs|>r4<Q!TB{MvR>Xmg#QNRqwpN}Kj%wmo=h4<j)
sd($I8vyg4RmU18Q1RrHk&fi=qjki2KpI^s`-fSeiE}m%ohlCgW0a|wz5&!@I

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.py
new file mode 100755
index 00000000..8da823aa
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#  Copyright (C) 2013 Allyn Bauer
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Manager for loading/accesing input device mappings.
+"""
+
+__author__ = 'Bitcraze AB/Allyn Bauer'
+__all__ = ['ConfigManager']
+
+import sys
+import json
+import logging
+import glob
+import os
+import copy
+
+from .singleton import Singleton
+from cflib.utils.callbacks import Caller
+
+logger = logging.getLogger(__name__)
+
+
+class ConfigManager():
+    """ Singleton class for managing input processing """
+    conf_needs_reload = Caller()
+    configs_dir = sys.path[1] + "/input"
+
+    __metaclass__ = Singleton
+
+    def __init__(self):
+        """Initialize and create empty config list"""
+        self._list_of_configs = []
+
+    def get_config(self, config_name):
+        """Get the button and axis mappings for an input device."""
+        try:
+            idx = self._list_of_configs.index(config_name)
+            return self._input_config[idx]
+        except:
+            return None
+    
+    def get_settings(self, config_name):
+        """Get the settings for an input device."""
+        try:
+            idx = self._list_of_configs.index(config_name)
+            return self._input_settings[idx]
+        except:
+            return None
+
+    def get_list_of_configs(self):
+        """Reload the configurations from file"""
+        try:
+            configs = [os.path.basename(f) for f in
+                       glob.glob(self.configs_dir + "/[A-Za-z]*.json")]
+            self._input_config = []
+            self._input_settings = []
+            self._list_of_configs = []
+            for conf in configs:
+                logger.debug("Parsing [%s]", conf)
+                json_data = open(self.configs_dir + "/%s" % conf)
+                data = json.load(json_data)
+                new_input_device = {}
+                new_input_settings = {"updateperiod":10, "springythrottle":True}
+                for s in data["inputconfig"]["inputdevice"]:
+                    if s == "axis":
+                        for a in data["inputconfig"]["inputdevice"]["axis"]:
+                            axis = {}
+                            axis["scale"] = a["scale"]
+                            axis["offset"] = a["offset"] if "offset" in a else 0.0
+                            axis["type"] = a["type"]
+                            axis["key"] = a["key"]
+                            axis["name"] = a["name"]
+                            try:
+                                ids = a["ids"]
+                            except:
+                                ids = [a["id"]]
+                            for id in ids:
+                                locaxis = copy.deepcopy(axis)
+                                if "ids" in a:
+                                    if id == a["ids"][0]:
+                                        locaxis["scale"] = locaxis["scale"] * -1
+                                locaxis["id"] = id
+                                # 'type'-'id' defines unique index for axis
+                                index = "%s-%d" % (a["type"], id)
+                                new_input_device[index] = locaxis
+                    else:
+                        new_input_settings[s] = data["inputconfig"]["inputdevice"][s]
+                self._input_config.append(new_input_device)
+                self._input_settings.append(new_input_settings)
+                json_data.close()
+                self._list_of_configs.append(conf[:-5])
+        except Exception as e:
+            logger.warning("Exception while parsing inputconfig file: %s ", e)
+        return self._list_of_configs
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/config_manager.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..51f6358a17701d6345bff2f8a43213c3bd918ffc
GIT binary patch
literal 3562
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
D7pp8S

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/guiconfig.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/guiconfig.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..c08a5488a3f3fbab088fa8501cfa241ea285020f
GIT binary patch
literal 1832
zcmcgsO>fjl5G{MYnBgPCqDA6FABbd-5?{C=5L%L51ql=p&t7&AvW(p`ap<u}-R+=3
z;j-D&{xtp$KLB2pop4+2CCp6MbX9j(SG`x&y{|VXt1tilnN#=a;r}-bcLGrm%^?Gt
z2ULW##3ZDmM@64ZkLH-*Hz3oe)qv&$3RoYK8PaM*^AQEQM<>@vkLesoC+tHHXaQ#<
z`iJP8=&$*8_BM;I@tv<ACI{uIb}Gwr?VMWFR#}}DWwlgURj7})^fF(sO_^uDtSgn*
z)uLQBw$;{|%~slL>n83x{Oy%Kx7jDHUhMfX^n<3{mjmTDd=8@kHa~=Dt*t(Sel+tI
zS6w2*8}~5WLkJCIkOm|G2{;)>Ba}&>VPVUFl3CQ}BTpdI4Bm|P9I?r|+}bq%JENRA
zVK+SXG2By#vyeXb$jihBbRLp@Kt5zUz{XOAya0cn&afw-vmTxIz)%3NLw2$ap=R1E
zf2`GMW*V*Pg~~cO)g_MVFdAU1pFotZtenrPTqo=U{^7{mL_icpd&$^K+S)8+%Ll#j
z>_cu5uyF`V1RIHPZ!n9G>y?h*cZ9`jTc7CMyO{agEKI3)a)`>io%O~a*HvuF_i?^x
zjd9~k<Kksgw)L>P-XwhI4u<3EqHq{&;hS^h94n7~40j3v?mh!N;6!v1(1#(}?QRa^
zJ*-vn#d0Xizss_mk>x4F0eG?fE*SVbAb<cikU5iMxym|n;7oX%MS__yaqvHwaQ8Xn
zx-o^SYOj{v5upkduikFEcZU%dui$@JS6XmSi_B+I{V6w7U73w%NM7m9AJ*13;V?X(
zYbwB3=!EH$;1X_)gqIRNA!LcT62_!jkk(^d<$i%^Nt=SUX*dl*Uzl^PT@0b`1`Ny^
zdi!NTC~sbE8$00_Ky=CDs-tJrie|Nzu(-@9h!{TsJ54J*Olc~Ol%}h?Xbh{2ZJHK!
zo~BZ;$rcRB6yis?SBH3ld*~5{^vEz6hU3wAD`HHOI}ly8S%ZeNm(ga%baPWLmw3MX
zuh@Ihio4Q^yKNt~Zv*>#vpMpQcaQvjZM)}#-(#@-8a}j5Zu4~v7yE>c*-wE$*m>6S
W@tix%-GUh6xfq8xf+;F*EBp=}d#=s^

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..3e22bf8fd4eddad80cf08030c3f123c337ac4114
GIT binary patch
literal 11733
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
Df1&Vz

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.py
new file mode 100755
index 00000000..34d3c4d1
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.py
@@ -0,0 +1,412 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Module to read input devices and send controls to the Crazyflie.
+
+This module reads input from joysticks or other input devices and sends control
+set-points to the Crazyflie. It can be configured in the UI.
+
+Various drivers can be used to read input device data. Currently is uses the
+PySDL2 driver, but in the future native support will be provided for Linux and
+Windows drivers.
+
+The input device's axes and buttons are mapped to software inputs using a
+configuration file.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['JoystickReader']
+
+import sys
+import os
+import re
+import glob
+import traceback
+import logging
+import shutil
+
+import inputreaders as readers
+import inputinterfaces as interfaces
+
+logger = logging.getLogger(__name__)
+
+from cfclient.utils.config import Config
+from cfclient.utils.config_manager import ConfigManager
+
+from cfclient.utils.periodictimer import PeriodicTimer
+from cflib.utils.callbacks import Caller
+import mux
+from .mux import InputMux
+from .mux.nomux import NoMux
+from .mux.takeovermux import TakeOverMux
+from .mux.takeoverselectivemux import TakeOverSelectiveMux
+
+MAX_THRUST = 65000
+
+class JoystickReader(object):
+    """
+    Thread that will read input from devices/joysticks and send control-set
+    points to the Crazyflie
+    """
+    inputConfig = []
+
+    def __init__(self, do_device_discovery=True):
+        self._input_device = None
+
+        self.min_thrust = 0
+        self.max_thrust = 0
+        self._thrust_slew_rate = 0
+        self.thrust_slew_enabled = False
+        self.thrust_slew_limit = 0
+        self.has_pressure_sensor = False
+
+        self.max_rp_angle = 0
+        self.max_yaw_rate = 0
+
+        self._old_thrust = 0
+        self._old_raw_thrust = 0
+        self._old_alt_hold = False
+        self.springy_throttle = True
+
+        self.trim_roll = Config().get("trim_roll")
+        self.trim_pitch = Config().get("trim_pitch")
+
+        self._input_map = None
+
+        self._mux = [NoMux(self), TakeOverSelectiveMux(self), TakeOverMux(self)]
+        # Set NoMux as default
+        self._selected_mux = self._mux[0]
+
+        if Config().get("flightmode") is "Normal":
+            self.max_yaw_rate = Config().get("normal_max_yaw")
+            self.max_rp_angle = Config().get("normal_max_rp")
+            # Values are stored at %, so use the functions to set the values
+            self.min_thrust = Config().get("normal_min_thrust")
+            self.max_thrust = Config().get("normal_max_thrust")
+            self.thrust_slew_limit = Config().get("normal_slew_limit")
+            self.thrust_slew_rate = Config().get("normal_slew_rate")
+        else:
+            self.max_yaw_rate = Config().get("max_yaw")
+            self.max_rp_angle = Config().get("max_rp")
+            # Values are stored at %, so use the functions to set the values
+            self.min_thrust = Config().get("min_thrust")
+            self.max_thrust = Config().get("max_thrust")
+            self.thrust_slew_limit = Config().get("slew_limit")
+            self.thrust_slew_rate = Config().get("slew_rate")
+
+        self._dev_blacklist = None
+        if len(Config().get("input_device_blacklist")) > 0:
+            self._dev_blacklist = re.compile(
+                            Config().get("input_device_blacklist"))
+        logger.info("Using device blacklist [{}]".format(
+                            Config().get("input_device_blacklist")))
+
+
+        self._available_devices = {}
+
+        # TODO: The polling interval should be set from config file
+        self._read_timer = PeriodicTimer(0.01, self.read_input)
+
+        if do_device_discovery:
+            self._discovery_timer = PeriodicTimer(1.0, 
+                            self._do_device_discovery)
+            self._discovery_timer.start()
+
+        # Check if user config exists, otherwise copy files
+        if not os.path.exists(ConfigManager().configs_dir):
+            logger.info("No user config found, copying dist files")
+            os.makedirs(ConfigManager().configs_dir)
+
+        for f in glob.glob(sys.path[0] +
+                           "/cfclient/configs/input/[A-Za-z]*.json"):
+            dest = os.path.join(ConfigManager().
+                                configs_dir, os.path.basename(f))
+            if not os.path.isfile(dest):
+                logger.debug("Copying %s", f)
+                shutil.copy2(f, ConfigManager().configs_dir)
+
+        ConfigManager().get_list_of_configs()
+
+        self.input_updated = Caller()
+        self.rp_trim_updated = Caller()
+        self.emergency_stop_updated = Caller()
+        self.device_discovery = Caller()
+        self.device_error = Caller()
+        self.althold_updated = Caller()
+        self.alt1_updated = Caller()
+        self.alt2_updated = Caller()
+
+        # Call with 3 bools (rp_limiting, yaw_limiting, thrust_limiting)
+        self.limiting_updated = Caller()
+
+    def _get_device_from_name(self, device_name):
+        """Get the raw device from a name"""
+        for d in readers.devices():
+            if d.name == device_name:
+                return d
+        return None
+
+    def set_alt_hold_available(self, available):
+        """Set if altitude hold is available or not (depending on HW)"""
+        self.has_pressure_sensor = available
+
+    def enable_alt_hold(self, althold):
+        """Enable or disable altitude hold"""
+        self._old_alt_hold = althold
+
+    def _do_device_discovery(self):
+        devs = self.available_devices()
+
+        # This is done so that devs can easily get access
+        # to limits without creating lots of extra code
+        for d in devs:
+            d.input = self
+
+        if len(devs):
+            self.device_discovery.call(devs)
+            self._discovery_timer.stop()
+
+    def available_mux(self):
+        return self._mux
+
+    def set_mux(self, name=None, mux=None):
+        old_mux = self._selected_mux
+        if name:
+            for m in self._mux:
+                if m.name == name:
+                    self._selected_mux = m
+        elif mux:
+            self._selected_mux = mux
+
+        old_mux.close()
+
+        logger.info("Selected MUX: {}".format(self._selected_mux.name))
+
+    def available_devices(self):
+        """List all available and approved input devices.
+        This function will filter available devices by using the
+        blacklist configuration and only return approved devices."""
+        devs = readers.devices()
+        devs += interfaces.devices()
+        approved_devs = []
+
+        for dev in devs:
+            if ((not self._dev_blacklist) or 
+                    (self._dev_blacklist and not
+                     self._dev_blacklist.match(dev.name))):
+                dev.input = self
+                approved_devs.append(dev)
+
+        return approved_devs 
+
+    def enableRawReading(self, device_name):
+        """
+        Enable raw reading of the input device with id deviceId. This is used
+        to get raw values for setting up of input devices. Values are read
+        without using a mapping.
+        """
+        if self._input_device:
+            self._input_device.close()
+            self._input_device = None
+
+        for d in readers.devices():
+            if d.name == device_name:
+                self._input_device = d
+
+        # Set the mapping to None to get raw values
+        self._input_device.input_map = None
+        self._input_device.open()
+
+    def get_saved_device_mapping(self, device_name):
+        """Return the saved mapping for a given device"""
+        config = None
+        device_config_mapping = Config().get("device_config_mapping")
+        if device_name in device_config_mapping.keys():
+            config = device_config_mapping[device_name]
+
+        logging.debug("For [{}] we recommend [{}]".format(device_name, config))
+        return config
+
+    def stop_raw_reading(self):
+        """Disable raw reading of input device."""
+        if self._input_device:
+            self._input_device.close()
+            self._input_device = None
+
+    def read_raw_values(self):
+        """ Read raw values from the input device."""
+        [axes, buttons, mapped_values] = self._input_device.read(include_raw=True)
+        dict_axes = {}
+        dict_buttons = {}
+
+        for i, a in enumerate(axes):
+            dict_axes[i] = a
+
+        for i, b in enumerate(buttons):
+            dict_buttons[i] = b
+
+        return [dict_axes, dict_buttons, mapped_values]
+
+    def set_raw_input_map(self, input_map):
+        """Set an input device map"""
+        # TODO: Will not work!
+        if self._input_device:
+            self._input_device.input_map = input_map
+
+    def set_input_map(self, device_name, input_map_name):
+        """Load and set an input device map with the given name"""
+        settings = ConfigManager().get_settings(input_map_name)
+        if settings:
+            self.springy_throttle = settings["springythrottle"]
+            self._input_map = ConfigManager().get_config(input_map_name)
+        self._get_device_from_name(device_name).input_map = self._input_map
+        self._get_device_from_name(device_name).input_map_name = input_map_name
+        Config().get("device_config_mapping")[device_name] = input_map_name
+
+    def start_input(self, device_name, role="Device", config_name=None):
+        """
+        Start reading input from the device with name device_name using config
+        config_name. Returns True if device supports mapping, otherwise False
+        """
+        try:
+            #device_id = self._available_devices[device_name]
+            # Check if we supplied a new map, if not use the preferred one
+            device = self._get_device_from_name(device_name)
+            self._selected_mux.add_device(device, role)
+            # Update the UI with the limiting for this device
+            self.limiting_updated.call(device.limit_rp,
+                                       device.limit_yaw,
+                                       device.limit_thrust)
+            self._read_timer.start()
+            return device.supports_mapping
+        except Exception:
+            self.device_error.call(
+                     "Error while opening/initializing  input device\n\n%s" %
+                     (traceback.format_exc()))
+
+        if not self._input_device:
+            self.device_error.call(
+                     "Could not find device {}".format(device_name))
+        return False
+
+    def resume_input(self):
+        self._selected_mux.resume()
+        self._read_timer.start()
+
+    def pause_input(self, device_name = None):
+        """Stop reading from the input device."""
+        self._read_timer.stop()
+        self._selected_mux.pause()
+
+    def _set_thrust_slew_rate(self, rate):
+        self._thrust_slew_rate = rate
+        if rate > 0:
+            self.thrust_slew_enabled = True
+        else:
+            self.thrust_slew_enabled = False
+
+    def _get_thrust_slew_rate(self):
+        return self._thrust_slew_rate
+
+    def read_input(self):
+        """Read input data from the selected device"""
+        try:
+            data = self._selected_mux.read()
+
+            if data:
+                if data.toggled.althold:
+                    try:
+                        self.althold_updated.call(str(data.althold))
+                    except Exception as e:
+                        logger.warning("Exception while doing callback from"
+                                       "input-device for althold: {}".format(e))
+
+                if data.toggled.estop:
+                    try:
+                        self.emergency_stop_updated.call(data.estop)
+                    except Exception as e:
+                        logger.warning("Exception while doing callback from"
+                                       "input-device for estop: {}".format(e))
+
+                if data.toggled.alt1:
+                    try:
+                        self.alt1_updated.call(data.alt1)
+                    except Exception as e:
+                        logger.warning("Exception while doing callback from"
+                                       "input-device for alt1: {}".format(e))
+                if data.toggled.alt2:
+                    try:
+                        self.alt2_updated.call(data.alt2)
+                    except Exception as e:
+                        logger.warning("Exception while doing callback from"
+                                       "input-device for alt2: {}".format(e))
+
+                # Update the user roll/pitch trim from device
+                if data.toggled.pitchNeg and data.pitchNeg:
+                    self.trim_pitch -= 1
+                if data.toggled.pitchPos and data.pitchPos:
+                    self.trim_pitch += 1
+                if data.toggled.rollNeg and data.rollNeg:
+                    self.trim_roll -= 1
+                if data.toggled.rollPos and data.rollPos:
+                    self.trim_roll += 1
+
+                if data.toggled.pitchNeg or data.toggled.pitchPos or \
+                        data.toggled.rollNeg or data.toggled.rollPos:
+                    self.rp_trim_updated.call(self.trim_roll, self.trim_pitch)
+
+                # Thrust might be <0 here, make sure it's not otherwise we'll
+                # get an error.
+                if data.thrust < 0:
+                    data.thrust = 0
+                if data.thrust > 0xFFFF:
+                    data.thrust = 0xFFFF
+
+                # If we are using alt hold the data is not in a percentage
+                if not data.althold:
+                    data.thrust = JoystickReader.p2t(data.thrust)
+
+                self.input_updated.call(data.roll + self.trim_roll,
+                                        data.pitch + self.trim_pitch,
+                                        data.yaw, data.thrust)
+            else:
+                self.input_updated.call(0, 0, 0, 0)
+        except Exception:
+            logger.warning("Exception while reading inputdevice: %s",
+                           traceback.format_exc())
+            self.device_error.call("Error reading from input device\n\n%s" %
+                                     traceback.format_exc())
+            self.input_updated.call(0, 0, 0, 0)
+            self._read_timer.stop()
+
+    @staticmethod
+    def p2t(percentage):
+        """Convert a percentage to raw thrust"""
+        return int(MAX_THRUST * (percentage / 100.0))
+
+    thrust_slew_rate = property(_get_thrust_slew_rate, _set_thrust_slew_rate)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..9b26a5bbbc2cbee07572abccdebf8393c0943072
GIT binary patch
literal 13812
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
HjoJSTv2EBl

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.py
new file mode 100755
index 00000000..c27e0157
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Find all the available input interfaces and try to initialize them.
+
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['InputInterface']
+
+import os
+import glob
+import logging
+from ..inputreaderinterface import InputReaderInterface
+
+logger = logging.getLogger(__name__)
+
+found_interfaces = [os.path.splitext(os.path.basename(f))[0] for
+             f in glob.glob(os.path.dirname(__file__) + "/[A-Za-z]*.py")]
+if len(found_interfaces) == 0:
+    found_interfaces = [os.path.splitext(os.path.basename(f))[0] for
+                 f in glob.glob(os.path.dirname(__file__) +
+                                "/[A-Za-z]*.pyc")]
+
+logger.info("Found interfaces: {}".format(found_interfaces))
+
+initialized_interfaces = []
+available_interfaces = []
+
+for interface in found_interfaces:
+    try:
+        module = __import__(interface, globals(), locals(), [interface], -1)
+        main_name = getattr(module, "MODULE_MAIN")
+        initialized_interfaces.append(getattr(module, main_name)())
+        logger.info("Successfully initialized [{}]".format(interface))
+    except Exception as e:
+        logger.info("Could not initialize [{}]: {}".format(interface, e))
+
+def devices():
+    # Todo: Support rescanning and adding/removing devices
+    if len(available_interfaces) == 0:
+        for reader in initialized_interfaces:
+            devs = reader.devices()
+            for dev in devs:
+                available_interfaces.append(InputInterface(dev["name"],
+                                                           dev["id"],
+                                                           reader))
+    return available_interfaces
+
+class InputInterface(InputReaderInterface):
+    def __init__(self, dev_name, dev_id, dev_reader):
+        super(InputInterface, self).__init__(dev_name, dev_id, dev_reader)
+
+        # These devices cannot be mapped and configured
+        self.supports_mapping = False
+
+        # Ask the reader if it wants to limit
+        # roll/pitch/yaw/thrust for all devices
+        self.limit_rp = dev_reader.limit_rp
+        self.limit_thrust = dev_reader.limit_thrust
+        self.limit_yaw = dev_reader.limit_yaw
+
+    def open(self):
+        self._reader.open(self.id)
+
+    def close(self):
+        self._reader.close(self.id)
+
+    def read(self, include_raw=False):
+        mydata = self._reader.read(self.id)
+        # Merge interface returned data into InputReader Data Item
+        for key in mydata.keys():
+            self.data.set(key, mydata[key])
+
+        return self.data
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..0b8ff01cd5178a884db66a9b124ccbe1efb1dace
GIT binary patch
literal 3290
zcmd5;-EJFI5T3Jk?AmdD`je(^8gR=67AUoZxB($V+BBlHO{I+>bW2!mc8{HP*SpT{
zacj59Rr(NI@;bZ$uYq^~_-1zP1R*ZqqQUYR&zUo4{%4L$zc19k_8)!cQ}L<d_baUI
zH$@Z>Ie>~BMS(><AQlBB3d$6f$N`e$Wpc_>e2$zs3h=E^RG~qYoGK|jNBv8rYjg?`
zb?Oo6GWE-(OVppIeuMM^9|cuf#n0A$o1Dez;nEaYCcVg9%y@~KKU#D&Cg(EgE2J+M
z)E5?|HT943YyVRIuPi@?wRP%Wr2z0+p$kT^8fS%_ajw(&XFk~4|B0Mcn)sw|kX|L-
zq*L6!DR7koh=`pcL~EpPkzS)<o=z=t)=95W|Gk2!*O`cmtfWxg5Y?-N|7!2(6IIsl
z(~CN#zbNIjW{e93qi%^1NC4~iSbM=Dy@jo}IhDFfT^O^(6exP;+~K6|wwF2e+W__L
zFb-N?6t&EuZh0@gF!Bx}-3sI3$N(Chc0FHbEe~uaowQ5>YG^_)3SVo!G`L%@XY&ws
zFEoDYfw+0kEMotVMLe8J2>-xmC$w<TM#+)6Hf6E37wB}xm@VL-^L%r|@it!Xe{^>^
z2|=KYMgE2CXDnhn8NsqMLqBi5IWcU`lc!tXK7Qan*?hQ@tz*AC^5I9;9YxXPoC`tg
z`J0n{;kciSqM#Kg?Dxz=K4qEGIVH%cV%^8e4gn0ld8I1inha?Cl#GP}*`gDoW1>C^
zf#^ihxI@QW^B>ZQ#h1_)W{!^0Xcjg3r-UD53H*{w+B<5}9*PpYyX`WR?MNUDqp>&8
zP(u_3?F!q$mPI<2sLrzJo`uC+XXcq(!TBThD9|rMgvYQjZ#dL(U<0Ez%N1fOSrX;o
z%|s!Pws0}XG(hJt8R*VIQ3;)4n)J1AvJMx<q#K2L!v}~>wlSQT!zAuR;X%jm=ETm(
zgi+R!(#zqQ{T&w;hsJeL5B4Q!+GLen#$;Vl3#x&&tg327`Ap*vhvyzv)&YQVJlsfH
z1Ho2;V60h!U_@b*mFO+zR)y?5-abcqy6&^2CjfcMIa3z(EzT*DcZ{r+NFgdXaLc^!
zBe}o<fmZ|v1YQ-q%Es(UE9|t{Aru2NjD|Xu$Q!0jotCq0FUqu$&K(VhNoq28fK-KX
zPk5tnfcT_C)4;`iV-C|%W=w5Hn0T+Gk|d3pj=FLaX>_HaIQhH@15+>1Jna{fWo~#5
zc*eGnx>c%`8_2WI(}Nf1SF*AS1flNZi(~*|sG^0=#aF7B%iKLz#tEw2=JZMuY#t^T
z;%5_zpJ8R}5dCNW>>Ic(h2lp^rvDF~-GoIyU`;(^2RYze>5{o`fc!mkn2+O+xvTQo
zxwXGeC(>9LncP*trQ@3DKy`G7_E-eJYFrgNG7s}$ur5xx#6oKRg`Q;j4D*a9GYl;y
zEg|`rha-&rXcTBS^<K&38%%;JGp=7qiLAj97c1kTKx)Ohs@AMF7j!WuxP4t$y4H0E
zNid4AwYh_AE-#xiVJ|UYQD;5~_6{dEl+$A@aipSZjasQzYJUKVk@j?h#Gm=$jqpUI
z*ycz0lLDM;D9Ll!i;@F5i;`ZC#~`<`ydHU%&t4Ck3q!_WPn*YJ#*DqgBp!HtS*KDI
zL(d$Fz#vRzAab}}w;OUa@eC8?12Y>&q0wU_!9VaaEzCTbyZmqD+}mn%sf+9|?k1wI
zo1_EJ*zA)nPdAU}nW;qz!vPQY!gcl&v%j#`pQ3r1#a_7O8Iy{O122qaoZBo|NDs!o
z9-1(T#Ro0#Xa2l{<6JeoyF!r2eUW?cWeY}Rp8`}Yl!V1YzM+<_C99+!sVl04NBuMF
VI?gKA0(Q66l4`1(fVZvM{GX*Q`(*$C

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.py
new file mode 100755
index 00000000..80c76d07
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+#  USA.
+
+"""
+Leap Motion reader for controlling the Crazyflie. Note that this reader needs
+the Leap Motion SDK to be manually copied. See lib/leapsdk/__init__.py for
+more info.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LeapmotionReader']
+
+try:
+    import leapsdk.Leap as Leap
+    from leapsdk.Leap import CircleGesture, KeyTapGesture, ScreenTapGesture, SwipeGesture
+except Exception as e:
+    raise Exception("Leap Motion library probably not installed ({})".format(e))
+
+import logging
+import time
+
+logger = logging.getLogger(__name__)
+
+MODULE_MAIN = "LeapmotionReader"
+MODULE_NAME = "Leap Motion"
+
+class LeapListener(Leap.Listener):
+
+    def set_data_callback(self, callback):
+        self._dcb = callback
+        self._nbr_of_fingers = 0
+
+    def on_init(self, controller):
+        logger.info("Initialized")
+
+    def on_connect(self, controller):
+        logger.info("Connected")
+
+    def on_disconnect(self, controller):
+        # Note: not dispatched when running in a debugger.
+        logger.info("Disconnected")
+
+    def on_exit(self, controller):
+        logger.info("Exited")
+
+    def nbr_of_fingers(self):
+        return self._nbr_of_fingers
+
+    def on_frame(self, controller):
+        # Get the most recent frame and report some basic information
+        frame = controller.frame()
+        data = {"roll": 0, "pitch": 0, "yaw": 0, "thrust": 0}
+        #logger.info("Frame id: %d, timestamp: %d, hands: %d, fingers: %d, tools: %d, gestures: %d" % (
+        #                              frame.id, frame.timestamp, len(frame.hands), len(frame.fingers), len(frame.tools), len(frame.gestures())))
+        if not frame.hands.is_empty:
+            # Get the first hand
+            hand = frame.hands[0]
+
+            normal = hand.palm_normal
+            direction = hand.direction
+            # Pich and roll are mixed up...
+
+            if len(hand.fingers) >= 4:
+                data["roll"] = -direction.pitch * Leap.RAD_TO_DEG / 30.0
+                data["pitch"] = -normal.roll * Leap.RAD_TO_DEG / 30.0
+                data["yaw"] = direction.yaw * Leap.RAD_TO_DEG / 70.0
+                data["thrust"] = (hand.palm_position[1] - 80)/150.0 # Use the elevation of the hand for thrust
+
+            if data["thrust"] < 0.0:
+                data["thrust"] = 0.0
+            if data["thrust"] > 1.0:
+                data["thrust"] = 1.0
+
+        self._dcb(data)
+
+class LeapmotionReader:
+    """Used for reading data from input devices using the PyGame API."""
+    def __init__(self):
+        #pygame.init()
+        self._ts = 0
+        self._listener = LeapListener()
+        self._listener.set_data_callback(self.leap_callback)
+        self._controller = Leap.Controller()
+        self._controller.add_listener(self._listener)
+        self.name = MODULE_NAME
+
+        self.limit_rp = True
+        self.limit_thrust = True
+        self.limit_yaw = True
+
+        self.data = {"roll": 0.0, "pitch": 0.0, "yaw": 0.0,
+                     "thrust": -1.0, "estop": False, "exit": False,
+                     "althold": False, "alt1": False, "alt2": False,
+                     "pitchNeg": False, "rollNeg": False,
+                     "pitchPos": False, "rollPos": False}
+        logger.info("Initialized Leap")
+
+    def open(self, deviceId):
+        """Initialize the reading and open the device with deviceId and set the mapping for axis/buttons using the
+        inputMap"""
+        return
+
+    def leap_callback(self, data):
+        for k in data.keys():
+            self.data[k] = data[k]
+
+    def read(self, id):
+        """Read input from the selected device."""
+        return self.data
+
+    def close(self, id):
+        return
+
+    def devices(self):
+        """List all the available devices."""
+        dev = []
+
+        # According to API doc only 0 or 1 devices is supported
+        #logger.info("Devs: {}".format(self._controller.is_connected))
+        if self._controller.is_connected:
+            dev.append({"id": 0, "name": "Leapmotion"})
+        
+        return dev
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/leapmotion.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..7715c47e3d587d68a8815f5c15244d4fe9273e17
GIT binary patch
literal 6071
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
SdpK6Hjp){{qOVn3ee>Tc@~4yl

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.py
new file mode 100755
index 00000000..290e9628
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.py
@@ -0,0 +1,141 @@
+import logging
+from threading import Thread
+import time
+
+try:
+    import cwiid
+except ImportError as e:
+    raise Exception("Missing cwiid (wiimote) driver {}".format(e))
+
+logger = logging.getLogger(__name__)
+
+MODULE_MAIN = "WiimoteReader"
+MODULE_NAME = "WII"
+
+class _Reader(object):
+    # needs attributes:
+    # - name
+    # - limit_rp
+    # - limit_thrust
+    # - limit_yaw
+    # - open
+    def devices(self):
+        """List all the available connections"""
+        raise NotImplemented()
+    def open(self, device_id):
+        """Initialize the reading and open the device with deviceId and set the mapping for axis/buttons using the
+        inputMap"""
+        return
+    def close(self, device_id):
+        return
+    def read(self, device_id):
+        """Read input from the selected device."""
+        raise NotImplemented()
+
+
+TWO = 1
+ONE = 2
+B = 4
+A = 8
+MINUS = 16
+HOME = 128
+LEFT = 256
+RIGHT = 512
+DOWN = 1024
+UP = 2048
+PLUS = 4096
+
+
+class HandleWiimote(Thread):
+    def __init__(self, reader, wii, *args):
+        super(HandleWiimote, self).__init__(*args)
+        self.reader = reader
+        self.wii = wii
+        self.daemon = True
+
+    def run(self):
+        logger.info("\n\nRUNNING THREAD!\n\n\n")
+        t_delta = 100
+        move_delta = .3
+        max_move = 8
+        min_sample = .1
+        max_sample = .01
+        sample = min_sample
+        while True:
+            button = self.wii.state['buttons']
+            pitch = self.reader.data['pitch']
+            roll = self.reader.data['roll']
+            if button & ONE:
+                logger.info("UP!! {}".format(self.reader.data['thrust']))
+                self.reader.data['thrust'] += t_delta
+            if button & TWO:
+                logger.info("DOWN!! {}".format(self.reader.data['thrust']))
+                self.reader.data['thrust'] -= t_delta * 3
+                if self.reader.data['thrust'] < -1:
+                    self.reader.data['thrust'] = -1
+            if button & RIGHT:
+                logger.info("RIGHT PITCH {}".format(pitch))
+                self.reader.data['pitch'] = min(max_move, pitch + move_delta)
+            if button & LEFT:
+                logger.info("LEFT PITCH {}".format(pitch))
+                self.reader.data['pitch'] = max(-max_move, pitch - move_delta)
+            if button & UP:
+                logger.info("UP ROLL {}".format(roll))
+                self.reader.data['roll'] = max(-max_move, roll - move_delta)
+            if button & DOWN:
+                logger.info("DOWN ROLL {}".format(roll))
+                self.reader.data['roll'] = min(max_move, roll + move_delta)
+            if button & B:
+                # KILL
+                self.reader.data['thrust'] = -1
+
+            if button:
+                sample = max(max_sample, sample/3)
+            else:
+                sample = min(min_sample, sample*3)
+                self.adjust()
+            time.sleep(sample)
+
+    def adjust(self):
+        pitch = self.reader.data['pitch']
+        if pitch > 1.2:
+            self.reader.data['pitch'] -= 1
+        elif pitch < -1.2:
+            self.reader.data['pitch'] += 1
+        else:
+            self.reader.data['pitch'] = 0
+        roll = self.reader.data['roll']
+        if roll > 1.2:
+            self.reader.data['roll'] -= 1
+        elif roll < -1.2:
+            self.reader.data['roll'] += 1
+        else:
+            self.reader.data['roll'] = 0
+
+class WiimoteReader(_Reader):
+    name = MODULE_NAME
+
+    def __init__(self):
+        self.limit_rp = False
+        self.limit_thrust = False
+        self.limit_yaw = False
+
+        print "Press 1 + 2 to connect wii"
+        time.sleep(1)
+        self.wm = cwiid.Wiimote()
+        self.wm.rpt_mode = cwiid.RPT_BTN
+        logger.info("FOUND WIIMOTE")
+        self.data = {"roll": 0.0, "pitch": 0.0, "yaw": 0.0,
+                     "thrust": -1.0, "estop": False, "exit": False,
+                     "althold": False, "alt1": False, "alt2": False,
+                     "pitchNeg": False, "rollNeg": False,
+                     "pitchPos": False, "rollPos": False}
+        self.wii_thread = HandleWiimote(self, self.wm)
+        self.wii_thread.start()
+
+    def read(self, dev_id):
+        return self.data
+
+    def devices(self):
+        """List all the available connections"""
+        return [{"id": 0, "name": "WII@{}".format(self.wm)}]
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/wiimote.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..a7367d572ff1f96996c426106d5de2b58bd85b78
GIT binary patch
literal 6087
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.py
new file mode 100755
index 00000000..f12f702d
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+#  USA.
+
+"""
+Input interface that supports receiving commands via ZMQ.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['ZMQReader']
+
+try:
+    import zmq
+except Exception as e:
+    raise Exception("ZMQ library probably not installed ({})".format(e))
+
+from cfclient.utils.config import Config
+if not Config().get("enable_zmq_input"):
+    raise Exception("ZMQ input disabled in config file")
+
+import logging
+import time
+import pprint
+from threading import Thread
+
+ZMQ_PULL_PORT = 1024 + 188
+
+logger = logging.getLogger(__name__)
+
+MODULE_MAIN = "ZMQReader"
+MODULE_NAME = "ZMQ"
+
+class _PullReader(Thread):
+
+    def __init__(self, receiver, callback, *args):
+        super(_PullReader, self).__init__(*args)
+        self._receiver = receiver
+        self._cb = callback
+        self.daemon = True
+
+    def run(self):
+        while True:
+            self._cb(self._receiver.recv_json())
+
+class ZMQReader:
+    """Used for reading data from input devices using the PyGame API."""
+    def __init__(self):
+        context = zmq.Context()
+        receiver = context.socket(zmq.PULL)
+        self._bind_addr = "tcp://127.0.0.1:{}".format(ZMQ_PULL_PORT)
+        # If the port is already bound an exception will be thrown
+        # and caught in the initialization of the readers and handled.
+        receiver.bind(self._bind_addr)
+        logger.info("Biding ZMQ at {}".format(self._bind_addr))
+
+        self.name = MODULE_NAME
+
+        self.limit_rp = False
+        self.limit_thrust = False
+        self.limit_yaw = False
+
+        self.data = {"roll": 0.0, "pitch": 0.0, "yaw": 0.0,
+                     "thrust": -1.0, "estop": False, "exit": False,
+                     "althold": False, "alt1": False, "alt2": False,
+                     "pitchNeg": False, "rollNeg": False,
+                     "pitchPos": False, "rollPos": False}
+
+        logger.info("Initialized ZMQ")
+
+        self._receiver_thread = _PullReader(receiver, self._cmd_callback)
+        self._receiver_thread.start()
+
+    def _cmd_callback(self, cmd):
+        for k in cmd["ctrl"].keys():
+            self.data[k] = cmd["ctrl"][k]
+
+    def open(self, device_id):
+        """Initialize the reading and open the device with deviceId and set the mapping for axis/buttons using the
+        inputMap"""
+        return
+
+    def read(self, device_id):
+        """Read input from the selected device."""
+
+        return self.data
+
+    def close(self, device_id):
+        return
+
+    def devices(self):
+        """List all the available connections"""
+        # As a temporary workaround we always say we have ZMQ
+        # connected. If it's not connected, there's just no data.
+        return [{"id": 0, "name": "ZMQ@{}".format(self._bind_addr)}]
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputinterfaces/zmqpull.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..e1193a55587204f671f4fe287d21eeaa117ab9f1
GIT binary patch
literal 4432
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
G_J0AuhZ}DI

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.py
new file mode 100755
index 00000000..7e41cd73
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Interface for reading input devices and interfaces
+"""
+
+from time import time
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class _ToggleState(dict):
+    def __getattr__(self, attr):
+        return self.get(attr)
+
+    __setattr__ = dict.__setitem__
+    __delattr__ = dict.__delitem__
+
+
+class InputData:
+    def __init__(self):
+        #self._toggled = {}
+        self._axes = ("roll", "pitch", "yaw", "thrust")
+        self._buttons = ("alt1", "alt2", "estop", "exit", "pitchNeg",
+                         "pitchPos", "rollNeg", "rollPos", "althold",
+                         "muxswitch")
+        for axis in self._axes:
+            self.__dict__[axis] = 0.0
+        self.toggled = _ToggleState()
+        self._prev_btn_values = {}
+        for button in self._buttons:
+            self.__dict__[button] = False
+            self.toggled[button] = False
+            self._prev_btn_values[button] = False
+
+    def get_all_indicators(self):
+        return self._axes + self._buttons
+
+    def _check_toggle(self, key, data):
+        if not key in self._prev_btn_values:
+            self._prev_btn_values[key] = data
+        elif self._prev_btn_values[key] != data:
+            self._prev_btn_values[key] = data
+            return True
+        return False
+
+    def reset_axes(self):
+        for axis in self._axes:
+            self.__dict__[axis] = 0.0
+
+    def reset_buttons(self):
+        for button in self._buttons:
+            self.__dict__[button] = False
+
+    def set(self, name, value):
+        try:
+            if name in self._buttons:
+                if self._check_toggle(name, value):
+                    self.toggled[name] = True
+                else:
+                    self.toggled[name] = False
+        except KeyError:
+            pass
+        self.__dict__[name] = value
+
+    def get(self, name):
+        return self.__dict__[name]
+
+
+class InputReaderInterface(object):
+    def __init__(self, dev_name, dev_id, dev_reader):
+        """Initialize the reader"""
+        # Set if the device supports mapping and can be configured
+        self.supports_mapping = True
+
+        # Set if the MUX should automatically limit roll/pitch/thrust/yaw
+        # according to the settings in the UI
+        self.limit_rp = True
+        self.limit_thrust = True
+        self.limit_yaw = True
+
+        self.input = None
+
+        self._reader = dev_reader
+        self.id = dev_id
+        self.name = dev_name
+        self.input_map = None
+        self.input_map_name = ""
+        self.data = None
+        self._prev_pressed = None
+        self.reader_name = dev_reader.name
+
+        self.data = InputData()
+
+        # Stateful things
+        self._old_thrust = 0
+        self._old_raw_thrust = 0
+        self._old_alt_hold = False
+
+        self._prev_thrust = 0
+        self._last_time = 0
+        # How low you have to pull the thrust to bypass the slew-rate (0-100%)
+        self.thrust_stop_limit = -90
+
+    def open(self):
+        """Initialize the reading and open the device with deviceId and set the
+        mapping for axis/buttons using the inputMap"""
+        return
+
+    def read(self):
+        """Read input from the selected device."""
+        return None
+
+    def close(self):
+        return
+
+    @staticmethod
+    def devices():
+        """List all the available devices."""
+        return []
+
+    def _cap_rp(self, rp):
+        ret = rp * self.input.max_rp_angle
+        if ret > self.input.max_rp_angle:
+            ret = self.input.max_rp_angle
+        elif ret < -1 * self.input.max_rp_angle:
+            ret = -1 * self.input.max_rp_angle
+
+        return ret
+
+    def _scale_rp(self, roll, pitch):
+        return [self._cap_rp(roll), self._cap_rp(pitch)]
+
+    def _scale_and_deadband_yaw(self, yaw):
+        return InputReaderInterface.deadband(yaw, 0.2) * self.input.max_yaw_rate
+
+    def _limit_thrust(self, thrust, althold, emergency_stop):
+        # Thust limiting (slew, minimum and emergency stop)
+        if self.input.springy_throttle:
+            if althold and self.input.has_pressure_sensor:
+                thrust = int(round(InputReaderInterface.deadband(thrust, 0.2)
+                                   * 32767 + 32767))  # Convert to uint16
+            else:
+                # Scale the thrust to percent (it's between 0 and 1)
+                thrust *= 100
+
+                # The default action is to just use the thrust...
+                limited_thrust = thrust
+                if limited_thrust > self.input.max_thrust:
+                    limited_thrust = self.input.max_thrust
+
+                # ... but if we are lowering the thrust, check the limit
+                if self._prev_thrust > thrust >= self.thrust_stop_limit and \
+                        not emergency_stop:
+                    # If we are above the limit, then don't use the slew...
+                    if thrust > self.input.thrust_slew_limit:
+                        limited_thrust = thrust
+                    else:
+                        # ... but if we are below first check if we "entered"
+                        # the limit, then set it to the limit
+                        if self._prev_thrust > self.input.thrust_slew_limit:
+                            limited_thrust = self.input.thrust_slew_limit
+                        else:
+                            # If we are "inside" the limit, then lower
+                            # according to the rate we have set each iteration
+                            lowering = (time() - self._last_time) * \
+                                self.input.thrust_slew_rate
+                            limited_thrust = self._prev_thrust - lowering
+                elif emergency_stop or thrust < self.thrust_stop_limit:
+                    # If the thrust have been pulled down or the
+                    # emergency stop has been activated then bypass
+                    # the slew and force 0
+                    self._prev_thrust = 0
+                    limited_thrust = 0
+
+                # For the next iteration set the previous thrust to the limited
+                # one (will be the slewed thrust if we are slewing)
+                self._prev_thrust = limited_thrust
+
+                # Lastly make sure we're following the "minimum" thrust setting
+                if limited_thrust < self.input.min_thrust:
+                    self._prev_thrust = 0
+                    limited_thrust = 0
+
+                self._last_time = time()
+
+                thrust = limited_thrust
+        else:
+            thrust = thrust / 2 + 0.5
+            if althold and self.input.has_pressure_sensor:
+                thrust = 32767
+            else:
+                if thrust < -0.90 or emergency_stop:
+                    thrust = 0
+                else:
+                    thrust = self.input.min_thrust + thrust * (
+                        self.input.max_thrust -
+                        self.input.min_thrust)
+                if (self.input.thrust_slew_enabled and
+                            self.input.thrust_slew_limit > thrust and not
+                emergency_stop):
+                    if self._old_thrust > self.input.thrust_slew_limit:
+                        self._old_thrust = self.input.thrust_slew_limit
+                    if thrust < (self._old_thrust -
+                                     (self.input.thrust_slew_rate / 100)):
+                        thrust = self._old_thrust - \
+                                 self.input.thrust_slew_rate / 100
+                    if thrust < -1 or thrust < self.input.min_thrust:
+                        thrust = 0
+
+        self._old_thrust = thrust
+        self._old_raw_thrust = thrust
+        return thrust
+
+    def set_alt_hold_available(self, available):
+        """Set if altitude hold is available or not (depending on HW)"""
+        self.input._has_pressure_sensor = available
+
+    def enable_alt_hold(self, althold):
+        """Enable or disable altitude hold"""
+        self._old_alt_hold = althold
+
+    @staticmethod
+    def deadband(value, threshold):
+        if abs(value) < threshold:
+            value = 0
+        elif value > 0:
+            value -= threshold
+        elif value < 0:
+            value += threshold
+        return value / (1 - threshold)
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaderinterface.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..b768cf698b267463801adcd5ac995978e6ad369c
GIT binary patch
literal 8721
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
N0_wbfZ2IV>{{p8c|JMKj

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.py
new file mode 100755
index 00000000..11d1a8fe
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Find all the available input readers and try to import them.
+
+To create a new input device reader drop a .py file into this
+directory and it will be picked up automatically.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['InputDevice']
+
+import os
+import glob
+import logging
+from ..inputreaderinterface import InputReaderInterface
+
+logger = logging.getLogger(__name__)
+
+found_readers = [os.path.splitext(os.path.basename(f))[0] for
+                 f in glob.glob(os.path.dirname(__file__) + "/[A-Za-z]*.py")]
+if len(found_readers) == 0:
+    found_readers = [os.path.splitext(os.path.basename(f))[0] for
+                     f in glob.glob(os.path.dirname(__file__) +
+                                    "/[A-Za-z]*.pyc")]
+
+logger.info("Found readers: {}".format(found_readers))
+
+initialized_readers = []
+available_devices = []
+
+for reader in found_readers:
+    try:
+        module = __import__(reader, globals(), locals(), [reader], -1)
+        main_name = getattr(module, "MODULE_MAIN")
+        initialized_readers.append(getattr(module, main_name)())
+        logger.info("Successfully initialized [{}]".format(reader))
+    except Exception as e:
+        logger.info("Could not initialize [{}]: {}".format(reader, e))
+
+def devices():
+    # Todo: Support rescanning and adding/removing devices
+    if len(available_devices) == 0:
+        for r in initialized_readers:
+            devs = r.devices()
+            for dev in devs:
+                available_devices.append(InputDevice(dev["name"],
+                                                     dev["id"],
+                                                     r))
+    return available_devices
+
+
+class InputDevice(InputReaderInterface):
+    def __init__(self, dev_name, dev_id, dev_reader):
+        super(InputDevice, self).__init__(dev_name, dev_id, dev_reader)
+
+        # All devices supports mapping (and can be configured)
+        self.supports_mapping = True
+
+        # Limit roll/pitch/yaw/thrust for all devices
+        self.limit_rp = True
+        self.limit_thrust = True
+        self.limit_yaw = True
+
+    def open(self):
+        # TODO: Reset data?
+        self._reader.open(self.id)
+
+    def close(self):
+        self._reader.close(self.id)
+
+    def read(self, include_raw=False):
+        [axis, buttons] = self._reader.read(self.id)
+
+        # To support split axis we need to zero all the axis
+        self.data.reset_axes()
+
+        i = 0
+        for a in axis:
+            index = "Input.AXIS-%d" % i
+            try:
+                if self.input_map[index]["type"] == "Input.AXIS":
+                    key = self.input_map[index]["key"]
+                    axisvalue = a + self.input_map[index]["offset"]
+                    axisvalue = axisvalue / self.input_map[index]["scale"]
+                    self.data.set(key, axisvalue + self.data.get(key))
+            except (KeyError, TypeError):
+                pass
+            i += 1
+
+        # Workaround for fixing issues during mapping (remapping buttons while
+        # they are pressed.
+        self.data.reset_buttons()
+
+        i = 0
+        for b in buttons:
+            index = "Input.BUTTON-%d" % i
+            try:
+                if self.input_map[index]["type"] == "Input.BUTTON":
+                    key = self.input_map[index]["key"]
+                    self.data.set(key, True if b == 1 else False)
+            except (KeyError, TypeError):
+                # Button not mapped, ignore..
+                pass
+            i += 1
+
+        if self.limit_rp:
+            [self.data.roll, self.data.pitch] = self._scale_rp(self.data.roll,
+                                                               self.data.pitch)
+        if self.limit_thrust:
+            self.data.thrust = self._limit_thrust(self.data.thrust,
+                                                  self.data.althold,
+                                                  self.data.estop)
+        if self.limit_yaw:
+            self.data.yaw = self._scale_and_deadband_yaw(self.data.yaw)
+
+        if include_raw:
+            return [axis, buttons, self.data]
+        else:
+            return self.data
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..fbb91527005546476a12468b4d18d99d6b80b392
GIT binary patch
literal 4311
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
GrT+qP1<Mrx

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.py
new file mode 100755
index 00000000..a0fee1ba
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.py
@@ -0,0 +1,227 @@
+# -*- coding: utf8 -*-
+#     ||
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Linux joystick driver using the Linux input_joystick subsystem. Requires sysfs
+to be mounted on /sys and /dev/input/js* to be readable.
+
+This module is very linux specific but should work on any CPU platform
+"""
+
+import sys
+if not sys.platform.startswith('linux'):
+    raise Exception("Only supported on Linux")
+
+import struct
+import glob
+import os
+import ctypes
+import fcntl
+import logging
+
+logger = logging.getLogger(__name__)
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Joystick']
+
+JS_EVENT_FMT = "@IhBB"
+JE_TIME = 0
+JE_VALUE = 1
+JE_TYPE = 2
+JE_NUMBER = 3
+
+JS_EVENT_BUTTON = 0x001
+JS_EVENT_AXIS = 0x002
+JS_EVENT_INIT = 0x080
+
+#ioctls
+JSIOCGAXES = 0x80016a11
+JSIOCGBUTTONS = 0x80016a12
+
+MODULE_MAIN = "Joystick"
+MODULE_NAME = "linuxjsdev"
+
+class JEvent(object):
+    """
+    Joystick event class. Encapsulate single joystick event.
+    """
+    def __init__(self, evt_type, number, value):
+        self.type = evt_type
+        self.number = number
+        self.value = value
+
+    def __repr__(self):
+        return "JEvent(type={}, number={}, value={})".format(self.type,
+                   self.number, self.value)
+
+#Constants
+TYPE_BUTTON = 1
+TYPE_AXIS = 2
+
+class _JS():
+    def __init__(self, num, name):
+        self.num = num
+        self.name = name
+        self._f_name = "/dev/input/js{}".format(num)
+        self._f = None
+
+        self.opened = False
+        self.buttons = []
+        self.axes = []
+        self._prev_pressed = {}
+
+    def open(self):
+        if self._f:
+            raise Exception("{} at {} is already "
+                            "opened".format(self.name, self._f_name))
+
+        self._f = open("/dev/input/js{}".format(self.num), "r")
+        fcntl.fcntl(self._f.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
+
+        #Get number of axis and button
+        val = ctypes.c_int()
+        if fcntl.ioctl(self._f.fileno(), JSIOCGAXES, val) != 0:
+            self._f.close()
+            self._f = None
+            raise Exception("Failed to read number of axes")
+
+        self.axes = list(0 for i in range(val.value))
+        if fcntl.ioctl(self._f.fileno(), JSIOCGBUTTONS, val) != 0:
+            self._f.close()
+            self._f = None
+            raise Exception("Failed to read number of axes")
+
+        self.buttons = list(0 for i in range(val.value))
+        self.__initvalues()
+
+    def close(self):
+        """Open the joystick device"""
+        if not self._f:
+            return
+
+        logger.info("Closed {} ({})".format(self.name, self.num))
+
+        self._f.close()
+        self._f = None
+
+    def __initvalues(self):
+        """Read the buttons and axes initial values from the js device"""
+        for _ in range(len(self.axes) + len(self.buttons)):
+            data = self._f.read(struct.calcsize(JS_EVENT_FMT))
+            jsdata = struct.unpack(JS_EVENT_FMT, data)
+            self.__updatestate(jsdata)
+
+    def __updatestate(self, jsdata):
+        """Update the internal absolute state of buttons and axes"""
+        if jsdata[JE_TYPE] & JS_EVENT_AXIS != 0:
+            self.axes[jsdata[JE_NUMBER]] = jsdata[JE_VALUE] / 32768.0
+        elif jsdata[JE_TYPE] & JS_EVENT_BUTTON != 0:
+            self.buttons[jsdata[JE_NUMBER]] = jsdata[JE_VALUE]
+
+    def __decode_event(self, jsdata):
+        """ Decode a jsdev event into a dict """
+        #TODO: Add timestamp?
+        if jsdata[JE_TYPE] & JS_EVENT_AXIS != 0:
+            return JEvent(evt_type=TYPE_AXIS,
+                          number=jsdata[JE_NUMBER],
+                          value=jsdata[JE_VALUE] / 32768.0)
+        if jsdata[JE_TYPE] & JS_EVENT_BUTTON != 0:
+            return JEvent(evt_type=TYPE_BUTTON,
+                          number=jsdata[JE_NUMBER],
+                          value=jsdata[JE_VALUE] / 32768.0)
+
+    def _read_all_events(self):
+        """Consume all the events queued up in the JS device"""
+        try:
+            while True:
+                data = self._f.read(struct.calcsize(JS_EVENT_FMT))
+                jsdata = struct.unpack(JS_EVENT_FMT, data)
+                self.__updatestate(jsdata)
+        except IOError as e:
+            if e.errno != 11:
+                logger.info(str(e))
+                self._f.close()
+                self._f = None
+                raise IOError("Device has been disconnected")
+        except ValueError:
+            # This will happen if I/O operations are done on a closed device,
+            # which is the case when you first close and then open the device
+            # while switching device. But, in order for SDL2 to work on Linux
+            # (for debugging) the device needs to be closed before it's opened.
+            # This is the workaround to make both cases work.
+            pass
+
+    def read(self):
+        """ Returns a list of all joystick event since the last call """
+        if not self._f:
+            raise Exception("Joystick device not opened")
+
+        self._read_all_events()
+
+        return [self.axes, self.buttons]
+
+
+class Joystick():
+    """
+    Linux jsdev implementation of the Joystick class
+    """
+
+    def __init__(self):
+        self.name = MODULE_NAME
+        self._js = {}
+        self._devices = []
+
+    def devices(self):
+        """
+        Returns a dict with device_id as key and device name as value of all
+        the detected devices (result is cached once one or more device are
+        found).
+        """
+
+        if len(self._devices) == 0:
+            syspaths = glob.glob("/sys/class/input/js*")
+
+            for path in syspaths:
+                device_id = int(os.path.basename(path)[2:])
+                with open(path + "/device/name") as namefile:
+                    name = namefile.read().strip()
+                self._js[device_id] = _JS(device_id, name)
+                self._devices.append({"id": device_id, "name": name})
+
+        return self._devices
+
+    def open(self, device_id):
+        """
+        Open the joystick device. The device_id is given by available_devices
+        """
+        self._js[device_id].open()
+
+    def close(self, device_id):
+        """Open the joystick device"""
+        self._js[device_id].close()
+
+    def read(self, device_id):
+        """ Returns a list of all joystick event since the last call """
+        return self._js[device_id].read()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/linuxjsdev.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..4816fdfea45669fd14e80ae7cff76c50a9c7465c
GIT binary patch
literal 9030
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
ZZMFt45~cjJEqn>BvsF8OkoD2c{{o;!B+vi=

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.py
new file mode 100755
index 00000000..f998ad8e
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+#  USA.
+
+"""
+Driver for reading data from the PySDL2 API. Used from Inpyt.py for reading
+input data.
+"""
+
+import sys
+if sys.platform.startswith('linux'):
+    raise Exception("No SDL2 support on Linux")
+
+__author__ = 'Bitcraze AB'
+__all__ = ['PySDL2Reader']
+
+import sdl2
+import sdl2.ext
+import sdl2.hints
+from threading import Thread
+from Queue import Queue
+import time
+import logging
+
+logger = logging.getLogger(__name__)
+
+MODULE_MAIN = "PySDL2Reader"
+MODULE_NAME = "PySDL2"
+
+class _SDLEventDispatcher(Thread):
+    """Wrapper to read all SDL2 events from the global queue and distribute
+    them to the different devices"""
+    def __init__(self, callback):
+        Thread.__init__(self)
+        self._callback = callback
+        self.daemon = True
+        # SDL2 will Seg Fault on Linux if you read events after you
+        # have closed a device (and without opening a new one). Even if you
+        # have two devices open, it will crash after one.
+        self.enable = False
+
+    def run(self):
+        while True:
+            if self.enable:
+                for ev in sdl2.ext.get_events():
+                    try:
+                        if self._callback:
+                            self._callback(ev.jdevice.which, ev)
+                    except AttributeError:
+                        pass
+            time.sleep(0.01)
+
+
+class _JS():
+    """Wrapper for one input device"""
+    def __init__(self, sdl_index, sdl_id, name):
+        self.axes = []
+        self.buttons = []
+        self.name = MODULE_NAME
+        self._j = None
+        self._btn_count = 0
+        self._id = sdl_id
+        self._index = sdl_index
+        self._name = name
+        self._event_queue = Queue()
+
+    def open(self):
+        self._j = sdl2.SDL_JoystickOpen(self._index)
+        self._btn_count = sdl2.SDL_JoystickNumButtons(self._j)
+
+        self.axes = list(0 for i in range(sdl2.SDL_JoystickNumAxes(self._j)))
+        self.buttons = list(0 for i in range(sdl2.SDL_JoystickNumButtons(
+            self._j) + 4))
+
+    def close(self):
+        if self._j:
+            sdl2.joystick.SDL_JoystickClose(self._j)
+        self._j = None
+
+    def _set_fake_hat_button(self, btn=None):
+        self.buttons[self._btn_count] = 0
+        self.buttons[self._btn_count+1] = 0
+        self.buttons[self._btn_count+2] = 0
+        self.buttons[self._btn_count+3] = 0
+
+        if btn:
+            self.buttons[self._btn_count+btn] = 1
+
+    def add_event(self, event):
+        self._event_queue.put(event)
+
+    def read(self):
+        while not self._event_queue.empty():
+            e = self._event_queue.get_nowait()
+            if e.type == sdl2.SDL_JOYAXISMOTION:
+                self.axes[e.jaxis.axis] = e.jaxis.value / 32767.0
+
+            if e.type == sdl2.SDL_JOYBUTTONDOWN:
+                self.buttons[e.jbutton.button] = 1
+
+            if e.type == sdl2.SDL_JOYBUTTONUP:
+                self.buttons[e.jbutton.button] = 0
+
+            if e.type == sdl2.SDL_JOYHATMOTION:
+                if e.jhat.value == sdl2.SDL_HAT_CENTERED:
+                    self._set_fake_hat_button()
+                elif e.jhat.value == sdl2.SDL_HAT_UP:
+                    self._set_fake_hat_button(0)
+                elif e.jhat.value == sdl2.SDL_HAT_DOWN:
+                    self._set_fake_hat_button(1)
+                elif e.jhat.value == sdl2.SDL_HAT_LEFT:
+                    self._set_fake_hat_button(2)
+                elif e.jhat.value == sdl2.SDL_HAT_RIGHT:
+                    self._set_fake_hat_button(3)
+        return [self.axes, self.buttons]
+
+class PySDL2Reader():
+    """Used for reading data from input devices using the PySDL2 API."""
+    def __init__(self):
+        sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_JOYSTICK)
+        sdl2.SDL_SetHint(sdl2.hints.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
+                         "1")
+        sdl2.ext.init()
+        self._js = {}
+        self.name = MODULE_NAME
+        self._event_dispatcher = _SDLEventDispatcher(self._dispatch_events)
+        self._event_dispatcher.start()
+        self._devices = []
+
+    def open(self, device_id):
+        """Initialize the reading and open the device with deviceId and set
+        the mapping for axis/buttons using the inputMap"""
+        self._event_dispatcher.enable = True
+        self._js[device_id].open()
+
+    def close(self, device_id):
+        """Close the device"""
+        self._event_dispatcher.enable = False
+        self._js[device_id].close()
+
+    def read(self, device_id):
+        """Read input from the selected device."""
+        return self._js[device_id].read()
+
+    def _dispatch_events(self, device_id, event):
+        self._js[device_id].add_event(event)
+
+    def devices(self):
+        """List all the available devices."""
+        logger.info("Looking for devices")
+        names = []
+        if len(self._devices) == 0:
+            nbrOfInputs = sdl2.joystick.SDL_NumJoysticks()
+            logger.info("Found {} devices".format(nbrOfInputs))
+            for sdl_index in range(0, nbrOfInputs):
+                j = sdl2.joystick.SDL_JoystickOpen(sdl_index)
+                name = sdl2.joystick.SDL_JoystickName(j)
+                if names.count(name) > 0:
+                    name = "{0} #{1}".format(name, names.count(name) + 1)
+                sdl_id = sdl2.joystick.SDL_JoystickInstanceID(j)
+                self._devices.append({"id": sdl_id, "name": name})
+                self._js[sdl_id] = _JS(sdl_index, sdl_id, name)
+                names.append(name)
+                sdl2.joystick.SDL_JoystickClose(j)
+        return self._devices
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/inputreaders/pysdl2.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..141afec89ad6dd1d4462cddb511db7cfad27c97d
GIT binary patch
literal 8270
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
BNXY;I

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.py
new file mode 100755
index 00000000..402c4d87
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+#  02110-1301, USA.
+"""
+The mux is used to open one or more devices and mix the inputs from all
+of them into one "input" for the Crazyflie and UI.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['InputMux']
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class InputMux(object):
+    def __init__(self, input_layer):
+        self._devs = {"Device": None}
+        self.name = "N/A"
+        self.input = input_layer
+
+    def _open_new_device(self, dev, role):
+        # Silently close device if open as other role
+        for r in self._devs:
+            if self._devs[r]:
+                if self._devs[r] == dev:
+                    self._devs[r].close()
+                    self._devs[r] = None
+
+        if self._devs[role]:
+            self._devs[role].close()
+        self._devs[role] = dev
+        self._devs[role].open()
+
+    def supported_roles(self):
+        return self._devs.keys()
+
+    def add_device(self, dev, role):
+        logger.info("Adding device {} to MUX {}".format(dev.name, self.name))
+        self._open_new_device(dev, role)
+
+    def pause(self):
+        for d in [key for key in self._devs.keys() if self._devs[key]]:
+            self._devs[d].close()
+
+    def resume(self):
+        for d in [key for key in self._devs.keys() if self._devs[key]]:
+            self._devs[d].open()
+
+    def close(self):
+        """Close down the MUX and close all it's devices"""
+        for d in [key for key in self._devs.keys() if self._devs[key]]:
+            self._devs[d].close()
+            self._devs[d] = None
+
+    def read(self):
+        return None
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..ac147b6b3038f7b89e9ebfa6ad78bc2bf1f8f8e2
GIT binary patch
literal 3102
zcmc&$TW{P{5I(jyo9xoiloUdEsa2t}MM{l?l&2MtrV_o-1)PM^h}PQJXE)w@?Tyb_
znn3at{slji9{}Gs-b*SF5)W*XoXO0b+sro?AMcOV=EL~O53!p3c=-PnhItH<D0K+x
zs6$63uIhtvRpP0nrZP_*dP;k0P*ZwIU1F-vHF{a~uy{%RrqrcU&kt9)daqUIndczQ
z{bTJ9$ESX3{ISuAZwtQ|>D(`J?H8p#EK2Ps`sXy(#_#5dKTJ=3i#=&R8e8M{%3|nu
zv#eS4s2)Pk6R`QVthnv>a0)l<mfc^@dReOFn1e_6n&urCa6h#%X0*R^->yJ@#B-mH
zPt!l~)0qL`a4ClQ4nzY8c%|qSA#j8N5Uwyag2JgKI!gvv2n2`ADyfSGI4ld(P~l#S
z7soCL2QlBpFn2&wxd%6F1FcAKC*!JfN0Gs$3V3o)#}d^C6;`4Mc3L&4EJ+8L*d;I#
z&@`gTyTeK)NVRyk=#9>Lme`0s%DQK|Y(Z2)%yV$TaWT}v(FD6-R2BmrTN4mU0eLG1
z(z!KTqceM4<Uy7m1#z!Z24kCMCXmwu#6F0kG*4|5-NRx+F_k|BZ|IbA)|`eD(<%DG
zAqf2r=JbF-4B+{T$LhkH2<M*KESpyZ;PuK?<^C1bz+p~0iJI%$C;RH8$-igSq315n
zIh!tkFxix2lSBX)V@-R$df-h@-D@#xIue7BwhR7oRu~=5z-F>nE9j`@G6YECP!^fC
zJYsR^ui>_cCPle^%}hbv1<;xIK$N@YtUBw?4QIn$b#8)>Y29>4L{_1wu&98VVnBZ@
z3>dsu`pgJ8A)Q`WZe7P^W;_}drPWD9cukus8mFrCBL1`V`W%8qZlUZPHISM^bm<;q
z?6S)_6l#(RkX=cpJ*lLQ`3Sprk|fRhlLqGhdcpSg^x#KuE!Lt`Y*zI9x|E2gd9M&1
zn%1yuLs}BP3nB$BV1+Eq@EUa(3n|Ga?&&7UBptqlWN4UjZdD2J24>%4;`E{FznKOP
zUTmt1S-m)@sda+((p4wxDBKNo0d%E`S#W^*633r>rk*A2ZdDoa8cCs$aSR(Eb6BKs
zkXBMJ$-<yv5Jz2nxOTxzP}zQ)s4!M@RKoWmh96Lev5;h8L6_Q$hx%)9{{qQRm`3mZ
zZ;A5pKPC#QbCx`9$r=Y)!>Tr0AiM0iep0;7<srcO<4ZysG2ae;YCkd4N5++QJ<l^~
z*KZS6$@V97NIr+|Hk$C?n=h;KO2M#fD$9jiXse|urS2x*)8?wbwwRlf?>nC!QDmu!
zqG6GYGl<~|L}_;+zeC|ok`0m%Nm%gVCdfROh5LUWBq1(TcQFiYsymIP#%jZB)a19`
zqEU7kB#M$Ep1IT=<JD6_tfr7;6Q0c;Uj)m2eQm#y-XHQ)5#HcY3{r8#$CO;WP*){3
W?J62y+=3y7@u|CT+P&sp_x=LI*0}fp

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.py
new file mode 100755
index 00000000..d32965a1
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+#  02110-1301, USA.
+"""
+This mux will only use one device and not mix it with any other input.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['NoMux']
+
+from . import InputMux
+import logging
+logger = logging.getLogger(__name__)
+
+
+class NoMux(InputMux):
+    def __init__(self, *args):
+        super(NoMux, self).__init__(*args)
+        self.name = "Normal"
+        self._devs = {"Device": None}
+
+    def read(self):
+        if self._devs["Device"]:
+            data = self._devs["Device"].read()
+
+            return data
+        else:
+            return None
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/nomux.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..295adbfe806ffd6abffdc7e855a0a8fdd5e6bced
GIT binary patch
literal 1267
zcmchW&2G~`5XWcjd^DjINJvO<Kt6CV60HwNoGJtbi6S5e7gZvcmFwLk8?EigyM_jp
zQ#tasJOKP>69O+lt?YPac6L4Un@RlhV0>D>J1=Sb#CX5Ja#ILJgqIRD6{I4XLy1Vm
zq<Umys(Li-kx+e71G0UZ_DT0?IUqfxYlQCc8Qr594iD)&(KXSh>4>Ax(mnphDa82W
z!nmaBE|V|D+N7!NI_aE-q7$V*n^Gr5t&+O&No6h*;}Pc<@U4@^FLaxjde!-(vAc!K
zPK+<x;;T+xp7=iON%Nt*Gy+=6!t+<(ay+|#;hF3T=lv5b_W(kpaAZxKHJ2=c5>tMb
zM%XSnm=&h=2FpE#@c0S3LY)yUBf1jwgMcTvWK2i{7XryMgh)UmVBiZTe!#M6tHK8L
zbtpS!Z>Yj`E8XU>HU&s&YU8E!J$UM((m|O3ScsOO=0cjm@3fr-rD*3a<x7Ha=g_i6
zQ|att3oTo<%~F@%Wz29rvqm455Vd!QtF>P=b!N>)R?argtn<dYEa1=3zpQQ`k5=oP
z$v?(&TmgxDVkCCNnA$7edjbIv%P?Q}+eiNxOTnY^4d4WwF{XfuPfl_M7O3Ui%TQRW
z3zZMq&msBfk6#tOxS4`^^p3C`=S1Q_+>Lrs%IyggCna|Y4I9HFW!0$8!VH{pKKnPz
zp#9I@;A`P?j<XwxQ9tFRJmZs6HRUeLqC>}9n47J_TIh(PJp7Z`Yn%Dp)N{|o=i0xA
mi8(!h6~>siw{v@Q1qR^98^q5aA{Dm}F^EuVbVuxq+wpIm)glrA

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.py
new file mode 100755
index 00000000..d4cccf3e
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+#  02110-1301, USA.
+"""
+Mux for giving control to one device (slave/student) for all axis (roll/pitch/
+yaw/thrust) with the ability to take over all of them from a second device
+(master/teacher).
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['SelectiveMux']
+
+import logging
+
+from .takeoverselectivemux import TakeOverSelectiveMux
+
+logger = logging.getLogger(__name__)
+
+
+class TakeOverMux(TakeOverSelectiveMux):
+    def __init__(self, *args):
+        super(TakeOverMux, self).__init__(*args)
+        self.name = "Teacher (RPYT)"
+        self._muxing = {self._master: ("estop", "alt1", "alt2", "althold",
+                                       "exit"),
+                        self._slave: ("roll", "pitch", "yaw", "thrust")}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeovermux.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..02296e8241ecfc48e3669cb25e0c5d3c2052fd54
GIT binary patch
literal 1396
zcmcgs&2G~`5FXn}+N3Q%65_l^SE9A(ffGWd7m$F0k_xF2lI7YPd&AmmdB<sjdMig>
zg{R{IU}o)<K0%|*cy>Jh->ic_2b1^dhp#F0zkr^nM0QRr1E5Fd!Q6w)hnxf-vH-FH
z=m6#<3?UnV9zZ#S`4BuAz;Xm~2X4u97qT6YLs*dWF8l<z1-P7#*?Sg_Imd5elTYnh
zEKDPEwNh0sQd41Lv_K<FB}FDzDwQI%I$6ocVw=ed51m7zwMf>=ijeGeR4Yu2XtGJJ
zBP^QMlGU}sLSP|9a;3Dw4F|>KyA)<68^Qi&!S744XiO;*VI`%{`lOSvOf1SKLYbt6
zYz~hnc8@APqeM;eLyEU&xKHPq)G|f2lGKU%OTTmFkve3C&xl=6%FmSXz9Dj??F(|e
z1Ax8Gi0o5hk^rFT0Z)%cKb^UtDckr&BcgXi_LLaX--8<(vkyxjZanw};1PJFiUUxD
z+#buz0ldogoiv13{Bpz@X5j$^Ik%69Uvv$NaQfx);t+?Vk`_&k1CkPrFSo)8?sS^M
z=*%hQTA>pd6X)r0LEbBZ2D#)8o{+=ZyV!Ocdn=sSd$FC8V~1?!PMpBGy9BmRVDBYh
ze^-KSYuQYBVz^5zj#Z^Fj$NHqQcBlF+@b9%jNQ8RYC^TjL+9<1OTFm&Z*m(_IB?8<
zBPl9ODWj`?L87`bOIi&Z@xE*pTFIl7ScUee-e6&>NUN(TU3AK*MWt<|s=CFfY}XO5
zDsL*u$Mt5)_#6@02{G`Vdt)!~CSJ&$_kD3Q)E67XacQzv)6qHpFZ6+`<$1jzvTjkm
zkZbA&8fPZGD>7+m4h<a{tT?7S*~{AGInCW&pzR>+_NLK9rtW(2XC#;Sk&FppzHAOZ
hcJ2{>=*Arque;g55mb=nZ^XzSd1HUyf8sw5{sE<!VW<EA

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.py
new file mode 100755
index 00000000..cde7af02
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+#  02110-1301, USA.
+"""
+Mux for controlling roll/pitch from one device (slave/student) and the rest
+from a second device (master/teacher) with the possibility to take over
+roll/pitch as well.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['TakeOverSelectiveMux']
+
+import logging
+
+from . import InputMux
+
+logger = logging.getLogger(__name__)
+
+
+class TakeOverSelectiveMux(InputMux):
+    def __init__(self, *args):
+        super(TakeOverSelectiveMux, self).__init__(*args)
+        self._master = "Teacher"
+        self._slave = "Student"
+        self.name = "Teacher (RP)"
+        self._devs = {self._master: None, self._slave: None}
+
+        self._muxing = {self._master: ("thrust", "yaw", "estop", "alt1", "alt2",
+                                       "althold", "exit"),
+                        self._slave: ("roll", "pitch")}
+
+    def read(self):
+        try:
+            if self._devs[self._master] and self._devs[self._slave]:
+                dm = self._devs[self._master].read()
+                ds = self._devs[self._slave].read()
+                if not dm.muxswitch:
+                    for key in self._muxing[self._slave]:
+                        dm.set(key, ds.get(key))
+
+                return dm
+            else:
+                return None
+
+        except Exception as e:
+            logger.warning(e)
+            return None
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/input/mux/takeoverselectivemux.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..e24f03584f76f52794220b9cdbf47b64a85ffe4d
GIT binary patch
literal 2001
zcmc&!UvJ|?5TCW3q)D&W)A8p3fmnEQrR!Oo(1{0xpnw1Y1v#fXh=eTH-Xu;PJ9sy3
zg4(C@h{OXgd<8xOFMKCH0Q_d0rYAlC=VWJRXXnrE%x~8JdvEZ0_T`g|HlL6ALkv3t
z$%!!KibjfZk0xL|%6-ZMDt#J(=}_LKGN5^fMjcYdr&*UwkFKD)!#1WvV;J}7XQC^j
z??-*M9)>#{;}4L**UOb2*Nx8V$~ARa7S%-a94!i$P4&2`=en+p&dpho86Da(Ju{JY
z%iL7%K&Mr%-PGvDST_*mR9gdQ`PO2dT4$QbnKYZ4=0KkpZYnm5+S=lzC=0jNuGTI+
zHM%}C&ET2%snzGEEFTQ)E>3%dGd1Z?Mt^kVUc~w_Oum7|(3B=~#hJlvihuAO7Yf@U
z6UTS`qFO8+Hn)l9og)nU8i+xnktO0Raf+2NKE=b(!*<5X%&@1=Fl-It@K<z!ym~bA
z=t9viL<+VLC8!NVU;-`}GaV}6nV6sX^k~Bc^oTcmvbne2ye-^j6NMy{Y#%X?T@Une
zn@Qn^Z6WNf8@UeSZx39Tk2r0X);R{%Iz4wC%qUa6Z~?fqbnjd<55*TG)4I$>XjX+2
zhHD{~f`^df+Z*3@<J)e1+iTy~%tOmI8{Z;i-;!8aaR4!I1N5OR@$7P8ni#9Lf=N<T
zg-a4SY0_d!E|~}-<*ZdYH^LkPvEn2FZ0*Jb(5NO7S9Gi?$HJt|#D*M4hy@rZn$~j@
zoovt+Et+};eA$S>w;q>;Im|#RXAc)^<gALy;v~w(tuR`;qO?)Lkc;NaRm8w%Tq9zO
zQV1U`*70pbI>fMdKuEo;`tp0j^VL8J=s2r9KE|+rfvkTQWVA3+^lJ0lGi&cZYwQDR
zS45Wr%2{g#t&LPbI}6DEN~?FpPe&*;KKbEIx&&|)KPVSqfbkwPcv*mV^<zO#EBI41
zWN<#M-lD+u>2yHNpQJ7WQf@%AUAhR^Lb1+wOr#@^Lq==-0ux4c%=ed4G$zgAh6ow5
zcqg)H%xx|Rwx&h$#JKn_3t8{eRc02hz}v-`EbGa{G*a&Kw5hn<oW1xZR_6xJ=j~+0
z<EdGrgV7VurTX^}`8^E#3J57*X&`_xa8GG3@OHd?wWmVvCV9b<gwYNB4<JdBd7Upy
zECoa{`?|Tr|BYh=r~4Sv&Rx~t4f%|1*ClzKZ4aF;(Jak%4<~6^Zg?&ns)Z*N)sjb>
qi;GHsB^8a|X8G$(FrME{r4aA8ssBLZB=YQbJ>1#5<=yf2{eJ-?TiO@^

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/joystick/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/joystick/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..f98770b26655e6ba6ee2e856607abb6defe8a526
GIT binary patch
literal 245
zcmYL@K?=e^3`Iv>C<U(|UDk!>0wSKkjUwV^Xs0PntsR-9i+Tai;5og3859iU<^N9z
znY`!8bNM<t^Q)20oi-<m(wK{KWDeOlW}rw;c_80&+6%QLgsK?Jv=~yUG3t;~Cm5>S
z;X2u8@uEx>)|TE`o2fK^@L4U?{)SMHxR5^(?!h<cgdy6YY6(|P(Fw2agVer*mI`py
iH-z{JL*oY)a#ygHIudKwx;xY3o0~8D7Pkx4tZhDNA3`z!

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/joystick/linuxjsdev.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/joystick/linuxjsdev.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..45bc0a261684b6e4666f9b7509e4b3d94ebc7c92
GIT binary patch
literal 9305
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
DWOf+p

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.py
new file mode 100755
index 00000000..1e948b48
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+The input module that will read joysticks/input devices and send control set-
+points to the Crazyflie. It will also accept settings from the UI.
+
+This module can use different drivers for reading the input device data.
+Currently it can just use the PySdl2 driver but in the future there will be a
+Linux and Windows driver that can bypass PySdl2.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LogVariable', 'LogConfigReader', 'LogConfigRemoveThis']
+
+import glob
+import json
+import logging
+import os
+import shutil
+import sys
+
+logger = logging.getLogger(__name__)
+
+#from PyQt4 import QtCore, QtGui, uic
+#from PyQt4.QtCore import pyqtSlot, pyqtSignal
+
+from cflib.crazyflie.log import LogVariable, LogConfig
+
+
+class LogConfigReader():
+    """Reads logging configurations from file"""
+
+    def __init__(self, crazyflie):
+        self.dsList = []
+        # Check if user config exists, otherwise copy files
+        if (not os.path.exists(sys.path[1] + "/log")):
+            logger.info("No user config found, copying dist files")
+            os.makedirs(sys.path[1] + "/log")
+            for f in glob.glob(sys.path[0] +
+                               "/cfclient/configs/log/[A-Za-z]*.json"):
+                shutil.copy2(f, sys.path[1] + "/log")
+        self._cf = crazyflie
+        self._cf.connected.add_callback(self._connected)
+
+    def _read_config_files(self):
+        """Read and parse log configurations"""
+        configsfound = [os.path.basename(f) for f in
+                        glob.glob(sys.path[1] + "/log/[A-Za-z_-]*.json")]
+        new_dsList = []
+        for conf in configsfound:
+            try:
+                logger.info("Parsing [%s]", conf)
+                json_data = open(sys.path[1] + "/log/%s" % conf)
+                self.data = json.load(json_data)
+                infoNode = self.data["logconfig"]["logblock"]
+
+                logConf = LogConfig(infoNode["name"],
+                                    int(infoNode["period"]))
+                for v in self.data["logconfig"]["logblock"]["variables"]:
+                    if v["type"] == "TOC":
+                        logConf.add_variable(str(v["name"]), v["fetch_as"])
+                    else:
+                        logConf.add_variable("Mem", v["fetch_as"],
+                                             v["stored_as"],
+                                             int(v["address"], 16))
+                new_dsList.append(logConf)
+                json_data.close()
+            except Exception as e:
+                logger.warning("Exception while parsing logconfig file: %s", e)
+        self.dsList = new_dsList
+
+    def _connected(self, link_uri):
+        """Callback that is called once Crazyflie is connected"""
+
+        self._read_config_files()
+        # Just add all the configurations. Via callbacks other parts of the
+        # application will pick up these configurations and use them
+        for d in self.dsList:
+            try:
+                self._cf.log.add_config(d)
+            except KeyError as e:
+                logger.warning(str(e))
+            except AttributeError as e:
+                logger.warning(str(e))
+
+    def getLogConfigs(self):
+        """Return the log configurations"""
+        return self.dsList
+
+    def saveLogConfigFile(self, logconfig):
+        """Save a log configuration to file"""
+        filename = sys.path[1] + "/log/" + logconfig.name + ".json"
+        logger.info("Saving config for [%s]", filename)
+
+        # Build tree for JSON
+        saveConfig = {}
+        logconf = {'logblock': {'variables': []}}
+        logconf['logblock']['name'] = logconfig.name
+        logconf['logblock']['period'] = logconfig.period_in_ms
+        # Temporary until plot is fixed
+
+        for v in logconfig.variables:
+            newC = {}
+            newC['name'] = v.name
+            newC['stored_as'] = v.stored_as_string
+            newC['fetch_as'] = v.fetch_as_string
+            newC['type'] = "TOC"
+            logconf['logblock']['variables'].append(newC)
+
+        saveConfig['logconfig'] = logconf
+
+        json_data = open(filename, 'w')
+        json_data.write(json.dumps(saveConfig, indent=2))
+        json_data.close()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logconfigreader.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..5bc212757895ae145bd54f853d5f53b9df89c06d
GIT binary patch
literal 4251
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
OEt|{V+S<ac`hNjTB%X!<

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.py
new file mode 100755
index 00000000..d9c551c6
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Used to write log data to files.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['LogWriter']
+
+import os
+import sys
+import datetime
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+from cflib.crazyflie.log import LogConfig
+
+import traceback
+
+
+class LogWriter():
+    """Create a writer for a specific log block"""
+
+    def __init__(self, logblock, connected_ts=None, directory=None):
+        """Initialize the writer"""
+        self._block = logblock
+        self._dir = directory
+        self._connected_ts = connected_ts
+
+        self._dir = os.path.join(sys.path[1], "logdata",
+                                 connected_ts.strftime("%Y%m%dT%H-%M-%S"))
+        self._file = None
+        self._header_written = False
+        self._header_values = []
+        self._filename = None
+
+    def _write_header(self):
+        """Write the header to the file"""
+        if not self._header_written:
+            s = "Timestamp"
+            for v in self._block.variables:
+                s += "," + v.name
+                self._header_values.append(v.name)
+            s += '\n'
+            self._file.write(s)
+            self._header_written = True
+
+    def _new_data(self, timestamp, data, logconf):
+        """Callback when new data arrives from the Crazyflie"""
+        if self._file:
+            s = "%d" % timestamp
+            for col in self._header_values:
+                s += "," + str(data[col])
+            s += '\n'
+            self._file.write(s)
+
+    def writing(self):
+        """Return True if the file is open and we are using it,
+        otherwise false"""
+        return True if self._file else False
+
+    def stop(self):
+        """Stop the logging to file"""
+        if self._file:
+            self._file.close()
+            self._file = None
+            self._block.data_received_cb.remove_callback(self._new_data)
+            logger.info("Stopped logging of block [%s] to file [%s]",
+                        self._block.name, self._filename)
+            self._header_values = []
+
+    def start(self):
+        """Start the logging to file"""
+
+        # Due to concurrency let's not check first, just create
+        try:
+            os.makedirs(self._dir)
+        except OSError:
+            logger.debug("logdata directory already exists")
+
+        if not self._file:
+            time_now = datetime.datetime.now()
+            name = "{0}-{1}.csv".format(self._block.name,
+                                        time_now.strftime(
+                                            "%Y%m%dT%H-%M-%S"))
+            self._filename = os.path.join(self._dir, name)
+            self._file = open(self._filename, 'w')
+            self._write_header()
+            self._block.data_received_cb.add_callback(self._new_data)
+            logger.info("Started logging of block [%s] to file [%s]",
+                        self._block.name, self._filename)
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/logdatawriter.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..d41df77b73f2fa48831abd2082f3b08482223575
GIT binary patch
literal 3823
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
BTeJWG

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.py
new file mode 100755
index 00000000..cfdff4ee
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013-2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+Implementation of a periodic timer that will call a callback every time
+the timer expires once started.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['PeriodicTimer']
+
+import logging
+from threading import Thread
+from cflib.utils.callbacks import Caller
+import time
+
+logger = logging.getLogger(__name__)
+
+
+class PeriodicTimer:
+    """Create a periodic timer that will periodically call a callback"""
+
+    def __init__(self, period, callback):
+        self._callbacks = Caller()
+        self._callbacks.add_callback(callback)
+        self._started = False
+        self._period = period
+        self._thread = None
+
+    def start(self):
+        """Start the timer"""
+        if self._thread:
+            logger.warning("Timer already started, not restarting")
+            return
+        self._thread = _PeriodicTimerThread(self._period, self._callbacks)
+        self._thread.setDaemon(True)
+        self._thread.start()
+
+    def stop(self):
+        """Stop the timer"""
+        if self._thread:
+            self._thread.stop()
+            self._thread = None
+
+
+class _PeriodicTimerThread(Thread):
+    def __init__(self, period, caller):
+        super(_PeriodicTimerThread, self).__init__()
+        self._period = period
+        self._callbacks = caller
+        self._stop = False
+
+    def stop(self):
+        self._stop = True
+
+    def run(self):
+        while not self._stop:
+            time.sleep(self._period)
+            if self._stop:
+                break
+            self._callbacks.call()
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/periodictimer.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..04a5eb1373f92282b172bf825bba163593671ac3
GIT binary patch
literal 2914
zcmcgu-*4kY5FXq4(d1gLy7Kdozyg7C(p@9*079SxT6BPbs&Xn2d9hr3+dAji!MowE
zsBgU&{$Bn7_`b1|_7L#aI@!tYdUj{`o0)GC{JAy!HvRsmRN5!N@0V!y4~R@;3hBwz
zlgyV>bbQGI$vRR5GDQd9t`r@acV*fYkK>*cJ(>4q+Hc0t-H_>qm_TL&F+;h;Hg~vR
zHk7O{FGMayewl7c2Bof?z}8?ryG3J@(H$NshZuf0Ule9;N|(62D#Pj|Ou~h!^D4{J
z(B*SehwdzK;j_Fb!Zd+M^yfH9pN8f)Q=hBW(4Co9-8^69wXtDUrY5v5sh!Dohn6Py
za+lW0?<V|e&uw9R*cv*bo%}C62O|&jt_N{+R+}Wd8t=onsZ+eshkAQx_5p+evbZNC
zVDNZbqUmd=Ls&V}m1r{RaLHqceeCYc|J`;41MHsv^Xh4UE!cc+g=Rm4aEkqfNbCvZ
z%#)cf7d~C&xqwq~M=tbT+~sjO(`RR*_dFPFV3&w{x*<eNz=^bVn;0fpww`faOgu>n
zYaG|aEzqvdL347)yB<`fX(nBRCl9S}tSL?u=vJHCtn7`@7Zvshz42K!H{;{hd*el2
z%}na-n9e*uDRT2Dg(#hUv^aNXRXHy5<8gY@Opcc>FYI`QrpDAR^z_(qUgj>2$1q}f
zWAQe<ZEt{2N(Zy?z>4q$9il2y-xsIY!q>zWo}M)L&VVmex^fBNfy8RXtS3N$y3G!=
z4Q7ch9MOQi3o&7^BHY_ZwjW?lLo-YY#^U)ZoF9f|<w68G7v$yX!Ds;0h(Zx9s?$?b
zE9}pbx`c*%74PE1>+#;icSMIpJc8qLxby48%&SsebW|^mI$8~Bh(tu`7Tgo<z|O}!
z5hG9hfw$##yf@*TwPV(y{~;7w$Gq2e5c-O56YC<S^4?^`HTD&ViK`Y@P#>rh8UQsz
z@it<B-0q#}q%U|v@=tT;5xZ@ymdV`2ag&+xyvmjZhnjJ5R;6(qk<=8qh+AE`NqaYz
zN)c?yLo`dsdS1^T^aq1z8-o;0pEX^+kKvjwU;GDSf+!i1YnJ0+AI*LQ;qX<zBFDgQ
zm&l`*(?D@e+I0kB%lzwICpRM1g!2+?pyz84TjxMTR#O!aTD54Rf)O9Fd}wZwqD*}S
z?aiS|XMTgGsEW7qGA*s6pK5@Fx78A~qH-!k_bA?ixcxx>5mqe^A^(!HtboryX_M7{
z+R`bLIJm}=^#eIX=5o5Dq>8<M4dmx*1&isS7gWL`xi=mzj9FacHYPPacdIa<B%oF-
z%R~IUR9!mWh=CTpM)5j@CN6U}dI#bvx^6genW9h7lyG|9pf?B_+oZ_o4TzR)k|pYD
zy=EIA+4fm#SYNdEXobfpVzxWpE=6@d-qi}cyDIKh|6oLKQ-{k=jr&0hR)dF{#<XaH
i-WC)}v7a}j`uJT~w@jU$4^VvmCD`^h{ky@={l5VW|CKTT

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/pysdl2reader.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/pysdl2reader.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..371ba770c345e6c3e06779065a005f1d301ca081
GIT binary patch
literal 5784
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.py
new file mode 100755
index 00000000..3a0dab0e
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Singleton class.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Singleton']
+
+class Singleton(type):
+    """Class for making singletons"""
+    _instances = {}
+    
+    def __call__(cls, *args, **kwargs):
+        """Called when creating new class"""
+        if cls not in cls._instances:
+            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
+        return cls._instances[cls]
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/singleton.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..ec89e13b651a261d597761710075f458596fbfb2
GIT binary patch
literal 899
zcmcIiO>fgc5FM{WN`nv*H&j7=0LhnjFB~gA+B+bJ3%RVe>rLWhy|%m)MM1fh6NmmV
z{!afuXV$6dA84F;o}G;6y?IW0_i|GI`}@<t;giz+3q_CwCBTd@fms5jfCVLj6Pv=E
zDk=N{xB>Vz8$mgOY5t5Kd`&Q!R(|0uHa;^>hj2a#FQ|N3q3QH*n|-~+V>%CA1AW3>
z6yYs_C3>PE6i^YSEQKhEu9)V6+fE3sm?4`tT~_Ng@n+$HJQ$wGD{*|M2%ib|@w)<8
zA3^sKFaZ=?yb-WUVV}TW!0kg1)i3;`!<%W&L&H0QEA5;uv+dH-o}Jam?A~tsrSlQB
z;DkU`KA`r-28<SMTH6&=2Yk-IDnoOW%2^~b7#ENq*4-lDn3DCD)0{Gh5Pnc5mrZTu
z^}$owcFoEf43aJ0&0S^B41vdR*6wiGc<HKZY36-VZm@De4pyDFyMn2Cd|*ltuaXZ*
ze5D7KUe{Ksej!yi<;L;xh+35@8>3Xg#tc(>rB5iAMZTwqa>of>;#<^P@PF%|Q*UVX
hZqONxj^5&~wecPNclGJCMh7o?&pw?Fn#DLd7WW5?)2ILd

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.py
new file mode 100755
index 00000000..8232b6c3
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2015 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+#  USA.
+
+"""
+Give access to the LED driver memory via ZMQ.
+"""
+
+import cflib
+import cflib.crazyflie
+from cflib.crazyflie.mem import MemoryElement
+
+import logging
+from threading import Thread, Lock
+ZMQ_PULL_PORT = 1024 + 190
+logger = logging.getLogger(__name__)
+
+enabled = False
+try:
+    import zmq
+    enabled = True
+except Exception as e:
+    logger.warning("Not enabling ZMQ LED driver access,"
+                   "import failed ({})".format(e))
+
+class _PullReader(Thread):
+    """Blocking thread for reading from ZMQ socket"""
+    def __init__(self, receiver, callback, *args):
+        """Initialize"""
+        super(_PullReader, self).__init__(*args)
+        self._receiver = receiver
+        self._cb = callback
+        self.daemon = True
+        self.lock = Lock()
+
+    def run(self):
+        while True:
+            #self.lock.acquire()
+            self._cb(self._receiver.recv_json())
+
+
+class ZMQLEDDriver:
+    """Used for reading data from input devices using the PyGame API."""
+    def __init__(self, crazyflie):
+
+        if enabled:
+            self._cf = crazyflie
+            context = zmq.Context()
+            self._receiver = context.socket(zmq.PULL)
+            self._bind_addr = "tcp://*:{}".format(ZMQ_PULL_PORT)
+            # If the port is already bound an exception will be thrown
+            # and caught in the initialization of the readers and handled.
+            self._receiver.bind(self._bind_addr)
+            logger.info("Biding ZMQ for LED driver"
+                        "at {}".format(self._bind_addr))
+            self._receiver_thread = _PullReader(self._receiver,
+                                                self._cmd_callback)
+
+    def start(self):
+        if enabled:
+            self._receiver_thread.start()
+
+    def _cmd_callback(self, data):
+        """Called when new data arrives via ZMQ"""
+        if len(self._cf.mem.get_mems(MemoryElement.TYPE_DRIVER_LED)) > 0:
+            logger.info("Updating memory")
+            memory = self._cf.mem.get_mems(MemoryElement.TYPE_DRIVER_LED)[0]
+            for i_led in range(len(data["rgbleds"])):
+                memory.leds[i_led].set(data["rgbleds"][i_led][0],
+                                       data["rgbleds"][i_led][1],
+                                       data["rgbleds"][i_led][2])
+            memory.write_data(self._write_cb)
+
+    def _write_cb(self, mem, addr):
+        return
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_led_driver.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..9d11f15fe5930ef3137ec1223755222a21c76613
GIT binary patch
literal 3608
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.py b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.py
new file mode 100755
index 00000000..e2ea3fc0
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2015 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+#  USA.
+
+"""
+Give access to the parameter framework via ZMQ.
+"""
+
+import logging
+from threading import Thread, Lock
+ZMQ_PULL_PORT = 1024 + 189
+logger = logging.getLogger(__name__)
+
+enabled = False
+try:
+    import zmq
+    enabled = True
+except Exception as e:
+    logger.warning("Not enabling ZMQ param access, import failed ({})".format(e))
+
+
+class _PullReader(Thread):
+
+    def __init__(self, receiver, callback, *args):
+        super(_PullReader, self).__init__(*args)
+        self._receiver = receiver
+        self._cb = callback
+        self.daemon = True
+        self.lock = Lock()
+
+    def run(self):
+        while True:
+            self.lock.acquire()
+            self._cb(self._receiver.recv_json())
+
+
+class ZMQParamAccess:
+    """Used for reading data from input devices using the PyGame API."""
+    def __init__(self, crazyflie):
+
+        if enabled:
+            self._cf = crazyflie
+            context = zmq.Context()
+            self._receiver = context.socket(zmq.REP)
+            self._bind_addr = "tcp://*:{}".format(ZMQ_PULL_PORT)
+            # If the port is already bound an exception will be thrown
+            # and caught in the initialization of the readers and handled.
+            self._receiver.bind(self._bind_addr)
+            logger.info("Biding ZMQ for parameters at {}".format(self._bind_addr))
+            self._receiver_thread = _PullReader(self._receiver, self._cmd_callback)
+
+    def start(self):
+        if enabled:
+            self._receiver_thread.start()
+
+    def _cmd_callback(self, data):
+        #logger.info(data)
+        if data["cmd"] == "toc":
+            response = {"version":1, "toc": []}
+            self._receiver.send_json(response)
+            self._receiver_thread.lock.release()
+        if data["cmd"] == "set":
+            resp = {"version": 1}
+            group = data["name"].split(".")[0]
+            name = data["name"].split(".")[1]
+            self._cf.param.add_update_callback(group=group, name=name,
+                                               cb=self._param_callback)
+            self._cf.param.set_value(data["name"], str(data["value"]))
+
+    def _param_callback(self, name, value):
+        group = name.split(".")[0]
+        name_short = name.split(".")[1]
+        logger.info("Removing {}.{}".format(group, name_short))
+        self._cf.param.remove_update_callback(group=group, name=name_short,
+                                              cb=self._param_callback)
+
+        response = {"version":1, "cmd": "set", "name": name, "value": value}
+        self._receiver.send_json(response)
+        self._receiver_thread.lock.release()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.pyc b/pps_ws/src/d_fall_pps/crazyradio/cfclient/utils/zmq_param.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..168cdd01bcb4d437b887c1e9a61e7e9a3cf25da2
GIT binary patch
literal 3797
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
P0F|_7cFgO+?#}-J<hdv%

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfconfig/Makefile b/pps_ws/src/d_fall_pps/crazyradio/cfconfig/Makefile
new file mode 100755
index 00000000..78cdd24f
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfconfig/Makefile
@@ -0,0 +1,51 @@
+
+######### JTAG and environment configuration ##########
+OPENOCD_INTERFACE ?= interface/jtagkey.cfg
+OPENOCD_TARGET    ?= target/stm32.cfg
+#######################################################
+
+CONFIGFILE        := cblock.cfg
+BINFILE           := cblock.bin
+
+help:
+	@echo "Crazyflie config block makefile utility"
+	@echo
+	@echo "This script permits to read and write the config block of Crazyflie using"
+	@echo "openocd and any JTAG probe supported by it. The copter has to be connected"
+	@echo "to the jtag probe"
+	@echo
+	@echo "Available action:"
+	@echo "   help    : Display this message"
+	@echo "   read    : Read the configuration block from the copter using openOCD," 
+	@echo "             decode it and save it in a configuration file"
+	@echo "   write   : Compile the configuration file to a block and write the block"
+	@echo "             in the copter using openOCD"
+	@echo
+	@echo "Parameters that can be configured: [current value] (can be altered from command line)"
+	@echo "   CONFIGFILE    [$(CONFIGFILE)]"
+	@echo "   BINFILE       [$(BINFILE)]"
+	@echo "   OCD_INTERFACE [$(OPENOCD_INTERFACE)]"
+	@echo "   OCD_TARGET    [$(OPENOCD_TARGET)]"
+
+read: readbin bin2cfg
+
+write: cfg2bin writebin
+
+readbin:
+	openocd -d0 -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init -c targets \
+	    -c "reset halt" -c "dump_image $(BINFILE) 0x1FC00 1024" \
+	    -c "reset run" -c shutdown
+
+writebin:
+	openocd -d0 -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init -c targets  \
+	  -c "reset halt" -c "flash erase_sector 0 127 127" \
+	  -c "flash write_bank 0 $(BINFILE) 0x1FC00" -c "verify_image $(BINFILE) 0x1FC00" \
+	  -c "reset run" -c shutdown
+	
+bin2cfg:
+	./configblock.py extract $(BINFILE) $(CONFIGFILE)
+
+cfg2bin:
+	./configblock.py generate $(CONFIGFILE) $(BINFILE)
+
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfconfig/configblock.py b/pps_ws/src/d_fall_pps/crazyradio/cfconfig/configblock.py
new file mode 100755
index 00000000..c7a150fc
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfconfig/configblock.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Configuration block compiler/decompiler
+# Fully static version (to be considered as a prototype...)!
+from ConfigParser import ConfigParser
+import struct
+import sys
+
+# Radio speed enum type
+speeds = ["250K", "1M", "2M"]
+radioSpeedPos = 2
+
+defaultConfig = """#Crazyflie config block
+#default version generated from squatch
+[radio]
+channel= 100
+speed= 2M
+
+[calib]
+pitchTrim= 0.0
+rollTrim= 0.0
+"""
+
+config = """#Crazyflie config block
+#Block version %d extracted from copter
+[radio]
+channel= %d
+speed= %s
+
+[calib]
+pitchTrim= %f
+rollTrim= %f
+"""
+
+configVersion = 0
+structFormat = "<BBBff"
+
+
+def checksum256(st):
+    return reduce(lambda x, y: x + y, map(ord, st)) % 256
+
+
+def compileBlock(configFile, binFile):
+    config = ConfigParser()
+
+    config.read(configFile)
+
+    block = (configVersion,)
+
+    block += (config.getint("radio", "channel"),)
+    block += (speeds.index(config.get("radio", "speed").upper()),)
+
+    block += (config.getfloat("calib", "pitchTrim"),)
+    block += (config.getfloat("calib", "rollTrim"),)
+
+    bin = struct.pack(structFormat, *block)
+
+    # Adding some magic:
+    bin = "0xBC" + bin
+
+    bin += struct.pack("B", 256 - checksum256(bin))
+
+    # print("Config block checksum: %02x" % bin[len(bin)-1])
+
+    bfile = open(binFile, "w")
+    bfile.write(bin)
+    bfile.close()
+
+    print "Config block compiled successfully to", binFile
+
+
+def decompileBlock(binFile, configFile):
+    bfile = open(binFile)
+    bin = bfile.read()
+    bfile.close()
+
+    if (bin[0:4] != "0xBC" or
+        len(bin) < (struct.calcsize(structFormat) + 5) or
+        checksum256(bin[0:struct.calcsize(structFormat) + 5]) != 0):
+        print "Config block erased of altered, generating default file"
+        cfile = open(configFile, "w")
+        cfile.write(defaultConfig)
+        cfile.close()
+    else:
+        block = struct.unpack(structFormat,
+                              bin[4:struct.calcsize(structFormat) + 4])
+        if block[0] != configVersion:
+            print("Error! wrong configuration block version, "
+                  "this program must certainly be updated!")
+            return
+
+        block = (block[0:radioSpeedPos] + (speeds[block[radioSpeedPos]],) +
+                 block[radioSpeedPos + 1:len(block)])
+
+        cfile = open(configFile, "w")
+        cfile.write(config % block)
+        cfile.close()
+        print "Config block successfully extracted to", configFile
+
+if __name__ == "__main__":
+    if (len(sys.argv) < 4 or
+        (sys.argv[1] != "generate" and sys.argv[1] != "extract")):
+        print "Configuration block compiler/decompiler."
+        print "  Usage: %s <generate|extract> <infile> <outfile>" % sys.argv[0]
+
+    if sys.argv[1] == "generate":
+        compileBlock(sys.argv[2], sys.argv[3])
+    else:
+        decompileBlock(sys.argv[2], sys.argv[3])
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfheadless.py b/pps_ws/src/d_fall_pps/crazyradio/cfheadless.py
new file mode 100755
index 00000000..cdfe003d
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfheadless.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Headless client for the Crazyflie.
+"""
+
+import sys
+import os
+import logging
+import signal
+
+import cflib.crtp
+from cflib.crazyflie import Crazyflie
+import cfclient.utils
+from cfclient.utils.input import JoystickReader
+from cfclient.utils.config import Config
+
+if os.name == 'posix':
+    print 'Disabling standard output for libraries!'
+    stdout = os.dup(1)
+    os.dup2(os.open('/dev/null', os.O_WRONLY), 1)
+    sys.stdout = os.fdopen(stdout, 'w')
+
+# set SDL to use the dummy NULL video driver, 
+#   so it doesn't need a windowing system.
+os.environ["SDL_VIDEODRIVER"] = "dummy"
+
+class HeadlessClient():
+    """Crazyflie headless client"""
+
+    def __init__(self):
+        """Initialize the headless client and libraries"""
+        cflib.crtp.init_drivers()
+
+        self._jr = JoystickReader(do_device_discovery=False)
+
+        self._cf = Crazyflie(ro_cache=sys.path[0]+"/cflib/cache",
+                             rw_cache=sys.path[1]+"/cache")
+
+        signal.signal(signal.SIGINT, signal.SIG_DFL) 
+
+        self._devs = []
+
+        for d in self._jr.available_devices():
+            self._devs.append(d.name)
+
+    def setup_controller(self, input_config, input_device=0, xmode=False):
+        """Set up the device reader""" 
+        # Set up the joystick reader
+        self._jr.device_error.add_callback(self._input_dev_error)
+        print "Client side X-mode: %s" % xmode
+        if (xmode):
+            self._cf.commander.set_client_xmode(xmode)
+
+        devs = self._jr.available_devices()
+        print "Will use [%s] for input" % self._devs[input_device]
+        self._jr.start_input(self._devs[input_device])
+        self._jr.set_input_map(self._devs[input_device], input_config)
+
+    def controller_connected(self):
+        """ Return True if a controller is connected"""
+        return True if (len(self._jr.available_devices()) > 0) else False
+
+    def list_controllers(self):
+        """List the available controllers and input mapping"""
+        print "\nAvailable controllers:"
+        for i, dev in enumerate(self._devs):
+            print " - Controller #{}: {}".format(i, dev)
+        print "\nAvailable input mapping:"
+        for map in os.listdir(sys.path[1] + '/input'):
+            print " - " + map.split(".json")[0]
+
+    def connect_crazyflie(self, link_uri):
+        """Connect to a Crazyflie on the given link uri"""
+        self._cf.connection_failed.add_callback(self._connection_failed)
+        # 2014-11-25 chad: Add a callback for when we have a good connection.
+        self._cf.connected.add_callback(self._connected)
+        self._cf.param.add_update_callback(group="imu_sensors", name="HMC5883L",
+                cb=(lambda name, found:
+                    self._jr.set_alt_hold_available(eval(found))))
+        self._jr.althold_updated.add_callback(
+                lambda enabled: self._cf.param.set_value("flightmode.althold", enabled))
+
+        self._cf.open_link(link_uri)
+        self._jr.input_updated.add_callback(self._cf.commander.send_setpoint)
+
+    def _connected(self, link):
+        """Callback for a successful Crazyflie connection."""
+        print "Connected to {}".format(link)
+
+    def _connection_failed(self, link, message):
+        """Callback for a failed Crazyflie connection"""
+        print "Connection failed on {}: {}".format(link, message)
+        sys.exit(-1)
+
+    def _input_dev_error(self, message):
+        """Callback for an input device error"""
+        print "Error when reading device: {}".format(message)
+        sys.exit(-1)
+
+def main():
+    """Main Crazyflie headless application"""
+    import argparse
+
+    parser = argparse.ArgumentParser(prog="cfheadless")
+    parser.add_argument("-u", "--uri", action="store", dest="uri", type=str,
+                        default="radio://0/10/250K",
+                        help="URI to use for connection to the Crazyradio"
+                             " dongle, defaults to radio://0/10/250K")
+    parser.add_argument("-i", "--input", action="store", dest="input",
+                        type=str, default="PS3_Mode_1",
+                        help="Input mapping to use for the controller,"
+                             "defaults to PS3_Mode_1")
+    parser.add_argument("-d", "--debug", action="store_true", dest="debug",
+                        help="Enable debug output")
+    parser.add_argument("-c", "--controller", action="store", type=int,
+                        dest="controller", default=0,
+                        help="Use controller with specified id,"
+                             " id defaults to 0")
+    parser.add_argument("--controllers", action="store_true",
+                        dest="list_controllers",
+                        help="Only display available controllers and exit")
+    parser.add_argument("-x", "--x-mode", action="store_true", 
+                        dest="xmode", 
+                        help="Enable client-side X-mode") 
+    (args, unused) = parser.parse_known_args()
+
+    if args.debug:
+        logging.basicConfig(level=logging.DEBUG)
+    else:
+        logging.basicConfig(level=logging.INFO)
+
+    headless = HeadlessClient()
+
+    if (args.list_controllers):
+        headless.list_controllers()
+    else:
+        if headless.controller_connected():
+            headless.setup_controller(input_config=args.input,
+                                      input_device=args.controller,
+                                      xmode=args.xmode)
+            headless.connect_crazyflie(link_uri=args.uri)
+        else:
+            print "No input-device connected, exiting!"
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.py
new file mode 100755
index 00000000..536acc11
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+The Crazyflie Micro Quadcopter library API used to communicate with the
+Crazyflie Micro Quadcopter via a communication link.
+
+The API takes care of scanning, opening and closing the communication link
+as well as sending/receiving data from the Crazyflie.
+
+A link is described using an URI of the following format:
+    <interface>://<interface defined data>.
+See each link for the data that can be included in the URI for that interface.
+
+The two main uses-cases are scanning for Crazyflies available on a
+communication link and opening a communication link to a Crazyflie.
+
+Example of scanning for available Crazyflies on all communication links:
+cflib.crtp.init_drivers()
+available = cflib.crtp.scan_interfaces()
+for i in available:
+    print "Found Crazyflie on URI [%s] with comment [%s]"
+            % (available[0], available[1])
+
+Example of connecting to a Crazyflie with know URI (radio dongle 0 and
+radio channel 125):
+cf = Crazyflie()
+cf.open_link("radio://0/125")
+...
+cf.close_link()
+"""
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c691ab2630d8b6d69d40d6d126cd82676449f86c
GIT binary patch
literal 1182
zcmaJ>QEJ>U5Otc57D4YYECjre*qcJ3EXmT)HV{Zk+q6)+1f!MYSj3i$B=2^6upXk<
z=n;B>&d7<K&`^P8S)O@s^xoLJf1b0S%U@qbayxeL{u(dOaFR*#4d<QYGJQgOzju>_
zeXbO|as0=&thIs*Eu4jq9hbtkK{=@P%5k@alMiRmc_krO5VmPLqXiENHab)gD#iYT
zvDO^;y)11F;+mU`MRtM?{6={Y+$pdnc)^X)=6WA&t7rk-ND#I4v_`f^z!>+iQFRS?
z_sU3&%$*WSuPI0Jz@c=uiP=M~Siwmw1+)jLyl{GjXY3*mH}K`t8P!9KrLAkbA*|B6
zhKD%=97o!qN+lO+G0*ey3BgKj5Q>Nw8M{;p6c<%bh6oWeRuw89@Z<(o3bYY*CoxT%
z7(o1eFvbj2x4MOmg@$7!`r;1+#~Db#JN07ba8!J^=DOyq8YMQs+34b(A5rB&@kcZ!
z^ZD2<SjW5XylDw>G%eDOT=xnfS~TciVn1gB+i#T#7url49SZ66S~;Ii*$8zE_oC>O
z#UKd<l2DqI8!~#2T8CjUIkg=&(NGSFK=Lm4{8it@RF}d?+L=Um$zd9MkPev3+138Y
zuspn)K2SkeW0VN7PalZYbKDraiL|NXQd^MLT%&w5lAiTjqQd4^H5|TpIVD}-cq|%E
zDawp2D(C}BCou&(Z<b^DWXiHEqX_y`R39>B?^F5<gr_(ag|wn5Qd}nE<S+0suWX}o
zarY<4-yWRu^Yeqttq-cn+twEwpL-{AS(NBvfjc>UFKzI7v?VXl4s@=_+HH7-h)0c;
NU9A?caP9F*{sJ-ugXjPN

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.py
new file mode 100755
index 00000000..607735bb
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.py
@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Bootloading utilities for the Crazyflie.
+"""
+
+from cflib.utils.callbacks import Caller
+from .cloader import Cloader
+from .boottypes import BootVersion, TargetTypes, Target
+import zipfile
+import json
+import sys
+import time
+import logging
+logger = logging.getLogger(__name__)
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Bootloader']
+
+class Bootloader:
+
+    """Bootloader utility for the Crazyflie"""
+    def __init__(self, clink=None):
+        """Init the communication class by starting to communicate with the
+        link given. clink is the link address used after resetting to the
+        bootloader.
+
+        The device is actually considered in firmware mode.
+        """
+        self.clink = clink
+        self.in_loader = False
+
+        self.page_size = 0
+        self.buffer_pages = 0
+        self.flash_pages = 0
+        self.start_page = 0
+        self.cpuid = "N/A"
+        self.error_code = 0
+        self.protocol_version = 0
+
+        # Outgoing callbacks for progress
+        # int
+        self.progress_cb = None
+        # msg
+        self.error_cb = None
+        # bool
+        self.in_bootloader_cb = None
+        # Target
+        self.dev_info_cb = None
+
+        #self.dev_info_cb.add_callback(self._dev_info)
+        #self.in_bootloader_cb.add_callback(self._bootloader_info)
+
+        self._boot_plat = None
+
+        self._cload = Cloader(clink,
+                             info_cb=self.dev_info_cb,
+                             in_boot_cb=self.in_bootloader_cb)
+
+    def start_bootloader(self, warm_boot=False):
+        if warm_boot:
+            self._cload.open_bootloader_uri(self.clink)
+            started = self._cload.reset_to_bootloader(TargetTypes.NRF51)
+            if started:
+                started = self._cload.check_link_and_get_info()
+        else:
+            uri = self._cload.scan_for_bootloader()
+            
+            # Workaround for libusb on Windows (open/close too fast)
+            time.sleep(1)
+            
+            if uri:
+                self._cload.open_bootloader_uri(uri)
+                started = self._cload.check_link_and_get_info()
+            else:
+                started = False
+
+        if started:
+            self.protocol_version = self._cload.protocol_version
+
+            if self.protocol_version == BootVersion.CF1_PROTO_VER_0 or\
+                            self.protocol_version == BootVersion.CF1_PROTO_VER_1:
+                # Nothing more to do
+                pass
+            elif self.protocol_version == BootVersion.CF2_PROTO_VER:
+                self._cload.request_info_update(TargetTypes.NRF51)
+            else:
+                print "Bootloader protocol 0x{:X} not supported!".self.protocol_version
+
+        return started
+
+    def get_target(self, target_id):
+        return self._cload.request_info_update(target_id)
+
+    def read_cf1_config(self):
+        """Read a flash page from the specified target"""
+        target = self._cload.targets[0xFF]
+        config_page = target.flash_pages - 1
+
+        return self._cload.read_flash(addr=0xFF, page=config_page)
+
+    def write_cf1_config(self, data):
+        target = self._cload.targets[0xFF]
+        config_page = target.flash_pages - 1
+
+        to_flash = {"target": target, "data": data, "type": "CF1 config",
+                    "start_page": config_page}
+
+        self._internal_flash(target=to_flash)
+
+    def flash(self, filename, targets):
+        for target in targets:
+            if TargetTypes.from_string(target) not in self._cload.targets:
+                print "Target {} not found by bootloader".format(target)
+                return False
+
+        files_to_flash = ()
+        if zipfile.is_zipfile(filename):
+            # Read the manifest (don't forget to check so there is one!)
+            try:
+                zf = zipfile.ZipFile(filename)
+                j = json.loads(zf.read("manifest.json"))
+                files = j["files"]
+                if len(targets) == 0:
+                    # No targets specified, just flash everything
+                    for file in files:
+                        if files[file]["target"] in targets:
+                            targets[files[file]["target"]] += (files[file]["type"], )
+                        else:
+                            targets[files[file]["target"]] = (files[file]["type"], )
+
+                zip_targets = {}
+                for file in files:
+                    file_name = file
+                    file_info = files[file]
+                    if file_info["target"] in zip_targets:
+                        zip_targets[file_info["target"]][file_info["type"]] = {"filename": file_name}
+                    else:
+                        zip_targets[file_info["target"]] = {}
+                        zip_targets[file_info["target"]][file_info["type"]] = {"filename": file_name}
+            except KeyError as e:
+                print e
+                print "No manifest.json in {}".format(filename)
+                return
+
+            try:
+                # Match and create targets
+                for target in targets:
+                    t = targets[target]
+                    for type in t:
+                        file_to_flash = {}
+                        current_target = "{}-{}".format(target, type)
+                        file_to_flash["type"] = type
+                        # Read the data, if this fails we bail
+                        file_to_flash["target"] = self._cload.targets[TargetTypes.from_string(target)]
+                        file_to_flash["data"] = zf.read(zip_targets[target][type]["filename"])
+                        file_to_flash["start_page"] = file_to_flash["target"].start_page
+                        files_to_flash += (file_to_flash, )
+            except KeyError as e:
+                print "Could not find a file for {} in {}".format(current_target, filename)
+                return False
+
+        else:
+            if len(targets) != 1:
+                print "Not an archive, must supply one target to flash"
+            else:
+                file_to_flash = {}
+                file_to_flash["type"] = "binary"
+                f = open(filename, 'rb')
+                for t in targets:
+                    file_to_flash["target"] = self._cload.targets[TargetTypes.from_string(t)]
+                    file_to_flash["type"] = targets[t][0]
+                    file_to_flash["start_page"] = file_to_flash["target"].start_page
+                file_to_flash["data"] = f.read()
+                f.close()
+                files_to_flash += (file_to_flash, )
+
+        if not self.progress_cb:
+            print ""
+
+        file_counter = 0
+        for target in files_to_flash:
+            file_counter += 1
+            self._internal_flash(target, file_counter, len(files_to_flash))
+
+    def reset_to_firmware(self):
+        if self._cload.protocol_version == BootVersion.CF2_PROTO_VER:
+            self._cload.reset_to_firmware(TargetTypes.NRF51)
+        else:
+            self._cload.reset_to_firmware(TargetTypes.STM32)
+
+    def close(self):
+        if self._cload:
+            self._cload.close()
+
+    def _internal_flash(self, target, current_file_number=1, total_files=1):
+
+        image = target["data"]
+        t_data = target["target"]
+
+        start_page = target["start_page"]
+
+        # If used from a UI we need some extra things for reporting progress
+        factor = (100.0 * t_data.page_size) / len(image)
+        progress = 0
+
+        if self.progress_cb:
+            self.progress_cb("({}/{}) Starting...".format(current_file_number, total_files), int(progress))
+        else:
+            sys.stdout.write("Flashing {} of {} to {} ({}): ".format(current_file_number,
+                                                                     total_files,
+                                                                     TargetTypes.to_string(t_data.id),
+                                                                     target["type"]))
+            sys.stdout.flush()
+
+        if len(image) > ((t_data.flash_pages - start_page) *
+                         t_data.page_size):
+            if self.progress_cb:
+                self.progress_cb("Error: Not enough space to flash the image file.", int(progress))
+            else:
+                print "Error: Not enough space to flash the image file."
+            raise Exception()
+
+        if not self.progress_cb:
+            logger.info(("%d bytes (%d pages) " % ((len(image) - 1),
+                             int(len(image) / t_data.page_size) + 1)))
+            sys.stdout.write(("%d bytes (%d pages) " % ((len(image) - 1),
+                             int(len(image) / t_data.page_size) + 1)))
+            sys.stdout.flush()
+
+        #For each page
+        ctr = 0  # Buffer counter
+        for i in range(0, int((len(image) - 1) / t_data.page_size) + 1):
+            #Load the buffer
+            if ((i + 1) * t_data.page_size) > len(image):
+                self._cload.upload_buffer(t_data.addr, ctr, 0, image[i * t_data.page_size:])
+            else:
+                self._cload.upload_buffer(t_data.addr, ctr, 0, image[i * t_data.page_size:
+                                                  (i + 1) * t_data.page_size])
+
+            ctr += 1
+
+            if self.progress_cb:
+                progress += factor
+                self.progress_cb("({}/{}) Uploading buffer to {}...".format(current_file_number,
+                                                                            total_files,
+                                                                            TargetTypes.to_string(t_data.id)),
+
+                                 int(progress))
+            else:
+                sys.stdout.write(".")
+                sys.stdout.flush()
+
+            #Flash when the complete buffers are full
+            if ctr >= t_data.buffer_pages:
+                if self.progress_cb:
+                    self.progress_cb("({}/{}) Writing buffer to {}...".format(current_file_number,
+                                                                              total_files,
+                                                                              TargetTypes.to_string(t_data.id)),
+
+                                     int(progress))
+                else:
+                    sys.stdout.write("%d" % ctr)
+                    sys.stdout.flush()
+                if not self._cload.write_flash(t_data.addr, 0,
+                                         start_page + i - (ctr - 1),
+                                         ctr):
+                    if self.progress_cb:
+                        self.progress_cb("Error during flash operation (code %d)".format(self._cload.error_code),
+                                         int(progress))
+                    else:
+                        print "\nError during flash operation (code %d). Maybe"\
+                              " wrong radio link?" % self._cload.error_code
+                    raise Exception()
+
+                ctr = 0
+
+        if ctr > 0:
+            if self.progress_cb:
+                self.progress_cb("({}/{}) Writing buffer to {}...".format(current_file_number,
+                                                                          total_files,
+                                                                          TargetTypes.to_string(t_data.id)),
+                                 int(progress))
+            else:
+                sys.stdout.write("%d" % ctr)
+                sys.stdout.flush()
+            if not self._cload.write_flash(t_data.addr,
+                                 0,
+                                 (start_page +
+                                  (int((len(image) - 1) / t_data.page_size)) -
+                                  (ctr - 1)),
+                                 ctr):
+                if self.progress_cb:
+                    self.progress_cb("Error during flash operation (code %d)".format(self._cload.error_code),
+                                     int(progress))
+                else:
+                    print "\nError during flash operation (code %d). Maybe"\
+                          " wrong radio link?" % self._cload.error_code
+                raise Exception()
+
+
+        if self.progress_cb:
+            self.progress_cb("({}/{}) Flashing done!".format(current_file_number, total_files),
+                             int(100))
+        else:
+            print ""
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..7a9308cd970b3857545387746bf010b61da7192f
GIT binary patch
literal 8304
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.py
new file mode 100755
index 00000000..03d57557
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Bootloading utilities for the Crazyflie.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['BootVersion', 'TargetTypes', 'Target']
+
+class BootVersion:
+    CF1_PROTO_VER_0 = 0x00
+    CF1_PROTO_VER_1 = 0x01
+    CF2_PROTO_VER = 0x10
+
+    @staticmethod
+    def to_ver_string(ver):
+        if ver == BootVersion.CF1_PROTO_VER_0 or ver == BootVersion.CF1_PROTO_VER_1:
+            return "Crazyflie Nano Quadcopter (1.0)"
+        if ver == BootVersion.CF2_PROTO_VER:
+            return "Crazyflie 2.0"
+        return "Unknown"
+
+class TargetTypes:
+    STM32 = 0xFF
+    NRF51 = 0xFE
+
+    @staticmethod
+    def to_string(target):
+        if target == TargetTypes.STM32:
+            return "stm32"
+        if target == TargetTypes.NRF51:
+            return "nrf51"
+        return "Unknown"
+
+    @staticmethod
+    def from_string(name):
+        if name == "stm32":
+            return TargetTypes.STM32
+        if name == "nrf51":
+            return TargetTypes.NRF51
+        return 0
+
+class Target:
+
+    def __init__(self, id):
+        self.id = id
+        self.protocol_version = 0xFF
+        self.page_size = 0
+        self.buffer_pages = 0
+        self.flash_pages = 0
+        self.start_page = 0
+        self.cpuid = ""
+        self.data = None
+
+    def __str__(self):
+        ret = ""
+        ret += "Target info: {} (0x{:X})\n".format(TargetTypes.to_string(self.id), self.id)
+        ret += "Flash pages: %d | Page size: %d | Buffer pages: %d |"\
+               " Start page: %d\n" % (self.flash_pages, self.page_size,
+                               self.buffer_pages, self.start_page)
+        ret += "%d KBytes of flash available for firmware image." % (
+                            (self.flash_pages - self.start_page) * self.page_size / 1024)
+        return ret
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/boottypes.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..90d465f1d2cbc96f07d096ca87420d253950c703
GIT binary patch
literal 2965
zcmcIm-%lGy5Z*ig030ZdAZ=Qy^b{qLsT7QfP*v1ct!Zebv`(_7DCOmJzT1O!&Ue=B
zItd9pCI4~X+s-$4wgaS(2KHlScW!rQ=bPEB{JqlnEqr_wit?vI|9_%po)bhu91}X?
z*b$K{qKb&B;-n&uNv6Lg5!J+dA>Ik`*D;OJUqeLoX;&5I(G3wTOuK3#S`@wJGFJG8
zpwY=Pn`A+x(-UuOb)v0S#*4Guv!}}2&x1E-aiY~$!`vYAj<z9nDevcwWu3l0Qn}Gt
z%KBlDpD25HHd4mc$-dC1&bgR^n%N{!<Q%z0NUo5Jw0l<^SB0WIR(QkISr@X`tdg~+
zCbR?ilputJhAH={nePbBg|I>l9Pz>xW@DndQ1d%jq*q#0xN8?H_GqzUiLUt?lTBRq
zx<Q(G&&EL%W+SU|ui4((-ZU$hY<9M`?E>}unZ8J~S1E5*k}m8j><-%gQ~BiZiGTD+
z`r9ArYg?Z0U}t96gj*Ioc&YLx{SyoZ)3#2tp=$NZD6~d-Hc+88Ed+(*^dO{QrPe$c
zo!QeYZ6&(j3Mqj7R=*6i%2~9C(H2=@eVh5TmTzoMG(c=jgFv_&PSshZ)`W|8k-+!U
zV5ofG!oc^3Su{>al?ain0T6b}{IY@7;ZV^&B8hKjzRc4hKK;A2oWYuPhkxDc6x^ZF
z>l9R!Fz$qWH39V_T>O{5)&CG&ry-K+LmK95AM&oC;{7YUvp6_ohYxpHO7nQPEkS@L
zKr$a-(mVX4H0jEN-F86~K5tID+4;_kD@(Kq;!44jT~bP(fFo%CdmNN{$icNTJ&}fV
z%;>t$U6;~Of;|$v<fNY+g8T#nNUXwjkPdX5zvHNNah?rJ*MA_jbbZ-baL_nKBo0U-
zHD+6t_*UKotP#xi%F>)7#hhu=a<e3GR~M&A07h{g{*r`g61<<3Vh9x!UKH05FlqZF
zo8Jk}9kATz%LxMCPZ0QKg23e*0%sHiuImu$sQM5VA(X{eb5haNAOxKga}De2$fDv?
zt+QE}C8$(<-@srLoG9PuH;OAxe;mhD#?Wc_I*OCPoR&JGVr-CG);ZC_(U>N$(m*#$
z6$?>d19qD=qmo$OqVZYO<`l59xaw3}-~Sa`h0nrSy7bHWg)>3Yb1YtyK*@DM;ouMq
z==dBlaB;kYig?Fig5Zj8OQQ;pRT*oHb;bn|SH))&t75#s1#uhG)x<izH*QT#dQI%2
zE7u9l7X-y+@98wo_Pn<jUUU2P+r8%(n+@{|jUFHt9>-?SyBB%q-cyQ`hv1cZhr@Tt
zq=$fU4{RD{n<k=fA9v0yy;8E+;|P1f%RnbVKT-T}iFH1F739j(Lt0@=3yN@ak-p?7
z2|aCwf#n1#vJ&x?*uVT7a2tTUU>e8`lK3*tm7PBeaL@WEGQR(e#F7Mc=N?^S%g#FC
zS5EQ9m{l4)TI3yq+4fkP6H_Jl_XwLq;?&)xM)%6X%nN=M`F@mz)5k(Erl$aTRt^P8
nLJtR;qqyOC-YYZB$pucf?~Av~k2pTA<#o62th#F}YwQ03HjIuK

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.py
new file mode 100755
index 00000000..47a108cf
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.py
@@ -0,0 +1,420 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Crazyflie radio bootloader for flashing firmware.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Cloader']
+
+import logging
+logger = logging.getLogger(__name__)
+
+import time
+import struct
+import math
+import random
+
+import cflib.crtp
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+
+from .boottypes import TargetTypes, Target
+
+
+class Cloader:
+    """Bootloader utility for the Crazyflie"""
+    def __init__(self, link, info_cb=None, in_boot_cb=None):
+        """Init the communication class by starting to communicate with the
+        link given. clink is the link address used after resetting to the
+        bootloader.
+
+        The device is actually considered in firmware mode.
+        """
+        self.link = None
+        self.uri = link
+        self.in_loader = False
+
+        self.page_size = 0
+        self.buffer_pages = 0
+        self.flash_pages = 0
+        self.start_page = 0
+        self.cpuid = "N/A"
+        self.error_code = 0
+        self.protocol_version = 0xFF
+
+        self._info_cb = info_cb
+        self._in_boot_cb = in_boot_cb
+
+        self.targets = {}
+        self.mapping = None
+        self._available_boot_uri = ("radio://0/110/2M", "radio://0/0/2M")
+
+    def close(self):
+        """ Close the link """
+        if self.link:
+            self.link.close()
+
+    def scan_for_bootloader(self):
+        link = cflib.crtp.get_link_driver("radio://0")
+        ts = time.time()
+        res = ()
+        while len(res) == 0 and (time.time() - ts) < 10:
+            res = link.scan_selected(self._available_boot_uri)
+
+        link.close()
+
+        if len(res) > 0:
+            return res[0]
+        return None
+
+    def reset_to_bootloader(self, target_id):
+        retry_counter = 5
+        pk = CRTPPacket()
+        pk.set_header(0xFF, 0xFF)
+        pk.data = (target_id, 0xFF)
+        self.link.send_packet(pk)
+
+        pk = self.link.receive_packet(1)
+
+        while ((not pk or pk.header != 0xFF or
+                struct.unpack("<BB", pk.data[0:2]) != (target_id, 0xFF)) and
+                retry_counter >= 0 ):
+
+            pk = self.link.receive_packet(1)
+            retry_counter -= 1
+
+        if pk:
+            new_address = (0xb1, ) + struct.unpack("<BBBB", pk.data[2:6][::-1])
+
+            pk = CRTPPacket()
+            pk.set_header(0xFF, 0xFF)
+            pk.data = (target_id, 0xF0, 0x00)
+            self.link.send_packet(pk)
+
+            addr = int(struct.pack("B"*5, *new_address).encode('hex'), 16)
+
+            time.sleep(0.2)
+            self.link.close()
+            time.sleep(0.2)
+            self.link = cflib.crtp.get_link_driver("radio://0/0/2M/{}".format(addr))
+
+            return True
+        else:
+            return False
+
+
+    def reset_to_bootloader1(self, cpu_id):
+        """ Reset to the bootloader
+        The parameter cpuid shall correspond to the device to reset.
+
+        Return true if the reset has been done and the contact with the
+        bootloader is established.
+        """
+        #Send an echo request and wait for the answer
+        #Mainly aim to bypass a bug of the crazyflie firmware that prevents
+        #reset before normal CRTP communication
+        pk = CRTPPacket()
+        pk.port = CRTPPort.LINKCTRL
+        pk.data = (1, 2, 3) + cpu_id
+        self.link.send_packet(pk)
+
+        pk = None
+        while True:
+            pk = self.link.receive_packet(2)
+            if not pk:
+                return False
+
+            if pk.port == CRTPPort.LINKCTRL:
+                break
+
+        #Send the reset to bootloader request
+        pk = CRTPPacket()
+        pk.set_header(0xFF, 0xFF)
+        pk.data = (0xFF, 0xFE) + cpu_id
+        self.link.send_packet(pk)
+
+        #Wait to ack the reset ...
+        pk = None
+        while True:
+            pk = self.link.receive_packet(2)
+            if not pk:
+                return False
+
+            if pk.port == 0xFF and tuple(pk.data) == (0xFF, 0xFE) + cpu_id:
+                pk.data = (0xFF, 0xF0) + cpu_id
+                self.link.send_packet(pk)
+                break
+
+        time.sleep(0.1)
+        self.link.close()
+        self.link = cflib.crtp.get_link_driver(self.clink_address)
+        #time.sleep(0.1)
+
+        return self._update_info()
+
+    def reset_to_firmware(self, target_id):
+        """ Reset to firmware
+        The parameter cpuid shall correspond to the device to reset.
+
+        Return true if the reset has been done
+        """
+        # The fake CPU ID is legacy from the Crazyflie 1.0
+        # In order to reset the CPU id had to be sent, but this
+        # was removed before launching it. But the length check is
+        # still in the bootloader. So to work around this bug so
+        # some extra data needs to be sent.
+        fake_cpu_id = (1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12)
+        #Send the reset to bootloader request
+        pk = CRTPPacket()
+        pk.set_header(0xFF, 0xFF)
+        pk.data = (target_id, 0xFF) + fake_cpu_id
+        self.link.send_packet(pk)
+
+        #Wait to ack the reset ...
+        pk = None
+        while True:
+            pk = self.link.receive_packet(2)
+            if not pk:
+                return False
+
+            if (pk.header == 0xFF and
+                struct.unpack("B" * len(pk.data),
+                              pk.data)[:2] == (target_id, 0xFF)):
+                # Difference in CF1 and CF2 (CPU ID)
+                if target_id == 0xFE:
+                    pk.data = (target_id, 0xF0, 0x01)
+                else:
+                    pk.data = (target_id, 0xF0) + fake_cpu_id
+                self.link.send_packet(pk)
+                break
+
+        time.sleep(0.1)
+
+    def open_bootloader_uri(self, uri=None):
+        if self.link:
+            self.link.close()
+        if uri:
+            self.link = cflib.crtp.get_link_driver(uri)
+        else:
+            self.link = cflib.crtp.get_link_driver(self.clink_address)
+
+    def check_link_and_get_info(self, target_id=0xFF):
+        """Try to get a connection with the bootloader by requesting info
+        5 times. This let roughly 10 seconds to boot the copter ..."""
+        for _ in range(0, 5):
+            if self._update_info(target_id):
+                if self._in_boot_cb:
+                    self._in_boot_cb.call(True, self.targets[target_id].protocol_version)
+                if self._info_cb:
+                    self._info_cb.call(self.targets[target_id])
+                if self.protocol_version != 1:
+                    return True
+                # Set radio link to a random address
+                addr = [0xbc] + map(lambda x: random.randint(0, 255), range(4))
+                return self._set_address(addr)
+        return False
+
+    def _set_address(self, new_address):
+        """ Change copter radio address.
+            This function works only with crazyradio CRTP link.
+        """
+
+        logging.debug("Setting bootloader radio address to"
+                      " {}".format(new_address))
+
+        if len(new_address) != 5:
+            raise Exception("Radio address should be 5 bytes long")
+
+        self.link.pause()
+
+        for _ in range(10):
+            logging.debug("Trying to set new radio address")
+            self.link.cradio.set_address((0xE7,) * 5)
+            pkdata = (0xFF, 0xFF, 0x11) + tuple(new_address)
+            self.link.cradio.send_packet(pkdata)
+            self.link.cradio.set_address(tuple(new_address))
+            if self.link.cradio.send_packet((0xff,)).ack:
+                logging.info("Bootloader set to radio address"
+                             " {}".format(new_address))
+                self.link.restart()
+                return True
+
+        self.link.restart()
+        return False
+
+    def request_info_update(self, target_id):
+        if target_id not in self.targets:
+            self._update_info(target_id)
+        if self._info_cb:
+            self._info_cb.call(self.targets[target_id])
+        return self.targets[target_id]
+
+    def _update_info(self, target_id):
+        """ Call the command getInfo and fill up the information received in
+        the fields of the object
+        """
+
+        #Call getInfo ...
+        pk = CRTPPacket()
+        pk.set_header(0xFF, 0xFF)
+        pk.data = (target_id, 0x10)
+        self.link.send_packet(pk)
+
+        #Wait for the answer
+        pk = self.link.receive_packet(2)
+
+        if (pk and pk.header == 0xFF and
+                struct.unpack("<BB", pk.data[0:2]) == (target_id, 0x10)):
+            tab = struct.unpack("BBHHHH", pk.data[0:10])
+            cpuid = struct.unpack("B" * 12, pk.data[10:22])
+            if not target_id in self.targets:
+                self.targets[target_id] = Target(target_id)
+            self.targets[target_id].addr = target_id
+            if len(pk.data) > 22:
+                self.targets[target_id].protocol_version = pk.datat[22]
+                self.protocol_version = pk.datat[22]
+            self.targets[target_id].page_size = tab[2]
+            self.targets[target_id].buffer_pages = tab[3]
+            self.targets[target_id].flash_pages = tab[4]
+            self.targets[target_id].start_page = tab[5]
+            self.targets[target_id].cpuid = "%02X" % cpuid[0]
+            for i in cpuid[1:]:
+                self.targets[target_id].cpuid += ":%02X" % i
+
+            if self.protocol_version == 0x10 and target_id == TargetTypes.STM32:
+                self._update_mapping(target_id)
+
+            return True
+
+        return False
+
+    def _update_mapping(self, target_id):
+        pk = CRTPPacket()
+        pk.set_header(0xff, 0xff)
+        pk.data = (target_id, 0x12)
+        self.link.send_packet(pk)
+
+        pk = self.link.receive_packet(2)
+
+        if (pk and pk.header == 0xFF and
+                struct.unpack("<BB", pk.data[0:2]) == (target_id, 0x12)):
+            m = pk.datat[2:]
+
+            if (len(m)%2)!=0:
+                raise Exception("Malformed flash mapping packet")
+
+            self.mapping = []
+            page = 0
+            for i in range(len(m)/2):
+                for j in range(m[2*i]):
+                    self.mapping.append(page)
+                    page += m[(2*i)+1]
+
+    def upload_buffer(self, target_id, page, address, buff):
+        """Upload data into a buffer on the Crazyflie"""
+        #print len(buff)
+        count = 0
+        pk = CRTPPacket()
+        pk.set_header(0xFF, 0xFF)
+        pk.data = struct.pack("=BBHH", target_id, 0x14, page, address)
+
+        for i in range(0, len(buff)):
+            pk.data += buff[i]
+
+            count += 1
+
+            if count > 24:
+                self.link.send_packet(pk)
+                count = 0
+                pk = CRTPPacket()
+                pk.set_header(0xFF, 0xFF)
+                pk.data = struct.pack("=BBHH", target_id, 0x14, page,
+                                      i + address + 1)
+
+        self.link.send_packet(pk)
+
+    def read_flash(self, addr=0xFF, page=0x00):
+        """Read back a flash page from the Crazyflie and return it"""
+        buff = ""
+
+        page_size = self.targets[addr].page_size
+
+        for i in range(0, int(math.ceil(page_size / 25.0))):
+            pk = None
+            retry_counter = 5
+            while ((not pk or pk.header != 0xFF or
+                    struct.unpack("<BB", pk.data[0:2]) != (addr, 0x1C))
+                    and retry_counter >= 0):
+                pk = CRTPPacket()
+                pk.set_header(0xFF, 0xFF)
+                pk.data = struct.pack("<BBHH", addr, 0x1C, page, (i * 25))
+                self.link.send_packet(pk)
+
+                pk = self.link.receive_packet(1)
+                retry_counter -= 1
+            if (retry_counter < 0):
+                return None
+            else:
+                buff += pk.data[6:]
+
+        return buff[0:page_size]  # For some reason we get one byte extra here...
+
+    def write_flash(self, addr, page_buffer, target_page, page_count):
+        """Initiate flashing of data in the buffer to flash."""
+        #print "Write page", flashPage
+        #print "Writing page [%d] and [%d] forward" % (flashPage, nPage)
+        pk = None
+        retry_counter = 5
+        #print "Flasing to 0x{:X}".format(addr)
+        while ((not pk or pk.header != 0xFF or
+                struct.unpack("<BB", pk.data[0:2]) != (addr, 0x18))
+                and retry_counter >= 0):
+            pk = CRTPPacket()
+            pk.set_header(0xFF, 0xFF)
+            pk.data = struct.pack("<BBHHH", addr, 0x18, page_buffer,
+                                  target_page, page_count)
+            self.link.send_packet(pk)
+            pk = self.link.receive_packet(1)
+            retry_counter -= 1
+
+        if retry_counter < 0:
+            self.error_code = -1
+            return False
+
+        self.error_code = ord(pk.data[3])
+
+        return ord(pk.data[2]) == 1
+
+    def decode_cpu_id(self, cpuid):
+        """Decode the CPU id into a string"""
+        ret = ()
+        for i in cpuid.split(':'):
+            ret += (eval("0x" + i), )
+
+        return ret
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/bootloader/cloader.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..2e08af236fe20c0ef672a99b002bf7af340b8cea
GIT binary patch
literal 12420
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/27A2C4BA.json b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/27A2C4BA.json
new file mode 100755
index 00000000..b71d9c65
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/27A2C4BA.json
@@ -0,0 +1,257 @@
+{
+  "sensorfusion6": {
+    "ki": {
+      "ident": 24, 
+      "group": "sensorfusion6", 
+      "name": "ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kp": {
+      "ident": 23, 
+      "group": "sensorfusion6", 
+      "name": "kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "cpu": {
+    "flash": {
+      "ident": 1, 
+      "group": "cpu", 
+      "name": "flash", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "id2": {
+      "ident": 4, 
+      "group": "cpu", 
+      "name": "id2", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 0
+    }, 
+    "id0": {
+      "ident": 2, 
+      "group": "cpu", 
+      "name": "id0", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 0
+    }, 
+    "id1": {
+      "ident": 3, 
+      "group": "cpu", 
+      "name": "id1", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 0
+    }
+  }, 
+  "version": {
+    "revision0": {
+      "ident": 25, 
+      "group": "version", 
+      "name": "revision0", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 0
+    }, 
+    "revision1": {
+      "ident": 26, 
+      "group": "version", 
+      "name": "revision1", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }
+  }, 
+  "pid_rate": {
+    "yaw_kp": {
+      "ident": 11, 
+      "group": "pid_rate", 
+      "name": "yaw_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kd": {
+      "ident": 7, 
+      "group": "pid_rate", 
+      "name": "roll_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kp": {
+      "ident": 8, 
+      "group": "pid_rate", 
+      "name": "pitch_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_ki": {
+      "ident": 6, 
+      "group": "pid_rate", 
+      "name": "roll_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_ki": {
+      "ident": 9, 
+      "group": "pid_rate", 
+      "name": "pitch_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kp": {
+      "ident": 5, 
+      "group": "pid_rate", 
+      "name": "roll_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_kd": {
+      "ident": 13, 
+      "group": "pid_rate", 
+      "name": "yaw_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_ki": {
+      "ident": 12, 
+      "group": "pid_rate", 
+      "name": "yaw_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kd": {
+      "ident": 10, 
+      "group": "pid_rate", 
+      "name": "pitch_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "pid_attitude": {
+    "yaw_kp": {
+      "ident": 20, 
+      "group": "pid_attitude", 
+      "name": "yaw_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kd": {
+      "ident": 16, 
+      "group": "pid_attitude", 
+      "name": "roll_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kp": {
+      "ident": 17, 
+      "group": "pid_attitude", 
+      "name": "pitch_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_ki": {
+      "ident": 15, 
+      "group": "pid_attitude", 
+      "name": "roll_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_ki": {
+      "ident": 18, 
+      "group": "pid_attitude", 
+      "name": "pitch_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kp": {
+      "ident": 14, 
+      "group": "pid_attitude", 
+      "name": "roll_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_kd": {
+      "ident": 22, 
+      "group": "pid_attitude", 
+      "name": "yaw_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_ki": {
+      "ident": 21, 
+      "group": "pid_attitude", 
+      "name": "yaw_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kd": {
+      "ident": 19, 
+      "group": "pid_attitude", 
+      "name": "pitch_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "imu_acc_lpf": {
+    "factor": {
+      "ident": 0, 
+      "group": "imu_acc_lpf", 
+      "name": "factor", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/81204C68.json b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/81204C68.json
new file mode 100755
index 00000000..bf0d26da
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/81204C68.json
@@ -0,0 +1,317 @@
+{
+  "acc": {
+    "y": {
+      "ident": 26, 
+      "group": "acc", 
+      "name": "y", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "x": {
+      "ident": 25, 
+      "group": "acc", 
+      "name": "x", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "z": {
+      "ident": 27, 
+      "group": "acc", 
+      "name": "z", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "zw": {
+      "ident": 28, 
+      "group": "acc", 
+      "name": "zw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "gyro": {
+    "y": {
+      "ident": 4, 
+      "group": "gyro", 
+      "name": "y", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "x": {
+      "ident": 3, 
+      "group": "gyro", 
+      "name": "x", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "z": {
+      "ident": 5, 
+      "group": "gyro", 
+      "name": "z", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "sys": {
+    "canfly": {
+      "ident": 2, 
+      "group": "sys", 
+      "name": "canfly", 
+      "pytype": "<b", 
+      "__class__": "LogTocElement", 
+      "ctype": "int8_t", 
+      "access": 0
+    }
+  }, 
+  "stabilizer": {
+    "thrust": {
+      "ident": 9, 
+      "group": "stabilizer", 
+      "name": "thrust", 
+      "pytype": "<H", 
+      "__class__": "LogTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "yaw": {
+      "ident": 8, 
+      "group": "stabilizer", 
+      "name": "yaw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll": {
+      "ident": 6, 
+      "group": "stabilizer", 
+      "name": "roll", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch": {
+      "ident": 7, 
+      "group": "stabilizer", 
+      "name": "pitch", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "motor": {
+    "m4": {
+      "ident": 21, 
+      "group": "motor", 
+      "name": "m4", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m1": {
+      "ident": 22, 
+      "group": "motor", 
+      "name": "m1", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m3": {
+      "ident": 24, 
+      "group": "motor", 
+      "name": "m3", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m2": {
+      "ident": 23, 
+      "group": "motor", 
+      "name": "m2", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }
+  }, 
+  "altHold": {
+    "vSpeed": {
+      "ident": 18, 
+      "group": "altHold", 
+      "name": "vSpeed", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "target": {
+      "ident": 16, 
+      "group": "altHold", 
+      "name": "target", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "err": {
+      "ident": 15, 
+      "group": "altHold", 
+      "name": "err", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedASL": {
+      "ident": 19, 
+      "group": "altHold", 
+      "name": "vSpeedASL", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedAcc": {
+      "ident": 20, 
+      "group": "altHold", 
+      "name": "vSpeedAcc", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "zSpeed": {
+      "ident": 17, 
+      "group": "altHold", 
+      "name": "zSpeed", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "vpid": {
+    "i": {
+      "ident": 31, 
+      "group": "vpid", 
+      "name": "i", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "p": {
+      "ident": 30, 
+      "group": "vpid", 
+      "name": "p", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pid": {
+      "ident": 29, 
+      "group": "vpid", 
+      "name": "pid", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "d": {
+      "ident": 32, 
+      "group": "vpid", 
+      "name": "d", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "baro": {
+    "aslRaw": {
+      "ident": 11, 
+      "group": "baro", 
+      "name": "aslRaw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "aslLong": {
+      "ident": 12, 
+      "group": "baro", 
+      "name": "aslLong", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pressure": {
+      "ident": 14, 
+      "group": "baro", 
+      "name": "pressure", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "temp": {
+      "ident": 13, 
+      "group": "baro", 
+      "name": "temp", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "asl": {
+      "ident": 10, 
+      "group": "baro", 
+      "name": "asl", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "pm": {
+    "state": {
+      "ident": 1, 
+      "group": "pm", 
+      "name": "state", 
+      "pytype": "<b", 
+      "__class__": "LogTocElement", 
+      "ctype": "int8_t", 
+      "access": 0
+    }, 
+    "vbat": {
+      "ident": 0, 
+      "group": "pm", 
+      "name": "vbat", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/892049D2.json b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/892049D2.json
new file mode 100755
index 00000000..7de82893
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/892049D2.json
@@ -0,0 +1,89 @@
+{
+  "stabilizer": {
+    "thrust": {
+      "ident": 4, 
+      "group": "stabilizer", 
+      "name": "thrust", 
+      "pytype": "<H", 
+      "__class__": "LogTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "yaw": {
+      "ident": 3, 
+      "group": "stabilizer", 
+      "name": "yaw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll": {
+      "ident": 1, 
+      "group": "stabilizer", 
+      "name": "roll", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch": {
+      "ident": 2, 
+      "group": "stabilizer", 
+      "name": "pitch", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "motor": {
+    "m4": {
+      "ident": 5, 
+      "group": "motor", 
+      "name": "m4", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m1": {
+      "ident": 6, 
+      "group": "motor", 
+      "name": "m1", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m3": {
+      "ident": 8, 
+      "group": "motor", 
+      "name": "m3", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m2": {
+      "ident": 7, 
+      "group": "motor", 
+      "name": "m2", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }
+  }, 
+  "pm": {
+    "vbat": {
+      "ident": 0, 
+      "group": "pm", 
+      "name": "vbat", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/9E5F2E7D.json b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/9E5F2E7D.json
new file mode 100755
index 00000000..9c5467ac
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/9E5F2E7D.json
@@ -0,0 +1,355 @@
+{
+  "acc": {
+    "y": {
+      "ident": 4, 
+      "group": "acc", 
+      "name": "y", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "x": {
+      "ident": 3, 
+      "group": "acc", 
+      "name": "x", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "z": {
+      "ident": 5, 
+      "group": "acc", 
+      "name": "z", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "zw": {
+      "ident": 6, 
+      "group": "acc", 
+      "name": "zw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "mag2": {
+      "ident": 7, 
+      "group": "acc", 
+      "name": "mag2", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "mag": {
+    "y": {
+      "ident": 35, 
+      "group": "mag", 
+      "name": "y", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "x": {
+      "ident": 34, 
+      "group": "mag", 
+      "name": "x", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "z": {
+      "ident": 36, 
+      "group": "mag", 
+      "name": "z", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "gyro": {
+    "y": {
+      "ident": 28, 
+      "group": "gyro", 
+      "name": "y", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "x": {
+      "ident": 27, 
+      "group": "gyro", 
+      "name": "x", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "z": {
+      "ident": 29, 
+      "group": "gyro", 
+      "name": "z", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "sys": {
+    "canfly": {
+      "ident": 2, 
+      "group": "sys", 
+      "name": "canfly", 
+      "pytype": "<b", 
+      "__class__": "LogTocElement", 
+      "ctype": "int8_t", 
+      "access": 0
+    }
+  }, 
+  "stabilizer": {
+    "thrust": {
+      "ident": 20, 
+      "group": "stabilizer", 
+      "name": "thrust", 
+      "pytype": "<H", 
+      "__class__": "LogTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "yaw": {
+      "ident": 19, 
+      "group": "stabilizer", 
+      "name": "yaw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll": {
+      "ident": 17, 
+      "group": "stabilizer", 
+      "name": "roll", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch": {
+      "ident": 18, 
+      "group": "stabilizer", 
+      "name": "pitch", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "motor": {
+    "m4": {
+      "ident": 13, 
+      "group": "motor", 
+      "name": "m4", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m1": {
+      "ident": 14, 
+      "group": "motor", 
+      "name": "m1", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m3": {
+      "ident": 16, 
+      "group": "motor", 
+      "name": "m3", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }, 
+    "m2": {
+      "ident": 15, 
+      "group": "motor", 
+      "name": "m2", 
+      "pytype": "<i", 
+      "__class__": "LogTocElement", 
+      "ctype": "int32_t", 
+      "access": 0
+    }
+  }, 
+  "altHold": {
+    "vSpeed": {
+      "ident": 24, 
+      "group": "altHold", 
+      "name": "vSpeed", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "target": {
+      "ident": 22, 
+      "group": "altHold", 
+      "name": "target", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "err": {
+      "ident": 21, 
+      "group": "altHold", 
+      "name": "err", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedASL": {
+      "ident": 25, 
+      "group": "altHold", 
+      "name": "vSpeedASL", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedAcc": {
+      "ident": 26, 
+      "group": "altHold", 
+      "name": "vSpeedAcc", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "zSpeed": {
+      "ident": 23, 
+      "group": "altHold", 
+      "name": "zSpeed", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "vpid": {
+    "i": {
+      "ident": 32, 
+      "group": "vpid", 
+      "name": "i", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "p": {
+      "ident": 31, 
+      "group": "vpid", 
+      "name": "p", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pid": {
+      "ident": 30, 
+      "group": "vpid", 
+      "name": "pid", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "d": {
+      "ident": 33, 
+      "group": "vpid", 
+      "name": "d", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "baro": {
+    "aslRaw": {
+      "ident": 9, 
+      "group": "baro", 
+      "name": "aslRaw", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "aslLong": {
+      "ident": 10, 
+      "group": "baro", 
+      "name": "aslLong", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pressure": {
+      "ident": 12, 
+      "group": "baro", 
+      "name": "pressure", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "temp": {
+      "ident": 11, 
+      "group": "baro", 
+      "name": "temp", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "asl": {
+      "ident": 8, 
+      "group": "baro", 
+      "name": "asl", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "pm": {
+    "state": {
+      "ident": 1, 
+      "group": "pm", 
+      "name": "state", 
+      "pytype": "<b", 
+      "__class__": "LogTocElement", 
+      "ctype": "int8_t", 
+      "access": 0
+    }, 
+    "vbat": {
+      "ident": 0, 
+      "group": "pm", 
+      "name": "vbat", 
+      "pytype": "<f", 
+      "__class__": "LogTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E20076B8.json b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E20076B8.json
new file mode 100755
index 00000000..ca46a4a3
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E20076B8.json
@@ -0,0 +1,499 @@
+{
+  "imu_sensors": {
+    "HMC5883L": {
+      "ident": 3, 
+      "group": "imu_sensors", 
+      "name": "HMC5883L", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }, 
+    "MS5611": {
+      "ident": 4, 
+      "group": "imu_sensors", 
+      "name": "MS5611", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }
+  }, 
+  "sensorfusion6": {
+    "ki": {
+      "ident": 30, 
+      "group": "sensorfusion6", 
+      "name": "ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kp": {
+      "ident": 29, 
+      "group": "sensorfusion6", 
+      "name": "kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "flightmode": {
+    "althold": {
+      "ident": 10, 
+      "group": "flightmode", 
+      "name": "althold", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 0
+    }
+  }, 
+  "firmware": {
+    "revision0": {
+      "ident": 50, 
+      "group": "firmware", 
+      "name": "revision0", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }, 
+    "revision1": {
+      "ident": 51, 
+      "group": "firmware", 
+      "name": "revision1", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 1
+    }, 
+    "modified": {
+      "ident": 52, 
+      "group": "firmware", 
+      "name": "modified", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }
+  }, 
+  "cpu": {
+    "flash": {
+      "ident": 6, 
+      "group": "cpu", 
+      "name": "flash", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 1
+    }, 
+    "id2": {
+      "ident": 9, 
+      "group": "cpu", 
+      "name": "id2", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }, 
+    "id0": {
+      "ident": 7, 
+      "group": "cpu", 
+      "name": "id0", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }, 
+    "id1": {
+      "ident": 8, 
+      "group": "cpu", 
+      "name": "id1", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }
+  }, 
+  "pid_rate": {
+    "yaw_kp": {
+      "ident": 17, 
+      "group": "pid_rate", 
+      "name": "yaw_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kd": {
+      "ident": 13, 
+      "group": "pid_rate", 
+      "name": "roll_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kp": {
+      "ident": 14, 
+      "group": "pid_rate", 
+      "name": "pitch_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_ki": {
+      "ident": 12, 
+      "group": "pid_rate", 
+      "name": "roll_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_ki": {
+      "ident": 15, 
+      "group": "pid_rate", 
+      "name": "pitch_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kp": {
+      "ident": 11, 
+      "group": "pid_rate", 
+      "name": "roll_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_kd": {
+      "ident": 19, 
+      "group": "pid_rate", 
+      "name": "yaw_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_ki": {
+      "ident": 18, 
+      "group": "pid_rate", 
+      "name": "yaw_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kd": {
+      "ident": 16, 
+      "group": "pid_rate", 
+      "name": "pitch_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "pid_attitude": {
+    "yaw_kp": {
+      "ident": 26, 
+      "group": "pid_attitude", 
+      "name": "yaw_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kd": {
+      "ident": 22, 
+      "group": "pid_attitude", 
+      "name": "roll_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kp": {
+      "ident": 23, 
+      "group": "pid_attitude", 
+      "name": "pitch_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_ki": {
+      "ident": 21, 
+      "group": "pid_attitude", 
+      "name": "roll_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_ki": {
+      "ident": 24, 
+      "group": "pid_attitude", 
+      "name": "pitch_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kp": {
+      "ident": 20, 
+      "group": "pid_attitude", 
+      "name": "roll_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_kd": {
+      "ident": 28, 
+      "group": "pid_attitude", 
+      "name": "yaw_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_ki": {
+      "ident": 27, 
+      "group": "pid_attitude", 
+      "name": "yaw_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kd": {
+      "ident": 25, 
+      "group": "pid_attitude", 
+      "name": "pitch_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "altHold": {
+    "vSpeedAccFac": {
+      "ident": 43, 
+      "group": "altHold", 
+      "name": "vSpeedAccFac", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedASLDeadband": {
+      "ident": 44, 
+      "group": "altHold", 
+      "name": "vSpeedASLDeadband", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "errDeadband": {
+      "ident": 33, 
+      "group": "altHold", 
+      "name": "errDeadband", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "altHoldErrMax": {
+      "ident": 35, 
+      "group": "altHold", 
+      "name": "altHoldErrMax", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kd": {
+      "ident": 36, 
+      "group": "altHold", 
+      "name": "kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pidAslFac": {
+      "ident": 40, 
+      "group": "altHold", 
+      "name": "pidAslFac", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "maxThrust": {
+      "ident": 48, 
+      "group": "altHold", 
+      "name": "maxThrust", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "ki": {
+      "ident": 37, 
+      "group": "altHold", 
+      "name": "ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedLimit": {
+      "ident": 46, 
+      "group": "altHold", 
+      "name": "vSpeedLimit", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "aslAlpha": {
+      "ident": 31, 
+      "group": "altHold", 
+      "name": "aslAlpha", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kp": {
+      "ident": 38, 
+      "group": "altHold", 
+      "name": "kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "baseThrust": {
+      "ident": 47, 
+      "group": "altHold", 
+      "name": "baseThrust", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "vBiasAlpha": {
+      "ident": 42, 
+      "group": "altHold", 
+      "name": "vBiasAlpha", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pidAlpha": {
+      "ident": 39, 
+      "group": "altHold", 
+      "name": "pidAlpha", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "altHoldChangeSens": {
+      "ident": 34, 
+      "group": "altHold", 
+      "name": "altHoldChangeSens", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "minThrust": {
+      "ident": 49, 
+      "group": "altHold", 
+      "name": "minThrust", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "vAccDeadband": {
+      "ident": 41, 
+      "group": "altHold", 
+      "name": "vAccDeadband", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "aslAlphaLong": {
+      "ident": 32, 
+      "group": "altHold", 
+      "name": "aslAlphaLong", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedASLFac": {
+      "ident": 45, 
+      "group": "altHold", 
+      "name": "vSpeedASLFac", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "imu_tests": {
+    "HMC5883L": {
+      "ident": 1, 
+      "group": "imu_tests", 
+      "name": "HMC5883L", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }, 
+    "MS5611": {
+      "ident": 2, 
+      "group": "imu_tests", 
+      "name": "MS5611", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }, 
+    "MPU6050": {
+      "ident": 0, 
+      "group": "imu_tests", 
+      "name": "MPU6050", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }
+  }, 
+  "imu_acc_lpf": {
+    "factor": {
+      "ident": 5, 
+      "group": "imu_acc_lpf", 
+      "name": "factor", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E8BC7DAD.json b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E8BC7DAD.json
new file mode 100755
index 00000000..ca46a4a3
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/cache/E8BC7DAD.json
@@ -0,0 +1,499 @@
+{
+  "imu_sensors": {
+    "HMC5883L": {
+      "ident": 3, 
+      "group": "imu_sensors", 
+      "name": "HMC5883L", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }, 
+    "MS5611": {
+      "ident": 4, 
+      "group": "imu_sensors", 
+      "name": "MS5611", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }
+  }, 
+  "sensorfusion6": {
+    "ki": {
+      "ident": 30, 
+      "group": "sensorfusion6", 
+      "name": "ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kp": {
+      "ident": 29, 
+      "group": "sensorfusion6", 
+      "name": "kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "flightmode": {
+    "althold": {
+      "ident": 10, 
+      "group": "flightmode", 
+      "name": "althold", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 0
+    }
+  }, 
+  "firmware": {
+    "revision0": {
+      "ident": 50, 
+      "group": "firmware", 
+      "name": "revision0", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }, 
+    "revision1": {
+      "ident": 51, 
+      "group": "firmware", 
+      "name": "revision1", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 1
+    }, 
+    "modified": {
+      "ident": 52, 
+      "group": "firmware", 
+      "name": "modified", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }
+  }, 
+  "cpu": {
+    "flash": {
+      "ident": 6, 
+      "group": "cpu", 
+      "name": "flash", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 1
+    }, 
+    "id2": {
+      "ident": 9, 
+      "group": "cpu", 
+      "name": "id2", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }, 
+    "id0": {
+      "ident": 7, 
+      "group": "cpu", 
+      "name": "id0", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }, 
+    "id1": {
+      "ident": 8, 
+      "group": "cpu", 
+      "name": "id1", 
+      "pytype": "<L", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint32_t", 
+      "access": 1
+    }
+  }, 
+  "pid_rate": {
+    "yaw_kp": {
+      "ident": 17, 
+      "group": "pid_rate", 
+      "name": "yaw_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kd": {
+      "ident": 13, 
+      "group": "pid_rate", 
+      "name": "roll_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kp": {
+      "ident": 14, 
+      "group": "pid_rate", 
+      "name": "pitch_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_ki": {
+      "ident": 12, 
+      "group": "pid_rate", 
+      "name": "roll_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_ki": {
+      "ident": 15, 
+      "group": "pid_rate", 
+      "name": "pitch_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kp": {
+      "ident": 11, 
+      "group": "pid_rate", 
+      "name": "roll_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_kd": {
+      "ident": 19, 
+      "group": "pid_rate", 
+      "name": "yaw_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_ki": {
+      "ident": 18, 
+      "group": "pid_rate", 
+      "name": "yaw_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kd": {
+      "ident": 16, 
+      "group": "pid_rate", 
+      "name": "pitch_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "pid_attitude": {
+    "yaw_kp": {
+      "ident": 26, 
+      "group": "pid_attitude", 
+      "name": "yaw_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kd": {
+      "ident": 22, 
+      "group": "pid_attitude", 
+      "name": "roll_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kp": {
+      "ident": 23, 
+      "group": "pid_attitude", 
+      "name": "pitch_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_ki": {
+      "ident": 21, 
+      "group": "pid_attitude", 
+      "name": "roll_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_ki": {
+      "ident": 24, 
+      "group": "pid_attitude", 
+      "name": "pitch_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "roll_kp": {
+      "ident": 20, 
+      "group": "pid_attitude", 
+      "name": "roll_kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_kd": {
+      "ident": 28, 
+      "group": "pid_attitude", 
+      "name": "yaw_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "yaw_ki": {
+      "ident": 27, 
+      "group": "pid_attitude", 
+      "name": "yaw_ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pitch_kd": {
+      "ident": 25, 
+      "group": "pid_attitude", 
+      "name": "pitch_kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "altHold": {
+    "vSpeedAccFac": {
+      "ident": 43, 
+      "group": "altHold", 
+      "name": "vSpeedAccFac", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedASLDeadband": {
+      "ident": 44, 
+      "group": "altHold", 
+      "name": "vSpeedASLDeadband", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "errDeadband": {
+      "ident": 33, 
+      "group": "altHold", 
+      "name": "errDeadband", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "altHoldErrMax": {
+      "ident": 35, 
+      "group": "altHold", 
+      "name": "altHoldErrMax", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kd": {
+      "ident": 36, 
+      "group": "altHold", 
+      "name": "kd", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pidAslFac": {
+      "ident": 40, 
+      "group": "altHold", 
+      "name": "pidAslFac", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "maxThrust": {
+      "ident": 48, 
+      "group": "altHold", 
+      "name": "maxThrust", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "ki": {
+      "ident": 37, 
+      "group": "altHold", 
+      "name": "ki", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedLimit": {
+      "ident": 46, 
+      "group": "altHold", 
+      "name": "vSpeedLimit", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "aslAlpha": {
+      "ident": 31, 
+      "group": "altHold", 
+      "name": "aslAlpha", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "kp": {
+      "ident": 38, 
+      "group": "altHold", 
+      "name": "kp", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "baseThrust": {
+      "ident": 47, 
+      "group": "altHold", 
+      "name": "baseThrust", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "vBiasAlpha": {
+      "ident": 42, 
+      "group": "altHold", 
+      "name": "vBiasAlpha", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "pidAlpha": {
+      "ident": 39, 
+      "group": "altHold", 
+      "name": "pidAlpha", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "altHoldChangeSens": {
+      "ident": 34, 
+      "group": "altHold", 
+      "name": "altHoldChangeSens", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "minThrust": {
+      "ident": 49, 
+      "group": "altHold", 
+      "name": "minThrust", 
+      "pytype": "<H", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint16_t", 
+      "access": 0
+    }, 
+    "vAccDeadband": {
+      "ident": 41, 
+      "group": "altHold", 
+      "name": "vAccDeadband", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "aslAlphaLong": {
+      "ident": 32, 
+      "group": "altHold", 
+      "name": "aslAlphaLong", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }, 
+    "vSpeedASLFac": {
+      "ident": 45, 
+      "group": "altHold", 
+      "name": "vSpeedASLFac", 
+      "pytype": "<f", 
+      "__class__": "ParamTocElement", 
+      "ctype": "float", 
+      "access": 0
+    }
+  }, 
+  "imu_tests": {
+    "HMC5883L": {
+      "ident": 1, 
+      "group": "imu_tests", 
+      "name": "HMC5883L", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }, 
+    "MS5611": {
+      "ident": 2, 
+      "group": "imu_tests", 
+      "name": "MS5611", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }, 
+    "MPU6050": {
+      "ident": 0, 
+      "group": "imu_tests", 
+      "name": "MPU6050", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 1
+    }
+  }, 
+  "imu_acc_lpf": {
+    "factor": {
+      "ident": 5, 
+      "group": "imu_acc_lpf", 
+      "name": "factor", 
+      "pytype": "<B", 
+      "__class__": "ParamTocElement", 
+      "ctype": "uint8_t", 
+      "access": 0
+    }
+  }
+}
\ No newline at end of file
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.py
new file mode 100755
index 00000000..0f7400da
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.py
@@ -0,0 +1,391 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+The Crazyflie module is used to easily connect/send/receive data
+from a Crazyflie.
+
+Each function in the Crazyflie has a class in the module that can be used
+to access that functionality. The same design is then used in the Crazyflie
+firmware which makes the mapping 1:1 in most cases.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Crazyflie']
+
+import logging
+logger = logging.getLogger(__name__)
+import time
+import datetime
+from threading import Thread
+
+from threading import Timer, Lock
+
+from .commander import Commander
+from .console import Console
+from .param import Param
+from .log import Log
+from .toccache import TocCache
+from .mem import Memory
+from .platformservice import PlatformService
+
+import cflib.crtp
+
+from cflib.utils.callbacks import Caller
+
+
+class State:
+    """Stat of the connection procedure"""
+    DISCONNECTED = 0
+    INITIALIZED = 1
+    CONNECTED = 2
+    SETUP_FINISHED = 3
+
+
+class Crazyflie():
+    """The Crazyflie class"""
+
+    # Called on disconnect, no matter the reason
+    disconnected = Caller()
+    # Called on unintentional disconnect only
+    connection_lost = Caller()
+    # Called when the first packet in a new link is received
+    link_established = Caller()
+    # Called when the user requests a connection
+    connection_requested = Caller()
+    # Called when the link is established and the TOCs (that are not cached)
+    # have been downloaded
+    connected = Caller()
+    # Called if establishing of the link fails (i.e times out)
+    connection_failed = Caller()
+    # Called for every packet received
+    packet_received = Caller()
+    # Called for every packet sent
+    packet_sent = Caller()
+    # Called when the link driver updates the link quality measurement
+    link_quality_updated = Caller()
+
+    state = State.DISCONNECTED
+
+    def __init__(self, link=None, ro_cache=None, rw_cache=None):
+        """
+        Create the objects from this module and register callbacks.
+
+        ro_cache -- Path to read-only cache (string)
+        rw_cache -- Path to read-write cache (string)
+        """
+        self.link = link
+        self._toc_cache = TocCache(ro_cache=ro_cache,
+                                   rw_cache=rw_cache)
+
+        self.incoming = _IncomingPacketHandler(self)
+        self.incoming.setDaemon(True)
+        self.incoming.start()
+
+        self.commander = Commander(self)
+        self.log = Log(self)
+        self.console = Console(self)
+        self.param = Param(self)
+        self.mem = Memory(self)
+        self.platform = PlatformService(self)
+
+        self.link_uri = ""
+
+        # Used for retry when no reply was sent back
+        self.packet_received.add_callback(self._check_for_initial_packet_cb)
+        self.packet_received.add_callback(self._check_for_answers)
+
+        self._answer_patterns = {}
+
+        self._send_lock = Lock()
+
+        self.connected_ts = None
+
+        # Connect callbacks to logger
+        self.disconnected.add_callback(
+            lambda uri: logger.info("Callback->Disconnected from [%s]", uri))
+        self.disconnected.add_callback(self._disconnected)
+        self.link_established.add_callback(
+            lambda uri: logger.info("Callback->Connected to [%s]", uri))
+        self.connection_lost.add_callback(
+            lambda uri, errmsg: logger.info("Callback->Connection lost to"
+                                            " [%s]: %s", uri, errmsg))
+        self.connection_failed.add_callback(
+            lambda uri, errmsg: logger.info("Callback->Connected failed to"
+                                            " [%s]: %s", uri, errmsg))
+        self.connection_requested.add_callback(
+            lambda uri: logger.info("Callback->Connection initialized[%s]", uri))
+        self.connected.add_callback(
+            lambda uri: logger.info("Callback->Connection setup finished [%s]", uri))
+
+    def _disconnected(self, link_uri):
+        """ Callback when disconnected."""
+        self.connected_ts = None
+
+    def _start_connection_setup(self):
+        """Start the connection setup by refreshing the TOCs"""
+        logger.info("We are connected[%s], request connection setup",
+                    self.link_uri)
+        print "We are connected[%s], request connection setup" % self.link_uri
+        self.log.refresh_toc(self._log_toc_updated_cb, self._toc_cache)
+
+    def _param_toc_updated_cb(self):
+        """Called when the param TOC has been fully updated"""
+        logger.info("Param TOC finished updating")
+        self.connected_ts = datetime.datetime.now()
+        self.connected.call(self.link_uri)
+        # Trigger the update for all the parameters
+        self.param.request_update_of_all_params()
+
+    def _mems_updated_cb(self):
+        """Called when the memories have been identified"""
+        logger.info("Memories finished updating")
+        self.param.refresh_toc(self._param_toc_updated_cb, self._toc_cache)
+
+    def _log_toc_updated_cb(self):
+        """Called when the log TOC has been fully updated"""
+        logger.info("Log TOC finished updating")
+        self.mem.refresh(self._mems_updated_cb)
+
+    def _link_error_cb(self, errmsg):
+        """Called from the link driver when there's an error"""
+        logger.warning("Got link error callback [%s] in state [%s]",
+                       errmsg, self.state)
+        if (self.link is not None):
+            self.link.close()
+        self.link = None
+        if (self.state == State.INITIALIZED):
+            self.connection_failed.call(self.link_uri, errmsg)
+        if (self.state == State.CONNECTED or
+                self.state == State.SETUP_FINISHED):
+            self.disconnected.call(self.link_uri)
+            self.connection_lost.call(self.link_uri, errmsg)
+        self.state = State.DISCONNECTED
+
+    def _link_quality_cb(self, percentage):
+        """Called from link driver to report link quality"""
+        self.link_quality_updated.call(percentage)
+
+    def _check_for_initial_packet_cb(self, data):
+        """
+        Called when first packet arrives from Crazyflie.
+
+        This is used to determine if we are connected to something that is
+        answering.
+        """
+        self.state = State.CONNECTED
+        self.link_established.call(self.link_uri)
+        self.packet_received.remove_callback(self._check_for_initial_packet_cb)
+
+    def open_link(self, link_uri):
+        """
+        Open the communication link to a copter at the given URI and setup the
+        connection (download log/parameter TOC).
+        """
+        self.connection_requested.call(link_uri)
+        self.state = State.INITIALIZED
+        self.link_uri = link_uri
+        try:
+            self.link = cflib.crtp.get_link_driver(link_uri,
+                                                   self._link_quality_cb,
+                                                   self._link_error_cb)
+
+            if not self.link:
+                message = "No driver found or malformed URI: {}"\
+                    .format(link_uri)
+                print message
+                logger.warning(message)
+                self.connection_failed.call(link_uri, message)
+            else:
+                # Add a callback so we can check that any data is coming
+                # back from the copter
+                self.packet_received.add_callback(self._check_for_initial_packet_cb)
+                self._start_connection_setup()
+                print "connection setup started"
+
+        except Exception as ex:  # pylint: disable=W0703
+            # We want to catch every possible exception here and show
+            # it in the user interface
+            import traceback
+            logger.error("Couldn't load link driver: %s\n\n%s",
+                         ex, traceback.format_exc())
+            exception_text = "Couldn't load link driver: %s\n\n%s" % (
+                             ex, traceback.format_exc())
+            print "connection probably failed"
+            print exception_text
+            if self.link:
+                self.link.close()
+                self.link = None
+            self.connection_failed.call(link_uri, exception_text)
+
+    def close_link(self):
+        """Close the communication link."""
+        logger.info("Closing link")
+        if (self.link is not None):
+            self.commander.send_setpoint(0, 0, 0, 0)
+        if (self.link is not None):
+            self.link.close()
+            self.link = None
+        self._answer_patterns = {}
+        self.disconnected.call(self.link_uri)
+
+    def add_port_callback(self, port, cb):
+        """Add a callback to cb on port"""
+        self.incoming.add_port_callback(port, cb)
+
+    def remove_port_callback(self, port, cb):
+        """Remove the callback cb on port"""
+        self.incoming.remove_port_callback(port, cb)
+
+    def _no_answer_do_retry(self, pk, pattern):
+        """Resend packets that we have not gotten answers to"""
+        logger.debug("Resending for pattern %s", pattern)
+        # Set the timer to None before trying to send again
+        self.send_packet(pk, expected_reply=pattern, resend=True)
+
+    def _check_for_answers(self, pk):
+        """
+        Callback called for every packet received to check if we are
+        waiting for an answer on this port. If so, then cancel the retry
+        timer.
+        """
+        longest_match = ()
+        if len(self._answer_patterns) > 0:
+            data = (pk.header,) + pk.datat
+            for p in self._answer_patterns.keys():
+                logger.debug("Looking for pattern match on %s vs %s", p, data)
+                if len(p) <= len(data):
+                    if p == data[0:len(p)]:
+                        match = data[0:len(p)]
+                        if len(match) >= len(longest_match):
+                            logger.debug("Found new longest match %s", match)
+                            longest_match = match
+        if len(longest_match) > 0:
+            self._answer_patterns[longest_match].cancel()
+            del self._answer_patterns[longest_match]
+
+    def send_packet(self, pk, expected_reply=(), resend=False, timeout=0.2):
+        """
+        Send a packet through the link interface.
+
+        pk -- Packet to send
+        expect_answer -- True if a packet from the Crazyflie is expected to
+                         be sent back, otherwise false
+
+        """
+        self._send_lock.acquire()
+        if self.link is not None:
+            if len(expected_reply) > 0 and not resend and \
+                    self.link.needs_resending:
+                pattern = (pk.header,) + expected_reply
+                logger.debug("Sending packet and expecting the %s pattern back",
+                             pattern)
+                new_timer = Timer(timeout,
+                                  lambda: self._no_answer_do_retry(pk, pattern))
+                self._answer_patterns[pattern] = new_timer
+                new_timer.start()
+            elif resend:
+                # Check if we have gotten an answer, if not try again
+                pattern = expected_reply
+                if pattern in self._answer_patterns:
+                    logger.debug("We want to resend and the pattern is there")
+                    if self._answer_patterns[pattern]:
+                        new_timer = Timer(timeout,
+                                          lambda:
+                                          self._no_answer_do_retry(
+                                              pk, pattern))
+                        self._answer_patterns[pattern] = new_timer
+                        new_timer.start()
+                else:
+                    logger.debug("Resend requested, but no pattern found: %s",
+                                 self._answer_patterns)
+            self.link.send_packet(pk)
+            self.packet_sent.call(pk)
+        self._send_lock.release()
+
+class _IncomingPacketHandler(Thread):
+    """Handles incoming packets and sends the data to the correct receivers"""
+    def __init__(self, cf):
+        Thread.__init__(self)
+        self.cf = cf
+        self.cb = []
+
+    def add_port_callback(self, port, cb):
+        """Add a callback for data that comes on a specific port"""
+        logger.debug("Adding callback on port [%d] to [%s]", port, cb)
+        self.add_header_callback(cb, port, 0, 0xff, 0x0)
+
+    def remove_port_callback(self, port, cb):
+        """Remove a callback for data that comes on a specific port"""
+        logger.debug("Removing callback on port [%d] to [%s]", port, cb)
+        for port_callback in self.cb:
+            if (port_callback[0] == port and port_callback[4] == cb):
+                self.cb.remove(port_callback)
+
+    def add_header_callback(self, cb, port, channel, port_mask=0xFF,
+                            channel_mask=0xFF):
+        """
+        Add a callback for a specific port/header callback with the
+        possibility to add a mask for channel and port for multiple
+        hits for same callback.
+        """
+        self.cb.append([port, port_mask, channel, channel_mask, cb])
+
+    def run(self):
+        while(True):
+            if self.cf.link is None:
+                time.sleep(1)
+                continue
+            pk = self.cf.link.receive_packet(1)
+
+            if pk is None:
+                continue
+
+            #All-packet callbacks
+            self.cf.packet_received.call(pk)
+
+            found = False
+            for cb in self.cb:
+                if (cb[0] == pk.port & cb[1] and
+                        cb[2] == pk.channel & cb[3]):
+                    try:
+                        cb[4](pk)
+                    except Exception:  # pylint: disable=W0703
+                        # Disregard pylint warning since we want to catch all
+                        # exceptions and we can't know what will happen in
+                        # the callbacks.
+                        import traceback
+                        logger.warning("Exception while doing callback on port"
+                                       " [%d]\n\n%s", pk.port,
+                                       traceback.format_exc())
+                    if (cb[0] != 0xFF):
+                        found = True
+
+            if not found:
+                logger.warning("Got packet on header (%d,%d) but no callback "
+                               "to handle it", pk.port, pk.channel)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4ce7384997efaabbe7f55a3e6b14360cde89900a
GIT binary patch
literal 15562
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.py
new file mode 100755
index 00000000..6e723c82
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Used for sending control setpoints to the Crazyflie
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Commander']
+
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+import struct
+
+
+class Commander():
+    """
+    Used for sending control setpoints to the Crazyflie
+    """
+
+    def __init__(self, crazyflie=None):
+        """
+        Initialize the commander object. By default the commander is in
+        +-mode (not x-mode).
+        """
+        self._cf = crazyflie
+        self._x_mode = False
+
+    def set_client_xmode(self, enabled):
+        """
+        Enable/disable the client side X-mode. When enabled this recalculates
+        the setpoints before sending them to the Crazyflie.
+        """
+        self._x_mode = enabled
+
+    def send_setpoint(self, roll, pitch, yaw, thrust,cmd1,cmd2,cmd3,cmd4,mode):
+        """
+        Send a new control setpoint for roll/pitch/yaw/thrust to the copter
+
+        The arguments roll/pitch/yaw/trust is the new setpoints that should
+        be sent to the copter
+        """
+        """
+        if thrust > 0xFFFF or thrust < 0:
+             raise ValueError("Thrust must be between 0 and 0xFFFF")
+
+         if self._x_mode:
+             roll, pitch = 0.707 * (roll - pitch), 0.707 * (roll + pitch)
+        """
+        pk = CRTPPacket()
+        pk.port = CRTPPort.COMMANDER
+        """ pk.data = struct.pack('<fffH', roll, -pitch, yaw, thrust) """
+        pk.data = struct.pack('<fffHHHHHB', roll, pitch, yaw, thrust,cmd1,cmd2,cmd3,cmd4,mode)
+        self._cf.send_packet(pk)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/commander.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..60e1d661e5f06bdbfeaf1bbe0d8449209016fa04
GIT binary patch
literal 2173
zcmcgt&5q+l5H353GcmJ>1KJA`_!tC<#oui}Y-V=_(r#FJ78t_8>a^PvXZ<I4M>9d;
zH2Vg;3un%}4i5ldwI|MK=7zM}8ds{TyQ};A>SucYJn;Yg_3v{@H=~E&Z}I6lL_)NH
zbZFsF;?e~amy#YOead@O^l8zjVnB-lIcyzJ?$N^It;vuQk6sg96aBKlWI$)*Bli0h
zg#V*fNsv}epjDY<<wYQ?(lk|$xv8tHG&(R<U{)$PZNzWeG|!Z;-@(aQW+W^scr-I3
z%ulPL5M`p8>|czr3zPBL9=&|=LdaLj7{1j`tH#VboV;^}Pk#!bfD;HI@$x<cehsKl
zT!sL6K*x$l(b;&wr@n^p1C0NJgwIKS!2UaBmk%L!4(*6yfM^vw#{qG?PnQt(N<RL5
znVC%F8JJ*}<erA0TCSBe(_pp@5|xTgZtiTzbdZ&upHC)5m8f7`RwlTz`p465aX#j3
zFf3x38V3GU<OtD_EmtugZ7J`=pjDpQm~en>$i}de75W@nxT*>jcB|pz$y4#{+2m}i
zjVi*r*72ndbtA(hPDP%_u!LGRS#5MEkpjzGXFI^tdTS#b#~CPz;|afnpF@L@bD!h~
zSm;PTaQg?$9qcEiSmr8BGR?m&eMrF41Uds(=a!~v@bgNQfwFrOYyz*1k|LLzTo|Q0
zZycUORxcH@o(*l<W3jlG>9+&m;9(IHZ+VV9XqNq(d*2qr@8Mp6h^6HrzT(JpA#2V|
z8b~fVE>He!GXDg{*y{h?=VV^HwDT$Mk?GSK^@Q}sAB;WP^=UVtokzPN?T%<SqHEu7
zhJ|Q7q&18_T{~zpE{cKEQU3+eQG5nA0}+(!@?IaYO$Lo64{LOeRk#(Gp;<K>jk0JP
zjI3&-8ozV)67dqv#il^O5z;$H?MZ~1qssRkB5PIx39_m-dD8h>+DPBMitCq$X4X#9
zH2sdpY(D0O7TJh1t!;n%;ra7N^T$sj@J3Xlg&EEPZJLcVtqonyDv2;6VsZ>efio<w
zZ3i6lyiFZ;!jNn&TSjbkgB9zxWyX4wMe^BR`FyW@u~)vd3TJ`gr`E6DRw8o!T4;OX
zPuOq-LGO;7V|U`r$K0K6dO|4*7032JLd@-Ilk?QJuQ;hBbY^5;l`3K%5l1BY0OEkh
zYHzfEMYzviXmNNw&pkdG4ciykSAB@}8w_G&R#nqs;5HeX_pxpp<Wx4Mwn7_({*X%%
h^VZ#S3*8$9W`l6j(*Dgyc!AA$&-I*<`_MV={RbRKBV7Oh

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.py
new file mode 100755
index 00000000..bdc56658
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+"""
+Crazyflie console is used to receive characters printed using printf
+from the firmware.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Console']
+
+import struct
+from cflib.utils.callbacks import Caller
+from cflib.crtp.crtpstack import CRTPPort
+
+
+class Console:
+    """
+    Crazyflie console is used to receive characters printed using printf
+    from the firmware.
+    """
+
+    receivedChar = Caller()
+
+    def __init__(self, crazyflie):
+        """
+        Initialize the console and register it to receive data from the copter.
+        """
+        self.cf = crazyflie
+        self.cf.add_port_callback(CRTPPort.CONSOLE, self.incoming)
+
+    def incoming(self, packet):
+        """
+        Callback for data received from the copter.
+        """
+        # This might be done prettier ;-)
+        console_text = "%s" % struct.unpack("%is" % len(packet.data), packet.data)
+
+        self.receivedChar.call(console_text)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/console.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7dbd2d9d6d6da40aa68d74f41c7059d3ff08e269
GIT binary patch
literal 1678
zcmcgs&2AGh5T4y^k|q>XB<PjBv<inN;)W0)XaEVRN~;Y*Z8%t4uhY2fpVkhgsGQ0(
z@esTY4*=hIle7o8L9KY!Gak>+H#2Gcyxsl&;m4t*#cJUD0^1ydBt#QXk0u@^KAqs;
zQ_`R$ptL~~9Pn#W8c^1xNs~P0Ta>maYty6+9_nk9wCNksCDG9YOhDsahdmsDbca>^
z<$RiI70MzvMXEw=!nsjNXp68?Qt3~)n#ENtt*T5|Ryw!1og1B>)ZDZ?t%@wPGZjvC
zm7T?v>UYg8Sl`uF!oCV$?%Fo^VeLZy!W#8FNNkJ6ahj^C=GT~y-W?nim0f|tn!DKM
zA&3GffCJPg)!?)y0b_&|0Rb}RLyMwukFhjRr*4S#Pcmggf5?`_(uXJB>~@H4wm~jD
zx*!rb5S`-Zk@e}c;n;vKebmT9LHJ!=5H`i@ZLY13Qw{VO*=2R&JV9Abv_UmOZLe1;
ziEVrpoGePH^j+#~Z`5nT2b>ehslAI&oFt+I%t8YH<5+%-__8k4{%E|v_u6tbbS{ew
z#da~;01~6pspS^p;=1PPfgwlCdz=QdB2$Ca?cmkUn|N<;XMB#Rvq4!Jab^all7mD{
z;afm5FtXC6H3NxeJihjJk$As6chLx;0gMoj;Kpz;6m&cXJ;Tl^I_RI^0AQ^{96f=&
zQ3>FF>Cxum<kRL?h@leDX_GE|zT^=a_gt3GU}FUz_b6i&PKzpZNH2yX`8(u>@!T?o
zBiqtu)Z=D%9dB$km)0HUc?q+YbyAf(eA&ERhhXVqi1~9jTq)Pq5s#C5&##qr>I<lZ
zRiEvRR3zer9%I*)Xm0yG7Z<mU5P6&_A#B$%S&__BW?4aqq>w^HJlD}3CfrTV+Ev{D
zyPYDqB>qD&9BwOU`5nL0uKl}k?m@hWKAziIQLUoqo)r-%X77WzmhI22PECI~OU5N<
r*+zXWD_i!N7`q%b7s1UX?B?fbU5Muo5SZZ}Yxym&<F9)g-bUj$IH8^j

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.py
new file mode 100755
index 00000000..2f1ab2fd
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.py
@@ -0,0 +1,539 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Enables logging of variables from the Crazyflie.
+
+When a Crazyflie is connected it's possible to download a TableOfContent of all
+the variables that can be logged. Using this it's possible to add logging
+configurations where selected variables are sent to the client at a
+specified period.
+
+Terminology:
+  Log configuration - A configuration with a period and a number of variables
+                      that are present in the TOC.
+  Stored as         - The size and type of the variable as declared in the
+                      Crazyflie firmware
+  Fetch as          - The size and type that a variable should be fetched as.
+                      This does not have to be the same as the size and type
+                      it's stored as.
+
+States of a configuration:
+  Created on host - When a configuration is created the contents is checked
+                    so that all the variables are present in the TOC. If not
+                    then the configuration cannot be created.
+  Created on CF   - When the configuration is deemed valid it is added to the
+                    Crazyflie. At this time the memory constraint is checked
+                    and the status returned.
+  Started on CF   - Any added block that is not started can be started. Once
+                    started the Crazyflie will send back logdata periodically
+                    according to the specified period when it's created.
+  Stopped on CF   - Any started configuration can be stopped. The memory taken
+                    by the configuration on the Crazyflie is NOT freed, the
+                    only effect is that the Crazyflie will stop sending
+                    logdata back to the host.
+  Deleted on CF   - Any block that is added can be deleted. When this is done
+                    the memory taken by the configuration is freed on the
+                    Crazyflie. The configuration will have to be re-added to
+                    be used again.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Log', 'LogTocElement']
+
+import struct
+import errno
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+from cflib.utils.callbacks import Caller
+from .toc import Toc, TocFetcher
+
+# Channels used for the logging port
+CHAN_TOC = 0
+CHAN_SETTINGS = 1
+CHAN_LOGDATA = 2
+
+# Commands used when accessing the Table of Contents
+CMD_TOC_ELEMENT = 0
+CMD_TOC_INFO = 1
+
+# Commands used when accessing the Log configurations
+CMD_CREATE_BLOCK = 0
+CMD_APPEND_BLOCK = 1
+CMD_DELETE_BLOCK = 2
+CMD_START_LOGGING = 3
+CMD_STOP_LOGGING = 4
+CMD_RESET_LOGGING = 5
+
+# Possible states when receiving TOC
+IDLE = "IDLE"
+GET_TOC_INF = "GET_TOC_INFO"
+GET_TOC_ELEMENT = "GET_TOC_ELEMENT"
+
+# The max size of a CRTP packet payload
+MAX_LOG_DATA_PACKET_SIZE = 30
+
+import logging
+logger = logging.getLogger(__name__)
+
+class LogVariable():
+    """A logging variable"""
+
+    TOC_TYPE = 0
+    MEM_TYPE = 1
+
+    def __init__(self, name="", fetchAs="uint8_t", varType=TOC_TYPE,
+                 storedAs="", address=0):
+        self.name = name
+        self.fetch_as = LogTocElement.get_id_from_cstring(fetchAs)
+        if (len(storedAs) == 0):
+            self.stored_as = self.fetch_as
+        else:
+            self.stored_as = LogTocElement.get_id_from_cstring(storedAs)
+        self.address = address
+        self.type = varType
+        self.stored_as_string = storedAs
+        self.fetch_as_string = fetchAs
+
+    def is_toc_variable(self):
+        """
+        Return true if the variable should be in the TOC, false if raw memory
+        variable
+        """
+        return self.type == LogVariable.TOC_TYPE
+
+    def get_storage_and_fetch_byte(self):
+        """Return what the variable is stored as and fetched as"""
+        return (self.fetch_as | (self.stored_as << 4))
+
+    def __str__(self):
+        return ("LogVariable: name=%s, store=%s, fetch=%s" %
+                (self.name, LogTocElement.get_cstring_from_id(self.stored_as),
+                 LogTocElement.get_cstring_from_id(self.fetch_as)))
+
+class LogConfig(object):
+    """Representation of one log configuration that enables logging
+    from the Crazyflie"""
+    _config_id_counter = 1
+
+    def __init__(self, name, period_in_ms):
+        """Initialize the entry"""
+        self.data_received_cb = Caller()
+        self.error_cb = Caller()
+        self.started_cb = Caller()
+        self.added_cb = Caller()
+        self.err_no = 0
+
+        self.id = LogConfig._config_id_counter
+        LogConfig._config_id_counter = (LogConfig._config_id_counter + 1) % 255
+        self.cf = None
+        self.period = period_in_ms / 10
+        self.period_in_ms = period_in_ms
+        self._added = False
+        self._started = False
+        self.valid = False
+        self.variables = []
+        self.default_fetch_as = []
+        self.name = name
+
+    def add_variable(self, name, fetch_as=None):
+        """Add a new variable to the configuration.
+
+        name - Complete name of the variable in the form group.name
+        fetch_as - String representation of the type the variable should be
+                   fetched as (i.e uint8_t, float, FP16, etc)
+
+        If no fetch_as type is supplied, then the stored as type will be used
+        (i.e the type of the fetched variable is the same as it's stored in the
+        Crazyflie)."""
+        if fetch_as:
+            self.variables.append(LogVariable(name, fetch_as))
+        else:
+            # We cannot determine the default type until we have connected. So
+            # save the name and we will add these once we are connected.
+            self.default_fetch_as.append(name)
+
+    def add_memory(self, name, fetch_as, stored_as, address):
+        """Add a raw memory position to log.
+
+        name - Arbitrary name of the variable
+        fetch_as - String representation of the type of the data the memory
+                   should be fetch as (i.e uint8_t, float, FP16)
+        stored_as - String representation of the type the data is stored as
+                    in the Crazyflie
+        address - The address of the data
+        """
+        self.variables.append(LogVariable(name, fetch_as, LogVariable.MEM_TYPE,
+                                          stored_as, address))
+
+    def _set_added(self, added):
+        if added != self._added:
+            self.added_cb.call(self, added)
+        self._added = added
+
+    def _get_added(self):
+        return self._added
+
+    def _set_started(self, started):
+        if started != self._started:
+            self.started_cb.call(self, started)
+        self._started = started
+
+    def _get_started(self):
+        return self._started
+
+    added = property(_get_added, _set_added)
+    started = property(_get_started, _set_started)
+
+    def create(self):
+        """Save the log configuration in the Crazyflie"""
+        pk = CRTPPacket()
+        pk.set_header(5, CHAN_SETTINGS)
+        pk.data = (CMD_CREATE_BLOCK, self.id)
+        for var in self.variables:
+            if (var.is_toc_variable() is False):  # Memory location
+                logger.debug("Logging to raw memory %d, 0x%04X",
+                             var.get_storage_and_fetch_byte(), var.address)
+                pk.data += struct.pack('<B', var.get_storage_and_fetch_byte())
+                pk.data += struct.pack('<I', var.address)
+            else:  # Item in TOC
+                logger.debug("Adding %s with id=%d and type=0x%02X",
+                             var.name,
+                             self.cf.log.toc.get_element_id(
+                             var.name), var.get_storage_and_fetch_byte())
+                pk.data += struct.pack('<B', var.get_storage_and_fetch_byte())
+                pk.data += struct.pack('<B', self.cf.log.toc.
+                                       get_element_id(var.name))
+        logger.debug("Adding log block id {}".format(self.id))
+        self.cf.send_packet(pk, expected_reply=(CMD_CREATE_BLOCK, self.id))
+
+    def start(self):
+        """Start the logging for this entry"""
+        if (self.cf.link is not None):
+            if (self._added is False):
+                self.create()
+                logger.debug("First time block is started, add block")
+            else:
+                logger.debug("Block already registered, starting logging"
+                             " for id=%d", self.id)
+                pk = CRTPPacket()
+                pk.set_header(5, CHAN_SETTINGS)
+                pk.data = (CMD_START_LOGGING, self.id, self.period)
+                self.cf.send_packet(pk, expected_reply=(CMD_START_LOGGING, self.id))
+
+    def stop(self):
+        """Stop the logging for this entry"""
+        if (self.cf.link is not None):
+            if (self.id is None):
+                logger.warning("Stopping block, but no block registered")
+            else:
+                logger.debug("Sending stop logging for block id=%d", self.id)
+                pk = CRTPPacket()
+                pk.set_header(5, CHAN_SETTINGS)
+                pk.data = (CMD_STOP_LOGGING, self.id)
+                self.cf.send_packet(pk, expected_reply=(CMD_STOP_LOGGING, self.id))
+
+    def delete(self):
+        """Delete this entry in the Crazyflie"""
+        if (self.cf.link is not None):
+            if (self.id is None):
+                logger.warning("Delete block, but no block registered")
+            else:
+                logger.debug("LogEntry: Sending delete logging for block id=%d"
+                             % self.id)
+                pk = CRTPPacket()
+                pk.set_header(5, CHAN_SETTINGS)
+                pk.data = (CMD_DELETE_BLOCK, self.id)
+                self.cf.send_packet(pk, expected_reply=(CMD_DELETE_BLOCK, self.id))
+
+    def unpack_log_data(self, log_data, timestamp):
+        """Unpack received logging data so it represent real values according
+        to the configuration in the entry"""
+        ret_data = {}
+        data_index = 0
+        for var in self.variables:
+            size = LogTocElement.get_size_from_id(var.fetch_as)
+            name = var.name
+            unpackstring = LogTocElement.get_unpack_string_from_id(
+                var.fetch_as)
+            value = struct.unpack(unpackstring,
+                                  log_data[data_index:data_index + size])[0]
+            data_index += size
+            ret_data[name] = value
+        self.data_received_cb.call(timestamp, ret_data, self)
+
+
+class LogTocElement:
+    """An element in the Log TOC."""
+    types = {0x01: ("uint8_t",  '<B', 1),
+             0x02: ("uint16_t", '<H', 2),
+             0x03: ("uint32_t", '<L', 4),
+             0x04: ("int8_t",   '<b', 1),
+             0x05: ("int16_t",  '<h', 2),
+             0x06: ("int32_t",  '<i', 4),
+             0x08: ("FP16",     '<h', 2),
+             0x07: ("float",    '<f', 4)}
+
+    @staticmethod
+    def get_id_from_cstring(name):
+        """Return variable type id given the C-storage name"""
+        for key in LogTocElement.types.keys():
+            if (LogTocElement.types[key][0] == name):
+                return key
+        raise KeyError("Type [%s] not found in LogTocElement.types!" % name)
+
+    @staticmethod
+    def get_cstring_from_id(ident):
+        """Return the C-storage name given the variable type id"""
+        try:
+            return LogTocElement.types[ident][0]
+        except KeyError:
+            raise KeyError("Type [%d] not found in LogTocElement.types"
+                           "!" % ident)
+
+    @staticmethod
+    def get_size_from_id(ident):
+        """Return the size in bytes given the variable type id"""
+        try:
+            return LogTocElement.types[ident][2]
+        except KeyError:
+            raise KeyError("Type [%d] not found in LogTocElement.types"
+                           "!" % ident)
+
+    @staticmethod
+    def get_unpack_string_from_id(ident):
+        """Return the Python unpack string given the variable type id"""
+        try:
+            return LogTocElement.types[ident][1]
+        except KeyError:
+            raise KeyError("Type [%d] not found in LogTocElement.types!" % ident)
+
+    def __init__(self, data=None):
+        """TocElement creator. Data is the binary payload of the element."""
+
+        if (data):
+            strs = struct.unpack("s" * len(data[2:]), data[2:])
+            strs = ("{}" * len(strs)).format(*strs).split("\0")
+            self.group = strs[0]
+            self.name = strs[1]
+
+            self.ident = ord(data[0])
+
+            self.ctype = LogTocElement.get_cstring_from_id(ord(data[1]))
+            self.pytype = LogTocElement.get_unpack_string_from_id(ord(data[1]))
+
+            self.access = ord(data[1]) & 0x10
+
+
+class Log():
+    """Create log configuration"""
+
+    # These codes can be decoded using os.stderror, but
+    # some of the text messages will look very strange
+    # in the UI, so they are redefined here
+    _err_codes = {
+            errno.ENOMEM: "No more memory available",
+            errno.ENOEXEC: "Command not found",
+            errno.ENOENT: "No such block id",
+            errno.E2BIG: "Block too large",
+            errno.EEXIST: "Block already exists"
+            }
+
+    def __init__(self, crazyflie=None):
+        self.log_blocks = []
+        # Called with newly created blocks
+        self.block_added_cb = Caller()
+
+        self.cf = crazyflie
+        self.toc = None
+        self.cf.add_port_callback(CRTPPort.LOGGING, self._new_packet_cb)
+
+        self.toc_updated = Caller()
+        self.state = IDLE
+        self.fake_toc_crc = 0xDEADBEEF
+
+        self._refresh_callback = None
+        self._toc_cache = None
+
+    def add_config(self, logconf):
+        """Add a log configuration to the logging framework.
+
+        When doing this the contents of the log configuration will be validated
+        and listeners for new log configurations will be notified. When
+        validating the configuration the variables are checked against the TOC
+        to see that they actually exist. If they don't then the configuration
+        cannot be used. Since a valid TOC is required, a Crazyflie has to be
+        connected when calling this method, otherwise it will fail."""
+
+        if not self.cf.link:
+            logger.error("Cannot add configs without being connected to a "
+                         "Crazyflie!")
+            return
+
+        # If the log configuration contains variables that we added without
+        # type (i.e we want the stored as type for fetching as well) then
+        # resolve this now and add them to the block again.
+        for name in logconf.default_fetch_as:
+            var = self.toc.get_element_by_complete_name(name)
+            if not var:
+                logger.warning("%s not in TOC, this block cannot be used!", name)
+                logconf.valid = False
+                raise KeyError("Variable {} not in TOC".format(name))
+            # Now that we know what type this variable has, add it to the log
+            # config again with the correct type
+            logconf.add_variable(name, var.ctype)
+
+        # Now check that all the added variables are in the TOC and that
+        # the total size constraint of a data packet with logging data is
+        # not
+        size = 0
+        for var in logconf.variables:
+            size += LogTocElement.get_size_from_id(var.fetch_as)
+            # Check that we are able to find the variable in the TOC so
+            # we can return error already now and not when the config is sent
+            if var.is_toc_variable():
+                if (self.toc.get_element_by_complete_name(var.name) is None):
+                    logger.warning("Log: %s not in TOC, this block cannot be"
+                                   " used!", var.name)
+                    logconf.valid = False
+                    raise KeyError("Variable {} not in TOC".format(var.name))
+
+        if (size <= MAX_LOG_DATA_PACKET_SIZE and
+                (logconf.period > 0 and logconf.period < 0xFF)):
+            logconf.valid = True
+            logconf.cf = self.cf
+            self.log_blocks.append(logconf)
+            self.block_added_cb.call(logconf)
+        else:
+            logconf.valid = False
+            raise AttributeError("The log configuration is too large or has an invalid parameter")
+
+    def refresh_toc(self, refresh_done_callback, toc_cache):
+        """Start refreshing the table of loggale variables"""
+
+        self._toc_cache = toc_cache
+        self._refresh_callback = refresh_done_callback
+        self.toc = None
+
+        pk = CRTPPacket()
+        pk.set_header(CRTPPort.LOGGING, CHAN_SETTINGS)
+        pk.data = (CMD_RESET_LOGGING, )
+        self.cf.send_packet(pk, expected_reply=(CMD_RESET_LOGGING,))
+
+    def _find_block(self, id):
+        for block in self.log_blocks:
+            if block.id == id:
+                return block
+        return None
+
+    def _new_packet_cb(self, packet):
+        """Callback for newly arrived packets with TOC information"""
+        chan = packet.channel
+        cmd = packet.datal[0]
+        payload = struct.pack("B" * (len(packet.datal) - 1), *packet.datal[1:])
+
+        if (chan == CHAN_SETTINGS):
+            id = ord(payload[0])
+            error_status = ord(payload[1])
+            block = self._find_block(id)
+            if (cmd == CMD_CREATE_BLOCK):
+                if (block is not None):
+                    if error_status == 0 or error_status == errno.EEXIST:
+                        if not block.added:
+                            logger.debug("Have successfully added id=%d", id)
+
+                            pk = CRTPPacket()
+                            pk.set_header(5, CHAN_SETTINGS)
+                            pk.data = (CMD_START_LOGGING, id, block.period)
+                            self.cf.send_packet(pk, expected_reply=(CMD_START_LOGGING, id))
+                            block.added = True
+                    else:
+                        msg = self._err_codes[error_status]
+                        logger.warning("Error %d when adding id=%d (%s)", error_status, id, msg)
+                        block.err_no = error_status
+                        block.added_cb.call(False)
+                        block.error_cb.call(block, msg)
+
+                else:
+                    logger.warning("No LogEntry to assign block to !!!")
+            if (cmd == CMD_START_LOGGING):
+                if (error_status == 0x00):
+                    logger.info("Have successfully started logging for id=%d", id)
+                    if block:
+                        block.started = True
+
+                else:
+                    msg = self._err_codes[error_status]
+                    logger.warning("Error %d when starting id=%d (%s)", error_status, id, msg)
+                    if block:
+                        block.err_no = error_status
+                        block.started_cb.call(False)
+                        # This is a temporary fix, we are adding a new issue
+                        # for this. For some reason we get an error back after
+                        # the block has been started and added. This will show
+                        # an error in the UI, but everything is still working.
+                        #block.error_cb.call(block, msg)
+
+            if (cmd == CMD_STOP_LOGGING):
+                if (error_status == 0x00):
+                    logger.info("Have successfully stopped logging for id=%d", id)
+                    if block:
+                        block.started = False
+
+            if (cmd == CMD_DELETE_BLOCK):
+                # Accept deletion of a block that isn't added. This could
+                # happen due to timing (i.e add/start/delete in fast sequence)
+                if error_status == 0x00 or error_status == errno.ENOENT:
+                    logger.info("Have successfully deleted id=%d", id)
+                    if block:
+                        block.started = False
+                        block.added = False
+
+            if (cmd == CMD_RESET_LOGGING):
+                # Guard against multiple responses due to re-sending
+                if not self.toc:
+                    logger.debug("Logging reset, continue with TOC download")
+                    self.log_blocks = []
+
+                    self.toc = Toc()
+                    toc_fetcher = TocFetcher(self.cf, LogTocElement,
+                                             CRTPPort.LOGGING,
+                                             self.toc, self._refresh_callback,
+                                             self._toc_cache)
+                    toc_fetcher.start()
+
+        if (chan == CHAN_LOGDATA):
+            chan = packet.channel
+            id = packet.datal[0]
+            block = self._find_block(id)
+            timestamps = struct.unpack("<BBB", packet.data[1:4])
+            timestamp = (timestamps[0] | timestamps[1] << 8 | timestamps[2] << 16)
+            logdata = packet.data[4:]
+            if (block is not None):
+                block.unpack_log_data(logdata, timestamp)
+            else:
+                logger.warning("Error no LogEntry to handle id=%d", id)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/log.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..40ace216e4a3a559c79ba9354e98c46ae7d1585c
GIT binary patch
literal 19930
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.py
new file mode 100755
index 00000000..d2ccacdc
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.py
@@ -0,0 +1,782 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Enables flash access to the Crazyflie.
+
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Memory', 'MemoryElement']
+
+import struct
+import errno
+from Queue import Queue
+from threading import Lock
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+from cflib.utils.callbacks import Caller
+from binascii import crc32
+import binascii
+
+# Channels used for the logging port
+CHAN_INFO = 0
+CHAN_READ = 1
+CHAN_WRITE = 2
+
+# Commands used when accessing the Settings port
+CMD_INFO_VER = 0
+CMD_INFO_NBR = 1
+CMD_INFO_DETAILS = 2
+
+# The max size of a CRTP packet payload
+MAX_LOG_DATA_PACKET_SIZE = 30
+
+import logging
+logger = logging.getLogger(__name__)
+
+class MemoryElement(object):
+    """A memory """
+
+    TYPE_I2C = 0
+    TYPE_1W = 1
+    TYPE_DRIVER_LED = 0x10
+
+    def __init__(self, id, type, size, mem_handler):
+        """Initialize the element with default values"""
+        self.id = id
+        self.type = type
+        self.size = size
+        self.mem_handler = mem_handler
+
+    @staticmethod
+    def type_to_string(t):
+        """Get string representation of memory type"""
+        if t == MemoryElement.TYPE_I2C:
+            return "I2C"
+        if t == MemoryElement.TYPE_1W:
+            return "1-wire"
+        if t == MemoryElement.TYPE_DRIVER_LED:
+            return "LED driver"
+        return "Unknown"
+
+    def new_data(self, mem, addr, data):
+        logger.info("New data, but not OW mem")
+
+    def __str__(self):
+        """Generate debug string for memory"""
+        return ("Memory: id={}, type={}, size={}".format(
+                self.id, MemoryElement.type_to_string(self.type), self.size))
+
+
+class LED:
+    """Used to set color/intensity of one LED in the LED-ring"""
+    def __init__(self):
+        """Initialize to off"""
+        self.r = 0
+        self.g = 0
+        self.b = 0
+        self.intensity = 100
+
+    def set(self, r, g, b, intensity=None):
+        """Set the R/G/B and optionally intensity in one call"""
+        self.r = r
+        self.g = g
+        self.b = b
+        if intensity:
+            self.intensity = intensity
+
+class LEDDriverMemory(MemoryElement):
+    """Memory interface for using the LED-ring mapped memory for setting RGB
+       values for all the LEDs in the ring"""
+    def __init__(self, id, type, size, mem_handler):
+        """Initialize with 12 LEDs"""
+        super(LEDDriverMemory, self).__init__(id=id, type=type, size=size,
+                                              mem_handler=mem_handler)
+        self._update_finished_cb = None
+        self._write_finished_cb = None
+
+        self.leds = []
+        for i in range(12):
+            self.leds.append(LED())
+
+    def new_data(self, mem, addr, data):
+        """Callback for when new memory data has been fetched"""
+        if mem.id == self.id:
+            logger.info("Got new data from the LED driver, but we don't care.")
+
+    def write_data(self, write_finished_cb):
+        """Write the saved LED-ring data to the Crazyflie"""
+        self._write_finished_cb = write_finished_cb
+        data = ()
+        for led in self.leds:
+            # In order to fit all the LEDs in one radio packet RGB565 is used
+            # to compress the colors. The calculations below converts 3 bytes
+            # RGB into 2 bytes RGB565. Then shifts the value of each color to
+            # LSB, applies the intensity  and shifts them back for correct
+            # alignment on 2 bytes.
+            R5 = (int)((((int(led.r) & 0xFF) * 249 + 1014) >> 11) & 0x1F) * led.intensity/100
+            G6 = (int)((((int(led.g) & 0xFF) * 253 + 505) >> 10) & 0x3F) * led.intensity/100
+            B5 = (int)((((int(led.b) & 0xFF) * 249 + 1014) >> 11) & 0x1F) * led.intensity/100
+            tmp = (R5 << 11) | (G6 << 5) | (B5 << 0)
+            data += (tmp >> 8, tmp & 0xFF)
+        self.mem_handler.write(self, 0x00, data, flush_queue=True)
+
+    def update(self, update_finished_cb):
+        """Request an update of the memory content"""
+        if not self._update_finished_cb:
+            self._update_finished_cb = update_finished_cb
+            self.valid = False
+            logger.info("Updating content of memory {}".format(self.id))
+            # Start reading the header
+            self.mem_handler.read(self, 0, 16)
+
+    def write_done(self, mem, addr):
+        if self._write_finished_cb and mem.id == self.id:
+            logger.info("Write to LED driver done")
+            self._write_finished_cb(self, addr)
+            self._write_finished_cb = None
+
+    def disconnect(self):
+        self._update_finished_cb = None
+        self._write_finished_cb = None
+
+
+class I2CElement(MemoryElement):
+    def __init__(self, id, type, size, mem_handler):
+        super(I2CElement, self).__init__(id=id, type=type, size=size, mem_handler=mem_handler)
+        self._update_finished_cb = None
+        self._write_finished_cb = None
+        self.elements = {}
+        self.valid = False
+
+    def new_data(self, mem, addr, data):
+        """Callback for when new memory data has been fetched"""
+        if mem.id == self.id:
+            if addr == 0:
+                done = False
+                # Check for header
+                if data[0:4] == "0xBC":
+                    logger.info("Got new data: {}".format(data))
+                    [self.elements["version"],
+                     self.elements["radio_channel"],
+                     self.elements["radio_speed"],
+                     self.elements["pitch_trim"],
+                     self.elements["roll_trim"]] = struct.unpack("<BBBff", data[4:15])
+                    if self.elements["version"] == 0:
+                        done = True
+                    elif self.elements["version"] == 1:
+                        self.datav0 = data
+                        self.mem_handler.read(self, 16, 5)
+
+            if addr == 16:
+                [radio_address_upper,
+                 radio_address_lower] = struct.unpack("<BI", self.datav0[15] + data[0:4])
+                self.elements["radio_address"] = int(radio_address_upper) << 32 | radio_address_lower
+
+                logger.info(self.elements)
+                data = self.datav0 + data
+                done = True
+
+            if done:
+                if self._checksum256(data[:len(data)-1]) == ord(data[len(data)-1]):
+                    self.valid = True
+                if self._update_finished_cb:
+                    self._update_finished_cb(self)
+                    self._update_finished_cb = None
+
+    def _checksum256(self, st):
+        return reduce(lambda x, y: x + y, map(ord, st)) % 256
+
+    def write_data(self, write_finished_cb):
+        if self.elements["version"] == 0:
+            data = (0x00, self.elements["radio_channel"], self.elements["radio_speed"],
+                    self.elements["pitch_trim"], self.elements["roll_trim"])
+            image = struct.pack("<BBBff", *data)
+        elif self.elements["version"] == 1:
+            data = (0x01, self.elements["radio_channel"], self.elements["radio_speed"],
+                    self.elements["pitch_trim"], self.elements["roll_trim"],
+                    self.elements["radio_address"] >> 32, self.elements["radio_address"] & 0xFFFFFFFF)
+            image = struct.pack("<BBBffBI", *data)
+        # Adding some magic:
+        image = "0xBC" + image
+        image += struct.pack("B", self._checksum256(image))
+
+        self._write_finished_cb = write_finished_cb
+
+        self.mem_handler.write(self, 0x00, struct.unpack("B"*len(image), image))
+
+    def update(self, update_finished_cb):
+        """Request an update of the memory content"""
+        if not self._update_finished_cb:
+            self._update_finished_cb = update_finished_cb
+            self.valid = False
+            logger.info("Updating content of memory {}".format(self.id))
+            # Start reading the header
+            self.mem_handler.read(self, 0, 16)
+
+    def write_done(self, mem, addr):
+        if self._write_finished_cb and mem.id == self.id:
+            self._write_finished_cb(self, addr)
+            self._write_finished_cb = None
+
+    def disconnect(self):
+        self._update_finished_cb = None
+        self._write_finished_cb = None
+
+
+class OWElement(MemoryElement):
+    """Memory class with extra functionality for 1-wire memories"""
+
+    element_mapping = {
+        1: "Board name",
+        2: "Board revision",
+        3: "Custom"
+    }
+
+    def __init__(self, id, type, size, addr, mem_handler):
+        """Initialize the memory with good defaults"""
+        super(OWElement, self).__init__(id=id, type=type, size=size, mem_handler=mem_handler)
+        self.addr = addr
+
+        self.valid = False
+
+        self.vid = None
+        self.pid = None
+        self.name = None
+        self.pins = None
+        self.elements = {}
+
+        self._update_finished_cb = None
+        self._write_finished_cb = None
+
+        self._rev_element_mapping = {}
+        for key in OWElement.element_mapping.keys():
+            self._rev_element_mapping[OWElement.element_mapping[key]] = key
+
+    def new_data(self, mem, addr, data):
+        """Callback for when new memory data has been fetched"""
+        if mem.id == self.id:
+            if addr == 0:
+                if self._parse_and_check_header(data[0:8]):
+                    logger.info("--> HEADER OK")
+                    if self._parse_and_check_elements(data[9:11]):
+                        self.valid = True
+                        self._update_finished_cb(self)
+                        self._update_finished_cb = None
+                    else:
+                        # We need to fetch the elements, find out the length
+                        (elem_ver, elem_len) = struct.unpack("BB", data[8:10])
+                        self.mem_handler.read(self, 8, elem_len + 3)
+                else:
+                    logger.info("--> HEADER NOT OK")
+                    # Call the update if the CRC check of the header fails, we're done here
+                    if self._update_finished_cb:
+                        self._update_finished_cb(self)
+                        self._update_finished_cb = None
+            elif addr == 0x08:
+                if self._parse_and_check_elements(data):
+                    logger.info("--> ELEMENT OK")
+                    self.valid = True
+                else:
+                    logger.info("--> ELEMENT NOT OK")
+                if self._update_finished_cb:
+                    self._update_finished_cb(self)
+                    self._update_finished_cb = None
+
+
+
+    def _parse_and_check_elements(self, data):
+        """Parse and check the CRC and length of the elements part of the memory"""
+        (elem_ver, elem_len, crc) = struct.unpack("<BBB", data[0] + data[1] + data[-1])
+        test_crc = crc32(data[:-1]) & 0x0ff
+        elem_data = data[2:-1]
+        if test_crc == crc:
+            while len(elem_data) > 0:
+                (eid, elen) = struct.unpack("BB", elem_data[:2])
+                self.elements[self.element_mapping[eid]] = elem_data[2:2+elen]
+                elem_data = elem_data[2+elen:]
+            return True
+        return False
+
+
+    def write_done(self, mem, addr):
+        if self._write_finished_cb:
+            self._write_finished_cb(self, addr)
+            self._write_finished_cb = None
+
+    def write_data(self, write_finished_cb):
+        # First generate the header part
+        header_data = struct.pack("<BIBB", 0xEB, self.pins, self.vid, self.pid)
+        header_crc = crc32(header_data) & 0x0ff
+        header_data += struct.pack("B", header_crc)
+
+        # Now generate the elements part
+        elem = ""
+        logger.info(self.elements.keys())
+        for element in reversed(self.elements.keys()):
+            elem_string = self.elements[element]
+            #logger.info(">>>> {}".format(elem_string))
+            key_encoding = self._rev_element_mapping[element]
+            elem += struct.pack("BB", key_encoding, len(elem_string))
+            elem += elem_string
+
+        elem_data = struct.pack("BB", 0x00, len(elem))
+        elem_data += elem
+        elem_crc = crc32(elem_data) & 0x0ff
+        elem_data += struct.pack("B", elem_crc)
+
+        data = header_data + elem_data
+
+        # Write data
+        p = ""
+        for s in data:
+            p += "0x{:02X} ".format(ord(s))
+        logger.info(p)
+
+        self.mem_handler.write(self, 0x00, struct.unpack("B"*len(data), data))
+
+        self._write_finished_cb = write_finished_cb
+
+    def update(self, update_finished_cb):
+        """Request an update of the memory content"""
+        if not self._update_finished_cb:
+            self._update_finished_cb = update_finished_cb
+            self.valid = False
+            logger.info("Updating content of memory {}".format(self.id))
+            # Start reading the header
+            self.mem_handler.read(self, 0, 11)
+        #else:
+        #    logger.warning("Already in progress of updating memory {}".format(self.id))
+
+    def _parse_and_check_header(self, data):
+        """Parse and check the CRC of the header part of the memory"""
+        #logger.info("Should parse header: {}".format(data))
+        (start, self.pins, self.vid, self.pid, crc) = struct.unpack("<BIBBB", data)
+        test_crc = crc32(data[:-1]) & 0x0ff
+        if start == 0xEB and crc == test_crc:
+            return True
+        return False
+
+    def __str__(self):
+        """Generate debug string for memory"""
+        return ("OW {} ({:02X}:{:02X}): {}".format(
+                self.addr, self.vid, self.pid, self.elements))
+
+    def disconnect(self):
+        self._update_finished_cb = None
+        self._write_finished_cb = None
+
+
+class _ReadRequest:
+    """Class used to handle memory reads that will split up the read in multiple packets in necessary"""
+    MAX_DATA_LENGTH = 20
+
+    def __init__(self, mem, addr, length, cf):
+        """Initialize the object with good defaults"""
+        self.mem = mem
+        self.addr = addr
+        self._bytes_left = length
+        self.data = ""
+        self.cf = cf
+
+        self._current_addr = addr
+
+    def start(self):
+        """Start the fetching of the data"""
+        self._request_new_chunk()
+
+    def resend(self):
+        logger.info("Sending write again...")
+        self._request_new_chunk()
+
+    def _request_new_chunk(self):
+        """Called to request a new chunk of data to be read from the Crazyflie"""
+        # Figure out the length of the next request
+        new_len = self._bytes_left
+        if new_len > _ReadRequest.MAX_DATA_LENGTH:
+            new_len = _ReadRequest.MAX_DATA_LENGTH
+
+        logger.info("Requesting new chunk of {}bytes at 0x{:X}".format(new_len, self._current_addr))
+
+        # Request the data for the next address
+        pk = CRTPPacket()
+        pk.set_header(CRTPPort.MEM, CHAN_READ)
+        pk.data = struct.pack("<BIB", self.mem.id, self._current_addr, new_len)
+        reply = struct.unpack("<BBBBB", pk.data[:-1])
+        self.cf.send_packet(pk, expected_reply=reply, timeout=1)
+
+    def add_data(self, addr, data):
+        """Callback when data is received from the Crazyflie"""
+        data_len = len(data)
+        if not addr == self._current_addr:
+            logger.warning("Address did not match when adding data to read request!")
+            return
+
+        # Add the data and calculate the next address to fetch
+        self.data += data
+        self._bytes_left -= data_len
+        self._current_addr += data_len
+
+        if self._bytes_left > 0:
+            self._request_new_chunk()
+            return False
+        else:
+            return True
+
+class _WriteRequest:
+    """Class used to handle memory reads that will split up the read in multiple packets in necessary"""
+    MAX_DATA_LENGTH = 25
+
+    def __init__(self, mem, addr, data, cf):
+        """Initialize the object with good defaults"""
+        self.mem = mem
+        self.addr = addr
+        self._bytes_left = len(data)
+        self._data = data
+        self.data = ""
+        self.cf = cf
+
+        self._current_addr = addr
+
+        self._sent_packet = None
+        self._sent_reply = None
+
+        self._addr_add = 0
+
+    def start(self):
+        """Start the fetching of the data"""
+        self._write_new_chunk()
+
+    def resend(self):
+        logger.info("Sending write again...")
+        self.cf.send_packet(self._sent_packet, expected_reply=self._sent_reply, timeout=1)
+
+    def _write_new_chunk(self):
+        """Called to request a new chunk of data to be read from the Crazyflie"""
+        # Figure out the length of the next request
+        new_len = len(self._data)
+        if new_len > _WriteRequest.MAX_DATA_LENGTH:
+            new_len = _WriteRequest.MAX_DATA_LENGTH
+
+        logger.info("Writing new chunk of {}bytes at 0x{:X}".format(new_len, self._current_addr))
+
+        data = self._data[:new_len]
+        self._data = self._data[new_len:]
+
+        pk = CRTPPacket()
+        pk.set_header(CRTPPort.MEM, CHAN_WRITE)
+        pk.data = struct.pack("<BI", self.mem.id, self._current_addr)
+        # Create a tuple used for matching the reply using id and address
+        reply = struct.unpack("<BBBBB", pk.data)
+        self._sent_reply = reply
+        # Add the data
+        pk.data += struct.pack("B"*len(data), *data)
+        self._sent_packet = pk
+        self.cf.send_packet(pk, expected_reply=reply, timeout=1)
+
+        self._addr_add = len(data)
+
+    def write_done(self, addr):
+        """Callback when data is received from the Crazyflie"""
+        if not addr == self._current_addr:
+            logger.warning("Address did not match when adding data to read request!")
+            return
+
+        if len(self._data) > 0:
+            self._current_addr += self._addr_add
+            self._write_new_chunk()
+            return False
+        else:
+            logger.info("This write request is done")
+            return True
+
+class Memory():
+    """Access memories on the Crazyflie"""
+
+    # These codes can be decoded using os.stderror, but
+    # some of the text messages will look very strange
+    # in the UI, so they are redefined here
+    _err_codes = {
+            errno.ENOMEM: "No more memory available",
+            errno.ENOEXEC: "Command not found",
+            errno.ENOENT: "No such block id",
+            errno.E2BIG: "Block too large",
+            errno.EEXIST: "Block already exists"
+            }
+
+    def __init__(self, crazyflie=None):
+        """Instantiate class and connect callbacks"""
+        self.mems = []
+        # Called when new memories have been added
+        self.mem_added_cb = Caller()
+        # Called when new data has been read
+        self.mem_read_cb = Caller()
+
+        self.mem_write_cb = Caller()
+
+        self.cf = crazyflie
+        self.cf.add_port_callback(CRTPPort.MEM, self._new_packet_cb)
+
+        self._refresh_callback = None
+        self._fetch_id = 0
+        self.nbr_of_mems = 0
+        self._ow_mem_fetch_index = 0
+        self._elem_data = ()
+        self._read_requests = {}
+        self._read_requests_lock = Lock()
+        self._write_requests = {}
+        self._write_requests_lock = Lock()
+        self._ow_mems_left_to_update = []
+
+        self._getting_count = False
+
+    def _mem_update_done(self, mem):
+        """Callback from each individual memory (only 1-wire) when reading of header/elements are done"""
+        if mem.id in self._ow_mems_left_to_update:
+            self._ow_mems_left_to_update.remove(mem.id)
+
+        logger.info(mem)
+
+        if len(self._ow_mems_left_to_update) == 0:
+            if self._refresh_callback:
+                self._refresh_callback()
+                self._refresh_callback = None
+
+    def get_mem(self, id):
+        """Fetch the memory with the supplied id"""
+        for m in self.mems:
+            if m.id == id:
+                return m
+
+        return None
+
+    def get_mems(self, type):
+        """Fetch all the memories of the supplied type"""
+        ret = ()
+        for m in self.mems:
+            if m.type == type:
+                ret += (m, )
+
+        return ret
+
+    def ow_search(self, vid=0xBC, pid=None, name=None):
+        """Search for specific memory id/name and return it"""
+        for m in self.get_mems(MemoryElement.TYPE_1W):
+            if pid and m.pid == pid or name and m.name == name:
+                return m
+
+        return None
+
+    def write(self, memory, addr, data, flush_queue=False):
+        """Write the specified data to the given memory at the given address"""
+        wreq = _WriteRequest(memory, addr, data, self.cf)
+        if not memory.id in self._write_requests:
+            self._write_requests[memory.id] = []
+
+        # Workaround until we secure the uplink and change messages for
+        # mems to non-blocking
+        self._write_requests_lock.acquire()
+        if flush_queue:
+            self._write_requests[memory.id] = self._write_requests[memory.id][:1]
+        self._write_requests[memory.id].insert(len(self._write_requests), wreq)
+        if len(self._write_requests[memory.id]) == 1:
+            wreq.start()
+        self._write_requests_lock.release()
+
+        return True
+
+
+    def read(self, memory, addr, length):
+        """Read the specified amount of bytes from the given memory at the given address"""
+        if memory.id in self._read_requests:
+            logger.warning("There is already a read operation ongoing for memory id {}".format(memory.id))
+            return False
+
+        rreq = _ReadRequest(memory, addr, length, self.cf)
+        self._read_requests[memory.id] = rreq
+
+        rreq.start()
+
+        return True
+
+    def refresh(self, refresh_done_callback):
+        """Start fetching all the detected memories"""
+        self._refresh_callback = refresh_done_callback
+        self._fetch_id = 0
+        for m in self.mems:
+            try:
+                self.mem_read_cb.remove_callback(m.new_data)
+                m.disconnect()
+            except Exception as e:
+                logger.info("Error when removing memory after update: {}".format(e))
+        self.mems = []
+
+        self.nbr_of_mems = 0
+        self._getting_count = False
+
+        logger.info("Requesting number of memories")
+        pk = CRTPPacket()
+        pk.set_header(CRTPPort.MEM, CHAN_INFO)
+        pk.data = (CMD_INFO_NBR, )
+        self.cf.send_packet(pk, expected_reply=(CMD_INFO_NBR,))
+
+    def _new_packet_cb(self, packet):
+        """Callback for newly arrived packets for the memory port"""
+        chan = packet.channel
+        cmd = packet.datal[0]
+        payload = struct.pack("B" * (len(packet.datal) - 1), *packet.datal[1:])
+        #logger.info("--------------->CHAN:{}=>{}".format(chan, struct.unpack("B"*len(payload), payload)))
+
+        if chan == CHAN_INFO:
+            if cmd == CMD_INFO_NBR:
+                self.nbr_of_mems = ord(payload[0])
+                logger.info("{} memories found".format(self.nbr_of_mems))
+
+                # Start requesting information about the memories, if there are any...
+                if self.nbr_of_mems > 0:
+                    if not self._getting_count:
+                        self._getting_count = True
+                        logger.info("Requesting first id")
+                        pk = CRTPPacket()
+                        pk.set_header(CRTPPort.MEM, CHAN_INFO)
+                        pk.data = (CMD_INFO_DETAILS, 0)
+                        self.cf.send_packet(pk, expected_reply=(CMD_INFO_DETAILS, 0))
+                else:
+                    self._refresh_callback()
+
+            if cmd == CMD_INFO_DETAILS:
+
+                # Did we get a good reply, otherwise try again:
+                if len(payload) < 5:
+                    # Workaround for 1-wire bug when memory is detected
+                    # but updating the info crashes the communication with
+                    # the 1-wire. Fail by saying we only found 1 memory (the I2C).
+                    logger.error("-------->Got good count, but no info on mem!")
+                    self.nbr_of_mems = 1
+                    if self._refresh_callback:
+                        self._refresh_callback()
+                        self._refresh_callback = None
+                    return
+
+                # Create information about a new memory
+                # Id - 1 byte
+                mem_id = ord(payload[0])
+                # Type - 1 byte
+                mem_type = ord(payload[1])
+                # Size 4 bytes (as addr)
+                mem_size = struct.unpack("I", payload[2:6])[0]
+                # Addr (only valid for 1-wire?)
+                mem_addr_raw = struct.unpack("B"*8, payload[6:14])
+                mem_addr = ""
+                for m in mem_addr_raw:
+                    mem_addr += "{:02X}".format(m)
+
+                if (not self.get_mem(mem_id)):
+                    if mem_type == MemoryElement.TYPE_1W:
+                        mem = OWElement(id=mem_id, type=mem_type, size=mem_size,
+                                        addr=mem_addr, mem_handler=self)
+                        self.mem_read_cb.add_callback(mem.new_data)
+                        self.mem_write_cb.add_callback(mem.write_done)
+                        self._ow_mems_left_to_update.append(mem.id)
+                    elif mem_type == MemoryElement.TYPE_I2C:
+                        mem = I2CElement(id=mem_id, type=mem_type, size=mem_size,
+                                         mem_handler=self)
+                        self.mem_read_cb.add_callback(mem.new_data)
+                        self.mem_write_cb.add_callback(mem.write_done)
+                    elif mem_type == MemoryElement.TYPE_DRIVER_LED:
+                        mem = LEDDriverMemory(id=mem_id, type=mem_type,
+                                              size=mem_size, mem_handler=self)
+                        logger.info(mem)
+                        self.mem_read_cb.add_callback(mem.new_data)
+                        self.mem_write_cb.add_callback(mem.write_done)
+                    else:
+                        mem = MemoryElement(id=mem_id, type=mem_type, size=mem_size, mem_handler=self)
+                        logger.info(mem)
+                    self.mems.append(mem)
+                    self.mem_added_cb.call(mem)
+                    #logger.info(mem)
+
+                    self._fetch_id = mem_id + 1
+
+                if self.nbr_of_mems - 1 >= self._fetch_id:
+                    logger.info("Requesting information about memory {}".format(self._fetch_id))
+                    pk = CRTPPacket()
+                    pk.set_header(CRTPPort.MEM, CHAN_INFO)
+                    pk.data = (CMD_INFO_DETAILS, self._fetch_id)
+                    self.cf.send_packet(pk, expected_reply=(CMD_INFO_DETAILS, self._fetch_id))
+                else:
+                    logger.info("Done getting all the memories, start reading the OWs")
+                    ows = self.get_mems(MemoryElement.TYPE_1W)
+                    # If there are any OW mems start reading them, otherwise we are done
+                    for ow_mem in self.get_mems(MemoryElement.TYPE_1W):
+                        ow_mem.update(self._mem_update_done)
+                    if len (self.get_mems(MemoryElement.TYPE_1W)) == 0:
+                        if self._refresh_callback:
+                            self._refresh_callback()
+                            self._refresh_callback = None
+
+        if chan == CHAN_WRITE:
+            id = cmd
+            (addr, status) = struct.unpack("<IB", payload[0:5])
+            logger.info("WRITE: Mem={}, addr=0x{:X}, status=0x{}".format(id, addr, status))
+            # Find the read request
+            if id in self._write_requests:
+                self._write_requests_lock.acquire()
+                wreq = self._write_requests[id][0]
+                if status == 0:
+                    if wreq.write_done(addr):
+                        #self._write_requests.pop(id, None)
+                        # Remove the first item
+                        self._write_requests[id].pop(0)
+                        self.mem_write_cb.call(wreq.mem, wreq.addr)
+
+                        # Get a new one to start (if there are any)
+                        if len(self._write_requests[id]) > 0:
+                            self._write_requests[id][0].start()
+
+                else:
+                    logger.info("Status {}: write resending...".format(status))
+                    wreq.resend()
+                self._write_requests_lock.release()
+
+        if chan == CHAN_READ:
+            id = cmd
+            (addr, status) = struct.unpack("<IB", payload[0:5])
+            data = struct.unpack("B"*len(payload[5:]), payload[5:])
+            logger.info("READ: Mem={}, addr=0x{:X}, status=0x{}, data={}".format(id, addr, status, data))
+            # Find the read request
+            if id in self._read_requests:
+                logger.info("READING: We are still interested in request for mem {}".format(id))
+                rreq = self._read_requests[id]
+                if status == 0:
+                    if rreq.add_data(addr, payload[5:]):
+                        self._read_requests.pop(id, None)
+                        self.mem_read_cb.call(rreq.mem, rreq.addr, rreq.data)
+                else:
+                    logger.info("Status {}: resending...".format(status))
+                    rreq.resend()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/mem.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d61009696ecb5f8d5e8365430732bb8265f8f88e
GIT binary patch
literal 27976
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
X-z`l)G`V;BBh!o1Uz+^L<f;D)@<<JV

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.py
new file mode 100755
index 00000000..8ce57240
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Enables reading/writing of parameter values to/from the Crazyflie.
+
+When a Crazyflie is connected it's possible to download a TableOfContent of all
+the parameters that can be written/read.
+
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Param', 'ParamTocElement']
+
+from cflib.utils.callbacks import Caller
+import struct
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+from .toc import Toc, TocFetcher
+from threading import Thread, Lock
+
+from Queue import Queue
+
+import logging
+logger = logging.getLogger(__name__)
+
+#Possible states
+IDLE = 0
+WAIT_TOC = 1
+WAIT_READ = 2
+WAIT_WRITE = 3
+
+TOC_CHANNEL = 0
+READ_CHANNEL = 1
+WRITE_CHANNEL = 2
+
+# TOC access command
+TOC_RESET = 0
+TOC_GETNEXT = 1
+TOC_GETCRC32 = 2
+
+
+# One element entry in the TOC
+class ParamTocElement:
+    """An element in the Log TOC."""
+
+    RW_ACCESS = 0
+    RO_ACCESS = 1
+
+    types = {0x08: ("uint8_t",  '<B'),
+             0x09: ("uint16_t", '<H'),
+             0x0A: ("uint32_t", '<L'),
+             0x0B: ("uint64_t", '<Q'),
+             0x00: ("int8_t",   '<b'),
+             0x01: ("int16_t",  '<h'),
+             0x02: ("int32_t",  '<i'),
+             0x03: ("int64_t",  '<q'),
+             0x05: ("FP16",     ''),
+             0x06: ("float",    '<f'),
+             0x07: ("double",   '<d')}
+
+    def __init__(self, data=None):
+        """TocElement creator. Data is the binary payload of the element."""
+        if (data):
+            strs = struct.unpack("s" * len(data[2:]), data[2:])
+            strs = ("{}" * len(strs)).format(*strs).split("\0")
+            self.group = strs[0]
+            self.name = strs[1]
+
+            self.ident = ord(data[0])
+
+            self.ctype = self.types[ord(data[1]) & 0x0F][0]
+            self.pytype = self.types[ord(data[1]) & 0x0F][1]
+
+            if ((ord(data[1]) & 0x40) != 0):
+                self.access = ParamTocElement.RO_ACCESS
+            else:
+                self.access = ParamTocElement.RW_ACCESS
+
+    def get_readable_access(self):
+        if (self.access == ParamTocElement.RO_ACCESS):
+            return "RO"
+        return "RW"
+
+
+class Param():
+    """
+    Used to read and write parameter values in the Crazyflie.
+    """
+
+    toc = Toc()
+
+    def __init__(self, crazyflie):
+        self.cf = crazyflie
+        self.param_update_callbacks = {}
+        self.group_update_callbacks = {}
+        self.all_update_callback = Caller()
+        self.param_updater = None
+
+        self.param_updater = _ParamUpdater(self.cf, self._param_updated)
+        self.param_updater.start()
+
+        self.cf.disconnected.add_callback(self.param_updater.close)
+
+        self.all_updated = Caller()
+        self._have_updated = False
+
+        self.values = {}
+
+    def request_update_of_all_params(self):
+        """Request an update of all the parameters in the TOC"""
+        for group in self.toc.toc:
+            for name in self.toc.toc[group]:
+                complete_name = "%s.%s" % (group, name)
+                self.request_param_update(complete_name)
+
+    def _check_if_all_updated(self):
+        """Check if all parameters from the TOC has at least been fetched
+        once"""
+        for g in self.toc.toc:
+            if not g in self.values:
+                return False
+            for n in self.toc.toc[g]:
+                if not n in self.values[g]:
+                    return False
+
+        return True
+
+    def _param_updated(self, pk):
+        """Callback with data for an updated parameter"""
+        var_id = pk.datal[0]
+        element = self.toc.get_element_by_id(var_id)
+        if element:
+            s = struct.unpack(element.pytype, pk.data[1:])[0]
+            s = s.__str__()
+            complete_name = "%s.%s" % (element.group, element.name)
+
+            # Save the value for synchronous access
+            if not element.group in self.values:
+                self.values[element.group] = {}
+            self.values[element.group][element.name] = s
+
+            # This will only be called once
+            if self._check_if_all_updated() and not self._have_updated:
+                self._have_updated = True
+                self.all_updated.call()
+
+            logger.debug("Updated parameter [%s]" % complete_name)
+            if complete_name in self.param_update_callbacks:
+                self.param_update_callbacks[complete_name].call(complete_name, s)
+            if element.group in self.group_update_callbacks:
+                self.group_update_callbacks[element.group].call(complete_name, s)
+            self.all_update_callback.call(complete_name, s)
+        else:
+            logger.debug("Variable id [%d] not found in TOC", var_id)
+
+    def remove_update_callback(self, group, name=None, cb=None):
+        """Remove the supplied callback for a group or a group.name"""
+        if not cb:
+            return
+
+        if not name:
+            if group in self.group_update_callbacks:
+                self.group_update_callbacks[group].remove_callback(cb)
+        else:
+            paramname = "{}.{}".format(group, name)
+            if paramname in self.param_update_callbacks:
+                self.param_update_callbacks[paramname].remove_callback(cb)
+
+    def add_update_callback(self, group=None, name=None, cb=None):
+        """
+        Add a callback for a specific parameter name. This callback will be
+        executed when a new value is read from the Crazyflie.
+        """
+        if not group and not name:
+            self.all_update_callback.add_callback(cb)
+        elif not name:
+            if not group in self.group_update_callbacks:
+                self.group_update_callbacks[group] = Caller()
+            self.group_update_callbacks[group].add_callback(cb)
+        else:
+            paramname = "{}.{}".format(group, name)
+            if not paramname in self.param_update_callbacks:
+                self.param_update_callbacks[paramname] = Caller()
+            self.param_update_callbacks[paramname].add_callback(cb)
+
+    def refresh_toc(self, refresh_done_callback, toc_cache):
+        """
+        Initiate a refresh of the parameter TOC.
+        """
+        self.toc = Toc()
+        toc_fetcher = TocFetcher(self.cf, ParamTocElement,
+                                CRTPPort.PARAM, self.toc,
+                                refresh_done_callback, toc_cache)
+        toc_fetcher.start()
+
+    def disconnected(self, uri):
+        """Disconnected callback from Crazyflie API"""
+        self.param_updater.close()
+        self._have_updated = False
+
+    def request_param_update(self, complete_name):
+        """
+        Request an update of the value for the supplied parameter.
+        """
+        self.param_updater.request_param_update(
+            self.toc.get_element_id(complete_name))
+
+    def set_value(self, complete_name, value):
+        """
+        Set the value for the supplied parameter.
+        """
+        element = self.toc.get_element_by_complete_name(complete_name)
+
+        if not element:
+            logger.warning("Cannot set value for [%s], it's not in the TOC!",
+                           complete_name)
+            raise KeyError("{} not in param TOC".format(complete_name))
+        elif element.access == ParamTocElement.RO_ACCESS:
+            logger.debug("[%s] is read only, no trying to set value", complete_name)
+            raise AttributeError("{} is read-only!".format(complete_name))
+        else:
+            varid = element.ident
+            pk = CRTPPacket()
+            pk.set_header(CRTPPort.PARAM, WRITE_CHANNEL)
+            pk.data = struct.pack('<B', varid)
+            pk.data += struct.pack(element.pytype, eval(value))
+            self.param_updater.request_param_setvalue(pk)
+
+
+class _ParamUpdater(Thread):
+    """This thread will update params through a queue to make sure that we
+    get back values"""
+    def __init__(self, cf, updated_callback):
+        """Initialize the thread"""
+        Thread.__init__(self)
+        self.setDaemon(True)
+        self.wait_lock = Lock()
+        self.cf = cf
+        self.updated_callback = updated_callback
+        self.request_queue = Queue()
+        self.cf.add_port_callback(CRTPPort.PARAM, self._new_packet_cb)
+        self._should_close = False
+        self._req_param = -1
+
+    def close(self, uri):
+        # First empty the queue from all packets
+        while not self.request_queue.empty():
+            self.request_queue.get()
+        # Then force an unlock of the mutex if we are waiting for a packet
+        # we didn't get back due to a disconnect for example.
+        try:
+            self.wait_lock.release()
+        except:
+            pass
+
+    def request_param_setvalue(self, pk):
+        """Place a param set value request on the queue. When this is sent to
+        the Crazyflie it will answer with the update param value. """
+        self.request_queue.put(pk)
+
+    def _new_packet_cb(self, pk):
+        """Callback for newly arrived packets"""
+        if pk.channel == READ_CHANNEL or pk.channel == WRITE_CHANNEL:
+            var_id = pk.datal[0]
+            if (pk.channel != TOC_CHANNEL and self._req_param == var_id
+                and pk is not None):
+                self.updated_callback(pk)
+                self._req_param = -1
+                try:
+                    self.wait_lock.release()
+                except:
+                    pass
+
+    def request_param_update(self, var_id):
+        """Place a param update request on the queue"""
+        pk = CRTPPacket()
+        pk.set_header(CRTPPort.PARAM, READ_CHANNEL)
+        pk.data = struct.pack('<B', var_id)
+        logger.debug("Requesting request to update param [%d]", var_id)
+        self.request_queue.put(pk)
+
+    def run(self):
+        while not self._should_close:
+            pk = self.request_queue.get()  # Wait for request update
+            self.wait_lock.acquire()
+            if self.cf.link:
+                self._req_param = pk.datal[0]
+                self.cf.send_packet(pk, expected_reply=(pk.datat[0:2]))
+            else:
+                self.wait_lock.release()
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/param.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2f3cf00bc16fad78ecb3bbf4bb7cdd384f04d9ac
GIT binary patch
literal 11522
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.py
new file mode 100755
index 00000000..cc6d06cc
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Used for sending control setpoints to the Crazyflie
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['PlatformService']
+
+from cflib.crtp.crtpstack import CRTPPacket, CRTPPort
+import struct
+
+
+class PlatformService():
+    """
+    Used for sending control setpoints to the Crazyflie
+    """
+
+    def __init__(self, crazyflie=None):
+        """
+        Initialize the platform object.
+        """
+        self._cf = crazyflie
+
+    def set_continous_wave(self, enabled):
+        """
+        Enable/disable the client side X-mode. When enabled this recalculates
+        the setpoints before sending them to the Crazyflie.
+        """
+        pk = CRTPPacket()
+        pk.set_header(CRTPPort.PLATFORM, 0)
+        pk.data = (0, enabled)
+        self._cf.send_packet(pk)
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/platformservice.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e0f185d94aab0d8b11573478ed5d61d8a63cfdf4
GIT binary patch
literal 1580
zcmcgr&2AGh5FT%mEe)-T0C8m{F1aKtgt+n77Ahf?5N(PmLBiVDPUCd{w4M|dl~el$
zybBM(>+k?D<D_XNZXCLjXFc|KX1;G`8oxK&KR^FERIvOS^nXRm^+{-e5lI0f0Xl#q
z+615*pqr32AaBB`33&@fEf9R#f-Hnl$fkM?bO;v!=K!BZq_kkzxy62;l4yT$Mn{vf
zj+`m9Esi2p7Fd@V=~$JvKo?;d;nYOCb^7gel3CMsx9R1MMMWnj+TOt}(g#_J<R>4R
z`ot<@|IpV7=mOg$c9Vm_AXUc(kz;$ctg*jB%a%K|+#?c(&=O9P3y=aX2ym0(5D)^H
z3_DFWeomqt(f2<v_?_aomur_@Zjv|?a0Vbf;X@C?J-&>@uih3GZJJp^&&*Uy=utVI
z8HGKc5!>}Uf}UZ6gjACS8$NH7O>mt~nL*EKhZOL_eW6`EEprpEZsXV8y>x%SJ3Mu0
z^0=y;{OV#?t60lPnq`tsVy9|bp^FvOXngJMWp-{+m|k`2OCY5s6jF96oMQ%{>B-<^
zd3iEk(guC&d=KC(fSCX!NgXEOJfL19q6AF*ooq5`vlh%kHa!<~9Z;{aGxvkUik3G;
zI?hb2t>eF^P*GnM7&)s=blA;HZF<qisVO4kZ8aHL7u80knVJ*b#;v?@1+JQPY^VwO
zVA0KVng7$kR|T?^7W6x7BokjM?p1OorzX{=PM8<Z)?k18VDEkM4!L|f#WZ19$b-P4
zWaWF?V@=ou+2t~KVho@<zS$Dr^_ijMakfP{r;bTa%o9H40fg4NxF@zmzY~(OY=|Pw
zjg)>~rKB3nGgcXil)6;3XK4MhFbTg(cvP-X-BkA`6xD}yoJ80NgN<;J1p`a&kXS-U
z=Qu6v6$Bm>iTCN=k4R6|Sb5=)s^{y!C^-9dSkD!%VqL>|Qv>>JQGpi^IUDZFFbKtZ
J@IY)d{sN4ejEDdL

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.py
new file mode 100755
index 00000000..763b3aa2
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+A generic TableOfContents module that is used to fetch, store and minipulate
+a TOC for logging or parameters.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['TocElement', 'Toc', 'TocFetcher']
+
+from cflib.crtp.crtpstack import CRTPPacket
+import struct
+
+import logging
+logger = logging.getLogger(__name__)
+
+TOC_CHANNEL = 0
+
+# Commands used when accessing the Table of Contents
+CMD_TOC_ELEMENT = 0
+CMD_TOC_INFO = 1
+
+# Possible states when receiving TOC
+IDLE = "IDLE"
+GET_TOC_INFO = "GET_TOC_INFO"
+GET_TOC_ELEMENT = "GET_TOC_ELEMENT"
+
+
+class TocElement:
+    """An element in the TOC."""
+    RW_ACCESS = 0
+    RO_ACCESS = 1
+
+    ident = 0
+    group = ""
+    name = ""
+    ctype = ""
+    pytype = ""
+    access = RO_ACCESS
+
+
+class Toc:
+    """Container for TocElements."""
+
+    def __init__(self):
+        self.toc = {}
+
+    def clear(self):
+        """Clear the TOC"""
+        self.toc = {}
+
+    def add_element(self, element):
+        """Add a new TocElement to the TOC container."""
+        try:
+            self.toc[element.group][element.name] = element
+        except KeyError:
+            self.toc[element.group] = {}
+            self.toc[element.group][element.name] = element
+
+    def get_element_by_complete_name(self, complete_name):
+        """Get a TocElement element identified by complete name from the
+        container."""
+        try:
+            return self.get_element_by_id(self.get_element_id(complete_name))
+        except ValueError:
+            # Item not found
+            return None
+
+    def get_element_id(self, complete_name):
+        """Get the TocElement element id-number of the element with the
+        supplied name."""
+        [group, name] = complete_name.split(".")
+        element = self.get_element(group, name)
+        if element:
+            return element.ident
+        else:
+            logger.warning("Unable to find variable [%s]", complete_name)
+            return None
+
+    def get_element(self, group, name):
+        """Get a TocElement element identified by name and group from the
+        container."""
+        try:
+            return self.toc[group][name]
+        except KeyError:
+            return None
+
+    def get_element_by_id(self, ident):
+        """Get a TocElement element identified by index number from the
+        container."""
+        for group in self.toc.keys():
+            for name in self.toc[group].keys():
+                if self.toc[group][name].ident == ident:
+                    return self.toc[group][name]
+        return None
+
+
+class TocFetcher:
+    """Fetches TOC entries from the Crazyflie"""
+    def __init__(self, crazyflie, element_class, port, toc_holder,
+                 finished_callback, toc_cache):
+        self.cf = crazyflie
+        self.port = port
+        self._crc = 0
+        self.requested_index = None
+        self.nbr_of_items = None
+        self.state = None
+        self.toc = toc_holder
+        self._toc_cache = toc_cache
+        self.finished_callback = finished_callback
+        self.element_class = element_class
+
+    def start(self):
+        """Initiate fetching of the TOC."""
+        logger.debug("[%d]: Start fetching...", self.port)
+        # Register callback in this class for the port
+        self.cf.add_port_callback(self.port, self._new_packet_cb)
+
+        # Request the TOC CRC
+        self.state = GET_TOC_INFO
+        pk = CRTPPacket()
+        pk.set_header(self.port, TOC_CHANNEL)
+        pk.data = (CMD_TOC_INFO, )
+        self.cf.send_packet(pk, expected_reply=(CMD_TOC_INFO,))
+
+    def _toc_fetch_finished(self):
+        """Callback for when the TOC fetching is finished"""
+        self.cf.remove_port_callback(self.port, self._new_packet_cb)
+        logger.debug("[%d]: Done!", self.port)
+        self.finished_callback()
+
+    def _new_packet_cb(self, packet):
+        """Handle a newly arrived packet"""
+        chan = packet.channel
+        if (chan != 0):
+            return
+        payload = struct.pack("B" * (len(packet.datal) - 1), *packet.datal[1:])
+
+        if (self.state == GET_TOC_INFO):
+            [self.nbr_of_items, self._crc] = struct.unpack("<BI", payload[:5])
+            logger.debug("[%d]: Got TOC CRC, %d items and crc=0x%08X",
+                         self.port, self.nbr_of_items, self._crc)
+
+            cache_data = self._toc_cache.fetch(self._crc)
+            if (cache_data):
+                self.toc.toc = cache_data
+                logger.info("TOC for port [%s] found in cache" % self.port)
+                self._toc_fetch_finished()
+            else:
+                self.state = GET_TOC_ELEMENT
+                self.requested_index = 0
+                self._request_toc_element(self.requested_index)
+
+        elif (self.state == GET_TOC_ELEMENT):
+            # Always add new element, but only request new if it's not the
+            # last one.
+            if self.requested_index != ord(payload[0]):
+                return
+            self.toc.add_element(self.element_class(payload))
+            logger.debug("Added element [%s]",
+                         self.element_class(payload).ident)
+            if (self.requested_index < (self.nbr_of_items - 1)):
+                logger.debug("[%d]: More variables, requesting index %d",
+                             self.port, self.requested_index + 1)
+                self.requested_index = self.requested_index + 1
+                self._request_toc_element(self.requested_index)
+            else:  # No more variables in TOC
+                self._toc_cache.insert(self._crc, self.toc.toc)
+                self._toc_fetch_finished()
+
+    def _request_toc_element(self, index):
+        """Request information about a specific item in the TOC"""
+        logger.debug("Requesting index %d on port %d", index, self.port)
+        pk = CRTPPacket()
+        pk.set_header(self.port, TOC_CHANNEL)
+        pk.data = (CMD_TOC_ELEMENT, index)
+        self.cf.send_packet(pk, expected_reply=(CMD_TOC_ELEMENT, index))
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toc.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..aa0e28b806abec352c54ff269edcf545f3adc1a8
GIT binary patch
literal 7071
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.py
new file mode 100755
index 00000000..855cfc62
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Access the TOC cache for reading/writing. It supports both user
+cache and dist cache.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['TocCache']
+
+import os
+import json
+from glob import glob
+
+import logging
+logger = logging.getLogger(__name__)
+
+from .log import LogTocElement  # pylint: disable=W0611
+from .param import ParamTocElement  # pylint: disable=W0611
+ 
+
+
+class TocCache():
+    """
+    Access to TOC cache. To turn of the cache functionality
+    don't supply any directories.
+    """
+    def __init__(self, ro_cache=None, rw_cache=None):
+        self._cache_files = []
+        if (ro_cache):
+            self._cache_files += glob(ro_cache + "/*.json")
+        if (rw_cache):
+            self._cache_files += glob(rw_cache + "/*.json")
+            if not os.path.exists(rw_cache):
+                os.makedirs(rw_cache)
+
+        self._rw_cache = rw_cache
+
+    def fetch(self, crc):
+        """ Try to get a hit in the cache, return None otherwise """
+        cache_data = None
+        pattern = "%08X.json" % crc
+        hit = None
+
+        for name in self._cache_files:
+            if (name.endswith(pattern)):
+                hit = name
+
+        if (hit):
+            try:
+                cache = open(hit)
+                cache_data = json.load(cache,
+                                       object_hook=self._decoder)
+                cache.close()
+            except Exception as exp:
+                logger.warning("Error while parsing cache file [%s]:%s",
+                               hit, str(exp))
+
+        return cache_data
+
+    def insert(self, crc, toc):
+        """ Save a new cache to file """
+        if self._rw_cache:
+            try:
+                filename = "%s/%08X.json" % (self._rw_cache, crc)
+                cache = open(filename, 'w')
+                cache.write(json.dumps(toc, indent=2,
+                            default=self._encoder))
+                cache.close()
+                logger.info("Saved cache to [%s]", filename)
+                self._cache_files += [filename]
+            except Exception as exp:
+                logger.warning("Could not save cache to file [%s]: %s",
+                               filename, str(exp))
+        else:
+            logger.warning("Could not save cache, no writable directory")
+
+    def _encoder(self, obj):
+        """ Encode a toc element leaf-node """
+        return {'__class__': obj.__class__.__name__,
+                'ident': obj.ident,
+                'group': obj.group,
+                'name': obj.name,
+                'ctype': obj.ctype,
+                'pytype': obj.pytype,
+                'access': obj.access}
+        raise TypeError(repr(obj) + ' is not JSON serializable')
+
+    def _decoder(self, obj):
+        """ Decode a toc element leaf-node """
+        if '__class__' in obj:
+            elem = eval(obj['__class__'])()
+            elem.ident = obj['ident']
+            elem.group = str(obj['group'])
+            elem.name = str(obj['name'])
+            elem.ctype = str(obj['ctype'])
+            elem.pytype = str(obj['pytype'])
+            elem.access = obj['access']
+            return elem
+        return obj
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crazyflie/toccache.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f7ec39db18ee58402fe076ba5710ded82671d339
GIT binary patch
literal 3809
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.py
new file mode 100755
index 00000000..f46e63cd
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""Scans and creates communication interfaces."""
+
+__author__ = 'Bitcraze AB'
+__all__ = []
+
+
+import logging
+logger = logging.getLogger(__name__)
+
+from .radiodriver import RadioDriver
+from .udpdriver import UdpDriver
+from .serialdriver import SerialDriver
+from .debugdriver import DebugDriver
+from .usbdriver import UsbDriver
+from .exceptions import WrongUriType
+
+DRIVERS = [RadioDriver, SerialDriver, UdpDriver, DebugDriver, UsbDriver]
+INSTANCES = []
+
+
+def init_drivers(enable_debug_driver=False):
+    """Initialize all the drivers."""
+    for driver in DRIVERS:
+        try:
+            if driver != DebugDriver or enable_debug_driver:
+                INSTANCES.append(driver())
+        except Exception:  # pylint: disable=W0703
+            continue
+
+
+def scan_interfaces(address = None):
+    """ Scan all the interfaces for available Crazyflies """
+    available = []
+    found = []
+    for instance in INSTANCES:
+        logger.debug("Scanning: %s", instance)
+        try:
+            found = instance.scan_interface(address)
+            available += found
+        except Exception:
+            raise
+    return available
+
+
+def get_interfaces_status():
+    """Get the status of all the interfaces"""
+    status = {}
+    for instance in INSTANCES:
+        try:
+            status[instance.get_name()] = instance.get_status()
+        except Exception:
+            raise
+    return status
+
+
+def get_link_driver(uri, link_quality_callback=None, link_error_callback=None):
+    """Return the link driver for the given URI. Returns None if no driver
+    was found for the URI or the URI was not well formatted for the matching
+    driver."""
+    for instance in INSTANCES:
+        try:
+            instance.connect(uri, link_quality_callback, link_error_callback)
+            return instance
+        except WrongUriType:
+            continue
+
+    return None
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..69bc9be5a104d24a54da83c47d8b7f4d927a029b
GIT binary patch
literal 2559
zcmcgt>uwuG6h6C~t7AK%X`!SlS|N~(P-B6Fil8cObE$%&{8%TXGN9FFckFDk7iVYO
z#wh(m@)$e^55Y6=7CZoa=gh`UCHPr3o|Bm~w=?IP?;G#0R^#^{|9BBo{mtV46BPX?
zOhVL$b*S&q5OtRl{5;A$>U*R-I+-CgOP3fhN68E&vy{zKzCis2a=1N5*&^i)>NjkA
zp0Xv%o78XG_5x)s%G=a$+x8-5%apHBf5o;Nl)Xp!E$ZJQM=g+Ql4_7@lUkwSEcI6<
zs@tUQP_jf<2;+SYFKJ5sE+s9xLf9XQ6aF?`xzu0dX0Nl%r~DkI7e|HmqayL+N<~I#
zKQ8lpT%>Vi(z5W=!l-Hx#Y(U1C5(TTnz)L7QU23s>EHPFQU~4+8^irUl%(ZmmA+J!
zbuPkrkc@7+nsD`0l}6c3FVEgoN8{m5r_9zzH(fmYuqun;L6z=LMk?mLU6eVL4HSI<
zbG}Cg>;1e<uN|6vMi&klhfY1xAJYX<^%WVHPCWKs5`*XzSoUn5X3PwoHmKSq$8pfc
z?*+;sE<&%v+vs~RJ4I>`e~LszS>~H#<tK7zy}sAsYh>nOHiMlXwt`;Zv4fAYv)9{y
zy0@{_GaPI*8mS_Yj;-@pjX2@5g;_<fDx#xIg$bu5w2R9uTkT*3iO~US-QzM>T}Eay
z$Wqnae6Stu?mp;Ev{89?G}7TK-PKjxO~L^J4&mwQxJpMxcVo;r!dx@zhGB|>h2i>W
zVmQyzst@)B+M1o@wj9s7<$U0@97+BReADDBG(Z<*ovlmffd%4wdhIa;Cm@kS7am=@
z<V@~!jbRWBFdTSXxbzUy56@PoVEn{j{7MAZ3zS2Qs=@dY+xdKnr#QU1vHqZ}{ODzr
zW}HcX<2pOqmviu}A}xmB_@8RQEMUwe$5}ZXs>(2sl7G`iO=CRrQ}ZEUJUe`JfjUZ(
zN@*=%PPdc=2IUwaFJNF=XcHB&diU4~z26E~f6FJh4MXmnGY9Ba0OGRKMs3Lv1$>J`
zyBse71e3nA06r8ZtuaC;LK<Mkb(abB=<uusv^=5C#x+_kpw$9epu32H{sgN&S4Q}P
zT$r);%fY`OCwaASG%#hTOlXHo9=TaWxv~&&*aDgz5iob&eTMJUVNLIYt|Qz*hvw_T
zd1j)l*$lG=yhW7faGXD}x&4~SF}WvX00AVipuwd_4*1cty5vf7AOEK={e}etWyVz@
z`OVVev=${H0&CO|x?K1N!OpsGM{0ksER>%P{GzM}H++1rB8^G7xYMba<bzJU3RihR
zQ5yf1g8YGJd1Q=wGaAkK7~GP@tbjlm`TQ1DW?Ebpg^Eoea>-pBie(fhYh0y<v&`oR
z&&Cj2GYMme*HILoib6_{s;aV@`u`{NnO}ShB?5cAeYh;{GmQGGv%t8!9eyoDA;K^z
z<1jRQp~Em5n`6Ws)?KX0w$9Rn#EFn#i(RWkEFYm<`Y;Syyd*!D6+gCidwvA$8#7Ku
zwM!mlR%|VgZS57l@N7}G+Q|u*Eywz(b}=Fnth#=iv_KWG)LRr8x$P*^D&Sp$H8w0U
y!Cf{V!Mq`&)ZW!w#p!vJm&rI&kME<deV92GI<M(0yDJd8`p><?zDBdXxbzQgH(aCu

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.py
new file mode 100755
index 00000000..b5364f3b
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+CRTP Driver main class.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['CRTPDriver']
+
+
+class CRTPDriver:
+    """ CTRP Driver main class
+
+    This class in inherited by all the CRTP link drivers.
+    """
+
+    def __init__(self):
+        """Driver constructor. Throw an exception if the driver is unable to
+        open the URI
+        """
+        self.needs_resending = True
+
+    def connect(self, uri, link_quality_callback, link_error_callback):
+        """Connect the driver to a specified URI
+
+        @param uri Uri of the link to open
+        @param link_quality_callback Callback to report link quality in percent
+        @param link_error_callback Callback to report errors (will result in
+               disconnection)
+        """
+
+    def send_packet(self, pk):
+        """Send a CRTP packet"""
+
+    def receive_packet(self, wait=0):
+        """Receive a CRTP packet.
+
+        @param wait The time to wait for a packet in second. -1 means forever
+
+        @return One CRTP packet or None if no packet has been received.
+        """
+
+    def get_status(self):
+        """
+        Return a status string from the interface.
+        """
+
+    def get_name(self):
+        """
+        Return a human readable name of the interface.
+        """
+
+    def scan_interface(self, address = None):
+        """
+        Scan interface for available Crazyflie quadcopters and return a list
+        with them.
+        """
+
+    def enum(self):
+        """Enumerate, and return a list, of the available link URI on this
+        system
+        """
+
+    def get_help(self):
+        """return the help message on how to form the URI for this driver
+        None means no help
+        """
+
+    def close(self):
+        """Close the link"""
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpdriver.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b3a490ee42b9334900c9ee9cb7b6c67a8c54c7f4
GIT binary patch
literal 3669
zcmd5<-EJc_6n2s}ZPJznR*S?HzCjA2ZM3&&L1nXx1X^{`v=vY;EXQ^ltMiBL*;2$E
z`%Lf-ybdqGcRZ8HmbB0t0+oDn#((F0=g+6TKlX;de*gQoiZy>d{2pQWeGtvqIcSHS
zcbM+73ou<`^d{3i_L{L@82j$L$Mgm}8TIMZ`yj(fa{4uY=IkrucqVPmRVuwd9Qqxs
zKD9wP`IF(FKMg~C)8=|_t3#KzVfY-xKnF+%h1?DaC4t?@Y&2?|Lm%*)>@_s(Gu@A?
zTTE|7W}E3<rnlqjkm*63++lhc87%EYW{)K&qdk)Q4G5o{Cbys%4mti#=hoM#IN;fQ
zZk!E9^XWC0Y0AUg@Mwh8=9gSYO8`HeR2|$+gFsF&JV^|U{mhsE&JHX|OJ0C9HdICK
zgR4|1+#y7};)=_hn;(@agDoI`7A4klI5ewrIZX`@MHES5i_+wA(^>LmJ9!)>g$+nL
zb(IMZz~shgFP!lv*EYWxk*aZrH|cDI4;G0(1vj1-nHiHeuV<+><7Wq7$QLgTPOg10
z*|;pdxbkD~)L4rd%px!|_R85Z__4x?Db9v6l3Mq}@;YqeVj*m9gAgBs_hb#Wp-4*_
zq)@aQeJsuO2=kL7&y5Oi8Y2{3a$g!{XBPe;^ez0OQaYLO%2|GfQPft9F2gah*k8BO
zn)soTsSVdc0n${eOFmgJIOR-PxWMBj%|?ol(l}-EaO-`>xx)SJZE=U^qbrNpf@`WY
z;L`Ro@-P0h^#~L)0Ak{!cJ;V+8d)ppfdnGJ93J8KRzTt5V&$f4>+t>-L^miNgY_6p
zQ3TMq!}afC=>*{lPsgMy;S>`Z+;pzSO<Z}o4iHr4D1St)$tU!Q!7Pz~Lh~E;Bt{uj
z&zpb_S0eXH+JJh1M7J4bJC75y!r@>onqtoz7*rqfgU38GGWQ%C3`+AG*Etg^m-Cl-
zQ?{DJxahdR9O_G6EN+}j&!+~}#MMgbx~@?Ku9%#Hj&xdw(cglsMy1B?pJJvCAPStt
zn9%Ia+VKQaZN4P6EhP6rhRWl|p$_tyE3#NvY#xl8No7`WlY|&*>wrs7=Y<KP*77-l
z!eH(5a!0NlVb3bmd6nToaYpL+ZsZc-)Re>j3EXYyO>jxIufX4{Fi^g=U{4evwrD5`
zxO^pT8WB$xZx~0>)~YDsV~<Bx!zeK|Buctp*6%AD<`fOtN)RmT4V5yfHR`orM?g^g
zdtk)V*#zjz)=rDqzauE9-Fco@nQ=0h{Z-iei(pt9EIx2}O*z_+fM%#QvyYE|``MI)
zwWbCow*j29`{JKsW8-J)+~y`NQ7XNc7f6Gg&(Uw8xFTZe3P7+ad<b{b+%54^y{yYD
zN+#X3T&kmBt)^I{4Jm?7{(d!rB59Hq9#0CaL7x6EytoN16lp<?a7_2(5oy*mh*SuK
zi2p$dkrld1>63C!2wf;4lGyQ(0TQZ}$u1G<wUc-Yt<vL`yiHT@5TQOIc?hyh*U0^A
zy>0?o>_ZIy9!P(q-yQS^gF$b1d$2j!-0c%>Odr9}t_HEJ!W>=W0x@;hB3`#H-J=_x
T)Q11;1HwT6|Mk25&Y<@v-jb{`

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.py
new file mode 100755
index 00000000..70261e25
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+CRTP packet and ports.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['CRTPPort', 'CRTPPacket']
+
+
+import struct
+
+
+class CRTPPort:
+    """
+    Lists the available ports for the CRTP.
+    """
+    CONSOLE = 0x00
+    PARAM = 0x02
+    COMMANDER = 0x03
+    MEM = 0x04
+    LOGGING = 0x05
+    STABILIZER = 0x06
+    DEBUGDRIVER = 0x0E
+    LINKCTRL = 0x0F
+    ALL = 0xFF
+
+
+class CRTPPacket(object):
+    """
+    A packet that can be sent via the CRTP.
+    """
+
+    def __init__(self, header=0, data=None):
+        """
+        Create an empty packet with default values.
+        """
+        self.size = 0
+        self._data = ""
+        # The two bits in position 3 and 4 needs to be set for legacy
+        # support of the bootloader
+        self.header = header | 0x3 << 2
+        self._port = (header & 0xF0) >> 4
+        self._channel = header & 0x03
+        if data:
+            self._set_data(data)
+
+    def _get_channel(self):
+        """Get the packet channel"""
+        return self._channel
+
+    def _set_channel(self, channel):
+        """Set the packet channel"""
+        self._channel = channel
+        self._update_header()
+
+    def _get_port(self):
+        """Get the packet port"""
+        return self._port
+
+    def _set_port(self, port):
+        """Set the packet port"""
+        self._port = port
+        self._update_header()
+
+    def get_header(self):
+        """Get the header"""
+        self._update_header()
+        return self.header
+
+    def set_header(self, port, channel):
+        """
+        Set the port and channel for this packet.
+        """
+        self._port = port
+        self.channel = channel
+        self._update_header()
+
+    def _update_header(self):
+        """Update the header with the port/channel values"""
+        # The two bits in position 3 and 4 needs to be set for legacy
+        # support of the bootloader
+        self.header = ((self._port & 0x0f) << 4 | 3 << 2 |
+                       (self.channel & 0x03))
+
+    #Some python madness to access different format of the data
+    def _get_data(self):
+        """Get the packet data"""
+        return self._data
+
+    def _set_data(self, data):
+        """Set the packet data"""
+        if type(data) == str:
+            self._data = data
+        elif type(data) == list or type(data) == tuple:
+            if len(data) == 1:
+                self._data = struct.pack("B", data[0])
+            elif len(data) > 1:
+                self._data = struct.pack("B" * len(data), *data)
+            else:
+                self._data = ""
+        else:
+            raise Exception("Data shall be of str, tupple or list type")
+
+    def _get_data_l(self):
+        """Get the data in the packet as a list"""
+        return list(self._get_data_t())
+
+    def _get_data_t(self):
+        """Get the data in the packet as a tuple"""
+        return struct.unpack("B" * len(self._data), self._data)
+
+    def __str__(self):
+        """Get a string representation of the packet"""
+        return "{}:{} {}".format(self._port, self.channel, self.datat)
+
+    data = property(_get_data, _set_data)
+    datal = property(_get_data_l, _set_data)
+    datat = property(_get_data_t, _set_data)
+    datas = property(_get_data, _set_data)
+    port = property(_get_port, _set_port)
+    channel = property(_get_channel, _set_channel)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/crtpstack.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3eb90eb5ed7b7874acca4dbdf74ef08c6117a12f
GIT binary patch
literal 5176
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
CDR(pg

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.py
new file mode 100755
index 00000000..80aa46f4
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.py
@@ -0,0 +1,858 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Fake link driver used to debug the UI without using the Crazyflie.
+
+The operation of this driver can be controlled in two ways, either by
+connecting to different URIs or by sending messages to the DebugDriver port
+though CRTP once connected.
+
+For normal connections a console thread is also started that will send
+generated console output via CRTP.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['DebugDriver']
+
+from threading import Thread
+from .crtpdriver import CRTPDriver
+from .crtpstack import CRTPPacket, CRTPPort
+from .exceptions import WrongUriType
+import Queue
+import re
+import time
+import struct
+from datetime import datetime
+from cflib.crazyflie.log import LogTocElement
+from cflib.crazyflie.param import ParamTocElement
+import random
+import string
+import errno
+
+import logging
+logger = logging.getLogger(__name__)
+
+# This setup is used to debug raw memory logging
+memlogging = {0x01: {"min": 0, "max": 255, "mod": 1, "vartype": 1},
+              0x02: {"min": 0, "max": 65000, "mod": 100, "vartype": 2},
+              0x03: {"min": 0, "max": 100000, "mod": 1000, "vartype": 3},
+              0x04: {"min":-100, "max": 100, "mod": 1, "vartype": 4},
+              0x05: {"min":-10000, "max": 10000, "mod": 2000, "vartype": 5},
+              0x06: {"min":-50000, "max": 50000, "mod": 1000, "vartype": 6},
+              0x07: {"min": 0, "max": 255, "mod": 1, "vartype": 1}}
+
+class FakeMemory:
+
+    TYPE_I2C = 0
+    TYPE_1W = 1
+
+    def __init__(self, type, size, addr, data=None):
+        self.type = type
+        self.size = size
+        self.addr = addr
+        self.data = [0] * size
+        if data:
+            for i in range(len(data)):
+                self.data[i] = data[i]
+
+    def erase(self):
+        self.data = [0] * self.size
+
+class DebugDriver (CRTPDriver):
+    """ Debug driver used for debugging UI/communication without using a
+    Crazyflie"""
+    def __init__(self):
+        self.fakeLoggingThreads = []
+        self._fake_mems = []
+        # Fill up the fake logging TOC with values and data
+        self.fakeLogToc = []
+        self.fakeLogToc.append({"varid": 0, "vartype": 5, "vargroup": "imu",
+                                "varname": "gyro_x", "min":-10000,
+                                "max": 10000, "mod": 1000})
+        self.fakeLogToc.append({"varid": 1, "vartype": 5, "vargroup": "imu",
+                                "varname": "gyro_y", "min":-10000,
+                                "max": 10000, "mod": 150})
+        self.fakeLogToc.append({"varid": 2, "vartype": 5, "vargroup": "imu",
+                                "varname": "gyro_z", "min":-10000,
+                                "max": 10000, "mod": 200})
+        self.fakeLogToc.append({"varid": 3, "vartype": 5, "vargroup": "imu",
+                                "varname": "acc_x", "min":-1000,
+                                "max": 1000, "mod": 15})
+        self.fakeLogToc.append({"varid": 4, "vartype": 5, "vargroup": "imu",
+                                "varname": "acc_y", "min":-1000,
+                                "max": 1000, "mod": 10})
+        self.fakeLogToc.append({"varid": 5, "vartype": 5, "vargroup": "imu",
+                                "varname": "acc_z", "min":-1000,
+                                "max": 1000, "mod": 20})
+        self.fakeLogToc.append({"varid": 6, "vartype": 7,
+                                "vargroup": "stabilizer", "varname": "roll",
+                                "min":-90, "max": 90, "mod": 2})
+        self.fakeLogToc.append({"varid": 7, "vartype": 7,
+                                "vargroup": "stabilizer", "varname": "pitch",
+                                "min":-90, "max": 90, "mod": 1.5})
+        self.fakeLogToc.append({"varid": 8, "vartype": 7,
+                                "vargroup": "stabilizer", "varname": "yaw",
+                                "min":-90, "max": 90, "mod": 2.5})
+        self.fakeLogToc.append({"varid": 9, "vartype": 7, "vargroup": "pm",
+                                "varname": "vbat", "min": 3.0,
+                                "max": 4.2, "mod": 0.1})
+        self.fakeLogToc.append({"varid": 10, "vartype": 6, "vargroup": "motor",
+                                "varname": "m1", "min": 0, "max": 65000,
+                                "mod": 1000})
+        self.fakeLogToc.append({"varid": 11, "vartype": 6, "vargroup": "motor",
+                                "varname": "m2", "min": 0, "max": 65000,
+                                "mod": 1000})
+        self.fakeLogToc.append({"varid": 12, "vartype": 6, "vargroup": "motor",
+                                "varname": "m3", "min": 0, "max": 65000,
+                                "mod": 1000})
+        self.fakeLogToc.append({"varid": 13, "vartype": 6, "vargroup": "motor",
+                                "varname": "m4", "min": 0, "max": 65000,
+                                "mod": 1000})
+        self.fakeLogToc.append({"varid": 14, "vartype": 2,
+                                "vargroup": "stabilizer", "varname": "thrust",
+                                "min": 0, "max": 65000, "mod": 1000})
+        self.fakeLogToc.append({"varid": 15, "vartype": 7,
+                                "vargroup": "baro", "varname": "asl",
+                                "min": 540, "max": 545, "mod": 0.5})
+        self.fakeLogToc.append({"varid": 16, "vartype": 7,
+                                "vargroup": "baro", "varname": "aslRaw",
+                                "min": 540, "max": 545, "mod": 1.0})
+        self.fakeLogToc.append({"varid": 17, "vartype": 7,
+                                "vargroup": "baro", "varname": "aslLong",
+                                "min": 540, "max": 545, "mod": 0.5})
+        self.fakeLogToc.append({"varid": 18, "vartype": 7,
+                                "vargroup": "baro", "varname": "temp",
+                                "min": 26, "max": 38, "mod": 1.0})
+        self.fakeLogToc.append({"varid": 19, "vartype": 7,
+                                "vargroup": "altHold", "varname": "target",
+                                "min": 542, "max": 543, "mod": 0.1})
+        self.fakeLogToc.append({"varid": 20, "vartype": 6,
+                                "vargroup": "gps", "varname": "lat",
+                                "min": 556112190, "max": 556112790,
+                                "mod": 10})
+        self.fakeLogToc.append({"varid": 21, "vartype": 6,
+                                "vargroup": "gps", "varname": "lon",
+                                "min": 129945110, "max": 129945710,
+                                "mod": 10})
+        self.fakeLogToc.append({"varid": 22, "vartype": 6,
+                                "vargroup": "gps", "varname": "hMSL",
+                                "min": 0, "max": 100000,
+                                "mod": 1000})
+        self.fakeLogToc.append({"varid": 23, "vartype": 6,
+                                "vargroup": "gps", "varname": "heading",
+                                "min": -10000000, "max": 10000000,
+                                "mod": 100000})
+        self.fakeLogToc.append({"varid": 24, "vartype": 6,
+                                "vargroup": "gps", "varname": "gSpeed",
+                                "min": 0, "max": 1000,
+                                "mod": 100})
+        self.fakeLogToc.append({"varid": 25, "vartype": 3,
+                                "vargroup": "gps", "varname": "hAcc",
+                                "min": 0, "max": 5000,
+                                "mod": 100})
+        self.fakeLogToc.append({"varid": 26, "vartype": 1,
+                                "vargroup": "gps", "varname": "fixType",
+                                "min": 0, "max": 5,
+                                "mod": 1})
+
+
+        # Fill up the fake logging TOC with values and data
+        self.fakeParamToc = []
+        self.fakeParamToc.append({"varid": 0, "vartype": 0x08,
+                                  "vargroup": "blah", "varname": "p",
+                                  "writable": True, "value": 100})
+        self.fakeParamToc.append({"varid": 1, "vartype": 0x0A,
+                                  "vargroup": "info", "varname": "cid",
+                                  "writable": False, "value": 1234})
+        self.fakeParamToc.append({"varid": 2, "vartype": 0x06,
+                                  "vargroup": "rpid", "varname": "prp",
+                                  "writable": True, "value": 1.5})
+        self.fakeParamToc.append({"varid": 3, "vartype": 0x06,
+                                  "vargroup": "rpid", "varname": "pyaw",
+                                  "writable": True, "value": 2.5})
+        self.fakeParamToc.append({"varid": 4, "vartype": 0x06,
+                                  "vargroup": "rpid", "varname": "irp",
+                                  "writable": True, "value": 3.5})
+        self.fakeParamToc.append({"varid": 5, "vartype": 0x06,
+                                  "vargroup": "rpid", "varname": "iyaw",
+                                  "writable": True, "value": 4.5})
+        self.fakeParamToc.append({"varid": 6, "vartype": 0x06,
+                                  "vargroup": "rpid", "varname": "drp",
+                                  "writable": True, "value": 5.5})
+        self.fakeParamToc.append({"varid": 7, "vartype": 0x06,
+                                  "vargroup": "rpid", "varname": "dyaw",
+                                  "writable": True, "value": 6.5})
+        self.fakeParamToc.append({"varid": 8, "vartype": 0x06,
+                                  "vargroup": "apid", "varname": "prp",
+                                  "writable": True, "value": 7.5})
+        self.fakeParamToc.append({"varid": 9, "vartype": 0x06,
+                                  "vargroup": "apid", "varname": "pyaw",
+                                  "writable": True, "value": 8.5})
+        self.fakeParamToc.append({"varid": 10, "vartype": 0x06,
+                                  "vargroup": "apid", "varname": "irp",
+                                  "writable": True, "value": 9.5})
+        self.fakeParamToc.append({"varid": 11, "vartype": 0x06,
+                                  "vargroup": "apid", "varname": "iyaw",
+                                  "writable": True, "value": 10.5})
+        self.fakeParamToc.append({"varid": 12, "vartype": 0x06,
+                                  "vargroup": "apid", "varname": "drp",
+                                  "writable": True, "value": 11.5})
+        self.fakeParamToc.append({"varid": 13, "vartype": 0x06,
+                                  "vargroup": "apid", "varname": "dyaw",
+                                  "writable": True, "value": 12.5})
+        self.fakeParamToc.append({"varid": 14, "vartype": 0x08,
+                                  "vargroup": "flightctrl",
+                                  "varname": "xmode", "writable": True,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 15, "vartype": 0x08,
+                                  "vargroup": "flightctrl",
+                                  "varname": "ratepid", "writable": True,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 16, "vartype": 0x08,
+                                  "vargroup": "imu_sensors",
+                                  "varname": "HMC5883L", "writable": False,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 17, "vartype": 0x08,
+                                  "vargroup": "imu_sensors",
+                                  "varname": "MS5611", "writable": False,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 18, "vartype": 0x0A,
+                                  "vargroup": "firmware",
+                                  "varname": "revision0", "writable": False,
+                                  "value": 0xdeb})
+        self.fakeParamToc.append({"varid": 19, "vartype": 0x09,
+                                  "vargroup": "firmware",
+                                  "varname": "revision1", "writable": False,
+                                  "value": 0x99})
+        self.fakeParamToc.append({"varid": 20, "vartype": 0x08,
+                                  "vargroup": "firmware",
+                                  "varname": "modified", "writable": False,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 21, "vartype": 0x08,
+                                  "vargroup": "imu_tests",
+                                  "varname": "MPU6050", "writable": False,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 22, "vartype": 0x08,
+                                  "vargroup": "imu_tests",
+                                  "varname": "HMC5883L", "writable": False,
+                                  "value": 1})
+        self.fakeParamToc.append({"varid": 23, "vartype": 0x08,
+                                  "vargroup": "imu_tests",
+                                  "varname": "MS5611", "writable": False,
+                                  "value": 1})
+
+        self.fakeflash = {}
+        self._random_answer_delay = True
+        self.queue = Queue.Queue()
+        self._packet_handler = _PacketHandlingThread(self.queue,
+                                                     self.fakeLogToc,
+                                                     self.fakeParamToc,
+                                                     self._fake_mems)
+        self._packet_handler.start()
+
+    def scan_interface(self, address):
+        return [["debug://0/0", "Normal connection"],
+                ["debug://0/1", "Fail to connect"],
+                ["debug://0/2", "Incomplete log TOC download"],
+                ["debug://0/3", "Insert random delays on replies"],
+                ["debug://0/4", "Insert random delays on replies and random TOC CRCs"],
+                ["debug://0/5", "Normal but random TOC CRCs"],
+                ["debug://0/6", "Normal but empty I2C and OW mems"]]
+
+
+    def get_status(self):
+        return "Ok"
+
+    def get_name(self):
+        return "debug"
+
+    def connect(self, uri, linkQualityCallback, linkErrorCallback):
+
+        if not re.search("^debug://", uri):
+            raise WrongUriType("Not a debug URI")
+
+        self._packet_handler.linkErrorCallback = linkErrorCallback
+        self._packet_handler.linkQualityCallback = linkQualityCallback
+
+        # Debug-options for this driver that
+        # is set by using different connection URIs
+        self._packet_handler.inhibitAnswers = False
+        self._packet_handler.doIncompleteLogTOC = False
+        self._packet_handler.bootloader = False
+        self._packet_handler._random_answer_delay = False
+        self._packet_handler._random_toc_crcs = False
+
+        if (re.search("^debug://.*/1\Z", uri)):
+            self._packet_handler.inhibitAnswers = True
+        if (re.search("^debug://.*/110\Z", uri)):
+            self._packet_handler.bootloader = True
+        if (re.search("^debug://.*/2\Z", uri)):
+            self._packet_handler.doIncompleteLogTOC = True
+        if (re.search("^debug://.*/3\Z", uri)):
+            self._packet_handler._random_answer_delay = True
+        if (re.search("^debug://.*/4\Z", uri)):
+            self._packet_handler._random_answer_delay = True
+            self._packet_handler._random_toc_crcs = True
+        if (re.search("^debug://.*/5\Z", uri)):
+            self._packet_handler._random_toc_crcs = True
+
+        if len(self._fake_mems) == 0:
+            # Insert some data here
+            self._fake_mems.append(FakeMemory(type=0, size=100, addr=0))
+            self._fake_mems.append(FakeMemory(type=1, size=112, addr=0x1234567890ABCDEF,
+                                              data=[0xeb, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x44, 0x00, 0x0e,
+                                                    0x01, 0x09, 0x62, 0x63, 0x4c, 0x65, 0x64, 0x52, 0x69, 0x6e,
+                                                    0x67, 0x02, 0x01, 0x62, 0x55]))
+            #self._fake_mems.append(FakeMemory(type=1, size=112, addr=0xFEDCBA0987654321,
+            #                                  data=[0xeb, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x44, 0x00, 0x44,
+            #                                        0x01, 0x2e, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+            #                                        0x61, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x6c,
+            #                                        0x6f, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20,
+            #                                        0x74, 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x6a, 0x75, 0x73,
+            #                                        0x74, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x21, 0x02, 0x12,
+            #                                        0x53, 0x75, 0x70, 0x65, 0x72, 0x20, 0x72, 0x65, 0x76, 0x69,
+            #                                        0x73, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x35, 0x35, 0xc8]))
+
+        if (re.search("^debug://.*/6\Z", uri)):
+            logger.info("------------->Erasing memories on connect")
+            for m in self._fake_mems:
+                m.erase()
+
+        self.fakeConsoleThread = None
+
+        if (not self._packet_handler.inhibitAnswers and not self._packet_handler.bootloader):
+            self.fakeConsoleThread = FakeConsoleThread(self.queue)
+            self.fakeConsoleThread.start()
+
+        if (self._packet_handler.linkQualityCallback is not None):
+            self._packet_handler.linkQualityCallback(0)
+
+    def receive_packet(self, time=0):
+        if time == 0:
+            try:
+                return self.queue.get(False)
+            except Queue.Empty:
+                return None
+        elif time < 0:
+            try:
+                return self.queue.get(True)
+            except Queue.Empty:
+                return None
+        else:
+            try:
+                return self.queue.get(True, time)
+            except Queue.Empty:
+                return None
+
+    def send_packet(self, pk):
+        self._packet_handler.handle_packet(pk)
+
+    def close(self):
+        logger.info("Closing debugdriver")
+        for f in self._packet_handler.fakeLoggingThreads:
+            f.stop()
+        if self.fakeConsoleThread:
+            self.fakeConsoleThread.stop()
+
+
+class _PacketHandlingThread(Thread):
+    """Thread for handling packets asynchronously"""
+    def __init__(self, out_queue, fake_log_toc, fake_param_toc, fake_mems):
+        Thread.__init__(self)
+        self.setDaemon(True)
+        self.queue = out_queue
+        self.fakeLogToc = fake_log_toc
+        self.fakeParamToc = fake_param_toc
+        self._fake_mems = fake_mems
+        self._in_queue = Queue.Queue()
+
+        self.inhibitAnswers = False
+        self.doIncompleteLogTOC = False
+        self.bootloader = False
+        self._random_answer_delay = False
+        self._random_toc_crcs = False
+
+        self.linkErrorCallback = None
+        self.linkQualityCallback = None
+        random.seed(None)
+        self.fakeLoggingThreads = []
+
+        self._added_blocks = []
+
+        self.nowAnswerCounter = 4
+
+    def handle_packet(self, pk):
+        self._in_queue.put(pk)
+
+    def run(self):
+        while (True):
+            pk = self._in_queue.get(True)
+            if (self.inhibitAnswers):
+                self.nowAnswerCounter = self.nowAnswerCounter - 1
+                logger.debug("Not answering with any data, will send link errori"
+                             " in %d retries", self.nowAnswerCounter)
+                if (self.nowAnswerCounter == 0):
+                    self.linkErrorCallback("Nothing is answering, and it"
+                                           " shouldn't")
+            else:
+                if (pk.port == 0xFF):
+                    self._handle_bootloader(pk)
+                elif (pk.port == CRTPPort.DEBUGDRIVER):
+                    self._handle_debugmessage(pk)
+                elif (pk.port == CRTPPort.COMMANDER):
+                    pass
+                elif (pk.port == CRTPPort.LOGGING):
+                    self._handle_logging(pk)
+                elif (pk.port == CRTPPort.PARAM):
+                    self.handleParam(pk)
+                elif (pk.port == CRTPPort.MEM):
+                    self._handle_mem_access(pk)
+                else:
+                    logger.warning("Not handling incoming packets on port [%d]",
+                               pk.port)
+
+    def _handle_mem_access(self, pk):
+        chan = pk.channel
+        cmd = struct.unpack("B", pk.data[0])[0]
+        payload = struct.pack("B" * (len(pk.datal) - 1), *pk.datal[1:])
+
+        if chan == 0:  # Info channel
+            p_out = CRTPPacket()
+            p_out.set_header(CRTPPort.MEM, 0)
+            if cmd == 1:  # Request number of memories
+                p_out.data = (1, len(self._fake_mems))
+            if cmd == 2:
+                id = ord(payload[0])
+                logger.info("Getting mem {}".format(id))
+                m = self._fake_mems[id]
+                p_out.data = struct.pack('<BBBIQ', 2, id, m.type, m.size, m.addr)
+            self._send_packet(p_out)
+
+        if chan == 1:  # Read channel
+            id = cmd
+            addr = struct.unpack("I", payload[0:4])[0]
+            length = ord(payload[4])
+            status = 0
+            logger.info("MEM: Read {}bytes at 0x{:X} from memory {}".format(length, addr, id))
+            m = self._fake_mems[id]
+            p_out = CRTPPacket()
+            p_out.set_header(CRTPPort.MEM, 1)
+            p_out.data = struct.pack("<BIB", id, addr, status)
+            p_out.data += struct.pack("B"*length, *m.data[addr:addr+length])
+            self._send_packet(p_out)
+
+        if chan == 2:  # Write channel
+            id = cmd
+            addr = struct.unpack("I", payload[0:4])[0]
+            data = payload[4:]
+            logger.info("MEM: Write {}bytes at 0x{:X} to memory {}".format(len(data), addr, id))
+            m = self._fake_mems[id]
+
+            for i in range(len(data)):
+                m.data[addr+i] = ord(data[i])
+
+            status = 0
+
+            p_out = CRTPPacket()
+            p_out.set_header(CRTPPort.MEM, 2)
+            p_out.data = struct.pack("<BIB", id, addr, status)
+            self._send_packet(p_out)
+
+    def _handle_bootloader(self, pk):
+        cmd = pk.datal[1]
+        if (cmd == 0x10):  # Request info about copter
+            p = CRTPPacket()
+            p.set_header(0xFF, 0xFF)
+            pageSize = 1024
+            buffPages = 10
+            flashPages = 100
+            flashStart = 1
+            p.data = struct.pack('<BBHHHH', 0xFF, 0x10, pageSize, buffPages,
+                                 flashPages, flashStart)
+            p.data += struct.pack('B' * 12, 0xA0A1A2A3A4A5)
+            self._send_packet(p)
+            logging.info("Bootloader: Sending info back info")
+        elif (cmd == 0x14):  # Upload buffer
+            [page, addr] = struct.unpack('<HH', p.data[0:4])
+        elif (cmd == 0x18):  # Flash page
+            p = CRTPPacket()
+            p.set_header(0xFF, 0xFF)
+            p.data = struct.pack('<BBH', 0xFF, 0x18, 1)
+            self._send_packet(p)
+        elif (cmd == 0xFF):  # Reset to firmware
+            logger.info("Bootloader: Got reset command")
+        else:
+            logger.warning("Bootloader: Unknown command 0x%02X", cmd)
+
+    def _handle_debugmessage(self, pk):
+        if (pk.channel == 0):
+            cmd = struct.unpack("B", pk.data[0])[0]
+            if (cmd == 0):  # Fake link quality
+                newLinkQuality = struct.unpack("B", pk.data[1])[0]
+                self.linkQualityCallback(newLinkQuality)
+            elif (cmd == 1):
+                self.linkErrorCallback("DebugDriver was forced to disconnect!")
+            else:
+                logger.warning("Debug port: Not handling cmd=%d on channel 0",
+                               cmd)
+        else:
+            logger.warning("Debug port: Not handling channel=%d",
+                           pk.channel)
+
+    def _handle_toc_access(self, pk):
+        chan = pk.channel
+        cmd = struct.unpack("B", pk.data[0])[0]
+        logger.info("TOC access on port %d", pk.port)
+        if (chan == 0):  # TOC Access
+            cmd = struct.unpack("B", pk.data[0])[0]
+            if (cmd == 0):  # Reqest variable info
+                p = CRTPPacket()
+                p.set_header(pk.port, 0)
+                varIndex = 0
+                if (len(pk.data) > 1):
+                    varIndex = struct.unpack("B", pk.data[1])[0]
+                    logger.debug("TOC[%d]: Requesting ID=%d", pk.port,
+                                 varIndex)
+                else:
+                    logger.debug("TOC[%d]: Requesting first index..surprise,"
+                                 " it 0 !", pk.port)
+
+                if (pk.port == CRTPPort.LOGGING):
+                    l = self.fakeLogToc[varIndex]
+                if (pk.port == CRTPPort.PARAM):
+                    l = self.fakeParamToc[varIndex]
+
+                vartype = l["vartype"]
+                if (pk.port == CRTPPort.PARAM and l["writable"] is True):
+                    vartype = vartype | (0x10)
+
+                p.data = struct.pack("<BBB", cmd, l["varid"], vartype)
+                for ch in l["vargroup"]:
+                    p.data += ch
+                p.data += '\0'
+                for ch in l["varname"]:
+                    p.data += ch
+                p.data += '\0'
+                if (self.doIncompleteLogTOC is False):
+                    self._send_packet(p)
+                elif (varIndex < 5):
+                    self._send_packet(p)
+                else:
+                    logger.info("TOC: Doing incomplete TOC, stopping after"
+                                " varIndex => 5")
+
+            if (cmd == 1):  # TOC CRC32 request
+                fakecrc = 0
+                if (pk.port == CRTPPort.LOGGING):
+                    tocLen = len(self.fakeLogToc)
+                    fakecrc = 0xAAAAAAAA
+                if (pk.port == CRTPPort.PARAM):
+                    tocLen = len(self.fakeParamToc)
+                    fakecrc = 0xBBBBBBBB
+
+                if self._random_toc_crcs:
+                    fakecrc = int(''.join(random.choice("ABCDEF" + string.digits) for x in range(8)), 16)
+                    logger.debug("Generated random TOC CRC: 0x%x", fakecrc)
+                logger.info("TOC[%d]: Requesting TOC CRC, sending back fake"
+                            " stuff: %d", pk.port, len(self.fakeLogToc))
+                p = CRTPPacket()
+                p.set_header(pk.port, 0)
+                p.data = struct.pack('<BBIBB', 1, tocLen, fakecrc, 16, 24)
+                self._send_packet(p)
+
+    def handleParam(self, pk):
+        chan = pk.channel
+        cmd = struct.unpack("B", pk.data[0])[0]
+        logger.debug("PARAM: Port=%d, Chan=%d, cmd=%d", pk.port,
+                     chan, cmd)
+        if (chan == 0):  # TOC Access
+            self._handle_toc_access(pk)
+        elif (chan == 2):  # Settings access
+            varId = pk.datal[0]
+            formatStr = ParamTocElement.types[self.fakeParamToc
+                                              [varId]["vartype"]][1]
+            newvalue = struct.unpack(formatStr, pk.data[1:])[0]
+            self.fakeParamToc[varId]["value"] = newvalue
+            logger.info("PARAM: New value [%s] for param [%d]", newvalue,
+                        varId)
+            # Send back the new value
+            p = CRTPPacket()
+            p.set_header(pk.port, 2)
+            p.data += struct.pack("<B", varId)
+            p.data += struct.pack(formatStr, self.fakeParamToc[varId]["value"])
+            self._send_packet(p)
+        elif (chan == 1):
+            p = CRTPPacket()
+            p.set_header(pk.port, 1)
+            varId = cmd
+            p.data += struct.pack("<B", varId)
+            formatStr = ParamTocElement.types[self.fakeParamToc
+                                              [varId]["vartype"]][1]
+            p.data += struct.pack(formatStr, self.fakeParamToc[varId]["value"])
+            logger.info("PARAM: Getting value for %d", varId)
+            self._send_packet(p)
+
+    def _handle_logging(self, pk):
+        chan = pk.channel
+        cmd = struct.unpack("B", pk.data[0])[0]
+        logger.debug("LOG: Chan=%d, cmd=%d", chan, cmd)
+        if (chan == 0):  # TOC Access
+            self._handle_toc_access(pk)
+        elif (chan == 1):  # Settings access
+            if (cmd == 0):
+                blockId = ord(pk.data[1])
+                if blockId not in self._added_blocks:
+                    self._added_blocks.append(blockId)
+                    logger.info("LOG:Adding block id=%d", blockId)
+                    listofvars = pk.data[3:]
+                    fakeThread = _FakeLoggingDataThread(self.queue, blockId,
+                                                        listofvars,
+                                                        self.fakeLogToc)
+                    self.fakeLoggingThreads.append(fakeThread)
+                    fakeThread.start()
+                    # Anser that everything is ok
+                    p = CRTPPacket()
+                    p.set_header(5, 1)
+                    p.data = struct.pack('<BBB', 0, blockId, 0x00)
+                    self._send_packet(p)
+                else:
+                    p = CRTPPacket()
+                    p.set_header(5, 1)
+                    p.data = struct.pack('<BBB', 0, blockId, errno.EEXIST)
+                    self._send_packet(p)
+            if (cmd == 1):
+                logger.warning("LOG: Appending block not implemented!")
+            if (cmd == 2):
+                blockId = ord(pk.data[1])
+                logger.info("LOG: Should delete block %d", blockId)
+                success = False
+                for fb in self.fakeLoggingThreads:
+                    if (fb.blockId == blockId):
+                        fb._disable_logging()
+                        fb.stop()
+
+                        p = CRTPPacket()
+                        p.set_header(5, 1)
+                        p.data = struct.pack('<BBB', cmd, blockId, 0x00)
+                        self._send_packet(p)
+                        logger.info("LOG: Deleted block=%d", blockId)
+                        success = True
+                if (success is False):
+                    logger.warning("LOG: Could not delete block=%d, not found",
+                                   blockId)
+                    # TODO: Send back error code
+
+            if (cmd == 3):
+                blockId = ord(pk.data[1])
+                period = ord(pk.data[2]) * 10  # Sent as multiple of 10 ms
+                logger.info("LOG:Starting block %d", blockId)
+                success = False
+                for fb in self.fakeLoggingThreads:
+                    if (fb.blockId == blockId):
+                        fb._enable_logging()
+                        fb.period = period
+                        p = CRTPPacket()
+                        p.set_header(5, 1)
+                        p.data = struct.pack('<BBB', cmd, blockId, 0x00)
+                        self._send_packet(p)
+                        logger.info("LOG:Started block=%d", blockId)
+                        success = True
+                if (success is False):
+                    logger.info("LOG:Could not start block=%d, not found",
+                                blockId)
+                    # TODO: Send back error code
+            if (cmd == 4):
+                blockId = ord(pk.data[1])
+                logger.info("LOG:Pausing block %d", blockId)
+                success = False
+                for fb in self.fakeLoggingThreads:
+                    if (fb.blockId == blockId):
+                        fb._disable_logging()
+                        p = CRTPPacket()
+                        p.set_header(5, 1)
+                        p.data = struct.pack('<BBB', cmd, blockId, 0x00)
+                        self._send_packet(p)
+                        logger.info("LOG:Pause block=%d", blockId)
+                        success = True
+                if (success is False):
+                    logger.warning("LOG:Could not pause block=%d, not found",
+                                   blockId)
+                    # TODO: Send back error code
+            if (cmd == 5):
+                logger.info("LOG: Reset logging, but doing nothing")
+                p = CRTPPacket()
+                p.set_header(5, 1)
+                p.data = struct.pack('<BBB', cmd, 0x00, 0x00)
+                self._send_packet(p)
+                import traceback
+                logger.info(traceback.format_exc())
+        elif (chan > 1):
+            logger.warning("LOG: Uplink packets with channels > 1 not"
+                           " supported!")
+
+    def _send_packet(self, pk):
+        # Do not delay log data
+        if self._random_answer_delay and pk.port != 0x05 and pk.channel != 0x02:
+            # Calculate a delay between 0ms and 250ms
+            delay = random.randint(0, 250)/1000.0
+            logger.debug("Delaying answer %.2fms", delay*1000)
+            time.sleep(delay)
+        self.queue.put(pk)
+
+class _FakeLoggingDataThread (Thread):
+    """Thread that will send back fake logging data via CRTP"""
+
+    def __init__(self, outQueue, blockId, listofvars, fakeLogToc):
+        Thread.__init__(self)
+        self.starttime = datetime.now()
+        self.outQueue = outQueue
+        self.setDaemon(True)
+        self.mod = 0
+        self.blockId = blockId
+        self.period = 0
+        self.listofvars = listofvars
+        self.shouldLog = False
+        self.fakeLogToc = fakeLogToc
+        self.fakeLoggingData = []
+        self.setName("Fakelog block=%d" % blockId)
+        self.shouldQuit = False
+
+        logging.info("FakeDataLoggingThread created for blockid=%d", blockId)
+        i = 0
+        while (i < len(listofvars)):
+            varType = ord(listofvars[i])
+            var_stored_as = (varType >> 8)
+            var_fetch_as = (varType & 0xFF)
+            if (var_stored_as > 0):
+                addr = struct.unpack("<I", listofvars[i + 1:i + 5])
+                logger.debug("FakeLoggingThread: We should log a memory addr"
+                             " 0x%04X", addr)
+                self.fakeLoggingData.append([memlogging[var_fetch_as],
+                                            memlogging[var_fetch_as]["min"],
+                                            1])
+                i = i + 5
+            else:
+                varId = ord(listofvars[i])
+                logger.debug("FakeLoggingThread: We should log variable from"
+                             " TOC: id=%d, type=0x%02X", varId, varType)
+                for t in self.fakeLogToc:
+                    if (varId == t["varid"]):
+                        # Each touple will have var data and current fake value
+                        self.fakeLoggingData.append([t, t["min"], 1])
+                i = i + 2
+
+    def _enable_logging(self):
+        self.shouldLog = True
+        logging.info("_FakeLoggingDataThread: Enable thread [%s] at period %d",
+                     self.getName(), self.period)
+
+    def _disable_logging(self):
+        self.shouldLog = False
+        logging.info("_FakeLoggingDataThread: Disable thread [%s]",
+                     self.getName())
+
+    def stop(self):
+        self.shouldQuit = True
+
+    def run(self):
+        while(self.shouldQuit is False):
+            if (self.shouldLog is True):
+
+                p = CRTPPacket()
+                p.set_header(5, 2)
+                p.data = struct.pack('<B', self.blockId)
+                timestamp = int((datetime.now()-self.starttime).total_seconds()*1000)
+                p.data += struct.pack('BBB', timestamp&0xff, (timestamp>>8)&0x0ff, (timestamp>>16)&0x0ff)  # Timestamp
+
+                for d in self.fakeLoggingData:
+                    # Set new value
+                    d[1] = d[1] + d[0]["mod"] * d[2]
+                    # Obej the limitations
+                    if (d[1] > d[0]["max"]):
+                        d[1] = d[0]["max"]  # Limit value
+                        d[2] = -1  # Switch direction
+                    if (d[1] < d[0]["min"]):
+                        d[1] = d[0]["min"]  # Limit value
+                        d[2] = 1  # Switch direction
+                    # Pack value
+                    formatStr = LogTocElement.types[d[0]["vartype"]][1]
+                    p.data += struct.pack(formatStr, d[1])
+                self.outQueue.put(p)
+            time.sleep(self.period / 1000.0)  # Period in ms here
+
+
+class FakeConsoleThread (Thread):
+    """Thread that will send back fake console data via CRTP"""
+    def __init__(self, outQueue):
+        Thread.__init__(self)
+        self.outQueue = outQueue
+        self.setDaemon(True)
+        self._should_run = True
+
+    def stop(self):
+        self._shoud_run = False
+
+    def run(self):
+        # Temporary hack to test GPS from firmware by sending NMEA string on console
+        long_val = 0
+        lat_val = 0
+        alt_val = 0
+
+        while(self._should_run):
+
+            long_val += 1
+            lat_val += 1
+            alt_val += 1.0
+
+            long_string = "5536.677%d" % (long_val % 99)
+            lat_string = "01259.645%d" % (lat_val % 99)
+            alt_string = "%.1f" % (alt_val % 100.0)
+
+            # Copy of what is sent from the module, but note that only
+            # the GPGGA message is being simulated, the others are fixed...
+            self._send_text("Time is now %s\n" % datetime.now())
+            self._send_text("$GPVTG,,T,,M,0.386,N,0.716,K,A*2E\n")
+            self._send_text("$GPGGA,135544.0")
+            self._send_text("0,%s,N,%s,E,1,04,2.62,3.6,M,%s,M,,*58\n" % (long_string, lat_string, alt_string))
+            self._send_text("$GPGSA,A,3,31,20,23,07,,,,,,,,,3.02,2.62,1.52*05\n")
+            self._send_text("$GPGSV,2,1,07,07,09,181,15,13,63,219,26,16,02,097,,17,05,233,20*7E\n")
+            self._send_text("$GPGSV,2,2,07,20,42,119,35,23,77,097,27,31,12,032,19*47\n")
+            self._send_text("$GPGLL,5536.67734,N,01259.64578,E,135544.00,A,A*68\n")
+
+            time.sleep(2)
+
+    def _send_text(self, message):
+        p = CRTPPacket()
+        p.set_header(0, 0)
+
+        us = "%is" % len(message)
+        # This might be done prettier ;-)
+        p.data = struct.pack(us, message)
+
+        self.outQueue.put(p)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/debugdriver.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f28249048fede5906ec54557b98d959df95754e6
GIT binary patch
literal 27858
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.py
new file mode 100755
index 00000000..cb286cac
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Exception used when the URI is not for the current driver
+(ie. radio:// for the serial driver ...)
+It basically means that an other driver could do the job
+It does NOT means that the URI is good or bad
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['WrongUriType', 'CommunicationException']
+
+
+class WrongUriType (Exception):
+    """ Wrong type of URI for this interface """
+    pass
+
+
+class CommunicationException (Exception):
+    """ Communication problem when communicating with a Crazyflie """
+    pass
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/exceptions.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3eafb262e3d3ac3a49949b7b8635706859b222b8
GIT binary patch
literal 1069
zcmcgq!H&}~5OvZvyM%?~o<|%|IJCI~La+;nl~#+Cb_JA!<=Rebl-QB&&@ET^CO!f_
zhaX^^w&`-?0+IYOGoBgGyqUqTd+GO&Ki+e)`v&-bj>o-05QNMTBQlSOh=~}GWlZKM
z#6%8AiN+!MM#xt}KFo*VdS@N=))5g2nPx{J<4=V2)eV=8S4KnYq=5BGYVa!w*Tn@W
z2ikZjjqOalwN`2m!m7{GrkRoxuuLfPIL~`0C#_=D&Icxw$)ohb!-6@*SygSImP|WL
z$2>3%2A{HfXKva`fH2)-pUffz3L_m{y`9~TyH(!O7y($`f{E1K#eU9|=azku@Z!v$
zp#IJpy}Y(+wrQk4MZ>wN>sDhiq36E&p6)2*z~k;CNTeG%MT|)?&BC2~hyYzy@R$=!
z*-^FS2nkc#OItE7Aq%7P#|V^aR!d5K+9|aWZ57lo1xkhClx8TB1GpKg`O4HX5AAHq
zO3D1?_%*w{98WjS%Q|lwN7pWQmgj<&$Sp-n?zmNrcR9z11;+X&m-`93NwX>T@3<jK
zNt8r?-})1bfIs+y#+pSX>+Ra`1GRbyYvotK;Cx^HmX9LFBt>AR2n8HE?xcU$c9`85
zPv7=cmh6kzjfJ&-Wo$o|z$o>1(5c^&j=UnI`d@W->+^KGFwY)@`i~KkIEhXMr=#DH
Cn-~iK

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.py
new file mode 100755
index 00000000..1b00698b
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.py
@@ -0,0 +1,424 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Crazyradio CRTP link driver.
+
+This driver is used to communicate with the Crazyflie using the Crazyradio
+USB dongle.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['RadioDriver']
+
+import logging
+logger = logging.getLogger(__name__)
+
+from cflib.crtp.crtpdriver import CRTPDriver
+from .crtpstack import CRTPPacket
+from .exceptions import WrongUriType
+import threading
+import Queue
+import re
+import array
+import binascii
+import struct
+
+from cflib.drivers.crazyradio import Crazyradio
+from usb import USBError
+
+
+class RadioDriver(CRTPDriver):
+    """ Crazyradio link driver """
+    def __init__(self):
+        """ Create the link driver """
+        CRTPDriver.__init__(self)
+        self.cradio = None
+        self.uri = ""
+        self.link_error_callback = None
+        self.link_quality_callback = None
+        self.in_queue = None
+        self.out_queue = None
+        self._thread = None
+
+    def connect(self, uri, link_quality_callback, link_error_callback):
+        """
+        Connect the link driver to a specified URI of the format:
+        radio://<dongle nbr>/<radio channel>/[250K,1M,2M]
+
+        The callback for linkQuality can be called at any moment from the
+        driver to report back the link quality in percentage. The
+        callback from linkError will be called when a error occurs with
+        an error message.
+        """
+
+        # check if the URI is a radio URI
+        if not re.search("^radio://", uri):
+            raise WrongUriType("Not a radio URI")
+
+        # Open the USB dongle
+        if not re.search("^radio://([0-9]+)((/([0-9]+))((/(250K|1M|2M))?(/([A-F0-9]+))?)?)?$",
+                         uri):
+            raise WrongUriType('Wrong radio URI format!')
+
+        uri_data = re.search("^radio://([0-9]+)((/([0-9]+))"
+                             "((/(250K|1M|2M))?(/([A-F0-9]+))?)?)?$",
+                             uri)
+
+        self.uri = uri
+
+        channel = 2
+        if uri_data.group(4):
+            channel = int(uri_data.group(4))
+
+        datarate = Crazyradio.DR_2MPS
+        if uri_data.group(7) == "250K":
+            datarate = Crazyradio.DR_250KPS
+        if uri_data.group(7) == "1M":
+            datarate = Crazyradio.DR_1MPS
+        if uri_data.group(7) == "2M":
+            datarate = Crazyradio.DR_2MPS
+
+        if self.cradio is None:
+            self.cradio = Crazyradio(devid=int(uri_data.group(1)))
+        else:
+            raise Exception("Link already open!")
+
+        if self.cradio.version >= 0.4:
+            self.cradio.set_arc(10)
+        else:
+            logger.warning("Radio version <0.4 will be obsoleted soon!")
+
+        self.cradio.set_channel(channel)
+
+        self.cradio.set_data_rate(datarate)
+
+        if uri_data.group(9):
+            addr = str(uri_data.group(9))
+            new_addr = struct.unpack("<BBBBB", binascii.unhexlify(addr))
+            self.cradio.set_address(new_addr)
+
+        # Prepare the inter-thread communication queue
+        self.in_queue = Queue.Queue()
+        # Limited size out queue to avoid "ReadBack" effect
+        self.out_queue = Queue.Queue(1)
+
+        # Launch the comm thread
+        self._thread = _RadioDriverThread(self.cradio, self.in_queue,
+                                          self.out_queue,
+                                          link_quality_callback,
+                                          link_error_callback)
+        self._thread.start()
+
+        self.link_error_callback = link_error_callback
+
+    def receive_packet(self, time=0):
+        """
+        Receive a packet though the link. This call is blocking but will
+        timeout and return None if a timeout is supplied.
+        """
+        if time == 0:
+            try:
+                return self.in_queue.get(False)
+            except Queue.Empty:
+                return None
+        elif time < 0:
+            try:
+                return self.in_queue.get(True)
+            except Queue.Empty:
+                return None
+        else:
+            try:
+                return self.in_queue.get(True, time)
+            except Queue.Empty:
+                return None
+
+    def send_packet(self, pk):
+        """ Send the packet pk though the link """
+        # if self.out_queue.full():
+        #    self.out_queue.get()
+        if (self.cradio is None):
+            return
+
+        try:
+            self.out_queue.put(pk, True, 2)
+        except Queue.Full:
+            if self.link_error_callback:
+                self.link_error_callback("RadioDriver: Could not send packet"
+                                         " to copter")
+
+    def pause(self):
+        self._thread.stop()
+        self._thread = None
+
+    def restart(self):
+        if self._thread:
+            return
+
+        self._thread = _RadioDriverThread(self.cradio, self.in_queue,
+                                          self.out_queue,
+                                          self.link_quality_callback,
+                                          self.link_error_callback)
+        self._thread.start()
+
+    def close(self):
+        """ Close the link. """
+        # Stop the comm thread
+        self._thread.stop()
+
+        # Close the USB dongle
+        try:
+            if self.cradio:
+                self.cradio.close()
+        except:
+            # If we pull out the dongle we will not make this call
+            pass
+        self.cradio = None
+
+        # Clear callbacks
+        self.link_error_callback = None
+        self.link_quality_callback = None
+
+    def _scan_radio_channels(self, start=0, stop=125):
+        """ Scan for Crazyflies between the supplied channels. """
+        return list(self.cradio.scan_channels(start, stop, (0xff,)))
+
+    def scan_selected(self, links):
+        to_scan = ()
+        for l in links:
+            one_to_scan = {}
+            uri_data = re.search("^radio://([0-9]+)((/([0-9]+))"
+                                 "(/(250K|1M|2M))?)?$",
+                                 l)
+
+            one_to_scan["channel"] = int(uri_data.group(4))
+
+            datarate = Crazyradio.DR_2MPS
+            if uri_data.group(6) == "250K":
+                datarate = Crazyradio.DR_250KPS
+            if uri_data.group(6) == "1M":
+                datarate = Crazyradio.DR_1MPS
+            if uri_data.group(6) == "2M":
+                datarate = Crazyradio.DR_2MPS
+
+            one_to_scan["datarate"] = datarate
+
+            to_scan += (one_to_scan, )
+
+        found = self.cradio.scan_selected(to_scan, (0xFF, 0xFF, 0xFF))
+
+        ret = ()
+        for f in found:
+            dr_string = ""
+            if f["datarate"] == Crazyradio.DR_2MPS:
+                dr_string = "2M"
+            if f["datarate"] == Crazyradio.DR_250KPS:
+                dr_string = "250K"
+            if f["datarate"] == Crazyradio.DR_1MPS:
+                dr_string = "1M"
+
+            ret += ("radio://0/{}/{}".format(f["channel"], dr_string),)
+
+        return ret
+
+    def scan_interface(self, address):
+        """ Scan interface for Crazyflies """
+        if self.cradio is None:
+            try:
+                self.cradio = Crazyradio()
+            except Exception:
+                print "Crazyradio() didnt initialize"
+                return []
+        else:
+            print "Cannot scann for links while the link is open!"
+            raise Exception("Cannot scann for links while the link is open!")
+
+        # FIXME: implements serial number in the Crazyradio driver!
+        serial = "N/A"
+
+        logger.info("v%s dongle with serial %s found", self.cradio.version,
+                    serial)
+        found = []
+
+        if address != None:
+            addr = "{:X}".format(address)
+            new_addr = struct.unpack("<BBBBB", binascii.unhexlify(addr))
+            self.cradio.set_address(new_addr)
+
+        self.cradio.set_arc(1)
+
+        self.cradio.set_data_rate(self.cradio.DR_250KPS)
+
+        if address == None or address == 0xE7E7E7E7E7:
+            found += map(lambda c: ["radio://0/{}/250K".format(c), ""],
+                         self._scan_radio_channels())
+            self.cradio.set_data_rate(self.cradio.DR_1MPS)
+            found += map(lambda c: ["radio://0/{}/1M".format(c), ""],
+                         self._scan_radio_channels())
+            self.cradio.set_data_rate(self.cradio.DR_2MPS)
+            found += map(lambda c: ["radio://0/{}/2M".format(c), ""],
+                         self._scan_radio_channels())
+        else:
+            found += map(lambda c: ["radio://0/{}/250K/{:X}".format(c, address), ""],
+                         self._scan_radio_channels())
+            self.cradio.set_data_rate(self.cradio.DR_1MPS)
+            found += map(lambda c: ["radio://0/{}/1M/{:X}".format(c, address), ""],
+                         self._scan_radio_channels())
+            self.cradio.set_data_rate(self.cradio.DR_2MPS)
+            found += map(lambda c: ["radio://0/{}/2M/{:X}".format(c, address), ""],
+                         self._scan_radio_channels())
+
+        self.cradio.close()
+        self.cradio = None
+
+        return found
+
+    def get_status(self):
+        if self.cradio is None:
+            try:
+                self.cradio = Crazyradio()
+            except USBError as e:
+                return "Cannot open Crazyradio. Permission problem?"\
+                       " ({})".format(str(e))
+            except Exception as e:
+                return str(e)
+
+        ver = self.cradio.version
+        self.cradio.close()
+        self.cradio = None
+
+        return "Crazyradio version {}".format(ver)
+
+    def get_name(self):
+        return "radio"
+
+
+# Transmit/receive radio thread
+class _RadioDriverThread (threading.Thread):
+    """
+    Radio link receiver thread used to read data from the
+    Crazyradio USB driver. """
+
+    RETRYCOUNT_BEFORE_DISCONNECT = 10
+
+    def __init__(self, cradio, inQueue, outQueue, link_quality_callback,
+                 link_error_callback):
+        """ Create the object """
+        threading.Thread.__init__(self)
+        self.cradio = cradio
+        self.in_queue = inQueue
+        self.out_queue = outQueue
+        self.sp = False
+        self.link_error_callback = link_error_callback
+        self.link_quality_callback = link_quality_callback
+        self.retryBeforeDisconnect = self.RETRYCOUNT_BEFORE_DISCONNECT
+
+    def stop(self):
+        """ Stop the thread """
+        self.sp = True
+        try:
+            self.join()
+        except Exception:
+            pass
+
+    def run(self):
+        """ Run the receiver thread """
+        dataOut = array.array('B', [0xFF])
+        waitTime = 0
+        emptyCtr = 0
+
+        while(True):
+            if (self.sp):
+                break
+
+            try:
+                ackStatus = self.cradio.send_packet(dataOut)
+            except Exception as e:
+                import traceback
+                self.link_error_callback("Error communicating with crazy radio"
+                                         " ,it has probably been unplugged!\n"
+                                         "Exception:%s\n\n%s" % (e,
+                                         traceback.format_exc()))
+
+            # Analise the in data packet ...
+            if ackStatus is None:
+                if (self.link_error_callback is not None):
+                    self.link_error_callback("Dongle communication error"
+                                             " (ackStatus==None)")
+                continue
+
+            if (self.link_quality_callback is not None):
+                self.link_quality_callback((10 - ackStatus.retry) * 10)
+
+            # If no copter, retry
+            if ackStatus.ack is False:
+                self.retryBeforeDisconnect = self.retryBeforeDisconnect - 1
+                if (self.retryBeforeDisconnect == 0 and
+                        self.link_error_callback is not None):
+                    self.link_error_callback("Too many packets lost")
+                continue
+            self.retryBeforeDisconnect = self.RETRYCOUNT_BEFORE_DISCONNECT
+
+            data = ackStatus.data
+
+            # If there is a copter in range, the packet is analysed and the
+            # next packet to send is prepared
+            if (len(data) > 0):
+                inPacket = CRTPPacket(data[0], list(data[1:]))
+                # print "<- " + inPacket.__str__()
+                self.in_queue.put(inPacket)
+                waitTime = 0
+                emptyCtr = 0
+            else:
+                emptyCtr += 1
+                if (emptyCtr > 10):
+                    emptyCtr = 10
+                    # Relaxation time if the last 10 packet where empty
+                    waitTime = 0.01
+                else:
+                    waitTime = 0
+
+            # get the next packet to send of relaxation (wait 10ms)
+            outPacket = None
+            try:
+                outPacket = self.out_queue.get(True, waitTime)
+            except Queue.Empty:
+                outPacket = None
+
+            dataOut = array.array('B')
+
+            if outPacket:
+                # print "-> " + outPacket.__str__()
+                dataOut.append(outPacket.header)
+                for X in outPacket.data:
+                    if type(X) == int:
+                        dataOut.append(X)
+                    else:
+                        dataOut.append(ord(X))
+            else:
+                dataOut.append(0xFF)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/radiodriver.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5c813e26bef192cc8be323764abc89307a9d2694
GIT binary patch
literal 12349
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
BOSk|4

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.py
new file mode 100755
index 00000000..d6cae55f
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+An early serial link driver. This could still be used (after some fixing) to
+run high-speed CRTP with the Crazyflie. The UART can be run at 2Mbit.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['SerialDriver']
+
+from .crtpdriver import CRTPDriver
+
+from .exceptions import WrongUriType
+
+import re
+
+
+class SerialDriver (CRTPDriver):
+    def __init__(self):
+        None
+
+    def connect(self, uri, linkQualityCallback, linkErrorCallback):
+        #check if the URI is a serial URI
+        if not re.search("^serial://", uri):
+            raise WrongUriType("Not a serial URI")
+
+        #Check if it is a valid serial URI
+        uriRe = re.search("^serial://([a-z A-Z 0-9]+)/?([0-9]+)?$", uri)
+        if not uriRe:
+            raise Exception("Invalid serial URI")
+
+    def get_name(self):
+        return "serial"
+
+    def scan_interface(self, address):
+        return []
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/serialdriver.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cf7f19bdc9799ac60bf5936db90efd5de8f4a75e
GIT binary patch
literal 1867
zcmcgsU2YRG5T4yllQy)plnWRMp{*cn#Tyk+`iH1gBG|P2q=J>>brRFfZe%-cQ<WFW
z5jYEn;5r-tX1r-C#4}Cg+3|SB{^s*|f?o^uABR8R3z+-@{65EF4pF24ZIm9gJxG1%
zq2fatKw5z;fI$V?74T?21KA7=s?e_Dyb5UzvKq8&6eKIq#pqf125<rJp*>5>t#FPG
z`HrH#ku%A4HfBa@#WR+v{D`Gm9ZUU??e~>oq8Mc<Ggf68>qs^-GG!s}TB(^S29kBv
zsmgmR%ocS$%2{9a`VBLb=(QE^zh)=O_L=QVwx#*mxSJ_S`$~4O5$`j>bJ~;QxMh!C
zb(DQrH#ZR2rm_M9B-_}u^Ehugc-sz`^3V{Huyqu4Ou10n$o-uz^4@_~`{SWhzwy_c
zl1y=%ILreS5=lmafCwUbJ*Ob4zEdhhI4#XU+zKlgASnEDY|%EeC~yN0N6P_dmN3Y>
zS>*C+W@Oe4(F#tOx2Q#Zq%fjP9W~K*;|1T_YqZA3%0V<7n&iYpMvEv-x;)Dgv_wW|
zHMAxYNM;9P?Jz1YZ*pzJu_a7NqH<-Eq=CBeQGh21(+mzOGzWeU6$|hgIq=}zg9{(b
z9k9rR4;LPM^&p=*1QUlZ6*#Yec?s4P<J8R&K)W{$UCXBu)LImoB{CM4@yjYZh<DAs
zzkI?&-Z*0$jW%0tJpOopC0Y*;OKttGSw`30{FrA73$d(I+`-)kLIu#$R`Fx7l0`oz
zi=_(RITdnfRgs5;DyHNSGQdbHdmF!0>u*N5s~vA4xg9Q!?5&A?N9#gQtrU+@vAmw^
ztKo5?$a5*|Dyoi7@Rz;CVA-pB|CS(ShWFPBDfQ-r3wq8aTq;+W{q>-y;_FG9<a{94
zP`@lb0!M@3xZ)$&y{lJ)GjJJ{H$^1yG}Y3W>%n~!4>W@3_-?Sdb-9pF$wH993#q^+
z?}`#j64!f@WKg7|45zWHu?6&s7fDbdUEy}xe|+&Vo(9y=Tsx|IbHQAFE^vL`5|K%g
z7GjE-k8HotI4zf`v6SghGby`BOt<7xl$Wo_xR(_Z5$?F_%g*usq-6RT!}AUG_^OY2
K`b+qq5B>m~z{2eS

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.py
new file mode 100755
index 00000000..cb6453b3
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+""" CRTP UDP Driver. Work either with the UDP server or with an UDP device
+See udpserver.py for the protocol"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['UdpDriver']
+
+
+from .crtpdriver import CRTPDriver
+from .crtpstack import CRTPPacket
+from .exceptions import WrongUriType
+import Queue
+import re
+import struct
+from socket import socket
+
+
+class UdpDriver(CRTPDriver):
+    def __init__(self):
+        None
+
+    def connect(self, uri, linkQualityCallback, linkErrorCallback):
+        #check if the URI is a radio URI
+        if not re.search("^udp://", uri):
+            raise WrongUriType("Not an UDP URI")
+
+        self.queue = Queue.Queue()
+        self.socket = socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self.addr = ("localhost", 7777)
+        self.socket.connect(self.addr)
+
+        #Add this to the server clients list
+        self.socket.sendto("\xFF\x01\x01\x01", self.addr)
+
+    def receive_packet(self, time=0):
+        data, addr = self.socket.recvfrom(1024)
+
+        if data:
+            data = struct.unpack('b' * (len(data) - 1), data[0:len(data) - 1])
+            pk = CRTPPacket()
+            pk.port = data[0]
+            pk.data = data[1:]
+            return pk
+
+        try:
+            if time == 0:
+                return self.rxqueue.get(False)
+            elif time < 0:
+                while True:
+                    return self.rxqueue.get(True, 10)
+            else:
+                return self.rxqueue.get(True, time)
+        except Queue.Empty:
+            return None
+
+    def send_packet(self, pk):
+        raw = (pk.port,) + struct.unpack("B"* len(pk.data), pk.data)
+
+        cksum = 0
+        for i in raw:
+            cksum += i
+
+        cksum %= 256
+
+        data = ''.join(chr(v) for v in (raw + (cksum,)))
+
+        #print tuple(data)
+        self.socket.sendto(data, self.addr)
+
+    def close(self):
+        #Remove this from the server clients list
+        self.socket.sendto("\xFF\x01\x02\x02", self.addr)
+
+    def get_name(self):
+        return "udp"
+
+    def scan_interface(self, address):
+        return []
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/udpdriver.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cb845b82eba07b47050b5b3820bc2441ef2e033a
GIT binary patch
literal 3552
zcmcguPjlN;5MRlT6FW(p(sq)<47hOdfyONt2IzEXl9nly(8>uZrbVMlFNqvka;4`q
zVG<6sUw~owD%?15<+E^M_yFbiTPbZ*&WUZU_w8G~w|{<b_tpNLZT<fJUk^g5J~jM*
zj;Ht)BogU^I?{I}a%BsQD^X3Nx+FCj)}>z;hvNxJCS=%<exn>WB$<@ql=P=Co|I@x
zlBV>VQt>V6w~qK}=}#-)lw?MRGt!^IJFb|zY+~1zJQsN;@?C$H%k(<aG%f~dFMDhE
z+UqO#+ADdyYx2eRMwaiiO>DPK-hPU|HaJx%Ob&&1R*8dDrIFc<L(}RR(;i2oQfG0r
z-`<2C-yG$c&B81xxaCr8!#w!Wv~MoiCdTX0sN6PoFzS%l76^?hCyvYR1>ugdrECUz
z8+n#)t>^LD{>a2H@L4VA8kj3G>b^{g3(*qAUxFB<1Yr?|g~@P~K?*c6&yk@z)5F;b
z=7mf{qJ}ca#H2D);`KUHST3aDN6#`hp%lwIczSA5Wmp~Z)hsoonH&`++3et7loUO1
z-R*2>x(w9*W)hq3%9Y!}-Md$M`-L^b?r2o_Pm6Amhuz5E43fl$q+5h}JhDYM#2b(C
zt{ruej;PG(Xy4Kx-;dMS`u-yLg7FY1qzQb8Shf5oFji!M@Hled$TL@p%VH52SDrcY
zvm+@KYBH!1PzPAGE_Q;yK||>zU!fe4YbY~CI)Fk`nJKR0R3adF;z;UN@tTAQs|D!~
z@Zr;Lw>U#dX4@6!*1bClpCk)|WIHQt9J~vc=*J7kaXR#^Aij<(DC*&fNN%h`!~}V`
z?a_DGFLHlu#zwg(<UB5DX~)gm{+-oZYl_X@H_Ko7D_?jwzqXtYq9|9LFiTSt+G^o6
zvRQ}o9yda~M8<h+&*MLd)1CX{Ac^h%GSc@Ln6~FC`K>(9@+0ZV!@`@xzSr<*gvEW!
zIqRNv7M!MYzQ*bxA*036&<n-KKVX=<BZwfE4_!ILuO<hs<h#TQzyY`*K9E2G#ckmN
z*a%R0U3M8r!JCLUpmeA+P`Pwa7az#cv-lndlLFe?_lZX+>|mSUD$oE^$O`u8RdY!0
zpR#vV?6l;6yrzWmh;3ZyZKUUqiA#(_d{OFlMh3I8(~|se0gxh`-UlQdy1yFpq*$Kw
z$6Bv6LNV)A=PXDG7ap|Sgu9!0HY~AV^Kk`>aXMnM35LX^9%HK*8fCdvCJJnzV9NIt
zfvUP?tYY$ZkQ7GM)^dQ8^;G4zh9kT0F-aYg5^#i`jfcj2ha#G%z#r|LJg6*BD?(t<
zPkcIT><vhYHi$S2Zo|0-fX(7tcWcg!Gl!??%)9g86mJYRJyE><0tP7p-q6Sp`~hGA
z24DkUXXY<doGbGH6L1G4?kx~ME05+BR#2|X#}GgHL^ck9&6<3M5uk*sRF-#WV**>u
zuV6|14$HFCkO9#5zY?W*P$(>wC}8<V*{pg2Gs`R&e3${8m`CU25QeKaSpQq+2`qS{
zJ@B1yJJ$lH`C7cHo9v!^*0f$;-!iG$8|611(r|jwkPZu}_cqBq$pXnG5;~@M9At5-
z==WI16m5E96pG&aRHJ8k@KgaC?iAyp`W5RMC+{4J0sJv(RN;|bwk&zhhrr{kbH;7C
z?OJ%$!)fg@b?El%)s8$$+EJ%sgjTD*<GLNb_GTQoq{iVCyyT$?ldLcSn6ksWFMXmR
z>Jv)?j!;igwyK#yn=Q>dO>3uM2Wp$2218?B8eEnD2KJc2Tdz(ErbKNoD;@hYo3JU0
zQwT9@TM-5+u2R<In?Y!{DWSdM=uxSzTJ*~I^~T`)!z>yn7<x2To38f(xho`#Bv(m3
z0(s?wawl~m^stq1dp!mXr&(*x&RlGsY1W&y4xQ9C<oi(;zVsp(+wCmJkY4ycub!oY
zyl$3PO^->_yBTGLMfES+9J{313(d&JSz73oOFNjS)kO;naj9mVQV29r*UQx0!2MB*
T3m^^DgJx|Gy@-8@-?`d<(dzY-

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.py
new file mode 100755
index 00000000..bd4a596a
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.py
@@ -0,0 +1,254 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Crazyflie USB driver.
+
+This driver is used to communicate with the Crazyflie using the USB connection.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['UsbDriver']
+
+import logging
+logger = logging.getLogger(__name__)
+
+from cflib.crtp.crtpdriver import CRTPDriver
+from .crtpstack import CRTPPacket
+from .exceptions import WrongUriType
+import threading
+import Queue
+import re
+import time
+
+from cflib.drivers.cfusb import CfUsb
+from usb import USBError
+
+
+class UsbDriver(CRTPDriver):
+    """ Crazyradio link driver """
+    def __init__(self):
+        """ Create the link driver """
+        CRTPDriver.__init__(self)
+        self.cfusb = None
+        self.uri = ""
+        self.link_error_callback = None
+        self.link_quality_callback = None
+        self.in_queue = None
+        self.out_queue = None
+        self._thread = None
+        self.needs_resending = False
+
+    def connect(self, uri, link_quality_callback, link_error_callback):
+        """
+        Connect the link driver to a specified URI of the format:
+        radio://<dongle nbr>/<radio channel>/[250K,1M,2M]
+
+        The callback for linkQuality can be called at any moment from the
+        driver to report back the link quality in percentage. The
+        callback from linkError will be called when a error occues with
+        an error message.
+        """
+
+        # check if the URI is a radio URI
+        if not re.search("^usb://", uri):
+            raise WrongUriType("Not a radio URI")
+
+        # Open the USB dongle
+        if not re.search("^usb://([0-9]+)$",
+                         uri):
+            raise WrongUriType('Wrong radio URI format!')
+
+        uri_data = re.search("^usb://([0-9]+)$",
+                             uri)
+
+        self.uri = uri
+
+        if self.cfusb is None:
+            self.cfusb = CfUsb(devid=int(uri_data.group(1)))
+            if self.cfusb.dev:
+                self.cfusb.set_crtp_to_usb(True)
+                time.sleep(1) # Wait for the blocking queues in the firmware to time out
+            else:
+                self.cfusb = None
+                raise Exception("Could not open {}".format(self.uri))
+
+        else:
+            raise Exception("Link already open!")
+
+        # Prepare the inter-thread communication queue
+        self.in_queue = Queue.Queue()
+        # Limited size out queue to avoid "ReadBack" effect
+        self.out_queue = Queue.Queue(50)
+
+        # Launch the comm thread
+        self._thread = _UsbReceiveThread(self.cfusb, self.in_queue,
+                                          link_quality_callback,
+                                          link_error_callback)
+        self._thread.start()
+
+        self.link_error_callback = link_error_callback
+
+    def receive_packet(self, time=0):
+        """
+        Receive a packet though the link. This call is blocking but will
+        timeout and return None if a timeout is supplied.
+        """
+        if time == 0:
+            try:
+                return self.in_queue.get(False)
+            except Queue.Empty:
+                return None
+        elif time < 0:
+            try:
+                return self.in_queue.get(True)
+            except Queue.Empty:
+                return None
+        else:
+            try:
+                return self.in_queue.get(True, time)
+            except Queue.Empty:
+                return None
+
+    def send_packet(self, pk):
+        """ Send the packet pk though the link """
+        # if self.out_queue.full():
+        #    self.out_queue.get()
+        if (self.cfusb is None):
+            return
+
+        try:
+            dataOut = (pk.header,)
+            dataOut += pk.datat
+            self.cfusb.send_packet(dataOut)
+        except Queue.Full:
+            if self.link_error_callback:
+                self.link_error_callback("UsbDriver: Could not send packet"
+                                         " to Crazyflie")
+
+    def pause(self):
+        self._thread.stop()
+        self._thread = None
+
+    def restart(self):
+        if self._thread:
+            return
+
+        self._thread = _UsbReceiveThread(self.cfusb, self.in_queue,
+                                          self.link_quality_callback,
+                                          self.link_error_callback)
+        self._thread.start()
+
+    def close(self):
+        """ Close the link. """
+        # Stop the comm thread
+        self._thread.stop()
+
+        # Close the USB dongle
+        try:
+            if self.cfusb:
+                self.cfusb.set_crtp_to_usb(False)
+                self.cfusb.close()
+        except Exception as e:
+            # If we pull out the dongle we will not make this call
+            logger.info("Could not close {}".format(e))
+            pass
+        self.cfusb = None
+
+    def scan_interface(self, address):
+        """ Scan interface for Crazyflies """
+        if self.cfusb is None:
+            try:
+                self.cfusb = CfUsb()
+            except Exception as e:
+                logger.warn("Exception while scanning for Crazyflie USB: {}".format(str(e)))
+                return []
+        else:
+            raise Exception("Cannot scan for links while the link is open!")
+
+        # FIXME: implements serial number in the Crazyradio driver!
+        #serial = "N/A"
+
+        found = self.cfusb.scan()
+
+        self.cfusb.close()
+        self.cfusb = None
+
+        return found
+
+    def get_status(self):
+        return "No information available"
+
+    def get_name(self):
+        return "UsbCdc"
+
+
+# Transmit/receive radio thread
+class _UsbReceiveThread (threading.Thread):
+    """
+    Radio link receiver thread used to read data from the
+    Crazyradio USB driver. """
+
+    #RETRYCOUNT_BEFORE_DISCONNECT = 10
+
+    def __init__(self, cfusb, inQueue, link_quality_callback,
+                 link_error_callback):
+        """ Create the object """
+        threading.Thread.__init__(self)
+        self.cfusb = cfusb
+        self.in_queue = inQueue
+        self.sp = False
+        self.link_error_callback = link_error_callback
+        self.link_quality_callback = link_quality_callback
+
+    def stop(self):
+        """ Stop the thread """
+        self.sp = True
+        try:
+            self.join()
+        except Exception:
+            pass
+
+    def run(self):
+        """ Run the receiver thread """
+
+        while(True):
+            if (self.sp):
+                break
+            try:
+                # Blocking until USB data available
+                data = self.cfusb.receive_packet()
+                if len(data) > 0:
+                    pk = CRTPPacket(data[0], list(data[1:]))
+                    self.in_queue.put(pk)
+            except Exception as e:
+                import traceback
+                self.link_error_callback("Error communicating with the Crazyflie"
+                                         " ,it has probably been unplugged!\n"
+                                         "Exception:%s\n\n%s" % (e,
+                                         traceback.format_exc()))
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/crtp/usbdriver.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0706d7d037264bc184b7d997125cf8538a1288fe
GIT binary patch
literal 7434
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
A!T<mO

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.py
new file mode 100755
index 00000000..f074d797
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Drivers for the link interfaces that can be used by CRTP.
+"""
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..55c08d1fb913181c63048089e35802f5bfab0ad3
GIT binary patch
literal 254
zcmYL@UrGZ(492JSL0RY-n#X-<a{x;zwD?p+tb*u6n4QVe!P!4drpoqUJw&h53z(u{
z;QJ-{ARifDCdKLF{N~JGjkK>+QYgGJ@5+(cl{Z)KUQujP_~Il`cM0;IA%^x7LYq0&
z*0HE(GdSBq#c&YzPz|ttetDV~kL5Kz-ztcF=ZH!j%|GN-SKN0Eqf7QU)G;t_W)F6|
zo$Ut6++g1eeG5u*=&80bk~%0Zg+2>9y{ORteuzY&4Vh@(5BW}$7ft66k(X1QMP+^f
Dy_ZA0

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.py
new file mode 100755
index 00000000..117fd869
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+USB driver for the Crazyflie.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['CfUsb']
+
+
+import os
+import usb
+import logging
+import sys
+import time
+import array
+import binascii
+
+logger = logging.getLogger(__name__)
+
+USB_VID = 0x0483
+USB_PID = 0x5740
+
+try:
+    import usb.core
+    pyusb_backend = None
+    if os.name == "nt":
+        import usb.backend.libusb0 as libusb0
+        pyusb_backend = libusb0.get_backend()
+    pyusb1 = True
+except:
+    pyusb1 = False
+
+
+def _find_devices():
+    """
+    Returns a list of CrazyRadio devices currently connected to the computer
+    """
+    ret = []
+
+    logger.info("Looking for devices....")
+
+    if pyusb1:
+        for d in usb.core.find(idVendor=USB_VID, idProduct=USB_PID, find_all=1, backend=pyusb_backend):
+            ret.append(d)
+    else:
+        busses = usb.busses()
+        for bus in busses:
+            for device in bus.devices:
+                if device.idVendor == USB_VID:
+                    if device.idProduct == USB_PID:
+                        ret += [device, ]
+
+    return ret
+
+
+class CfUsb:
+    """ Used for communication with the Crazyradio USB dongle """
+
+    def __init__(self, device=None, devid=0):
+        """ Create object and scan for USB dongle if no device is supplied """
+        self.dev = None
+        self.handle = None
+        self._last_write = 0
+        self._last_read = 0
+
+        if device is None:
+            devices = _find_devices()
+            try:
+                self.dev = devices[devid]
+            except Exception:
+                self.dev = None
+
+
+        if self.dev:
+            if (pyusb1 is True):
+                self.dev.set_configuration(1)
+                self.handle = self.dev
+                self.version = float("{0:x}.{1:x}".format(self.dev.bcdDevice >> 8,
+                                     self.dev.bcdDevice & 0x0FF))
+            else:
+                self.handle = self.dev.open()
+                self.handle.setConfiguration(1)
+                self.handle.claimInterface(0)
+                self.version = float(self.dev.deviceVersion)
+
+    def get_serial(self):
+        return usb.util.get_string(self.dev, 255, self.dev.iSerialNumber)
+
+    def close(self):
+        if (pyusb1 is False):
+            if self.handle:
+                self.handle.releaseInterface()
+                self.handle.reset()
+        else:
+            if self.dev:
+                self.dev.reset()
+
+        self.handle = None
+        self.dev = None
+
+    def scan(self):
+        # TODO: Currently only supports one device
+        if self.dev:
+            return [("usb://0","")]
+        return []
+
+    def set_crtp_to_usb(self, crtp_to_usb):
+        if crtp_to_usb:
+            _send_vendor_setup(self.handle, 0x01, 0x01, 1, ())
+        else:
+            _send_vendor_setup(self.handle, 0x01, 0x01, 0, ())
+
+    ### Data transfers ###
+    def send_packet(self, dataOut):
+        """ Send a packet and receive the ack from the radio dongle
+            The ack contains information about the packet transmition
+            and a data payload if the ack packet contained any """
+        try:
+            if (pyusb1 is False):
+                count = self.handle.bulkWrite(1, dataOut, 20)
+            else:
+                count = self.handle.write(endpoint=1, data=dataOut, timeout=20)
+        except usb.USBError as e:
+            pass
+
+
+    def receive_packet(self):
+        dataIn = ()
+        try:
+            if (pyusb1 is False):
+                dataIn = self.handle.bulkRead(0x81, 64, 20)
+            else:
+                dataIn = self.handle.read(0x81, 64, timeout=20)
+        except usb.USBError as e:
+            try:
+                if e.backend_error_code == -7 or e.backend_error_code == -116:
+                    # Normal, the read was empty
+                    pass
+                else:
+                    raise IOError("Crazyflie disconnected")
+            except AttributeError as e:
+                # pyusb < 1.0 doesn't implement getting the underlying error
+                # number and it seems as if it's not possible to detect
+                # if the cable is disconnected. So this detection is not
+                # supported, but the "normal" case will work.
+                pass
+
+        return dataIn
+
+
+
+#Private utility functions
+def _send_vendor_setup(handle, request, value, index, data):
+    if pyusb1:
+        handle.ctrl_transfer(usb.TYPE_VENDOR, request, wValue=value,
+                             wIndex=index, timeout=1000, data_or_wLength=data)
+    else:
+        handle.controlMsg(usb.TYPE_VENDOR, request, data, value=value,
+                          index=index, timeout=1000)
+
+
+def _get_vendor_setup(handle, request, value, index, length):
+    if pyusb1:
+        return handle.ctrl_transfer(usb.TYPE_VENDOR | 0x80, request,
+                                    wValue=value, wIndex=index, timeout=1000,
+                                    data_or_wLength=length)
+    else:
+        return handle.controlMsg(usb.TYPE_VENDOR | 0x80, request, length,
+                                 value=value, index=index, timeout=1000)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/cfusb.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a9e4663144f91afb1d934d409fa194847e80b9b9
GIT binary patch
literal 5866
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
XKHYQ!uboceX~9*uYR$#dakKef&c_KZ

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.py
new file mode 100755
index 00000000..8bdcfebe
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+USB driver for the Crazyradio USB dongle.
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Crazyradio']
+
+
+import os
+import usb
+import logging
+logger = logging.getLogger(__name__)
+
+#USB parameters
+CRADIO_VID = 0x1915
+CRADIO_PID = 0x7777
+
+# Dongle configuration requests
+#See http://wiki.bitcraze.se/projects:crazyradio:protocol for documentation
+SET_RADIO_CHANNEL = 0x01
+SET_RADIO_ADDRESS = 0x02
+SET_DATA_RATE = 0x03
+SET_RADIO_POWER = 0x04
+SET_RADIO_ARD = 0x05
+SET_RADIO_ARC = 0x06
+ACK_ENABLE = 0x10
+SET_CONT_CARRIER = 0x20
+SCANN_CHANNELS = 0x21
+LAUNCH_BOOTLOADER = 0xFF
+
+try:
+    import usb.core
+    pyusb_backend = None
+    if os.name == "nt":
+        import usb.backend.libusb0 as libusb0
+        pyusb_backend = libusb0.get_backend()
+    pyusb1 = True
+except:
+    pyusb1 = False
+
+
+def _find_devices():
+    """
+    Returns a list of CrazyRadio devices currently connected to the computer
+    """
+    ret = []
+
+    if pyusb1:
+        for d in usb.core.find(idVendor=0x1915, idProduct=0x7777, find_all=1, backend=pyusb_backend):
+            ret.append(d)
+    else:
+        busses = usb.busses()
+        for bus in busses:
+            for device in bus.devices:
+                if device.idVendor == CRADIO_VID:
+                    if device.idProduct == CRADIO_PID:
+                        ret += [device, ]
+    return ret
+
+
+class _radio_ack:
+    ack = False
+    powerDet = False
+    retry = 0
+    data = ()
+
+
+class Crazyradio:
+    """ Used for communication with the Crazyradio USB dongle """
+    #configuration constants
+    DR_250KPS = 0
+    DR_1MPS = 1
+    DR_2MPS = 2
+
+    P_M18DBM = 0
+    P_M12DBM = 1
+    P_M6DBM = 2
+    P_0DBM = 3
+
+    def __init__(self, device=None, devid=0):
+        """ Create object and scan for USB dongle if no device is supplied """
+        if device is None:
+            try:
+                device = _find_devices()[devid]
+            except Exception:
+                print "Cannot find a Crazyradio Dongle"
+                raise Exception("Cannot find a Crazyradio Dongle")
+
+        self.dev = device
+
+        if (pyusb1 is True):
+            self.dev.set_configuration(1)
+            self.handle = self.dev
+            self.version = float("{0:x}.{1:x}".format(self.dev.bcdDevice >> 8,
+                                 self.dev.bcdDevice & 0x0FF))
+        else:
+            self.handle = self.dev.open()
+            self.handle.setConfiguration(1)
+            self.handle.claimInterface(0)
+            self.version = float(self.dev.deviceVersion)
+
+        if self.version < 0.3:
+            raise "This driver requires Crazyradio firmware V0.3+"
+            print "This driver requires Crazyradio firmware V0.3+"
+
+        if self.version < 0.4:
+            logger.warning("You should update to Crazyradio firmware V0.4+")
+            print "You should update to Crazyradio firmware V0.4+"
+
+
+        #Reset the dongle to power up settings
+        self.set_data_rate(self.DR_2MPS)
+        self.set_channel(2)
+        self.arc = -1
+        if self.version >= 0.4:
+            self.set_cont_carrier(False)
+            self.set_address((0xE7,) * 5)
+            self.set_power(self.P_0DBM)
+            self.set_arc(3)
+            self.set_ard_bytes(32)
+        print "crazyradio initialized"
+
+    def close(self):
+        if (pyusb1 is False):
+            if self.handle:
+                self.handle.releaseInterface()
+                self.handle.reset()
+        else:
+            if self.dev:
+                self.dev.reset()
+
+        self.handle = None
+        self.dev = None
+
+    ### Dongle configuration ###
+    def set_channel(self, channel):
+        """ Set the radio channel to be used """
+        _send_vendor_setup(self.handle, SET_RADIO_CHANNEL, channel, 0, ())
+
+    def set_address(self, address):
+        """ Set the radio address to be used"""
+        if len(address) != 5:
+            print "Crazyradio: the radio address shall be 5 bytes long"
+            raise Exception("Crazyradio: the radio address shall be 5"
+                            " bytes long")
+
+        _send_vendor_setup(self.handle, SET_RADIO_ADDRESS, 0, 0, address)
+
+    def set_data_rate(self, datarate):
+        """ Set the radio datarate to be used """
+        _send_vendor_setup(self.handle, SET_DATA_RATE, datarate, 0, ())
+
+    def set_power(self, power):
+        """ Set the radio power to be used """
+        _send_vendor_setup(self.handle, SET_RADIO_POWER, power, 0, ())
+
+    def set_arc(self, arc):
+        """ Set the ACK retry count for radio communication """
+        _send_vendor_setup(self.handle, SET_RADIO_ARC, arc, 0, ())
+        self.arc = arc
+
+    def set_ard_time(self, us):
+        """ Set the ACK retry delay for radio communication """
+        # Auto Retransmit Delay:
+        # 0000 - Wait 250uS
+        # 0001 - Wait 500uS
+        # 0010 - Wait 750uS
+        # ........
+        # 1111 - Wait 4000uS
+
+        # Round down, to value representing a multiple of 250uS
+        t = int((us / 250) - 1)
+        if (t < 0):
+            t = 0
+        if (t > 0xF):
+            t = 0xF
+        _send_vendor_setup(self.handle, SET_RADIO_ARD, t, 0, ())
+
+    def set_ard_bytes(self, nbytes):
+        _send_vendor_setup(self.handle, SET_RADIO_ARD, 0x80 | nbytes, 0, ())
+
+    def set_cont_carrier(self, active):
+        if active:
+            _send_vendor_setup(self.handle, SET_CONT_CARRIER, 1, 0, ())
+        else:
+            _send_vendor_setup(self.handle, SET_CONT_CARRIER, 0, 0, ())
+
+    def _has_fw_scan(self):
+        #return self.version >= 0.5
+        # FIXME: Mitigation for Crazyradio firmware bug #9
+        return False
+
+    def scan_selected(self, selected, packet):
+        result = ()
+        for s in selected:
+            self.set_channel(s["channel"])
+            self.set_data_rate(s["datarate"])
+            status = self.send_packet(packet)
+            if status and status.ack:
+                result = result + (s,)
+
+        return result
+
+
+    def scan_channels(self, start, stop, packet):
+        if self._has_fw_scan():  # Fast firmware-driven scan
+            _send_vendor_setup(self.handle, SCANN_CHANNELS, start, stop,
+                               packet)
+            return tuple(_get_vendor_setup(self.handle, SCANN_CHANNELS,
+                                           0, 0, 64))
+        else:  # Slow PC-driven scan
+            result = tuple()
+            for i in range(start, stop + 1):
+                self.set_channel(i)
+                status = self.send_packet(packet)
+                if status and status.ack:
+                    result = result + (i,)
+            return result
+
+    ### Data transferts ###
+    def send_packet(self, dataOut):
+        """ Send a packet and receive the ack from the radio dongle
+            The ack contains information about the packet transmition
+            and a data payload if the ack packet contained any """
+        ackIn = None
+        data = None
+        try:
+            if (pyusb1 is False):
+                self.handle.bulkWrite(1, dataOut, 1000)
+                data = self.handle.bulkRead(0x81, 64, 1000)
+            else:
+                self.handle.write(endpoint=1, data=dataOut, timeout=1000)
+                data = self.handle.read(0x81, 64, timeout=1000)
+        except usb.USBError:
+            pass
+
+        if data is not None:
+            ackIn = _radio_ack()
+            if data[0] != 0:
+                ackIn.ack = (data[0] & 0x01) != 0
+                ackIn.powerDet = (data[0] & 0x02) != 0
+                ackIn.retry = data[0] >> 4
+                ackIn.data = data[1:]
+            else:
+                ackIn.retry = self.arc
+
+        return ackIn
+
+
+#Private utility functions
+def _send_vendor_setup(handle, request, value, index, data):
+    if pyusb1:
+        handle.ctrl_transfer(usb.TYPE_VENDOR, request, wValue=value,
+                             wIndex=index, timeout=1000, data_or_wLength=data)
+    else:
+        handle.controlMsg(usb.TYPE_VENDOR, request, data, value=value,
+                          index=index, timeout=1000)
+
+
+def _get_vendor_setup(handle, request, value, index, length):
+    if pyusb1:
+        return handle.ctrl_transfer(usb.TYPE_VENDOR | 0x80, request,
+                                    wValue=value, wIndex=index, timeout=1000,
+                                    data_or_wLength=length)
+    else:
+        return handle.controlMsg(usb.TYPE_VENDOR | 0x80, request, length,
+                                 value=value, index=index, timeout=1000)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/drivers/crazyradio.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d1609aa6f8b03c5cca508565b9065d939f830156
GIT binary patch
literal 9622
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

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.py
new file mode 100755
index 00000000..dd277b67
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Various utilities that is needed by the cflib.
+"""
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/__init__.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9cb912d8126dc08dbd5fbdcd9e63c645575bff6f
GIT binary patch
literal 241
zcmYL@L23d~42GZWLK*N3zU6E*J%ES{=}Nj8C`A|H`Ci1p8)ukgDC5C;h+d}`XhsAB
z`I7vQKY9I|7eB|}ujn2zvAVUC!jPOh7$>eOUq<cCP<)1tn_hgcoLQN~>m{gX@mHcm
z$)5&O$j5W$)2jF=M@yX=5K|KoOKaT|QrQTXrY4A8xDB>Ouv@-|{eD>uqEtiM3VsVn
q7a`#}<cy|(#Ex4PxF3S?9RWnXvLde9L1*T>uA9_n+AORWOZN{)qCczv

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.py b/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.py
new file mode 100755
index 00000000..7a90f058
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2011-2013 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Callback objects used in the Crazyflie library
+"""
+
+__author__ = 'Bitcraze AB'
+__all__ = ['Caller']
+
+
+class Caller():
+    """ An object were callbacks can be registered and called """
+
+    def __init__(self):
+        """ Create the object """
+        self.callbacks = []
+
+    def add_callback(self, cb):
+        """ Register cb as a new callback. Will not register duplicates. """
+        if ((cb in self.callbacks) is False):
+            self.callbacks.append(cb)
+
+    def remove_callback(self, cb):
+        """ Un-register cb from the callbacks """
+        self.callbacks.remove(cb)
+
+    def call(self, *args):
+        """ Call the callbacks registered with the arguments args """
+        for cb in self.callbacks:
+            cb(*args)
diff --git a/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.pyc b/pps_ws/src/d_fall_pps/crazyradio/cflib/utils/callbacks.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7a74eeba740be7823fb8adb7248b5c4ead6e1897
GIT binary patch
literal 1737
zcmcgs&2AGh5T4znp$QZ!0VfVvAf!FeZb3p^P(_=nT&f;6ph6I`ylW@fbpKd8grIUN
zN1lg=;B|NbX1qT|a3+yvJegU0zWHYC&abV(kM}>{Mo|AcxV}Wuk5Ng0F=_|K4oDZK
zXt-p^4Uir925<@RZrlOcg^|~zU3;hoLy;t*h|XCa&Q)YITWBR&oG~*~Y*>mft4R_o
zmc(Hx%GE$$$4!T^i7=wr!J(m(=nPf1upIgXML$KQ5EcSLjZhrIK^R<XbZcZ|gxIy7
z9t0zAgZ4g0#SXF>7F()Pv8cwX(aKn;SgEG5HW-pjWYWgbj0gaUi!wyfTd1Z07XaK+
zH1a5<Z==E*l`zVdTT#GHJ=$#gsM?H`t4q67GVyQ)tMqHM{8^qVpD3|p`A0j);^bs!
zv_fpDUlf`zwXe&_mwbXHaP;^(D&xXvKSFv#e_`ST?WVrnVr4e*V9w(#Hk?x`O_0Hj
zXlcY2N6o)*sJJ=6c@K;O=bKP^wP)^9^wI$*`?L0a+{J>LKNf{dXeL;umMzP>>?}?a
zmgT19naM?w#1ZnPcaf$Jo)K8CsAb0@(aO$)C<>KH%W*(Z4XG$x2WvMllaP`(R1!Iw
z+6L}j=U?vbV4xyyjto{b;gr_%@erfzO}5jF8PPN;^VAMYlR+a9R9{5|U58So`6qQ%
zen8H36o0Et-q!yFUCrcBr>JElugL!Z)#rOP0Y@LU;VaIH9hdKTfj$o4?1~F}ABQ-g
ze-+<wgZFM3o0*M@a=J)WhK~ta#1Cb*{8S@jXM&nS;kB_t38`t%aIHsT+wrd9zxu9a
zB2}E50eU!3bGb<9FMYtd%p=YNJMiQP=y3?>ExhWhwf@_C5MVBNgrW&w&*{1SZhxcS
sJ*@yxaluVgHGHu!v%GBK)2qYn5$nQhIek&FxPPB;(=F(^J*VIK4YlH;tN;K2

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/crazyradio/cfzmq.py b/pps_ws/src/d_fall_pps/crazyradio/cfzmq.py
new file mode 100755
index 00000000..66d2b8c1
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/cfzmq.py
@@ -0,0 +1,366 @@
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __                           
+#  +------+      / __ )(_) /_______________ _____  ___ 
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2015 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#  
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA  02110-1301, USA.
+
+"""
+Server used to connect to a Crazyflie using ZMQ.
+"""
+
+import sys
+import os
+import logging
+import signal
+import zmq
+import Queue
+from threading import Thread
+import cflib.crtp
+from cflib.crazyflie import Crazyflie
+from cflib.crazyflie.log import LogConfig
+
+if os.name == 'posix':
+    print 'Disabling standard output for libraries!'
+    stdout = os.dup(1)
+    os.dup2(os.open('/dev/null', os.O_WRONLY), 1)
+    sys.stdout = os.fdopen(stdout, 'w')
+
+# set SDL to use the dummy NULL video driver, 
+#   so it doesn't need a windowing system.
+os.environ["SDL_VIDEODRIVER"] = "dummy"
+
+# Main command socket for control (ping/pong)
+ZMQ_SRV_PORT = 2000
+# Log data socket (publish)
+ZMQ_LOG_PORT = 2001
+# Param value updated (publish)
+ZMQ_PARAM_PORT = 2002
+# Async event for connection, like connection lost (publish)
+ZMQ_CONN_PORT = 2003
+# Control set-poins for Crazyflie (pull)
+ZMQ_CTRL_PORT = 2004
+
+# Timeout before giving up when verifying param write
+PARAM_TIMEOUT = 2
+# Timeout before giving up connection
+CONNECT_TIMEOUT = 5
+# Timeout before giving up adding/starting log config
+LOG_TIMEOUT = 10
+
+logger = logging.getLogger(__name__)
+
+class _SrvThread(Thread):
+
+    def __init__(self, socket, log_socket, param_socket, conn_socket, cf, *args):
+        super(_SrvThread, self).__init__(*args)
+        self._socket = socket
+        self._log_socket = log_socket
+        self._param_socket = param_socket
+        self._conn_socket = conn_socket
+        self._cf = cf
+
+        self._cf.connected.add_callback(self._connected)
+        self._cf.connection_failed.add_callback(self._connection_failed)
+        self._cf.connection_lost.add_callback(self._connection_lost)
+        self._cf.disconnected.add_callback(self._disconnected)
+        self._cf.connection_requested.add_callback(self._connection_requested)
+        self._cf.param.all_updated.add_callback(self._tocs_updated)
+        self._cf.param.all_update_callback.add_callback(self._all_param_update)
+
+        self._conn_queue = Queue.Queue(1)
+        self._param_queue = Queue.Queue(1)
+        self._log_started_queue = Queue.Queue(1)
+        self._log_added_queue = Queue.Queue(1)
+
+        self._logging_configs = {}
+
+    def _connection_requested(self, uri):
+        conn_ev = {"version": 1, "event": "requested", "uri": uri}
+        self._conn_socket.send_json(conn_ev)
+
+    def _connected(self, uri):
+        conn_ev = {"version": 1, "event": "connected", "uri": uri}
+        self._conn_socket.send_json(conn_ev)
+
+    def _connection_failed(self, uri, msg):
+        logger.info("Connection failed to {}: {}".format(uri, msg))
+        resp = {"version": 1, "status": 1, "msg": msg}
+        self._conn_queue.put_nowait(resp)
+        conn_ev = {"version": 1, "event": "failed", "uri": uri, "msg": msg}
+        self._conn_socket.send_json(conn_ev)
+
+    def _connection_lost(self, uri, msg):
+        conn_ev = {"version": 1, "event": "lost", "uri": uri, "msg": msg}
+        self._conn_socket.send_json(conn_ev)
+
+    def _disconnected(self, uri):
+        conn_ev = {"version": 1, "event": "disconnected", "uri": uri}
+        self._conn_socket.send_json(conn_ev)
+
+    def _tocs_updated(self):
+        # First do the log
+        log_toc = self._cf.log.toc.toc
+        log = {}
+        for group in log_toc:
+            log[group] = {}
+            for name in log_toc[group]:
+                log[group][name] = {"type": log_toc[group][name].ctype}
+        # The the params
+        param_toc = self._cf.param.toc.toc
+        param = {}
+        for group in param_toc:
+            param[group] = {}
+            for name in param_toc[group]:
+                param[group][name] = {
+                    "type": param_toc[group][name].ctype,
+                    "access": "RW" if param_toc[group][name].access == 0 else "RO",
+                    "value": self._cf.param.values[group][name]}
+
+        resp = {"version": 1, "status": 0, "log": log, "param": param}
+        self._conn_queue.put_nowait(resp)
+
+    def _handle_scanning(self):
+        resp = {"version": 1}
+        interfaces = cflib.crtp.scan_interfaces()
+        resp["interfaces"] = []
+        for i in interfaces:
+            resp["interfaces"].append({"uri": i[0], "info": i[1]})
+        return resp
+
+    def _handle_connect(self, uri):
+        self._cf.open_link(uri)
+        return self._conn_queue.get(block=True)
+
+    def _logging_started(self, conf, started):
+        out = {"version": 1, "name": conf.name}
+        if started:
+            out["event"] = "started"
+        else:
+            out["event"] = "stopped"
+        self._log_socket.send_json(out)
+        self._log_started_queue.put_nowait(started)
+
+    def _logging_added(self, conf, added):
+        out = {"version": 1, "name": conf.name}
+        if added:
+            out["event"] = "created"
+        else:
+            out["event"] = "deleted"
+        self._log_socket.send_json(out)
+        self._log_added_queue.put_nowait(added)
+
+    def _handle_logging(self, data):
+        resp = {"version": 1}
+        if data["action"] == "create":
+            lg = LogConfig(data["name"], data["period"])
+            for v in data["variables"]:
+                lg.add_variable(v)
+            lg.started_cb.add_callback(self._logging_started)
+            lg.added_cb.add_callback(self._logging_added)
+            try:
+                lg.data_received_cb.add_callback(self._logdata_callback)
+                self._logging_configs[data["name"]] = lg
+                self._cf.log.add_config(lg)
+                lg.create()
+                self._log_added_queue.get(block=True, timeout=LOG_TIMEOUT)
+                resp["status"] = 0
+            except KeyError as e:
+                resp["status"] = 1
+                resp["msg"] = str(e)
+            except AttributeError as e:
+                resp["status"] = 2
+                resp["msg"] = str(e)
+            except Queue.Empty:
+                resp["status"] = 3
+                resp["msg"] = "Log configuration did not start"
+        if data["action"] == "start":
+            try:
+                self._logging_configs[data["name"]].start()
+                self._log_started_queue.get(block=True, timeout=LOG_TIMEOUT)
+                resp["status"] = 0
+            except KeyError as e:
+                resp["status"] = 1
+                resp["msg"] = "{} config not found".format(str(e))
+            except Queue.Empty:
+                resp["status"] = 2
+                resp["msg"] = "Log configuration did not stop"
+        if data["action"] == "stop":
+            try:
+                self._logging_configs[data["name"]].stop()
+                self._log_started_queue.get(block=True, timeout=LOG_TIMEOUT)
+                resp["status"] = 0
+            except KeyError as e:
+                resp["status"] = 1
+                resp["msg"] = "{} config not found".format(str(e))
+            except Queue.Empty:
+                resp["status"] = 2
+                resp["msg"] = "Log configuration did not stop"
+        if data["action"] == "delete":
+            try:
+                self._logging_configs[data["name"]].delete()
+                self._log_added_queue.get(block=True, timeout=LOG_TIMEOUT)
+                resp["status"] = 0
+            except KeyError as e:
+                resp["status"] = 1
+                resp["msg"] = "{} config not found".format(str(e))
+            except Queue.Empty:
+                resp["status"] = 2
+                resp["msg"] = "Log configuration did not stop"
+
+        return resp
+
+    def _handle_param(self, data):
+        resp = {"version": 1}
+        group = data["name"].split(".")[0]
+        name = data["name"].split(".")[1]
+        self._cf.param.add_update_callback(group=group, name=name,
+                                           cb=self._param_callback)
+        try:
+            self._cf.param.set_value(data["name"], str(data["value"]))
+            answer = self._param_queue.get(block=True, timeout=PARAM_TIMEOUT)
+            resp["name"] = answer["name"]
+            resp["value"] = answer["value"]
+            resp["status"] = 0
+        except KeyError as e:
+            resp["status"] = 1
+            resp["msg"] = str(e)
+        except AttributeError as e:
+            resp["status"] = 2
+            resp["msg"] = str(e)
+        except Queue.Empty:
+            resp["status"] = 3
+            resp["msg"] = "Timeout when setting parameter" \
+                          "{}".format(data["name"])
+        return resp
+
+    def _all_param_update(self, name, value):
+        resp = {"version": 1, "name": name, "value": value}
+        self._param_socket.send_json(resp)
+
+    def _param_callback(self, name, value):
+        group = name.split(".")[0]
+        name_short = name.split(".")[1]
+        self._cf.param.remove_update_callback(group=group, name=name_short)
+        self._param_queue.put_nowait({"name": name, "value": value})
+
+    def _logdata_callback(self, ts, data, conf):
+        out = {"version": 1, "name": conf.name, "event": "data",
+               "timestamp": ts, "variables": {}}
+        for d in data:
+            out["variables"][d] = data[d]
+        self._log_socket.send_json(out)
+
+    def run(self):
+        logger.info("Starting server thread")
+        while True:
+            # Wait for the command
+            cmd = self._socket.recv_json()
+            response = {"version": 1}
+            logger.info("Got command {}".format(cmd))
+            if cmd["cmd"] == "scan":
+                response = self._handle_scanning()
+            elif cmd["cmd"] == "connect":
+                response = self._handle_connect(cmd["uri"])
+            elif cmd["cmd"] == "disconnect":
+                self._cf.close_link()
+                response["status"] = 0
+            elif cmd["cmd"] == "log":
+                response = self._handle_logging(cmd)
+            elif cmd["cmd"] == "param":
+                response = self._handle_param(cmd)
+            else:
+                response["status"] = 0xFF
+                response["msg"] = "Unknown command {}".format(cmd["cmd"])
+            self._socket.send_json(response)
+
+
+class _CtrlThread(Thread):
+    def __init__(self, socket, cf, *args):
+        super(_CtrlThread, self).__init__(*args)
+        self._socket = socket
+        self._cf = cf
+
+    def run(self):
+        while True:
+            cmd = self._socket.recv_json()
+            self._cf.commander.send_setpoint(cmd["roll"], cmd["pitch"],
+                                             cmd["yaw"], cmd["thrust"])
+
+
+class ZMQServer():
+    """Crazyflie ZMQ server"""
+
+    def __init__(self, base_url):
+        """Start threads and bind ports"""
+        cflib.crtp.init_drivers(enable_debug_driver=True)
+        self._cf = Crazyflie(ro_cache=sys.path[0]+"/cflib/cache",
+                             rw_cache=sys.path[1]+"/cache")
+
+        signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+        self._base_url = base_url
+        self._context = zmq.Context()
+
+        cmd_srv = self._bind_zmq_socket(zmq.REP, "cmd", ZMQ_SRV_PORT)
+        log_srv = self._bind_zmq_socket(zmq.PUB, "log", ZMQ_LOG_PORT)
+        param_srv = self._bind_zmq_socket(zmq.PUB, "param", ZMQ_PARAM_PORT)
+        ctrl_srv = self._bind_zmq_socket(zmq.PULL, "ctrl", ZMQ_CTRL_PORT)
+        conn_srv = self._bind_zmq_socket(zmq.PUB, "conn", ZMQ_CONN_PORT)
+
+        self._scan_thread = _SrvThread(cmd_srv, log_srv, param_srv, conn_srv,
+                                       self._cf)
+        self._scan_thread.start()
+
+        self._ctrl_thread = _CtrlThread(ctrl_srv, self._cf)
+        self._ctrl_thread.start()
+
+    def _bind_zmq_socket(self, pattern, name, port):
+        srv = self._context.socket(pattern)
+        srv_addr = "{}:{}".format(self._base_url, port)
+        srv.bind(srv_addr)
+        logger.info("Biding ZMQ {} server"
+                    "at {}".format(name, srv_addr))
+        return srv
+
+
+def main():
+    """Main Crazyflie ZMQ application"""
+    import argparse
+
+    parser = argparse.ArgumentParser(prog="cfzmq")
+    parser.add_argument("-u", "--url", action="store", dest="url", type=str,
+                        default="tcp://127.0.0.1",
+                        help="URL where ZMQ will accept connections")
+    parser.add_argument("-d", "--debug", action="store_true", dest="debug",
+                        help="Enable debug output")
+    (args, unused) = parser.parse_known_args()
+
+    if args.debug:
+        logging.basicConfig(level=logging.DEBUG)
+    else:
+        logging.basicConfig(level=logging.INFO)
+
+    ZMQServer(args.url)
+
+    # CRTL-C to exit
diff --git a/pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.py b/pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.py
new file mode 100755
index 00000000..1d24fd78
--- /dev/null
+++ b/pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+#
+#     ||          ____  _ __
+#  +------+      / __ )(_) /_______________ _____  ___
+#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
+#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
+#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
+#
+#  Copyright (C) 2014 Bitcraze AB
+#
+#  Crazyflie Nano Quadcopter Client
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 51
+#  Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+"""
+In order to use the Leap Motion as an input device reader the libraries from
+the Leap Motion SDK has to be placed in this directory. This can be done by
+downloading the SDK from the http://developer.leapmotion.com website, unpacking
+it and copying the following files:
+
+  * LeapSDK\lib\Leap.py                     -> leapsdk\Leap.py
+  * LeapSDK\lib\<your_arch>\LeapPython.so   -> leapsdk\LeapPython.so
+  * LeapSDK\lib\<your_arch>\libLeap.so      -> leapsdk\libLeap.so
+"""
+
+
diff --git a/pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.pyc b/pps_ws/src/d_fall_pps/crazyradio/leapsdk/__init__.pyc
new file mode 100755
index 0000000000000000000000000000000000000000..e6513482535ac9c98b047a49e71c2c2a61f75aa9
GIT binary patch
literal 651
zcmaJ;%W51k5F9%P2Z8^g4?Zj=JGvXQ<lw^^+aciKLof)9)SHo4s}_<b%j7@vCHaB0
zgdO7t5vWEz-Cb2JefV=M*5>NYbdT$Rzn^($!C<@YJL5rjyEv@>esfk_2B2gyK_Qqj
zLKzXRQAfC;;wXU5paZxNrves#I)ji<w+62CZb&*g%&<wc6R(?ZFRox@2Id|RqSpp3
zbFqlaU|m8}D9t^5tFd7nuCf#mhFMtJ2T$6%@S~ZkTven6V=3`WN;ZU^BBtK6<y~{!
z8?M>okX^w~FomeimN|qgJctEDaekuQkoUB&(Z+eqXF>p+w&_{yo@cwSH_^u#-doER
zRMl*^Pi1e~&u5yF(#edgR`Yc(BZWQ_^Sx^Sm-;UWduo;%!%LFql=!l!?_Q1=l(J-$
zTJW)ZhP>ubj<iELxMGt2g)%03;P=R~hV#ZdTpET@@-jBtk`?6R#8N32Tv3WIr+j3x
P^BvjA<LZRZnRoXWp$^Fr

literal 0
HcmV?d00001

diff --git a/pps_ws/src/d_fall_pps/msg/ControllerOutput.msg b/pps_ws/src/d_fall_pps/msg/ControlParameters.msg
similarity index 55%
rename from pps_ws/src/d_fall_pps/msg/ControllerOutput.msg
rename to pps_ws/src/d_fall_pps/msg/ControlParameters.msg
index 455f8557..bcaa8f38 100644
--- a/pps_ws/src/d_fall_pps/msg/ControllerOutput.msg
+++ b/pps_ws/src/d_fall_pps/msg/ControlParameters.msg
@@ -1,7 +1,4 @@
-this is copied from dusan and might therefore need modification
-also you need to add this file in the CMakeLists.txt
-
-
+#copied from Dusan, eventually remove onboardControllerType
 float32 roll
 float32 pitch
 float32 yaw
diff --git a/pps_ws/src/d_fall_pps/src/ViconDataNode.cpp b/pps_ws/src/d_fall_pps/src/ViconDataPublisher.cpp
similarity index 81%
rename from pps_ws/src/d_fall_pps/src/ViconDataNode.cpp
rename to pps_ws/src/d_fall_pps/src/ViconDataPublisher.cpp
index 61ba7e02..3ef987b3 100644
--- a/pps_ws/src/d_fall_pps/src/ViconDataNode.cpp
+++ b/pps_ws/src/d_fall_pps/src/ViconDataPublisher.cpp
@@ -1,13 +1,18 @@
-///////////////////////////////////////////////////////////////////////////////
+//    ROS node that publishes the data from the Vicon system
+//    Copyright (C) 2017  Dusan Zikovic, Cyrill Burgener, Marco Mueller, Philipp Friedli
 //
-// Copyright (C) OMG Plc 2009.
-// All rights reserved.  This software is protected by copyright
-// law and international treaties.  No part of this software / document
-// may be reproduced or distributed in any form or by any means,
-// whether transiently or incidentally to some other use of this software,
-// without the written permission of the copyright owner.
+//    This program is free software: you can redistribute it and/or modify
+//    it under the terms of the GNU General Public License as published by
+//    the Free Software Foundation, either version 3 of the License, or
+//    (at your option) any later version.
 //
-///////////////////////////////////////////////////////////////////////////////
+//    This program is distributed in the hope that it will be useful,
+//    but WITHOUT ANY WARRANTY; without even the implied warranty of
+//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//    GNU General Public License for more details.
+//
+//    You should have received a copy of the GNU General Public License
+//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <string.h>
 #include "DataStreamClient.h"
@@ -17,7 +22,7 @@
 using namespace ViconDataStreamSDK::CPP;
 
 int main(int argc, char* argv[]) {
-    ros::init(argc, argv, "ViconDataNode");
+    ros::init(argc, argv, "ViconDataPublisher");
 
     ros::NodeHandle nodeHandle("~");
     ros::Time::init();
-- 
GitLab