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><html><head/><body><p><span style=" font-size:12pt;">The Crazyflie Nano Quadcopter client is a multi-platform client<br/>for controlling, bootloading and logging the Crazyflie Nano<br/>Quadcopter. For more info visit our homepage.</span></p><p><a href="http://www.bitcraze.se"><span style=" text-decoration: underline; color:#0000ff;">Bitcraze Homepage</span></a><span style=" font-size:12pt;"><br/></span><a href="http://wiki.bitcraze.se"><span style=" text-decoration: underline; color:#0000ff;">Bitcraze Wiki</span></a><span style=" font-size:12pt;"><br/></span><a href="http://forum.bitcraze.se"><span style=" text-decoration: underline; color:#0000ff;">Bitcraze Forum</span></a></p><p><br/></p></body></html></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><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Cfclient #version#</span></p></body></html></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: <b>Not connected</b></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: <b>IDLE</b></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 zcmc&)TXP&o74F$xT~^jzmTV_ZvL-fcfg{<0BoK_TCCiS8YE4G6lWY>UMl-Y0NV7A_ z%&ct@yC|>=2$kZA2mU~+;F(7#;0I6?e*lVC9(c+#m+w10H*b;x3M3?TtM8Z7=klHI zseUvu_KN-LZCj;3Rs6q#5YHJ(IZCw=8>($6r=qrzs3@nZoSO2hs*QwB*HpVE=^^D0 zsot<^4=W?B8&T~M<&LP`QRUXvLkuw{O^20#O!dZ9dtCJ<RC_}8CRKY<cN<aul<G~Z z_O$BFsP>HN&8qgS>dmS4oa!A{?c=)LsPgAkZ$Y⪙KXQ-4n{Ksoj&xsjE}Ym^y*k zm~5X?&N1aoD`#9iQtF{nZ?>P{hTI9Y`=oMDsV=rPk=LD8&ZKhZR2SW+^3tcc?TzLP ziSs=~W1Bl}V=D|3KeQY-YB-+dhuaM=ZuDc<X(VBzzwcOy+epxEC9=MK(DgmH(e<L< zT`O`MR=`GMk!v|icOx%tXoo@9+wMnJ;)OwD%Mb0nh8GNu;al;};#hnP%UJdj8-u%z ztINq0@+;koD>BA4S&#P*{Ir@lOqGnWdA{H#k2)Df^jfkKMs6}B$s2tyVNKt&n=DBx zQk;z9-~Pe7$%Y>$GLmGx?ZEPr5jNS};Z|h8jkO!CtLu7FPM{a$A*JFoh?eZG4O#@r zQr}zBbc<1TQEz?`E8vK?);1E*&t^CNa1YA}@p(ipSQE#C7)J-D0wY#ZE^H6+q`-pC z5P8qsXmSlQyT=`Q9LVFgJZ{vhA!>4avd2y*uzGH%BYW(0dZE+znH@)iYik=TH(RaM zmCe;_QlIA5S~rqO)Ti0b^{Z>^Xu&QkH`lLq(sCL9`uf$4SJ;6|F;_QMH=9VPB8qPz zyR;Mb+@-DbY?t<<aM!gH5GmuM=QIK1=k^bhoiJGPy{&&i)%^pL{PIPFm@6ALir<{Z zB#6-XeIy2G4AIeKW<unh2_ZHULT@HUlrv1JX^$#rL=tu7j7kEcUzfx&<&IPQAhi#1 zfMYDAZkVE^vTr7=5Plrs1Q-1lqD^b7(G8=2L$uh64Cw{iB3}3}Qlxootbw0gy1JAn zn;gOYGm!t{n_!d#e}?)YR##EG6?MO&J^&{ozgv}31DZ(cJD`Y845|A%F|5GH4;9;W zMj7wc)$W+uJtpaKZdgWw7RpOOkF<2ER63oP&Xh`L^U_hZJC}`nyj(iZ(jO>MWfk>( zA+pSg9t)+q>OkFzQeADJ?qsQMXrS&C>$uY=%By&iR4^O`^+{1|#nePA*Dp4-Vz7~u zAD+M*LOhL#qX_XqcF<%))JaQgE%uMy<d!EILipOG@@pa}t#@kD#P3Sc;mMT9^d;Zw zZ8_HE4bI6;DC0lglX)E7^iF7Eik+DAS@!UDVJ=_}>YZ%$RFMq%iBJOdiQE?^%8rAL z5}sM7?{y$s0n`*MSEp-vzCH-H=t=U(cJex=j1km-a|%@G|A$j}gKO73)I5!}*onBm z??xtanT_%SFM&}KHRUG#{ae~(Ce~I9)=QYO=LUVrP~>)8A0udhKodgDk{awPs%<|J z17?T5Lxiy0Pm(Z@;bCX&J=f7`eZC3BlPmCGo>$O?C@7(EiCjo!+Bwg!g!@T4cWm83 z2O=ENz0yapk1o`VWLtsl`kf3~L2(NWY(K={VhI2X`tiz+6>MW+v}LiB+!DMctIIes z*xt|(h1wx4be*(}W06l?rxWW|vnX-Ac(5UhHJH=uO#5@@a<M+D#EKHpQo`=$(`b}X z8ClVGJit$fD9}$}#;+s9pFyOI1!JO8H|o`Tb-cQO9DXYc#_0-v5ieB4_>t=vRuBZI zLDZpwVa;fhm@#Mr0S}oMbxz@KHMu?E9~=-haz9?Twp`yl!}3&DuLjl@C@rMejQVbq zGYN@=wR&zGTidQC5NgyR(&hw){}!j<x|K0ue0=x6!N#y}Sbyw!ucD&wDDEE^pvu$C z=mGFk5NB8_VewO9!Nk_fjgbfb5o0FAJf?QX=?Z1-0R!^EgG)v_{iIC)>rb1$Sc{%s z=YzjxtDlnYf0gc7?NB~jd9}gsv*y#%9FympE%u|=SL89=@W62C>>!q=-HZ(POBoK$ zi@t+|EH9pO&koQE=;krn9UsU8=PYEj4a9NX5J=yODdep%Y2-4k-4L2A&8jaqE^S?2 zySBc1X=&?nOy1v&4g@WRSttDWoszG29>@|ZCrSqVGWdMyH1RVS2g*XsA(3ZT>fX>w zCng`JZM4IfsXRA|pGBQXZJMM@$^6Fia5M`A)#Ml&HPBv}=NP?+NOaM4%a2|21(s11 z&6ij~y$pY18(WB%u?P0UJe%&l<hB=k5K=K8D7tO=eOKCR{VGD8*&MbvKgUt2T1}b+ zlfcC!Wo3jY^!?4q>m_xR?8BAWksP&V1Y90cfd{TH3PK7t_FX6l4u{EC`cVWAaV>D% zdnH{#uZgQU8cjh=w#6C0j7S+%#x%6Wbox7|^A-M|Fm#UfI-j+gF0>0Rt?1U1NRZ3% z2Orj;v@GN*m3pxilawX?JQtHYg$|F|w=mF+%*$wBB9mxjoY0ZzQ!?0h5k3h#%6>^k zy<hlJ8qSx^2k9~hf1!W1bvTM02#4kh;?e(ZE0l@>=qvj<JmWUN9oGfkzybX9!#Rt} zoEdpIV07li7VT<-;m~#Ck+j5tGaE=IWKjWp8sMd%h@9v4yrguX)CfmQ1@p6*@<#}% ziH8tUaWLcn=NdBkzV8=i7^VplS=iO8<O?&88%B8e+Kdm<g^0_%#OMm6tBjTzQ4Hid ztw20>s{TZyQS+J7`wCkk=;-hm#e5!f{1_n~<!q@=`#2Ihi%LzVLUuKK><T5wE`ww! zTv;ByaErOd)He{7v=q1Q-hq}siVcgM5hr4mQ~m^V7u-P=#85-vS;ap`l$7){54@@c zL$C{Q9JKZcz&GA98>n__YHvtIUGfHS)*gfdX$&D`!o6sQ^N=6O7$|6znv6c)Bm--D zI1K%vw3R!gbhr%a=v$St&3Nlv{7&P3<D4Ux#3f52d!{iF9OU8XRFjZEE^cvwy$x55 z=i<0q{cZDWNQrGRUuNp7jEEvMlYfP&bBqW%3a3ZLqLuE)f&?<SaTLdFp2t#tiV)Ll zQ?Sk_@jGqIRcMjN4V!9=Ay)*1P@2QFf0IIbVQOuRi6I4{yu7q@Vd>(93wo;!6eqkn zDf_~7pS3E~+|H?riw~i|Aqw?b%=RHd*%b3M8HBIjMk3$rL!}--*MUbx=`jno7Tph0 zFk6*uCg=}pk_A|o_Z%oq=t;0UB;jH0F*QKQCpu7g=?;OUR&TjV6u;drfxN<~lx_sd zl;j??Pkn_c(Uz|=wT|dWSc{YV3_<QM2?yo3(j=#u#I+_lMG$hQ<AipnW6}>d1*D5o z6fAy&Wv?)znG!{8wwR(A%i~L&MDpJY65(f)=W23#hEjBhti*{fA;jFmaAnx2KU1$E z;0^)5wfb<qc5<R#HR<Wt2=c=-qQQ%Qg~-J{zM)*avx#-Zn*q|`D!du6a9IB2aS+^t z*8<%V<qz?cQ_mWYWeL0)<U`@{Ko5l{gpUt3$%mmwm<Z1aM)sUgk;Du=ZdT1wVKXsD zZOz2-?A>CXPY%3V%XLK+nH6s9Jwz}IFoh5kEb}qE#3I-N-Vcb7b$}BfOd>8!4DlI& z2~pup9LqKSI2~FRP2@#*(9}g!QsfjEOq{J#Sc(u|cud5p7v6C<Lw&DjQt=2Ab+&x- z3v5j5`<VOJvA4|Ita+9-G#@3=yDAsC<_4=c;h>V!7M&b?#MqAf_*;aSo~ttEsuNJ_ zvrzPNg>vWS<-iJ83uhv@Ruo$qvQWp%wbZ+Kc$CCl%S(=SQ<=`dy=UilSWk*f#<cTT zLrJc|-$#awf<L{LgEh%(Sb}>OvY$akUQzL7sTvYLQ$yC^gcN}(b#D%Zdl?Yt_29g! z!Cvo;spt_#Na2p|3=~{f8YPp*14F$b49TUn#DBu0c26#C)4Pv8u1tdB;^pxx5V241 zJ!p=hyZIa=GN;KWK4CV1hbYOffN}RNP-BU07PB^d-NW?>E|WE@$|_dx+3r4HAOwTp z5wK?^az9xBZ{G{{ZkcQPBBO8HxCeneLtPcR^$^u6FCPDZpy3IwWvj+KUSj7e6UHR$ zNe;mC^L~o^m9H=!A%XMN_7<lJU|3NL8v;gJc}KD{g;sDqPM0t>bKX#_@waUFp2P=7 zvTeeoKXFxDf=2)*%a&7$T&HilqN{q=zWfg(r{pJaOYzY-353G-j%)A5{oeC0y!c1X z%;}XO9we;-t+eQt9VGPa4KWaqP*rt@)PteSaX{ga`oK^R@H7snG^|b_g@37Efd^x4 zAka>{gOfPXm)Ha%33<bS+G!9$<E$;g>@ky<mY0{iUC~w-?k%s#NiJ);l5<i79`$Xl z(e^ES&)i2vA!4HHJNW8?&vp<Zv3MF^<*2G~No6K{&SD9m^Lj^hSN{&y{AYv|a?L~0 zj~UNa#4?i(xtNTBrE;~1Q{hE>rR1<d!x8JsHzRv>6@4UWh)`9X0TSRH|9#$?!d&xM z&<=?abq0gHT_Y}rJ_P>7=@zr#0|$K4gjs^O(x-lW<%EwJpdrx#3I>z1M9e(;7bWue zkKqPO%A-$YqjN$)2CN>O0!Bh3=y(9`V6L<nfF8YNVHe(_w&aG{)E`0cW*VogKYCbP zT-5F#)wlUB=}SU_sk&YLjf~vFo@VVQcXz_TJtN*@LiFpnU90aWV5d%YuR+XEDvtBZ z-BMwK>*1K{e<jS!Mc2c3QRTIsZ^S#{UEYGH9pm`Z)|FnYpV4L|$2^+nxg(m463Yuy zx3CkFYDr|KABd>XE{P)Ab`$d=GWy<Y9x3w<qe4WkFiU6TNc1QUSN#P+P&vR4Ovbn| zH*^l~d9#N8ox_Ws$OnyDE+5ob2Qvk~fHh(9XJGLwV)4%^!w|EcvNs@m3YaqQuZ)=} zdYY_6jdBavNy1L{NMX9H|2(4$jA-1C7Ep<6^><7w-#RIyYRuqjzJ}1GDJr}*lPcYO zgAuK!MzwDuC5o<#hw?;YWML<Q4~<s@i^%549@@{dn4*E#s+PLT<blaz3igoHAwmp# zWWPX&8;FK0<3`<>uS_AXgIAcYkHde1?^dtZ=k>ClMoaT`L~={k=~#VyRTiaR7$EBd zmuexYqEUPh>w9K=(YUo(yjp3Wg9PP85+CmCNJE6y<z7h>{U&E2TYmH4wd6}CjjPE) zP3j_XW29nHf0bymr8vg=phlVHs0n2;`;Kt2pLl+}sMUrk!pn$XIo$F#5B6P=^nEMY zN$7^hZgj`9-H!hBuw&!%uUH<@1QWjYy5~^r(NH^BM|N^@S$0W(a(J1i81r1j_r@uX 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: <b>Not connected</b></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: <b>IDLE</b></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>--></string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton"> + <property name="text"> + <string><--</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><html><head/><body><p>Quickconnect using the last connection parameters</p></body></html></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><html><head/><body><p>Connect to a Crazyflie</p></body></html></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><html><head/><body><p>Select what flightmode to use:</p><p> * Safe prevents crashing</p><p> * Crazy does not prevent crashing :)</p></body></html></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 zcmdT|TaO!A5w7-iJoig7NmhY13xg~~G7qeTfG8w0BeFr+VbkL%!U9_D?qj<>-EODP zagte0o`SfESN;?{@Hcqp2f+7LdoE!&4@hVt>^aAE?p1ZFzN$0Tf37TMfBjQ8lKH2K z-v=o6Z>VCC9%@f|p2QUyf~ZJbmAEEpReB&wx+cBa8C{oNUFo`{4au6)Yf82ty#>iy z(rbywH4RA@C0ml-l4Q%$Tb67^dMlExN^e!NHR-LD^O};@B-8CZ(~|MJn5v97BwmnP zaZ7Hz1z#Y0n-VXIS&;FTm`xdg$tC%%$Xk(L_O6lLqs}sgdWLGTo9Agif88GBMca<@ zSIK19o`%t}aklM7p=&3$ZOz2BUnU_sOgqZ6Y&J=v&?Wh#ohFmx_N$SZv}4ns4To5M zchN3k=e@*5Mfjy@KiqTcXzv$pdNc})<Gu34<X`w4cBo?4K=st^=Y?@~B_GWa$B|hQ zb=c!7+U*wbGd+3Xj?&z@#j{2-oP?=sa!Pkpm@w82j~+ideE6i}q1)A1C7DU^EB1S+ zcB4U*CJ=0Qmh8HcC1ZD^GcM<PxeG?#;eOs9aU*`n*dB`gBPs(E!6l%s@NS@Sm;wk> z7hF!Safxoyl5i_91sD^FmY78@fk~jr`AfVk4lPSuS7L?M!k$%$n@X(l&e^jrumKVq z;vaRW8~Z7$Bi!lxF4Vl|LL&-A^;7j!tNlAFhd)pL0M;rpuE=Rc{w(65J+5l6C*G-! z2Q{|Gbs0A(B*cSAaZQFbN~J5BGKPJ&bRLAB_by%PT|Vz!;l$sI46s#Qey6I@O;#@r zG|mRxn&86RhHA1Y*RVJKz)!F#Ls4RjH+0llfJ}#KSebP#G!Bx&M6_zl))#r<cK1i& zM2+VlbYVnOVxelbP%JHjqADz$s%DNlR9?C7<HOSWsKTIhs<MNdOfM|cnsm@Xm)PH+ z+8yPY+3nBWXLnlUW3WZj48J@Xz-I51hHLLkPuwV<?50WoJ=T2gb7$6!6%_kk8fN`C zy#Ih_qR4+G8pZ#`v##>*uc1*=R7Oc{kV3f%dHhYB^N?rpz~mobu5%5P0*ReXP2tlQ zRO}!~CW#9Ig$irjZ2Bxws2G_P6SX_dhuy*$m=U%#?lYi?DV~IVlUheui&7I7do$<q ziO$De04(E@&L<OGm7b;Zp5#MCDI2~t^EG59U^|FmQtk$u$poMZ<eb|`U`s&Lv3%x+ z`B%qy2g$^#ltn(9#6E3S4+}CI<bwg2*HJx3Vb<rp_}o=ZIxL1(Z|UOFuE75M>p1@h zD3-S*-nO?=X;p7mx4rGkE$^0hyAn}0Mg*396uSj~fp@{p#_(p5V$Fl!0(L;ZsZtt9 zii<=Ocr$l-E@;ZlH#Ip$lvI?3myZ_-MyEB|ep>}zl_OYRj?@JRc6p?6Hqs>CjhD1u zW=y?2YhgZ%Q-D^hWQ5xTkZ}^Wyu9}Eq(NP_-{4TNy2f~Zs&T?&dqc*X<Q(92*4{d6 zPar1|jJ9;BCZB*3OXS&!6QWB=+k{<~FuoJ-VyMa{&0r_~etXBFu$yG*G_>$6w9XX! zBNH7fEGAZI6Z_mu9pfoo<arVgjeG1&=6`|#|J$r+h5mOqPveGh1Wv!k4(@%B*l8M` z_<3s72B!eo(scl|6O*dN(lN?#9t74ELE!IjJ<TDUP7Q4RCfoGTFpj(V^oe<CQZnK+ zuZfC0O}jZeI$Wf7ujxdb6bks$V?7<oi_z2JF>x{&AiP06rB8sm68NGNKjT@HNxU_0 zxzhGN!uPx0$Cc~embYG6_s-#&H?YsyAECPJ5*}h3+5uuMErWuTxmBqvT7V*ws`A3& zuU6J?fQ#oSmi8c(l?%DZugb*{`0zZ6THYsvSldo;Kc@H(yt-2M)ly*bhhb)Z1-{h9 z<o`?tLIz~$hJA_=e2p0ACUy^l=O%`P4|vOFjhWpUDxvK$&6|%Qtt-ba8Q}u9DT5Ao zjt1(tWik{X-!jQdL{Hu`d^w21H0|T{;kVJDK5DYWDRj)2@Gath3!@*%h!uv+B<!cA z4=yalquZ{aeAP|=*E;lng42BNs%k7umLrpRM{E9lPWdUT52+@?f1FtAX$11?^>ps` zoQ?MdjOh%!Xe?#i*dmn-NA4hxj?19)5RbeMb^lCrr-cC+nV1}r7X;*Y;DB&TBvVNH z3y4xSI1$1SgapFf!m|husW;xm7!n2KChWw#(z7&sfV0^n?a};%zJ~*0h)5;ytm1;I zy#7SJvndC^z9AP$4^}YO!IBrLJZHYsVs_%GgEBZCYPz7<hSZm#*RG=5@};h+<K4)< z)WG=4Rxz++7d7wDIGWosiid8b#2A^X23mV!OGkN~SZ6gM&O)TgNl~uI5ys1?PF1}s z5?8+bqcpchU;JgV%0y9fg24oErECoRAENV0k>CFqC%!<@2cLMiz3bklw+@eA2i4&V zWd6P&mv~=jk{@JwJWJW6bq7J5M@VI;bA>xj{X9q7kpTbLoA>$7Q{+?6)l6C6S3Z^G zGkW?zV)bKI4_NK7I$%W>FO;IxRb=y&^bU$9Bn_|OwY+7dFs-fD#%8@$#kYZPwZp96 z@1mOPCY<5viqvkd8^Atv^K(wp4Du0YQtB^Gp1Mzc;)G_OK21>7rvrD(e;O%HluDu^ zed^m^;7qcY=3)FS>7GoD{~7z3aZSS_%mNqogD4HHRW6>E*rLzHtJkC&(7n=R?tc?3 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 zcmbVP|8g8h5$?G=-RVxhEZbNXPN;Q=DG_412;ic?P;umhG8mCsg(x|f*xI|DJMC$; zd)}FobSa5HFckms3OobP!9TnU4*=iSdnYSS6-8Onwq|yEdU|@k{(83l@0HG<@_&Bb zm)TDp|3Adz{tl6dbRms&jU+Wm>XI~MSeGu^3z9S?LmMxvc0;<2d3!;+cv-z~O1C+0 zx1`&ew-=?mD0V?cZLuABiB*<#s3qC59Ir@sMUGdcyDEkYw<TGWKa0E+`Lesl#rL*4 zbo99q>3nu*gESvcd~jSOlgtMGF!sS|nq@(0<0KdrQ|HtEk$X^SCH5rkTNlK65;zOd zFLGZN89M%cXI!MYcY!Zp#s)iO{MB@jrFJ`bI<pyPt_b3O-;O<J_%t87U{DsvI^p+E zw>$7Cb#rI>aUM*Z4U%*)u%*p?kd*0(EitDk)j=50saL%VlGw-Fot;U^DcLkgeLzd2 ziSufX&V4%FOR^7U_JSS)kmhP>F!7U;alnFAAbK{4JI~U5a;DqfPxGWWb@QnjFq-d8 z$FXxW!`mIVgdl&E`aYsygU26v_Ma8Q&*L(UdztlXRez_*2k9_Gux;sYeEr>Vabg*d z^gsC7-Kt?U#3R0aRqqmmI8gZrkNY`<1qOjWNPx}|0YqF)8-QC~8vt5E8wxb)v?Scy zYSPH}An2A0vSK)7pmEl4byCJYE%Mp94bsf^=@=7W0nZK|_Ya7n$hk-aZ;gy<GOCMj zumNW_;l~Dm>;5JlIL6|2;@D8L^-VQ%_M!Mi87<C-8nCdS7M>j3m5Uk!GU~`V8ed<c zCg#OJ_H(#m)SD&B(UOdoWwfI0Rb3fzs<yqGGK#JN@c{dFTWIVSgiSfyKd?8+llRdZ zPZ^aY1*{kbmqJ(EyPVn|^a1?bKd55nXzRiA$M?JO{jUz*+a9?h-y#4!L!G#1u;CfS z!eIv@<JceSkUhhQ^9-&-CQ_TH0jN+d%?AYnBXS%c*(5Ei$-}JZsir%e_%uU=Md<7Y zY9{Ir6h3e)w|#FDzl`xXNuqw7Wxcq6<Vg?F<@gpC*X5keW^?D6jRO-IFh-)n-9sx& zd$0CS4|MlcAV?~<+#XNW?I=p~)JGBV>aNp@Y1iI1E9SPjVOGsMrfvFMfesP!imkuk z4Y1{Lj*yD2E0B6}a9b{nV5`JjgXa!p{h=l=?hq)Lcu|+LhXz=Aa<FYK8Zxb!OAwwn z<RviU0m4^?VWc6qh}Vk+#bH~{7ig962I}BQhXlL{Zr!|S3Nrm1kZ#E)Ld({oY(g7` zFIsXoeQg2=dh^LaPc9bad{L4P%q+=KP0E%zZxN(^RR>^>OG0HCEdu*rQYj<=a`l@3 zR;y*vT7#);T=r!{wfD~vlr>I*A%iW9Fp7}xzxYHNeqD|_QaWQU+6Vv!uS>En=WPuL zG`_$?3*7V#Nv<Pb$X4zy1ZhIKaU7SRYasMBuBejgTTs242+{pnDmX&$Q_N*PJ-_D; zTx+g+&na4AvI3Ce2EATZ0I5uZJU+Gx_pvS0B2o2|8Oc@&;iqG(*#GQzJ34=0eg80u ztGxQHJ=QtS7o|=3mWv}HO6y#D4Q=-x#K&i>S|GmQ^bpA&aI19p(lh0Np9S~a?$%A{ zhul-h<P4d$A+t~y@5Lyb@Hr%+Lqc1(QH((s+z+`y8(9%2+F(5kH47<FVlPv6zR!vv ze5rAW$1s&tX`dA+vs~!Xecjz@T;^~-<a&A=2zizv4@hlSv3AOIN(Z?;jb=2(IUHBB zbT*=Yx;66AZjo4B1A*D08WZ?LrF|o!ATLDZ4N?;7Po!M?E4+2@LrAS|9-1adqi(L5 zJLbAsuXR8lZ=rX$cFnXvCMzJ5HT>_BV(C9mM9Ji@XfT2Geq_#0v|~UGM`VlXhW531 zkxP(D$ejHbH<2$3nlE390iPJeorMc2tc8qPs?=7cKmISJFQB*&0^8H_?tVniW<-oj z2rgr|4w0E{5)^s=%B@NV<|Mvbo#I2x)<X@oxDYNl20cM#cnzQ))o<+d<FYKu3WfJM z@B0uS8<EG}mnp79RyDR5FvW*X%+mZQnv|)YXcGPegKu)IA&<3($1%gC-l<>10dC-! zZsF+@H1kIfiaA*w)ZP{c+}jYL#a*qswqcvEagM502<HUd!JByMS1@~M{j&<s>Clb2 zKA|KvMHG|#^6ImM#aNqg_nKXFP=Zj4HgLz;;8g`AjQdxGCeV1Q6$u21zaj1d3-S<k z<RJ=EH4n9ibp_?*5nn8@80m{ed31K5N=@K3WO1UcCW{l+t4h!#c3RqL!vH$KC`wXC z9-+*zwT#y@izs3Eroc<ZZwb1G6Sy?4$Rj`q=tpruIYAX;;aMV8@Dc?Ld{>~B7{iGi zgC5Qg^<!^~=j;NAz4*ii@i&VDKNpzNRgGXAwMIC$^rGZ$RkO*q6$R+^pLHU<gQ4&? z#U{nKDFTY`P$)*;Vrv6J5qqi>i7PzVHsu-a>OJfVQL7<RX2s2<@v5@mvJxFTUD9h= zm=}!5Az$*L-stD0W%e#)=?K3Ip?rLbys_cC^KLRZ9=q@d=xz~16$z`W>@p}?+*l(V zqDMMShjVgeD8e6ONNWTLdMErLdT#{r_1OxS82<!o1I6Orfsh)=wB9r~&AM4fo|0_& z+cBH9PHhcTtL`g3L=jz%qUsVERbQ3pGJm5eDf&?q@;pPODuN^Y1;tZ{SFUU8|G!Lz zOz@xL(ThaWG;8hU_MP@>yHT0sX@$=qW?sb;e^`{4UV&ZVmDX!L8u|)8;Wv+#?T`$h z2+^8XaRKh&;jj6gP|**!KE8E(+x*_EzItII#F%L5Ru|uRgBsdLRWN@^A2lbMwWe9C 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 zcmdU0+ix7#c|Wtu+b&6w5_Pp@X=I!6I+e({j@#H#6j{_2k!fQnL&-YMW;ipu9CCJM zIcL@+6RRjHBX$s^D2k$h3-qB64T`=Lc`1;WKJ+aGiU4`*L;s2beRY4o@65~&Whu2E zv`tCw;W=l{oZEN%ecw4-|F8MxKfm{H-}O}fQ^)_S_~<t*rF^B@NG;X2lwVUlWNON< zD}O>ob=9t`psuzjR4}O?;?Y#`Xj-+Wi~Njg&q#h!MGe(&s&-RZ>|#pFv#LF-`g5v1 zhg!TeqoR4$Kcd=4Ou3<=1=U|v?L|{=s_3ZdA5-mPraY^nCDlK!+Q&_KPDLkF|D<Z4 zH05~}ol^bNs(sp&kErO3>OZ5}&!B7;@~mo~l^HIm=sDFtr`qT6Y*BTw@}ue_r5-Bv zz4m!-0IQCbRkre%RM1piY|inr_5ycjqjiFNcpiJ$ytftDYpMIAgKiWCc0cilQDBGK z9_qliv&0Tu9YzPXm&9@4Wve=f{na$^g7AaD_T9{FcGIM9yQ4l<n#~)ow`F&Sv6qEO zY=^O(9U5fI)p)^+T&;_T`MBAZo7tWl+na$*pos~%o)@5@JSzHhqcA&Iu{n3`_A!w_ zhdqqMW<hM`{^+pHZkYD>+%&NFwnEIW@9qTB9=X9FjC=MgZ@j|!^b^fV=s>SD^(;2( zT9|p*p}>CgTGl|h+^+C5{H(XIjVhZ$a&IdQT)$Ogd6MO@AEem?a(5GNCuBR*Sb9nN zeK+=l)KpF5;aU>wBnnIgJ9x)U-M%T3^Y11-Q<y>FUgE7`pYzrn{;i;&qz9(O0^g3@ zted3$jUfFX^ePS4+$h4tIKoEeW`T!{{BVZX@X=pE5<tAbJ4hi_5FqelO|>V;jO|IX z5k$(_Xt5V<BVku|(iLLo>_YMn(!>k=VH$*Jqhu38?oh}TwYcnz%jtAtP_om>no=+f zfg&4pI)36IKZpGFI~!|nudm-&yLaPyM(W&IzjN=-n|JTDQI>rzpB}-}jT`sgeW!C1 zjW%xIxZXmI8YTKZimO{mKUgJ!MFdu_U%Khuy?bfnKxaXJbuiGKJ-w<^Z`JQ~v2PvJ ztZFX}2bo^=AT*nmcRQUh4zo^YWpLndvRClYT+ozN{FZx7wvfGUV%MvC4XyPMn-0Zg zGLuDSs>n<inVBNfC^F3=Gh1ZlR4}i$p|cjK9r8a*eFQ2VB}Jh^psqkvW|l}-W{xX= zQZgr~3t4kgK?5>#ih7Wl)5>p1=8W>2l6gk?vyypM`E!zi?wXg(bILy=nRCitkPLJd z6cp6dqG&SA=K}fZIQMJ`$zjbT>cX2qJwIf+HGK2~Y~F{K`VdS4MgqGc4|c7o54m|Y z*4I>4X9E}k7AKg(ENo9wXi;h9<!Nra1!f$zZ78tgs5q;@f}`TR%8pcu3o2Wz6pyOx zSfz-am05I-vvT`{+CC|nQ{@!Os?$;haY4h*8I?W5i7}5|mFaYzlUL8N4`1k2EJLc; z|M{|8TUrS*i&ht;O2}GNJug*4;G*gUsS;8bRWC}F5S>++%|?qdsDFi|Y3Dy{(1KYY zdLY@{hSkuv7>R5P3Od*H(0q0p^g;+(3S}Ebo36J5g<E!#CLNFJ&AxQWe#gzWXp5*_ zFC{U}kvwYYEQR8JdGylW6JFX&L(J$2Z;AdX#xsSypA5@V9<?Grb$mAP(Thm%Xxp$G zIID(zpiWyCMM(9^<y^XQJ=D1-gfW#pcyC$1Zwv%Ac9isbxrGShZZgvJ!!#U^nv&&S zj@<sH?_L3$DUHvUTk^AbVYFn}FaiYw9?I1^r_jhbjYQVyoM8?hlRQ;D{0v6<2|i|F zq!K?=B0|e0WU91;GI>}p$+?WDpIaNPT11clmgEZGu$Q&aw6aQ$A*&Q%5Tt2e_r|#3 zPcXdZ92%DQC6`Ro|G6cTecdn`QT2DO*&$1eakW3ItoF+Y>x++Ds)4Lw76N4cDDZz@ zSoqJmMtr`UY?26{BPLg<gKRjkyMQZt3sn7mS><08t6XnU^aMLcVZ7tauq4nh0~GYA z{<D*)>)eUGqz@zT4is*X-G*Huv}N-!e<G+5>%Hy*AIH+=y>u7|{MDJ8W)78sv_e+~ zlRJV-sqmqq9ij~ZqEO}p=ni$aygwj7cL+OyF_h{DeTS2j@dr8lmI4>ifmY85%m*^p zPVPqef@@vh$Fhv!$j+is2TSyJIsotjD#4DO{5@|I$l_1~H`aSWs)0XHZ3<|d!M4WQ z;l#|=(Ru4ca7APj){{7pp-U6h$+T4X6|<Giv(G~{97Wa>VEZ;B;UHO8-Vyi<qv$L< ziiVXsqd_tPyh?2v><-aU;O7*`%|}a!!bAsA*Wr#heAA)pIv(93gexFD;HA>U^J=<b z%~^A`1#8ioM_T^Y8ucY>sn)2?A=j{)){?~(pIZJwzSPFl%4_VC`%Bb{T^2lHkc8%f z?Lu_nvcj#c5@BWCSe&n+nez$~Q+%Dpms!MT494|UjPjTGh^<kzLr_UpcNq_>=5@P9 zv}BnDqj2)pZLEA-0yWG`3bF+*XCuNYa<AqB;a;XNxj=*?=E>FA<^iB#Hx2X_-FP;+ z_x76BD|qXEVAJK#Uoq<ZB3cX271gCeTo-?IWX5GH9hwPoHS<9_P(c}S1Gv1$LT5NY zC?tRYZ#vJR-e4TkVmxHXh^{)K%Q~aY>4=X1Ydq3ahl*SX&Mj$Fav%v+(ulY|t0|BM zM0sfZ4&yRSs_iME4jiT2YnoBp4e^>rq#{F-ZDCY|X-N&ylB9{v(9dQ7UhWPf!1#RM zH1UGOI;2r)mE<u<y56Ev9g#%@lRKUvz#@t_PWD6!oNsU~)Ed;KhI|AxqEU0I=hW&X z-45D7PeO(3v6GBy%#8gA2mLJ`<OG|yjzOW61fwXBR{E<*4iSu!BLrinpw?j#y2O_s zp$p7al7@(aZMlf%30*=zh_kR82EHb6mXH!#JccGTf+LzZ+-B#SOs+BcKam5me^`~S zNaWvRHkurvYV(g_ei3f$n&{qwuPb}Wsu6cnr98TePy)Y;x9RHK&6_-C7l|z@yZ91{ z0yT17<B;IvW=`ia2K`5TM*CKEB7_Hj&50KKW+E`u?R67QkyznQL#3~Y#(;W95FTE1 z3|oiT3|L=+A$SA+(e||3si`!SrlL~F#)x#l6@wYTJ1m=~vuvt=Ua6UrmwzQS2y`_h z(=1igkx|vCKMuOUh6oOgh)XMz6E}}n1U7Mw?Wc%#r=>nigO?D%j%|2#Nh-p4E6Get zsY0B=;5dQR3@Jl3Bm6*+z1|{^3c-cU2uR1!a0M960)up@MWxOIkP0N0=nzdgf5hY} z6IywPIMAUfa{iFXYfLym1r3bP7eh1*J8F<2w+80$pHQRe_S5>z11l{810A!@-~-i$ z|6ajZr8PQ^#-z9K7D+|{v~rJJY<iVORr*MTXpjIUNbT-WjtLA<64WVE%c~0wA)7hc zE&<AeAoZXI-Ci&z`%RLWjVos7ZT<`D3Rv~IO(Gi@=`&2s4Xa?NLXU}T@!8BH+xT~Q zzl@sY`zhucha?DW7)JxhiHx5|@5)n;N|5dzBYKs?B0qr30dEW^NL_m`I1IfQF42g! zBMc}g4W|X6E+=8U7Cc!gtJYg|tPJxDqu*rl>rAdP5eX1mNT(`=G5a7WeJJN5>MC@l z1>kwrcql5P|0{;nw32Fi4$LmB&LUO5=+LVORy@k;#9k&)3_EVzf=PAoajqvpNK-k| zbEfgEbTq5<Pw7l-BTOxl^8m`Wrf%Ja7l$JNWDCw%pv7ZX29L+j%lnIJ`>14&l?|5) ze0S^i{rKl<0)n*DROvS@>j8AVZw@I>s0R~@V;~+41dk~pzK+N?LZfVSxT*`({{RO_ zQ<&F>lT5&%vnpMdK>V!QM*JJ50)cHj=*+5I|Ccj{C81TY)OXQjLl8SwUE$}q2LPDH zQRw%Fap<{n4k=0jJm>=U4;Xd^1F}^QJRaNcI(MMc{oJEKeaVI+L~XbHWG{{q*GK%X zw<^ey98dHA@`!IWF~xdPn2l~SM88RjbH<3raX`ix><#<J4@5Iox;H@?!SR7_4j1(W zGD|Xy{P-p4H5qzjk{E?+HkY+vwMs`gbK8Vxevk+4T7+`WA2Yeighx}xoQOnGj*PM4 zah5^PX*Lj-z87RhF&P`^e21^zW^$Hqk)63FH!~RK2A_zHqFg$Ez=VS6d<%&<_&4^w zU_g3EDN9{1V60rm7wah+*!0;6_C4noU;i4C76)@m4_C}*A4kJb(-0b*q3t^x_$O8f zFPc$0S+JjtN9JWg33yL7?B6jJO>aa^F2d_-SaqaR)&lI{6iSn@h9@VM7$2xD)cJf7 zEtXI}iTr%kPb<~MpQ5u8autFsHy((zz9E7u=URX?<=?}J3W9>Tvgr?{DHIL-Y)YC@ zAvH=`C>p6VEiEcoOsp{D;?$#>;C_wB<OyQ7qRC=xK~+z|L%)y-IV`H6UGk97)_DOr z2{lQK3W6|5!Z<U;Utuf3H&>Y>yN(+X3sVXf6XO0irXZ(4^eGmsv-pr?2=CPKr1-3{ z6<u>GIQ&%=?g-Iv58m{B8)~V5jZjkFrp>c+fSEkda~{nkpvFKU>q|=~Arg@b5nVnL zdGI#J!}rejF*OUOtUxQp`uYEh?ZG*;bi{rehAKAV@w-5Tm|OcNY=rYZJNyBXF>L{( zgOiWh0;(+80=nO&?B+(7YytZW&r_^4#1OHJhgOZIFTYnom&6Gu6FDZ!FTwy@iO(Lx z@#rN8^u{n=5(5Ux60>67qWD3V;$;a2!(8O*aUf@O2tf~`1JUA$o?(=Ak#FOkQ1AG` z=CCKRFOf@w3mw{|3fEEj4t7Kz=i`iL*G&vAPKt=PpI|*%de9&tsHaqIu~wq4k`(iJ z3`hcK1cHzTf|MYTI}<AX57Glv!L2D~>gp6=&i+5-?GRgesMPiM|4Q(V1jI-?AQcb} zNQWJwi6uRgmTJdR>6N?%hA10cpvU`LzIUh=xQI>U#u_sM*mfpW`Wsqk=xgd~1Ay=k zOW6I5+WoECy~4ly(BI`t`Fa`xss!hqfuD`pk1D=}_QGo18vP3-CFY?^K(j1534nz> z6u<{QC|rZ$?1G?P4k|{7zwBhs#VK0OfN+-$gHo-@MF5KKihZXGpWq^;KE_zF7et}~ zgpOrL4D+QUd?mI-FJr!UlVqnDp<r=f5!~*=D9hUZKqD1!bW=cB9P9x@#XX!i0FANT zvR*gLI)m;clo$oELyIF)v4tB4`AsQ051DA#o!~$lBeq$zGgGf16rWER+RumU$Z*bE ztOoz7fdC$<9FMR#)IbZA3>q9YHD#Ru+&GEPX}IA`7g0K99kqIY^C^FR{cVpLfV{!~ zb$s*&-v0!+!3Yg_i3NZS1aJT=u36X~!?HQ|eM$zOOFxyD&=ib}JmhLh1gxe`{S^B# zP5%^f^oiW0uz;f=L?0{lv86tSBtk^$Ji!O*hj@VPIcSruFQWj@flM9$ghQq0EsT#o z@zAu|nNn%P%7+lqlsU`mXVSBe8*K7_9c=fkwR^tkbSfW7oDwrE<~PS>5;Sod;(X<0 z$Q-M%g9|co?A=CdhMW7JNJ^I9pvrd(Y0I|KWZ1(U7`Q9q#sC`yX&2bJ;*1Y=%v~9t zgNm<>F3SFbeQNw%Q?eb8Kq<537-h(-boA-joA;mrvZU;*xaU*;$8AlRhs>tNxM(Ms zWx5vv9OAy54n|jcjNB5+b9I#4BF=!dnt@o!MM8E9d1j{`-jSa=Qii((e3^a3z8c2a zXi>1tbxfy=Yzv><P6fz0lu5x!^5CgoI(d!4#wkdM@ILOHy4WM=OJi!q+vHk{g<`k4 zbEDgs|DP87Z7x=9F5DYga1L9_<K2t)<}kD4q+GA~#q?nFnCL3DN<f6`?GD4V5(#yB ze8eD69N=IAR-R}Cyy9%3ZlsDGA`wyE&hyNXZ3Mg+KEr_+T#Bo#oJNuc5iU0ek0@>D zhj`?;MI)M?WSEI>2kX%hQ}jf}da69d)x5}UrYocxbUe<$>0m^48k>F^F37w!RfCUd za@OhkytOEC*F`A!+|J3y&@eeim~c}aUg4_*kiX8NL<{J@R^0hr7Vj{T2*M(BH<?hT zoi~`g%7o6oIR#o{?lP0xOeA_B@qq8K_#Tt*Ga-H!nH4i7w(%yFhmg>0H(HG0K%1j* zNt!_n+}W)+aG;Oh;=h!Rz1o;)OgCm4llWagezx&4J`?zzYMgNB&5&5~Q%8CYAN?j0 zsG;hW>W@&AlM-g+Vl^{zw;H&N7jSXm8ko;qz@6spSTe4@j>Oz_;RSkg|0UO@JeXh* zEB4LZcA^&Q94Z<wNugab=pVVhpK8Izlo@_VD*XZyKuEf(Ft|MBl^l*qAcCmEv5gP) zoom_&w06LehW5tk>>9U=J(^g}2gr>m_*5l?9QYO`WC|GtDX2K)tdb>?SIrUuQn)-| zY(RG`j?P%&j{$N3kvyD~IArdO@pR=8Po22fY_WWE0nu$u=;_)T<`&1Op1AN55X76b zZl#l-yZ1O1^4}+gcse8&GW-nVtQ-G|(F13bGp5y}>u(V0W1Ro7`@AB!+-<Ufsv*E# z$A1?1k-eq|{+PYqzb^h%4W88N3jP(npqe@ZV$dtX<z|%YDA$Uz(1u}g@u{rc=M?_# zkKAO@Qzm{;38Q>L;(P^@l;Fb`Bo9|t@sou-!t}Wx&0`4TMoX-+3`K}La5!Lrp2ncl zs9f)pP%|74gj%$0OSsIVR1_bf*bAtjC>R}3__n~=AWl!*(#m1nFVVO}g^!VpN5o*& z@a{+i_Cnt4u7uhLiPqs}$Zz*hB*5>s3r9`why&_vxwsq_iH74oN@af-W#J$ih3mIM z-ZzI_$~S`waT|G$c|1*6lS{{!f1oWZD1c8ym;@!{g9-o;;&D3W+MJmVJUhKJCIz>L zsr`VIq<U4;9<t(`aUDXwyQtZ}!8k;ED|htipDDF7qtX@LjDZgZy+_r)TQ81QB;vu~ zf;lr1wV1#2t~%6ATs5!)-^Z390I~l|;g=?4`4@*}8J-yh*-~e&Qmpq&vBq#XpTeVw z^3eglDuIegQ3CAze*8Mln&#!K$>I-;Af^-@k;n_qg7Bpg1gX&#M!~9%(wU+j=eZEO zf<EzQ)DC>YbKDMwO3qok`wD;ep;$D6ZKfA^o2D0;7Q4WT@pm7?ZOFx=b@QbqZu#h& zOW_2I`~sLJa4nBDa7zI9q-`JJ>nA9C9@vQVY3c`F9mw4vScF_cTFd^$W&h;@F&9is znE=z_hN~TBEjbdsfG!-G*vh>D&P;Ab(QnsLfRhknz1-~Qw@96@@};XxhD=JXcYe&G z!(@Yrn4lZXy^BP??qv)+_QxC>36M7(@Qv}vg2ZalVT^BmDovZNVC|0)0b3_(IBBUb z0SPZ!&*Hbv`U!^Trx4y)1SaMQj5yuSBS=^<tQa;yq?9-3&kn6gR`*m<%sGtr9j=N* z<JZOnW=`Y$Xr}c%Cq&F{F7$TXVYY>f7RA@(5VgzmIG^l+14V7&d<++m@I@1|S|RA4 zu%j#NsKG?OxQO-!%~89^wP87yk?YaUGV91t=Asuhj`JcDB2u}Khl`LN?`6wfZRaIE zlfF6K;x4o{Uqy79e2IyCgKovcaig=&x)WvHFbgBSQn0=@PKx*)XrcsUk2vkAQ$<#I p*?f8F3fC<@F1;<>_l3uW8^PeKlQnSH=odGE7wbziOP{H!{{afC`4s>F 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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°<3D;Mp{g_Yh}{J^47%Xl)OeV;5U&!##xGpuC-df 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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 zcmcgx-F6e#6+R=$vL$Q`m_I|BOv9qM6y&TnZD~rgYGem!ji4hEs0o=ho;k9|mS@DA z5uhy68|SWlfL`S#x>oxHz3D6TCKr8!K0y0@`)DKqX~T_TINRrU&N=({+sEa9FVz3~ z=pPS5HT)^#_pedRUqB+Iwm==V<*2BnqOyuAYNxEWP@hpzRdvhiprW=a%3*s}b!XHT zDz;u#TUDj2YHwEQIdzDy)KoO5w(3HUsHmp4=2cWzTMH^WqPC8zXkKkC%1*~rw4lCJ z>QJfQZ=r#mkE`hDs0)pXicY9#anyyztZJ_;@$e}~ea+p`iO%BCy&r6M_1#W0O>&*& z#yv=*epkEsZjigNar;I`Zl1awork+uT$87nc7r5x58@>5^}9i?>w$a!Zqx0gncGcw zcH(5mMYR`X!GX?oW*T*K1bg0y^DqlO*Y4VlypHz$G~DRw18ka;v1k_EA8{`n?mzfh zaWEzWP4E7_dqKFb^Hz*L70XBkwcG2P8~HpcH#hG4IEsI}_0ipYaiU{mbK}lN>;6cR zTzUh=d<dc;NyrcsqJt#B&l2UvI-~`WR)x+AofBG98nUe`uf4((nA0F@iL32}+&BRP zHQCabI4kC;6|y3q1M&R?4Dx-sobOw{qd6;MQ5vFNL*0AeuQi(+?Y3>*Em|`eizBj+ zwVf>O_i~<vUzM&fAM|uyMOSYyss>@Gjafmb8WZE8xw@Mk=v9hn(CNl{b^Yo`!RF@G z_Q2%&V71pX{xh>`vT!x>J3+VWqi5BGS=`IbYKRrv6KnG{Z1e{Fs`@#KIRR32<X>s{ z8?D#HaR>8W5yzL1i%5hO2&GJ=rd%pgfoNu=0_jwx0s+lRrAFzH)8-rq6+DQc^P;F@ z<ui?t+Q3rcG!(M{0=d0_ki^j2qJ=H1i&R$J&o;<#qr07{^Kz&%FuL6LZ*o_jRyp5x zcn4<BnR2IQR|i?4nf5v!eS+M@JINY~B~#J~tC>8<Z?KcSL@DG6S*Xb(0k~E*IImtf z6v7@v;ix@FE%R4NU4M{VQx$lP{kqCNR*vIXVU6Zp{~zlTM?Hj94>@ufd#pu~8@P#n z=8iRj?rvzkZdhndqa}te?DCFcrAPlFQu(bOY-CxQ37>_FV#7mUFnx~FqXbdp4>xR~ zY3d-VRCdlg3(hg;b^iVY!-I1LbHQRT+F=~R|Mr9A1lU^=_BO~~@@#@}_ptb;&cV0| zrj1-5T~geM5jM65;NL;7izwjIE4iI4Js@lA?(pYT_U6a&=^dRHck#CeejMfG_hc`+ zc)?GCZeI)Q#qqV$L{G?oMqF$!u6Fv8c<YsO;`A?_;1i6SIS^GYJLjAQtN2uUIu%j; zO;o7m0xcknPfO|x{4a~BAf%FdQC5fOsi+qfmAy|2lW4;l_TU{c3>lVyIm)WdV#8A# zU!}xEtw(Wp9Im3PN&jFQzA^2{oI;q-;(Yfdfth}<hu{#2JR3Q^X2bB>A1A~Sf)rw) zgQr0jOUI*&=5cF<K}am$V0E5TV<+V4QB)GBh#zz&=JPDb5<m&>dl*|Gi^Q_Lw^<w6 zpLdp{C{y)W-g#_*NT3WshAN+OP9S>CmS9+v8_URXT|r~8R2UXrR!J=nEy@v#y7Ube z^*gMheBQvK#E=NUB;x;nlj6y&$2!BBuu}Ty>c?E1J_Fyyeo*PL@-M+JiC1C(WodF$ zgFOFi$UL~DL#5Uq-=P6KUoTh+b`~S6g<Zg8&<(>_hAF@R_Dd?eJh6Zy?I)MjgD1z- zgK+@3i|yW!>(FWa+#MRpx4JRmx%i}gJusr)mifG?)nlsscPRlvpbW)EhVp5+pL5O` zXN7*$qf7DxYfAGJn?$tUGRZq6Gy#ud^!TN*#z<{SSiz%vSw}I)K!6)dwQ{Xeo2%7o zM{08+`s9bRZlIW7f<T2P!TgtRj2?j%>7~pMKx_hHB&|U-%VKRthUPX4gd7s7vk<w6 z{(goE)X(BZC><-vENl;w3yh)%ZrWA=Vr`{LFV3M^^#XNN=G2QBsno>fqX(Ix8X^~) z|JY1|dxss-wO*R#Qt`tq6n&E%DAP~+8W=wEMTsR*O13jU?f5Z}y|I2dM<Qp{mMY)_ z;QC>JEKDMD2e8xZ;tL^w|2A^4oa#Fa5`NeXkWr8k7W`0=MrnlCVE-i$3EBMaZrY7> z=3QkU=Z^LoYt^(Aikkfg6hjB7N;T)ze*&k;(+Y~n^m9~l0Z3Mk@Gy+85YnE6Tvp(8 zKxbqCaf||-B{qcr`a*!!9>gf~zyz*F1nkIwKQ9CL(gHOH@!Jj^5_OHjHy!tFApaOb zvYEIf*)~=)$u#uuk1j@!KXBW5kmaLEjYdPzlYZXQA$1_rz3!m3A`$cgug1s~>FxfG z2+5=Ri_D2AsZnE@bBzzE?Dr%K_rq<Ei@hI_l(~+oV}RavbpQ!Vm}ffPytUS9ZET9> zHSesCQ*<&p3ZQxf1V$&=Q9eC^;wX#W{?s+2FoG=qBagzXs?t(vxl9FFa^}Z*7BAZ5 zoDV=KRQwkMA8LW1GY57EeRxe{s#FOFQf{yCJBG&59NLuRrO$Tt=;>f2M83lmCMZuS zR?_RhyWekF!xOPlsWW|$KGiQv&Pw*D81*iY5d4ezeV2J3^GtCo@{;5i_xUrr4IQs4 zErZd538+;p!sdU$y~8rVX21Z1dGK_<tg>a=N*e$aY=@x${&G@<`Abw$XHVJ|s_*>C zDPY13K_;;Q3=os?VgMhDw}q;5VU(YW71Q(LpfxJ<9^T>Lks)Afr7{FlKE#_Idl%H+ zQ5i-THnE8OpF>~1x2XI@`3j!s?8<p{8uMi7aZ!XPUpx5;*{{$Q-$KWe^4TSR)=9pW z1`dBh4*#`$U`get<nVx)m_?Rc5yj%c7S-NqS#(CS1<C51;f1UxDhBq>j=Y)O8LuZ` z-#!%SZ^eqbe9E$5iNj|O%k~zwMSi`0Y}{?gqh(od_r=K;M>|?3B=7+=2=M{*bhm(R zkxEEf-W|9>mc>tzaah+7V}fc);&DS>01XZ4`i<K$HO5>9vDR@j%_V*}z2+77V&qD! zlb1_G$6vko{Nj7p9vT<Z;i1Iz=<HmkD`@qBuknT#G6Kru72XMOgd#2CEc3(BR(JR& zT4X%tiaU(B^wAbKUW`_Vnk26YcY`F+T}gLLp7lc;vw1st2kioI@GA6+UF%!fl==OH z(>z|%qf4AHXGU0$$%iZ!8yln0IO(JwpE^BSn#YspbQXvPf(^tjhnJH>q*-LKA05;< zJ&O|qw?59s<WN>F0z7y(xnwUGbko3Q>B2;x*O|H(_vqukLJ{Z8_&)DcoXe#}=Uuq* z)1^~L+vc5<NW8_Zmln&*7+)3_e;V{8-b$8AZ&@!yy&8F;0F5yUDEvL`0Q@>Ed=vZ+ zbsj-{j0GIk5x+%jhp{<DEC8Q{Nk9rR`f{*;A6t1=E>Iu5sviV-oF;Cto%Wd_8boru zuY{t})Y9Qwm3e!-a8mVLtQFn97<oU&l$OQ(7dTrYfv65|>-~&`54Hjvi%>m$JokRV zQ3e{1p-%#s1$^X^D7r@kK7A?Q!Y+SD8NPB+a!bylBX7Omn9E$pWUK!Y1i5~EN~ui? za1y>i?Dpk=FW<EV{HxY3y|TajX~wH#rhq8l5Bm9TnvMP(zz26<Y*wDy8)26BgqWNj zL8Q0F)r8^0zrqN(#xu*NJgQj6h}0=zkIAohjf5!7B02F6Xer6OJRbg)b54m*@{W4l i{t4tm7u$+5SMj%kMZB`im*(-iS_c1EK$jMluKgDb^&G|k 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