cfclient 2017.4__py3-none-any.whl → 2025.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cfclient/__init__.py +16 -11
- cfclient/configs/config.json +4 -3
- cfclient/configs/input/Generic_OS_X.json +1 -0
- cfclient/configs/input/Joystick.json +1 -0
- cfclient/configs/input/PS3_Mode_1.json +1 -0
- cfclient/configs/input/PS3_Mode_2.json +1 -0
- cfclient/configs/input/PS3_Mode_3.json +1 -0
- cfclient/configs/input/PS4_Mode_1.json +1 -0
- cfclient/configs/input/PS4_Mode_2.json +1 -0
- cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
- cfclient/configs/input/xbox360_mode1.json +1 -0
- cfclient/configs/log/PID_tuning/Attitude.json +46 -0
- cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
- cfclient/configs/log/PID_tuning/Position.json +46 -0
- cfclient/configs/log/PID_tuning/Velocity.json +46 -0
- cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
- cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
- cfclient/gui.py +44 -9
- cfclient/headless.py +3 -12
- cfclient/resources/log_param_doc.json +1 -0
- cfclient/ui/connectivity_manager.py +198 -0
- cfclient/ui/dialogs/about.py +53 -36
- cfclient/ui/dialogs/about.ui +23 -3
- cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
- cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
- cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
- cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
- cfclient/ui/dialogs/bootloader.py +448 -85
- cfclient/ui/dialogs/bootloader.ui +387 -134
- cfclient/ui/dialogs/cf2config.py +4 -4
- cfclient/ui/dialogs/cf2config.ui +3 -4
- cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
- cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
- cfclient/ui/dialogs/logconfigdialogue.py +401 -101
- cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
- cfclient/ui/icons/bl.webp +0 -0
- cfclient/ui/icons/bolt.webp +0 -0
- cfclient/ui/icons/cf21.webp +0 -0
- cfclient/ui/icons/checkmark_black.png +0 -0
- cfclient/ui/icons/checkmark_white.png +0 -0
- cfclient/ui/icons/create.png +0 -0
- cfclient/ui/icons/delete.png +0 -0
- cfclient/ui/icons/flapper.webp +0 -0
- cfclient/ui/icons/tag.webp +0 -0
- cfclient/ui/main.py +328 -258
- cfclient/ui/main.ui +184 -80
- cfclient/ui/pluginhelper.py +7 -1
- cfclient/ui/pose_logger.py +116 -0
- cfclient/ui/tab_toolbox.py +208 -0
- cfclient/ui/tabs/ColorLEDTab.py +752 -0
- cfclient/ui/tabs/ConsoleTab.py +48 -13
- cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
- cfclient/ui/tabs/ExampleTab.py +9 -16
- cfclient/ui/tabs/FlightTab.py +437 -325
- cfclient/ui/tabs/GpsTab.py +14 -20
- cfclient/ui/tabs/LEDRingTab.py +277 -0
- cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
- cfclient/ui/tabs/LogBlockTab.py +35 -35
- cfclient/ui/tabs/LogClientTab.py +85 -0
- cfclient/ui/tabs/LogTab.py +50 -27
- cfclient/ui/tabs/ParamTab.py +443 -57
- cfclient/ui/tabs/PlotTab.py +23 -25
- cfclient/ui/tabs/TuningTab.py +292 -0
- cfclient/ui/tabs/__init__.py +12 -2
- cfclient/ui/tabs/colorLEDTab.ui +624 -0
- cfclient/ui/tabs/consoleTab.ui +46 -0
- cfclient/ui/tabs/flightActionContainer.ui +103 -0
- cfclient/ui/tabs/flightTab.ui +724 -237
- cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
- cfclient/ui/tabs/lighthouse_tab.py +714 -0
- cfclient/ui/tabs/lighthouse_tab.ui +430 -0
- cfclient/ui/tabs/locopositioning_tab.py +606 -389
- cfclient/ui/tabs/locopositioning_tab.ui +370 -253
- cfclient/ui/tabs/logClientTab.ui +52 -0
- cfclient/ui/tabs/logTab.ui +1 -1
- cfclient/ui/tabs/paramTab.ui +204 -3
- cfclient/ui/tabs/tuningTab.ui +773 -0
- cfclient/ui/widgets/ai.py +37 -39
- cfclient/ui/widgets/hexspinbox.py +16 -10
- cfclient/ui/widgets/plotter.ui +39 -47
- cfclient/ui/widgets/plotwidget.py +57 -22
- cfclient/ui/widgets/super_slider.py +112 -0
- cfclient/ui/wizards/__init__.py +0 -0
- cfclient/ui/wizards/bslh_1.png +0 -0
- cfclient/ui/wizards/bslh_2.png +0 -0
- cfclient/ui/wizards/bslh_3.png +0 -0
- cfclient/ui/wizards/bslh_4.png +0 -0
- cfclient/ui/wizards/bslh_5.png +0 -0
- cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
- cfclient/utils/config_manager.py +5 -4
- cfclient/utils/input/__init__.py +77 -19
- cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
- cfclient/utils/input/inputreaderinterface.py +17 -7
- cfclient/utils/input/inputreaders/__init__.py +17 -0
- cfclient/utils/logconfigreader.py +245 -25
- cfclient/utils/logdatawriter.py +3 -1
- cfclient/utils/periodictimer.py +1 -1
- cfclient/utils/ui.py +336 -0
- cfclient/utils/zmq_led_driver.py +5 -0
- cfclient/utils/zmq_param.py +6 -0
- cfclient/version.py +34 -1
- cfclient-2025.12.1.dist-info/METADATA +70 -0
- cfclient-2025.12.1.dist-info/RECORD +152 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
- cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
- cfconfig/Makefile +51 -0
- cfconfig/configblock.py +111 -0
- cfloader/__init__.py +41 -55
- cfzmq/__init__.py +22 -14
- cfclient/ui/dialogs/cf1config.py +0 -265
- cfclient/ui/dialogs/cf1config.ui +0 -260
- cfclient/ui/tab.py +0 -96
- cfclient/ui/tabs/LEDTab.py +0 -169
- cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
- cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
- cfclient/ui/toolboxes/__init__.py +0 -45
- cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
- cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
- cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
- cfclient-2017.4.dist-info/METADATA +0 -22
- cfclient-2017.4.dist-info/RECORD +0 -104
- cfclient-2017.4.dist-info/metadata.json +0 -1
- /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
- /cfclient/ui/{toolboxes → tabs}/crtpSharkToolbox.ui +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
|
|
8
8
|
# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
|
|
9
9
|
#
|
|
10
|
-
# Copyright (C) 2011-
|
|
10
|
+
# Copyright (C) 2011-2023 Bitcraze AB
|
|
11
11
|
#
|
|
12
12
|
# Crazyflie Nano Quadcopter Client
|
|
13
13
|
#
|
|
@@ -30,15 +30,14 @@ buttons and axis to match controls for the Crazyflie.
|
|
|
30
30
|
import logging
|
|
31
31
|
|
|
32
32
|
import cfclient
|
|
33
|
-
from
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from
|
|
33
|
+
from PyQt6.QtCore import Qt
|
|
34
|
+
from PyQt6.QtCore import QThread
|
|
35
|
+
from PyQt6.QtCore import QTimer
|
|
36
|
+
from PyQt6.QtCore import pyqtSignal
|
|
37
|
+
from PyQt6.QtWidgets import QMessageBox
|
|
37
38
|
from cfclient.utils.config_manager import ConfigManager
|
|
38
|
-
from
|
|
39
|
-
from
|
|
40
|
-
from PyQt5 import uic
|
|
41
|
-
from PyQt5.Qt import * # noqa
|
|
39
|
+
from PyQt6 import QtWidgets
|
|
40
|
+
from PyQt6 import uic
|
|
42
41
|
|
|
43
42
|
__author__ = 'Bitcraze AB'
|
|
44
43
|
__all__ = ['InputConfigDialogue']
|
|
@@ -107,8 +106,12 @@ class InputConfigDialogue(QtWidgets.QWidget, inputconfig_widget_class):
|
|
|
107
106
|
"Press the button for Roll negative calibration"))
|
|
108
107
|
self.detectKillswitch.clicked.connect(
|
|
109
108
|
lambda: self._button_detect(
|
|
110
|
-
"
|
|
111
|
-
"Press the button
|
|
109
|
+
"estop", "Disarm/Kill",
|
|
110
|
+
"Press the button to disarm/kill (will disable motors)"))
|
|
111
|
+
self.detectArm.clicked.connect(
|
|
112
|
+
lambda: self._button_detect(
|
|
113
|
+
"arm", "Arm system",
|
|
114
|
+
"Press the button to arm the system (will enable motor ouput)"))
|
|
112
115
|
self.detectAlt1.clicked.connect(
|
|
113
116
|
lambda: self._button_detect(
|
|
114
117
|
"alt1", "Alternative function 1",
|
|
@@ -142,9 +145,10 @@ class InputConfigDialogue(QtWidgets.QWidget, inputconfig_widget_class):
|
|
|
142
145
|
self.detectYaw, self.detectThrust,
|
|
143
146
|
self.detectPitchPos, self.detectPitchNeg,
|
|
144
147
|
self.detectRollPos, self.detectRollNeg,
|
|
145
|
-
self.detectKillswitch, self.
|
|
146
|
-
self.
|
|
147
|
-
self.
|
|
148
|
+
self.detectKillswitch, self.detectArm,
|
|
149
|
+
self.detectExitapp, self._detect_assisted_control,
|
|
150
|
+
self.detectAlt1, self.detectAlt2,
|
|
151
|
+
self.detectMuxswitch]
|
|
148
152
|
|
|
149
153
|
self._button_to_detect = ""
|
|
150
154
|
self._axis_to_detect = ""
|
|
@@ -178,7 +182,8 @@ class InputConfigDialogue(QtWidgets.QWidget, inputconfig_widget_class):
|
|
|
178
182
|
"pitchNeg": self.pitchNeg,
|
|
179
183
|
"rollPos": self.rollPos,
|
|
180
184
|
"rollNeg": self.rollNeg,
|
|
181
|
-
"
|
|
185
|
+
"estop": self.killswitch,
|
|
186
|
+
"arm": self.arm,
|
|
182
187
|
"alt1": self.alt1,
|
|
183
188
|
"alt2": self.alt2,
|
|
184
189
|
"exitapp": self.exitapp,
|
|
@@ -205,16 +210,16 @@ class InputConfigDialogue(QtWidgets.QWidget, inputconfig_widget_class):
|
|
|
205
210
|
self._combined_button = QtWidgets.QPushButton('Combined Axis ' +
|
|
206
211
|
'Detection')
|
|
207
212
|
self.cancelButton = QtWidgets.QPushButton('Cancel')
|
|
208
|
-
self._popup.addButton(self.cancelButton, QMessageBox.DestructiveRole)
|
|
213
|
+
self._popup.addButton(self.cancelButton, QMessageBox.ButtonRole.DestructiveRole)
|
|
209
214
|
self._popup.setWindowTitle(caption)
|
|
210
|
-
self._popup.setWindowFlags(Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)
|
|
215
|
+
self._popup.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.MSWindowsFixedSizeDialogHint)
|
|
211
216
|
if len(directions) > 1:
|
|
212
217
|
self._popup.originalMessage = message
|
|
213
218
|
message = self._popup.originalMessage % directions[0]
|
|
214
219
|
self._combined_button.setCheckable(True)
|
|
215
220
|
self._combined_button.blockSignals(True)
|
|
216
221
|
self._popup.addButton(self._combined_button,
|
|
217
|
-
QMessageBox.ActionRole)
|
|
222
|
+
QMessageBox.ButtonRole.ActionRole)
|
|
218
223
|
self._popup.setText(message)
|
|
219
224
|
self._popup.show()
|
|
220
225
|
|
|
@@ -295,7 +300,7 @@ class InputConfigDialogue(QtWidgets.QWidget, inputconfig_widget_class):
|
|
|
295
300
|
scaled_value = InputConfigDialogue._scale(
|
|
296
301
|
self._input.max_yaw_rate, scaled_value
|
|
297
302
|
)
|
|
298
|
-
self._axisindicators[v].setValue(scaled_value)
|
|
303
|
+
self._axisindicators[v].setValue(int(scaled_value))
|
|
299
304
|
|
|
300
305
|
def _map_axis(self, function, key_id, scale):
|
|
301
306
|
self._map["Input.AXIS-{}".format(key_id)] = {}
|
|
@@ -505,13 +505,13 @@
|
|
|
505
505
|
<property name="geometry">
|
|
506
506
|
<rect>
|
|
507
507
|
<x>600</x>
|
|
508
|
-
<y>
|
|
508
|
+
<y>150</y>
|
|
509
509
|
<width>292</width>
|
|
510
|
-
<height>
|
|
510
|
+
<height>221</height>
|
|
511
511
|
</rect>
|
|
512
512
|
</property>
|
|
513
513
|
<layout class="QGridLayout" name="gridLayout_5">
|
|
514
|
-
<item row="
|
|
514
|
+
<item row="6" column="1">
|
|
515
515
|
<widget class="QPushButton" name="detectAlt2">
|
|
516
516
|
<property name="enabled">
|
|
517
517
|
<bool>false</bool>
|
|
@@ -521,7 +521,30 @@
|
|
|
521
521
|
</property>
|
|
522
522
|
</widget>
|
|
523
523
|
</item>
|
|
524
|
-
<item row="
|
|
524
|
+
<item row="2" column="1">
|
|
525
|
+
<widget class="QPushButton" name="detectKillswitch">
|
|
526
|
+
<property name="enabled">
|
|
527
|
+
<bool>false</bool>
|
|
528
|
+
</property>
|
|
529
|
+
<property name="text">
|
|
530
|
+
<string>Detect</string>
|
|
531
|
+
</property>
|
|
532
|
+
</widget>
|
|
533
|
+
</item>
|
|
534
|
+
<item row="7" column="0">
|
|
535
|
+
<widget class="QCheckBox" name="muxswitch">
|
|
536
|
+
<property name="enabled">
|
|
537
|
+
<bool>false</bool>
|
|
538
|
+
</property>
|
|
539
|
+
<property name="text">
|
|
540
|
+
<string>Mux switch</string>
|
|
541
|
+
</property>
|
|
542
|
+
<property name="checkable">
|
|
543
|
+
<bool>true</bool>
|
|
544
|
+
</property>
|
|
545
|
+
</widget>
|
|
546
|
+
</item>
|
|
547
|
+
<item row="5" column="0">
|
|
525
548
|
<widget class="QCheckBox" name="alt1">
|
|
526
549
|
<property name="enabled">
|
|
527
550
|
<bool>false</bool>
|
|
@@ -534,8 +557,8 @@
|
|
|
534
557
|
</property>
|
|
535
558
|
</widget>
|
|
536
559
|
</item>
|
|
537
|
-
<item row="
|
|
538
|
-
<widget class="QPushButton" name="
|
|
560
|
+
<item row="1" column="1">
|
|
561
|
+
<widget class="QPushButton" name="_detect_assisted_control">
|
|
539
562
|
<property name="enabled">
|
|
540
563
|
<bool>false</bool>
|
|
541
564
|
</property>
|
|
@@ -544,30 +567,33 @@
|
|
|
544
567
|
</property>
|
|
545
568
|
</widget>
|
|
546
569
|
</item>
|
|
547
|
-
<item row="
|
|
548
|
-
<widget class="
|
|
570
|
+
<item row="6" column="0">
|
|
571
|
+
<widget class="QCheckBox" name="alt2">
|
|
549
572
|
<property name="enabled">
|
|
550
573
|
<bool>false</bool>
|
|
551
574
|
</property>
|
|
552
575
|
<property name="text">
|
|
553
|
-
<string>
|
|
576
|
+
<string>Alt 2</string>
|
|
577
|
+
</property>
|
|
578
|
+
<property name="checkable">
|
|
579
|
+
<bool>true</bool>
|
|
554
580
|
</property>
|
|
555
581
|
</widget>
|
|
556
582
|
</item>
|
|
557
|
-
<item row="
|
|
558
|
-
<widget class="QCheckBox" name="
|
|
583
|
+
<item row="1" column="0">
|
|
584
|
+
<widget class="QCheckBox" name="_assisted_control">
|
|
559
585
|
<property name="enabled">
|
|
560
586
|
<bool>false</bool>
|
|
561
587
|
</property>
|
|
562
588
|
<property name="text">
|
|
563
|
-
<string>
|
|
589
|
+
<string>Assisted control</string>
|
|
564
590
|
</property>
|
|
565
591
|
<property name="checkable">
|
|
566
592
|
<bool>true</bool>
|
|
567
593
|
</property>
|
|
568
594
|
</widget>
|
|
569
595
|
</item>
|
|
570
|
-
<item row="
|
|
596
|
+
<item row="4" column="0">
|
|
571
597
|
<widget class="QCheckBox" name="exitapp">
|
|
572
598
|
<property name="enabled">
|
|
573
599
|
<bool>false</bool>
|
|
@@ -580,21 +606,21 @@
|
|
|
580
606
|
</property>
|
|
581
607
|
</widget>
|
|
582
608
|
</item>
|
|
583
|
-
<item row="
|
|
584
|
-
<widget class="QCheckBox" name="
|
|
609
|
+
<item row="2" column="0">
|
|
610
|
+
<widget class="QCheckBox" name="killswitch">
|
|
585
611
|
<property name="enabled">
|
|
586
612
|
<bool>false</bool>
|
|
587
613
|
</property>
|
|
588
614
|
<property name="text">
|
|
589
|
-
<string>
|
|
615
|
+
<string>Emergency stop</string>
|
|
590
616
|
</property>
|
|
591
617
|
<property name="checkable">
|
|
592
618
|
<bool>true</bool>
|
|
593
619
|
</property>
|
|
594
620
|
</widget>
|
|
595
621
|
</item>
|
|
596
|
-
<item row="
|
|
597
|
-
<widget class="QPushButton" name="
|
|
622
|
+
<item row="7" column="1">
|
|
623
|
+
<widget class="QPushButton" name="detectMuxswitch">
|
|
598
624
|
<property name="enabled">
|
|
599
625
|
<bool>false</bool>
|
|
600
626
|
</property>
|
|
@@ -603,21 +629,18 @@
|
|
|
603
629
|
</property>
|
|
604
630
|
</widget>
|
|
605
631
|
</item>
|
|
606
|
-
<item row="5" column="
|
|
607
|
-
<widget class="
|
|
632
|
+
<item row="5" column="1">
|
|
633
|
+
<widget class="QPushButton" name="detectAlt1">
|
|
608
634
|
<property name="enabled">
|
|
609
635
|
<bool>false</bool>
|
|
610
636
|
</property>
|
|
611
637
|
<property name="text">
|
|
612
|
-
<string>
|
|
613
|
-
</property>
|
|
614
|
-
<property name="checkable">
|
|
615
|
-
<bool>true</bool>
|
|
638
|
+
<string>Detect</string>
|
|
616
639
|
</property>
|
|
617
640
|
</widget>
|
|
618
641
|
</item>
|
|
619
642
|
<item row="4" column="1">
|
|
620
|
-
<widget class="QPushButton" name="
|
|
643
|
+
<widget class="QPushButton" name="detectExitapp">
|
|
621
644
|
<property name="enabled">
|
|
622
645
|
<bool>false</bool>
|
|
623
646
|
</property>
|
|
@@ -626,21 +649,21 @@
|
|
|
626
649
|
</property>
|
|
627
650
|
</widget>
|
|
628
651
|
</item>
|
|
629
|
-
<item row="
|
|
630
|
-
<widget class="QCheckBox" name="
|
|
652
|
+
<item row="3" column="0">
|
|
653
|
+
<widget class="QCheckBox" name="arm">
|
|
631
654
|
<property name="enabled">
|
|
632
655
|
<bool>false</bool>
|
|
633
656
|
</property>
|
|
634
657
|
<property name="text">
|
|
635
|
-
<string>
|
|
658
|
+
<string>Arm/disarm</string>
|
|
636
659
|
</property>
|
|
637
660
|
<property name="checkable">
|
|
638
661
|
<bool>true</bool>
|
|
639
662
|
</property>
|
|
640
663
|
</widget>
|
|
641
664
|
</item>
|
|
642
|
-
<item row="
|
|
643
|
-
<widget class="QPushButton" name="
|
|
665
|
+
<item row="3" column="1">
|
|
666
|
+
<widget class="QPushButton" name="detectArm">
|
|
644
667
|
<property name="enabled">
|
|
645
668
|
<bool>false</bool>
|
|
646
669
|
</property>
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# || ____ _ __
|
|
4
|
+
# +------+ / __ )(_) /_______________ _____ ___
|
|
5
|
+
# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
|
|
6
|
+
# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
|
|
7
|
+
# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
|
|
8
|
+
#
|
|
9
|
+
# Copyright (C) 2021-2023 Bitcraze AB
|
|
10
|
+
#
|
|
11
|
+
# This program is free software; you can redistribute it and/or
|
|
12
|
+
# modify it under the terms of the GNU General Public License
|
|
13
|
+
# as published by the Free Software Foundation; either version 2
|
|
14
|
+
# of the License, or (at your option) any later version.
|
|
15
|
+
#
|
|
16
|
+
# This program is distributed in the hope that it will be useful,
|
|
17
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
+
# GNU General Public License for more details.
|
|
20
|
+
# You should have received a copy of the GNU General Public License
|
|
21
|
+
# along with this program; if not, write to the Free Software
|
|
22
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
23
|
+
# MA 02110-1301, USA.
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
Dialog box used to configure base station geometry. Used from the lighthouse tab.
|
|
27
|
+
"""
|
|
28
|
+
import logging
|
|
29
|
+
|
|
30
|
+
import cfclient
|
|
31
|
+
from PyQt6 import QtWidgets
|
|
32
|
+
from PyQt6 import uic
|
|
33
|
+
from PyQt6.QtCore import QVariant, Qt, QAbstractTableModel, pyqtSignal
|
|
34
|
+
from cflib.localization import LighthouseSweepAngleAverageReader
|
|
35
|
+
from cflib.crazyflie.mem import LighthouseBsGeometry
|
|
36
|
+
from cfclient.ui.wizards.lighthouse_geo_bs_estimation_wizard import LighthouseBasestationGeometryWizard
|
|
37
|
+
|
|
38
|
+
__author__ = 'Bitcraze AB'
|
|
39
|
+
__all__ = ['LighthouseBsGeometryDialog']
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
(basestation_geometry_widget_class, connect_widget_base_class) = (
|
|
44
|
+
uic.loadUiType(
|
|
45
|
+
cfclient.module_path + '/ui/dialogs/lighthouse_bs_geometry_dialog.ui')
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class LighthouseBsGeometryTableModel(QAbstractTableModel):
|
|
50
|
+
def __init__(self, headers, parent=None, *args):
|
|
51
|
+
QAbstractTableModel.__init__(self, parent)
|
|
52
|
+
self._headers = headers
|
|
53
|
+
self._table_values = []
|
|
54
|
+
self._current_geos = {}
|
|
55
|
+
self._estimated_geos = {}
|
|
56
|
+
|
|
57
|
+
def rowCount(self, parent=None, *args, **kwargs):
|
|
58
|
+
return len(self._table_values)
|
|
59
|
+
|
|
60
|
+
def columnCount(self, parent=None, *args, **kwargs):
|
|
61
|
+
return len(self._headers)
|
|
62
|
+
|
|
63
|
+
def data(self, index, role=None):
|
|
64
|
+
if index.isValid():
|
|
65
|
+
value = self._table_values[index.row()][index.column()]
|
|
66
|
+
if role == Qt.ItemDataRole.DisplayRole:
|
|
67
|
+
return QVariant(value)
|
|
68
|
+
|
|
69
|
+
return QVariant()
|
|
70
|
+
|
|
71
|
+
def headerData(self, col, orientation, role=None):
|
|
72
|
+
if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
|
|
73
|
+
return QVariant(self._headers[col])
|
|
74
|
+
return QVariant()
|
|
75
|
+
|
|
76
|
+
def _compile_entry(self, current_geo, estimated_geo, index):
|
|
77
|
+
result = 'N/A'
|
|
78
|
+
if current_geo is not None:
|
|
79
|
+
result = '%.2f' % current_geo.origin[index]
|
|
80
|
+
if estimated_geo is not None:
|
|
81
|
+
result += ' -> %.2f' % estimated_geo.origin[index]
|
|
82
|
+
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
def _add_table_value(self, current_geo, estimated_geo, id, table_values):
|
|
86
|
+
x = self._compile_entry(current_geo, estimated_geo, 0)
|
|
87
|
+
y = self._compile_entry(current_geo, estimated_geo, 1)
|
|
88
|
+
z = self._compile_entry(current_geo, estimated_geo, 2)
|
|
89
|
+
|
|
90
|
+
table_values.append([id + 1, x, y, z])
|
|
91
|
+
|
|
92
|
+
def _add_table_value_for_id(self, current_geos, estimated_geos, table_values, id):
|
|
93
|
+
current_geo = None
|
|
94
|
+
if id in current_geos:
|
|
95
|
+
current_geo = current_geos[id]
|
|
96
|
+
|
|
97
|
+
estimated_geo = None
|
|
98
|
+
if id in estimated_geos:
|
|
99
|
+
estimated_geo = estimated_geos[id]
|
|
100
|
+
|
|
101
|
+
if current_geo is not None or estimated_geo is not None:
|
|
102
|
+
self._add_table_value(current_geo, estimated_geo, id, table_values)
|
|
103
|
+
|
|
104
|
+
def _add_table_values(self, current_geos, estimated_geos, table_values):
|
|
105
|
+
current_ids = set(current_geos.keys())
|
|
106
|
+
estimated_ids = set(estimated_geos.keys())
|
|
107
|
+
all_ids = current_ids.union(estimated_ids)
|
|
108
|
+
|
|
109
|
+
for id in all_ids:
|
|
110
|
+
self._add_table_value_for_id(current_geos, estimated_geos, table_values, id)
|
|
111
|
+
|
|
112
|
+
def _update_table_data(self):
|
|
113
|
+
self.layoutAboutToBeChanged.emit()
|
|
114
|
+
self._table_values = []
|
|
115
|
+
self._add_table_values(self._current_geos, self._estimated_geos, self._table_values)
|
|
116
|
+
self._table_values.sort(key=lambda row: row[0])
|
|
117
|
+
self.layoutChanged.emit()
|
|
118
|
+
|
|
119
|
+
def set_estimated_geos(self, geos):
|
|
120
|
+
self._estimated_geos = geos
|
|
121
|
+
self._update_table_data()
|
|
122
|
+
|
|
123
|
+
def set_current_geos(self, geos):
|
|
124
|
+
self._current_geos = geos
|
|
125
|
+
self._update_table_data()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class LighthouseBsGeometryDialog(QtWidgets.QWidget, basestation_geometry_widget_class):
|
|
129
|
+
|
|
130
|
+
_sweep_angles_received_and_averaged_signal = pyqtSignal(object)
|
|
131
|
+
_base_station_geometery_received_signal = pyqtSignal(object)
|
|
132
|
+
|
|
133
|
+
def __init__(self, lighthouse_tab, *args):
|
|
134
|
+
super(LighthouseBsGeometryDialog, self).__init__(*args)
|
|
135
|
+
self.setupUi(self)
|
|
136
|
+
|
|
137
|
+
self._lighthouse_tab = lighthouse_tab
|
|
138
|
+
|
|
139
|
+
self._estimate_geometry_button.clicked.connect(self._estimate_geometry_button_clicked)
|
|
140
|
+
|
|
141
|
+
self._write_to_cf_button.clicked.connect(self._write_to_cf_button_clicked)
|
|
142
|
+
|
|
143
|
+
self._sweep_angles_received_and_averaged_signal.connect(self._sweep_angles_received_and_averaged_cb)
|
|
144
|
+
self._base_station_geometery_received_signal.connect(self._basestation_geometry_received_signal_cb)
|
|
145
|
+
self._close_button.clicked.connect(self.close)
|
|
146
|
+
|
|
147
|
+
self._sweep_angle_reader = LighthouseSweepAngleAverageReader(
|
|
148
|
+
self._lighthouse_tab._helper.cf, self._sweep_angles_received_and_averaged_signal.emit)
|
|
149
|
+
|
|
150
|
+
self._base_station_geometry_wizard = LighthouseBasestationGeometryWizard(
|
|
151
|
+
self._lighthouse_tab._helper.cf, self._base_station_geometery_received_signal.emit)
|
|
152
|
+
|
|
153
|
+
self._lh_geos = None
|
|
154
|
+
self._newly_estimated_geometry = {}
|
|
155
|
+
|
|
156
|
+
# Table handlers
|
|
157
|
+
self._headers = ['id', 'x', 'y', 'z']
|
|
158
|
+
self._data_model = LighthouseBsGeometryTableModel(self._headers, self)
|
|
159
|
+
self._table_view.setModel(self._data_model)
|
|
160
|
+
|
|
161
|
+
self._table_view.verticalHeader().setVisible(False)
|
|
162
|
+
|
|
163
|
+
header = self._table_view.horizontalHeader()
|
|
164
|
+
header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
|
|
165
|
+
header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.Stretch)
|
|
166
|
+
header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.Stretch)
|
|
167
|
+
header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch)
|
|
168
|
+
|
|
169
|
+
self._update_ui()
|
|
170
|
+
|
|
171
|
+
def reset(self):
|
|
172
|
+
self._newly_estimated_geometry = {}
|
|
173
|
+
self._update_ui()
|
|
174
|
+
|
|
175
|
+
def _basestation_geometry_received_signal_cb(self, basestation_geometries):
|
|
176
|
+
self._newly_estimated_geometry = basestation_geometries
|
|
177
|
+
self.show()
|
|
178
|
+
self._update_ui()
|
|
179
|
+
|
|
180
|
+
def _sweep_angles_received_and_averaged_cb(self, averaged_angles):
|
|
181
|
+
self._averaged_angles = averaged_angles
|
|
182
|
+
self._newly_estimated_geometry = {}
|
|
183
|
+
|
|
184
|
+
for id, average_data in averaged_angles.items():
|
|
185
|
+
sensor_data = average_data[1]
|
|
186
|
+
rotation_bs_matrix, position_bs_vector = self._simple_estimator.estimate_geometry(sensor_data)
|
|
187
|
+
geo = LighthouseBsGeometry()
|
|
188
|
+
geo.rotation_matrix = rotation_bs_matrix
|
|
189
|
+
geo.origin = position_bs_vector
|
|
190
|
+
geo.valid = True
|
|
191
|
+
self._newly_estimated_geometry[id] = geo
|
|
192
|
+
|
|
193
|
+
self._update_ui()
|
|
194
|
+
|
|
195
|
+
def _estimate_geometry_button_clicked(self):
|
|
196
|
+
self._base_station_geometry_wizard.reset()
|
|
197
|
+
self._base_station_geometry_wizard.show()
|
|
198
|
+
self.hide()
|
|
199
|
+
|
|
200
|
+
def _write_to_cf_button_clicked(self):
|
|
201
|
+
if len(self._newly_estimated_geometry) > 0:
|
|
202
|
+
self._lighthouse_tab.write_and_store_geometry(self._newly_estimated_geometry)
|
|
203
|
+
self._newly_estimated_geometry = {}
|
|
204
|
+
|
|
205
|
+
self._update_ui()
|
|
206
|
+
|
|
207
|
+
def _update_ui(self):
|
|
208
|
+
self._write_to_cf_button.setEnabled(len(self._newly_estimated_geometry) > 0)
|
|
209
|
+
self._data_model.set_estimated_geos(self._newly_estimated_geometry)
|
|
210
|
+
|
|
211
|
+
def closeEvent(self, event):
|
|
212
|
+
self._stop_collection()
|
|
213
|
+
|
|
214
|
+
def _stop_collection(self):
|
|
215
|
+
self._sweep_angle_reader.stop_angle_collection()
|
|
216
|
+
|
|
217
|
+
def geometry_updated(self, geometry):
|
|
218
|
+
self._data_model.set_current_geos(geometry)
|
|
219
|
+
|
|
220
|
+
self._update_ui()
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<ui version="4.0">
|
|
3
|
+
<class>Form</class>
|
|
4
|
+
<widget class="QWidget" name="Form">
|
|
5
|
+
<property name="windowModality">
|
|
6
|
+
<enum>Qt::ApplicationModal</enum>
|
|
7
|
+
</property>
|
|
8
|
+
<property name="geometry">
|
|
9
|
+
<rect>
|
|
10
|
+
<x>0</x>
|
|
11
|
+
<y>0</y>
|
|
12
|
+
<width>443</width>
|
|
13
|
+
<height>555</height>
|
|
14
|
+
</rect>
|
|
15
|
+
</property>
|
|
16
|
+
<property name="windowTitle">
|
|
17
|
+
<string>Basestation Geometry Managment</string>
|
|
18
|
+
</property>
|
|
19
|
+
<layout class="QVBoxLayout" name="verticalLayout_4">
|
|
20
|
+
<item>
|
|
21
|
+
<layout class="QVBoxLayout" name="verticalLayout">
|
|
22
|
+
<item>
|
|
23
|
+
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
24
|
+
<item>
|
|
25
|
+
<widget class="QPushButton" name="_estimate_geometry_button">
|
|
26
|
+
<property name="text">
|
|
27
|
+
<string>Estimate Geometry</string>
|
|
28
|
+
</property>
|
|
29
|
+
</widget>
|
|
30
|
+
</item>
|
|
31
|
+
<item>
|
|
32
|
+
<spacer name="horizontalSpacer_2">
|
|
33
|
+
<property name="orientation">
|
|
34
|
+
<enum>Qt::Horizontal</enum>
|
|
35
|
+
</property>
|
|
36
|
+
<property name="sizeHint" stdset="0">
|
|
37
|
+
<size>
|
|
38
|
+
<width>40</width>
|
|
39
|
+
<height>20</height>
|
|
40
|
+
</size>
|
|
41
|
+
</property>
|
|
42
|
+
</spacer>
|
|
43
|
+
</item>
|
|
44
|
+
</layout>
|
|
45
|
+
</item>
|
|
46
|
+
<item>
|
|
47
|
+
<widget class="QTableView" name="_table_view"/>
|
|
48
|
+
</item>
|
|
49
|
+
<item>
|
|
50
|
+
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
|
51
|
+
<item>
|
|
52
|
+
<spacer name="horizontalSpacer_3">
|
|
53
|
+
<property name="orientation">
|
|
54
|
+
<enum>Qt::Horizontal</enum>
|
|
55
|
+
</property>
|
|
56
|
+
<property name="sizeHint" stdset="0">
|
|
57
|
+
<size>
|
|
58
|
+
<width>40</width>
|
|
59
|
+
<height>20</height>
|
|
60
|
+
</size>
|
|
61
|
+
</property>
|
|
62
|
+
</spacer>
|
|
63
|
+
</item>
|
|
64
|
+
</layout>
|
|
65
|
+
</item>
|
|
66
|
+
<item>
|
|
67
|
+
<widget class="Line" name="line">
|
|
68
|
+
<property name="orientation">
|
|
69
|
+
<enum>Qt::Horizontal</enum>
|
|
70
|
+
</property>
|
|
71
|
+
</widget>
|
|
72
|
+
</item>
|
|
73
|
+
<item>
|
|
74
|
+
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
75
|
+
<item>
|
|
76
|
+
<widget class="QPushButton" name="_write_to_cf_button">
|
|
77
|
+
<property name="text">
|
|
78
|
+
<string>Write to Crazyflie</string>
|
|
79
|
+
</property>
|
|
80
|
+
</widget>
|
|
81
|
+
</item>
|
|
82
|
+
<item>
|
|
83
|
+
<spacer name="horizontalSpacer">
|
|
84
|
+
<property name="orientation">
|
|
85
|
+
<enum>Qt::Horizontal</enum>
|
|
86
|
+
</property>
|
|
87
|
+
<property name="sizeHint" stdset="0">
|
|
88
|
+
<size>
|
|
89
|
+
<width>40</width>
|
|
90
|
+
<height>20</height>
|
|
91
|
+
</size>
|
|
92
|
+
</property>
|
|
93
|
+
</spacer>
|
|
94
|
+
</item>
|
|
95
|
+
<item>
|
|
96
|
+
<widget class="QPushButton" name="_close_button">
|
|
97
|
+
<property name="text">
|
|
98
|
+
<string>Close</string>
|
|
99
|
+
</property>
|
|
100
|
+
</widget>
|
|
101
|
+
</item>
|
|
102
|
+
</layout>
|
|
103
|
+
</item>
|
|
104
|
+
</layout>
|
|
105
|
+
</item>
|
|
106
|
+
</layout>
|
|
107
|
+
</widget>
|
|
108
|
+
<resources/>
|
|
109
|
+
<connections/>
|
|
110
|
+
</ui>
|