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.
Files changed (140) hide show
  1. cfclient/__init__.py +16 -11
  2. cfclient/configs/config.json +4 -3
  3. cfclient/configs/input/Generic_OS_X.json +1 -0
  4. cfclient/configs/input/Joystick.json +1 -0
  5. cfclient/configs/input/PS3_Mode_1.json +1 -0
  6. cfclient/configs/input/PS3_Mode_2.json +1 -0
  7. cfclient/configs/input/PS3_Mode_3.json +1 -0
  8. cfclient/configs/input/PS4_Mode_1.json +1 -0
  9. cfclient/configs/input/PS4_Mode_2.json +1 -0
  10. cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
  11. cfclient/configs/input/xbox360_mode1.json +1 -0
  12. cfclient/configs/log/PID_tuning/Attitude.json +46 -0
  13. cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
  14. cfclient/configs/log/PID_tuning/Position.json +46 -0
  15. cfclient/configs/log/PID_tuning/Velocity.json +46 -0
  16. cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
  17. cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
  18. cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
  19. cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
  20. cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
  21. cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
  22. cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
  23. cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
  24. cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
  25. cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
  26. cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
  27. cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
  28. cfclient/gui.py +44 -9
  29. cfclient/headless.py +3 -12
  30. cfclient/resources/log_param_doc.json +1 -0
  31. cfclient/ui/connectivity_manager.py +198 -0
  32. cfclient/ui/dialogs/about.py +53 -36
  33. cfclient/ui/dialogs/about.ui +23 -3
  34. cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
  35. cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
  36. cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
  37. cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
  38. cfclient/ui/dialogs/bootloader.py +448 -85
  39. cfclient/ui/dialogs/bootloader.ui +387 -134
  40. cfclient/ui/dialogs/cf2config.py +4 -4
  41. cfclient/ui/dialogs/cf2config.ui +3 -4
  42. cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
  43. cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
  44. cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
  45. cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
  46. cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
  47. cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
  48. cfclient/ui/dialogs/logconfigdialogue.py +401 -101
  49. cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
  50. cfclient/ui/icons/bl.webp +0 -0
  51. cfclient/ui/icons/bolt.webp +0 -0
  52. cfclient/ui/icons/cf21.webp +0 -0
  53. cfclient/ui/icons/checkmark_black.png +0 -0
  54. cfclient/ui/icons/checkmark_white.png +0 -0
  55. cfclient/ui/icons/create.png +0 -0
  56. cfclient/ui/icons/delete.png +0 -0
  57. cfclient/ui/icons/flapper.webp +0 -0
  58. cfclient/ui/icons/tag.webp +0 -0
  59. cfclient/ui/main.py +328 -258
  60. cfclient/ui/main.ui +184 -80
  61. cfclient/ui/pluginhelper.py +7 -1
  62. cfclient/ui/pose_logger.py +116 -0
  63. cfclient/ui/tab_toolbox.py +208 -0
  64. cfclient/ui/tabs/ColorLEDTab.py +752 -0
  65. cfclient/ui/tabs/ConsoleTab.py +48 -13
  66. cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
  67. cfclient/ui/tabs/ExampleTab.py +9 -16
  68. cfclient/ui/tabs/FlightTab.py +437 -325
  69. cfclient/ui/tabs/GpsTab.py +14 -20
  70. cfclient/ui/tabs/LEDRingTab.py +277 -0
  71. cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
  72. cfclient/ui/tabs/LogBlockTab.py +35 -35
  73. cfclient/ui/tabs/LogClientTab.py +85 -0
  74. cfclient/ui/tabs/LogTab.py +50 -27
  75. cfclient/ui/tabs/ParamTab.py +443 -57
  76. cfclient/ui/tabs/PlotTab.py +23 -25
  77. cfclient/ui/tabs/TuningTab.py +292 -0
  78. cfclient/ui/tabs/__init__.py +12 -2
  79. cfclient/ui/tabs/colorLEDTab.ui +624 -0
  80. cfclient/ui/tabs/consoleTab.ui +46 -0
  81. cfclient/ui/tabs/flightActionContainer.ui +103 -0
  82. cfclient/ui/tabs/flightTab.ui +724 -237
  83. cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
  84. cfclient/ui/tabs/lighthouse_tab.py +714 -0
  85. cfclient/ui/tabs/lighthouse_tab.ui +430 -0
  86. cfclient/ui/tabs/locopositioning_tab.py +606 -389
  87. cfclient/ui/tabs/locopositioning_tab.ui +370 -253
  88. cfclient/ui/tabs/logClientTab.ui +52 -0
  89. cfclient/ui/tabs/logTab.ui +1 -1
  90. cfclient/ui/tabs/paramTab.ui +204 -3
  91. cfclient/ui/tabs/tuningTab.ui +773 -0
  92. cfclient/ui/widgets/ai.py +37 -39
  93. cfclient/ui/widgets/hexspinbox.py +16 -10
  94. cfclient/ui/widgets/plotter.ui +39 -47
  95. cfclient/ui/widgets/plotwidget.py +57 -22
  96. cfclient/ui/widgets/super_slider.py +112 -0
  97. cfclient/ui/wizards/__init__.py +0 -0
  98. cfclient/ui/wizards/bslh_1.png +0 -0
  99. cfclient/ui/wizards/bslh_2.png +0 -0
  100. cfclient/ui/wizards/bslh_3.png +0 -0
  101. cfclient/ui/wizards/bslh_4.png +0 -0
  102. cfclient/ui/wizards/bslh_5.png +0 -0
  103. cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
  104. cfclient/utils/config_manager.py +5 -4
  105. cfclient/utils/input/__init__.py +77 -19
  106. cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
  107. cfclient/utils/input/inputreaderinterface.py +17 -7
  108. cfclient/utils/input/inputreaders/__init__.py +17 -0
  109. cfclient/utils/logconfigreader.py +245 -25
  110. cfclient/utils/logdatawriter.py +3 -1
  111. cfclient/utils/periodictimer.py +1 -1
  112. cfclient/utils/ui.py +336 -0
  113. cfclient/utils/zmq_led_driver.py +5 -0
  114. cfclient/utils/zmq_param.py +6 -0
  115. cfclient/version.py +34 -1
  116. cfclient-2025.12.1.dist-info/METADATA +70 -0
  117. cfclient-2025.12.1.dist-info/RECORD +152 -0
  118. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
  119. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
  120. cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
  121. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
  122. cfconfig/Makefile +51 -0
  123. cfconfig/configblock.py +111 -0
  124. cfloader/__init__.py +41 -55
  125. cfzmq/__init__.py +22 -14
  126. cfclient/ui/dialogs/cf1config.py +0 -265
  127. cfclient/ui/dialogs/cf1config.ui +0 -260
  128. cfclient/ui/tab.py +0 -96
  129. cfclient/ui/tabs/LEDTab.py +0 -169
  130. cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
  131. cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
  132. cfclient/ui/toolboxes/__init__.py +0 -45
  133. cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
  134. cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
  135. cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
  136. cfclient-2017.4.dist-info/METADATA +0 -22
  137. cfclient-2017.4.dist-info/RECORD +0 -104
  138. cfclient-2017.4.dist-info/metadata.json +0 -1
  139. /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
  140. /cfclient/ui/{toolboxes → tabs}/crtpSharkToolbox.ui +0 -0
@@ -7,7 +7,7 @@
7
7
  # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
8
8
  # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
9
9
  #
10
- # Copyright (C) 2011-2013 Bitcraze AB
10
+ # Copyright (C) 2011-2025 Bitcraze AB
11
11
  #
12
12
  # Crazyflie Nano Quadcopter Client
13
13
  #
@@ -30,23 +30,21 @@ The flight control tab shows telemetry data and flight settings.
30
30
  """
31
31
 
32
32
  import logging
33
+ from enum import Enum
33
34
 
34
- from PyQt5 import uic
35
- from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
36
- from PyQt5.QtWidgets import QMessageBox
35
+ from PyQt6 import uic
36
+ from PyQt6.QtCore import Qt, pyqtSignal
37
+ from PyQt6.QtWidgets import QMessageBox
37
38
 
38
39
  import cfclient
39
40
  from cfclient.ui.widgets.ai import AttitudeIndicator
40
41
 
41
- from cfclient.utils.config import Config
42
42
  from cflib.crazyflie.log import LogConfig
43
43
 
44
+ from cfclient.utils.config import Config
44
45
  from cfclient.utils.input import JoystickReader
45
46
 
46
- from cfclient.ui.tab import Tab
47
-
48
- PARAM_NAME_ALT_HOLD_TARGET = "posCtl.targetZ"
49
- LOG_NAME_ESTIMATED_Z = "posEstimatorAlt.estimatedZ"
47
+ from cfclient.ui.tab_toolbox import TabToolbox
50
48
 
51
49
  __author__ = 'Bitcraze AB'
52
50
  __all__ = ['FlightTab']
@@ -58,20 +56,50 @@ flight_tab_class = uic.loadUiType(cfclient.module_path +
58
56
 
59
57
  MAX_THRUST = 65536.0
60
58
 
59
+ TOOLTIP_ALTITUDE_HOLD = """\
60
+ Keeps the Crazyflie at its current altitude.
61
+ Thrust control becomes height velocity control. The Crazyflie
62
+ uses the barometer for height control and uses body-fixed coordinates."""
63
+
64
+ TOOLTIP_POSITION_HOLD = """\
65
+ Keeps the Crazyflie at its current 3D position. Pitch/Roll/
66
+ Thrust control becomes X/Y/Z velocity control. Uses world coordinates."""
67
+
68
+ TOOLTIP_HEIGHT_HOLD = """\
69
+ When activated, keeps the Crazyflie at 40cm above the ground.
70
+ Thrust control becomes height velocity control. Requires a height
71
+ sensor like the Z-Ranger deck or flow deck. Uses body-fixed coordinates.."""
72
+
73
+ TOOLTIP_HOVER = """\
74
+ When activated, keeps the Crazyflie at 40cm above the ground and tries to
75
+ keep the position in X and Y as well. Thrust control becomes height velocity
76
+ control. Requires a flow deck. Uses body-fixed coordinates."""
77
+
78
+
79
+ class CommanderAction(Enum):
80
+ TAKE_OFF = 1
81
+ LAND = 2
82
+ UP = 3
83
+ DOWN = 4
84
+ LEFT = 5
85
+ RIGHT = 6
86
+ FORWARD = 7
87
+ BACK = 8
61
88
 
62
- class FlightTab(Tab, flight_tab_class):
89
+
90
+ class FlightTab(TabToolbox, flight_tab_class):
63
91
  uiSetupReadySignal = pyqtSignal()
64
92
 
65
- _motor_data_signal = pyqtSignal(int, object, object)
66
- _imu_data_signal = pyqtSignal(int, object, object)
67
- _althold_data_signal = pyqtSignal(int, object, object)
68
- _baro_data_signal = pyqtSignal(int, object, object)
93
+ _log_data_signal = pyqtSignal(int, object, object)
94
+ _pose_data_signal = pyqtSignal(object, object)
69
95
 
70
96
  _input_updated_signal = pyqtSignal(float, float, float, float)
71
97
  _rp_trim_updated_signal = pyqtSignal(float, float)
72
98
  _emergency_stop_updated_signal = pyqtSignal(bool)
99
+ _arm_updated_signal = pyqtSignal(bool)
73
100
  _assisted_control_updated_signal = pyqtSignal(bool)
74
101
  _heighthold_input_updated_signal = pyqtSignal(float, float, float, float)
102
+ _hover_input_updated_signal = pyqtSignal(float, float, float, float)
75
103
 
76
104
  _log_error_signal = pyqtSignal(object, str)
77
105
 
@@ -82,123 +110,95 @@ class FlightTab(Tab, flight_tab_class):
82
110
 
83
111
  _limiting_updated = pyqtSignal(bool, bool, bool)
84
112
 
85
- def __init__(self, tabWidget, helper, *args):
86
- super(FlightTab, self).__init__(*args)
87
- self.setupUi(self)
88
-
89
- self.tabName = "Flight Control"
90
- self.menuName = "Flight Control"
113
+ LOG_NAME_THRUST = 'stabilizer.thrust'
114
+ LOG_NAME_MOTOR_1 = 'motor.m1'
115
+ LOG_NAME_MOTOR_2 = 'motor.m2'
116
+ LOG_NAME_MOTOR_3 = 'motor.m3'
117
+ LOG_NAME_MOTOR_4 = 'motor.m4'
118
+ LOG_NAME_CAN_FLY = 'sys.canfly'
119
+ LOG_NAME_SUPERVISOR_INFO = 'supervisor.info'
91
120
 
92
- self.tabWidget = tabWidget
93
- self.helper = helper
121
+ def __init__(self, helper):
122
+ super(FlightTab, self).__init__(helper, 'Flight Control')
123
+ self.setupUi(self)
94
124
 
95
125
  self.disconnectedSignal.connect(self.disconnected)
96
126
  self.connectionFinishedSignal.connect(self.connected)
97
127
  # Incomming signals
98
- self.helper.cf.connected.add_callback(
128
+ self._helper.cf.connected.add_callback(
99
129
  self.connectionFinishedSignal.emit)
100
- self.helper.cf.disconnected.add_callback(self.disconnectedSignal.emit)
130
+ self._helper.cf.disconnected.add_callback(self.disconnectedSignal.emit)
101
131
 
102
132
  self._input_updated_signal.connect(self.updateInputControl)
103
- self.helper.inputDeviceReader.input_updated.add_callback(
133
+ self._helper.inputDeviceReader.input_updated.add_callback(
104
134
  self._input_updated_signal.emit)
105
135
  self._rp_trim_updated_signal.connect(self.calUpdateFromInput)
106
- self.helper.inputDeviceReader.rp_trim_updated.add_callback(
136
+ self._helper.inputDeviceReader.rp_trim_updated.add_callback(
107
137
  self._rp_trim_updated_signal.emit)
108
138
  self._emergency_stop_updated_signal.connect(self.updateEmergencyStop)
109
- self.helper.inputDeviceReader.emergency_stop_updated.add_callback(
139
+ self._helper.inputDeviceReader.emergency_stop_updated.add_callback(
110
140
  self._emergency_stop_updated_signal.emit)
141
+ self._arm_updated_signal.connect(lambda: self.updateArm(from_controller=True))
142
+ self._helper.inputDeviceReader.arm_updated.add_callback(self._arm_updated_signal.emit)
111
143
 
112
- self.helper.inputDeviceReader.heighthold_input_updated.add_callback(
144
+ self._helper.inputDeviceReader.heighthold_input_updated.add_callback(
113
145
  self._heighthold_input_updated_signal.emit)
114
146
  self._heighthold_input_updated_signal.connect(
115
147
  self._heighthold_input_updated)
148
+ self._helper.inputDeviceReader.hover_input_updated.add_callback(
149
+ self._hover_input_updated_signal.emit)
150
+ self._hover_input_updated_signal.connect(
151
+ self._hover_input_updated)
116
152
 
117
- self.helper.inputDeviceReader.assisted_control_updated.add_callback(
153
+ self._helper.inputDeviceReader.assisted_control_updated.add_callback(
118
154
  self._assisted_control_updated_signal.emit)
119
155
 
120
156
  self._assisted_control_updated_signal.connect(
121
157
  self._assisted_control_updated)
122
158
 
123
- self._imu_data_signal.connect(self._imu_data_received)
124
- self._baro_data_signal.connect(self._baro_data_received)
125
- self._althold_data_signal.connect(self._althold_data_received)
126
- self._motor_data_signal.connect(self._motor_data_received)
159
+ self._pose_data_signal.connect(self._pose_data_received)
160
+ self._log_data_signal.connect(self._log_data_received)
127
161
 
128
162
  self._log_error_signal.connect(self._logging_error)
129
163
 
164
+ self._isConnected = False
165
+
130
166
  # Connect UI signals that are in this tab
131
167
  self.flightModeCombo.currentIndexChanged.connect(self.flightmodeChange)
132
168
  self.minThrust.valueChanged.connect(self.minMaxThrustChanged)
133
169
  self.maxThrust.valueChanged.connect(self.minMaxThrustChanged)
134
- self.thrustLoweringSlewRateLimit.valueChanged.connect(
135
- self.thrustLoweringSlewRateLimitChanged)
136
- self.slewEnableLimit.valueChanged.connect(
137
- self.thrustLoweringSlewRateLimitChanged)
170
+ self.thrustLoweringSlewRateLimit.valueChanged.connect(self.thrustLoweringSlewRateLimitChanged)
171
+ self.slewEnableLimit.valueChanged.connect(self.thrustLoweringSlewRateLimitChanged)
138
172
  self.targetCalRoll.valueChanged.connect(self._trim_roll_changed)
139
173
  self.targetCalPitch.valueChanged.connect(self._trim_pitch_changed)
140
174
  self.maxAngle.valueChanged.connect(self.maxAngleChanged)
141
175
  self.maxYawRate.valueChanged.connect(self.maxYawRateChanged)
142
176
  self.uiSetupReadySignal.connect(self.uiSetupReady)
143
- self.clientXModeCheckbox.toggled.connect(self.changeXmode)
144
177
  self.isInCrazyFlightmode = False
178
+
179
+ # Command Based Flight Control
180
+ self._can_fly_deprecated = 0
181
+ self.commanderTakeOffButton.clicked.connect(lambda: self._flight_command(CommanderAction.TAKE_OFF))
182
+ self.commanderLandButton.clicked.connect(lambda: self._flight_command(CommanderAction.LAND))
183
+ self.commanderLeftButton.clicked.connect(lambda: self._flight_command(CommanderAction.LEFT))
184
+ self.commanderRightButton.clicked.connect(lambda: self._flight_command(CommanderAction.RIGHT))
185
+ self.commanderForwardButton.clicked.connect(lambda: self._flight_command(CommanderAction.FORWARD))
186
+ self.commanderBackButton.clicked.connect(lambda: self._flight_command(CommanderAction.BACK))
187
+ self.commanderUpButton.clicked.connect(lambda: self._flight_command(CommanderAction.UP))
188
+ self.commanderDownButton.clicked.connect(lambda: self._flight_command(CommanderAction.DOWN))
189
+ self._update_flight_commander(False)
190
+
191
+ # Supervisor
192
+ self._supervisor_info_bitfield = 0
193
+ self.armButton.clicked.connect(self.updateArm)
194
+ self._update_supervisor_and_arming(False)
195
+
145
196
  self.uiSetupReady()
146
197
 
147
- self.clientXModeCheckbox.setChecked(Config().get("client_side_xmode"))
148
-
149
- self.crazyflieXModeCheckbox.clicked.connect(
150
- lambda enabled:
151
- self.helper.cf.param.set_value("flightmode.x",
152
- str(enabled)))
153
- self.helper.cf.param.add_update_callback(
154
- group="flightmode", name="xmode",
155
- cb=(lambda name, checked:
156
- self.crazyflieXModeCheckbox.setChecked(eval(checked))))
157
-
158
- self.ratePidRadioButton.clicked.connect(
159
- lambda enabled:
160
- self.helper.cf.param.set_value("flightmode.ratepid",
161
- str(enabled)))
162
-
163
- self.angularPidRadioButton.clicked.connect(
164
- lambda enabled:
165
- self.helper.cf.param.set_value("flightmode.ratepid",
166
- str(not enabled)))
167
-
168
- self._led_ring_headlight.clicked.connect(
169
- lambda enabled:
170
- self.helper.cf.param.set_value("ring.headlightEnable",
171
- str(enabled)))
172
-
173
- self.helper.cf.param.add_update_callback(
174
- group="flightmode", name="ratepid",
175
- cb=(lambda name, checked:
176
- self.ratePidRadioButton.setChecked(eval(checked))))
177
-
178
- self.helper.cf.param.add_update_callback(
179
- group="cpu", name="flash",
180
- cb=self._set_enable_client_xmode)
181
-
182
- self.helper.cf.param.add_update_callback(
183
- group="ring", name="headlightEnable",
184
- cb=(lambda name, checked:
185
- self._led_ring_headlight.setChecked(eval(checked))))
186
-
187
- self._ledring_nbr_effects = 0
188
-
189
- self.helper.cf.param.add_update_callback(
190
- group="ring",
191
- name="effect",
192
- cb=self._ring_effect_updated)
193
-
194
- self.helper.cf.param.add_update_callback(
195
- group="imu_sensors",
196
- cb=self._set_available_sensors)
197
-
198
- self.helper.cf.param.all_updated.add_callback(
199
- self._ring_populate_dropdown)
200
-
201
- self.logBaro = None
198
+ self._helper.cf.param.add_update_callback(group="imu_sensors", cb=self._set_available_sensors)
199
+
200
+ self._helper.cf.param.all_updated.add_callback(self._all_params_updated)
201
+
202
202
  self.logAltHold = None
203
203
 
204
204
  self.ai = AttitudeIndicator()
@@ -208,43 +208,32 @@ class FlightTab(Tab, flight_tab_class):
208
208
  self.targetCalPitch.setValue(Config().get("trim_pitch"))
209
209
  self.targetCalRoll.setValue(Config().get("trim_roll"))
210
210
 
211
- self.helper.inputDeviceReader.alt1_updated.add_callback(
212
- self.alt1_updated)
213
- self.helper.inputDeviceReader.alt2_updated.add_callback(
214
- self.alt2_updated)
215
211
  self._tf_state = 0
216
- self._ring_effect = 0
217
212
 
218
- # Connect callbacks for input device limiting of rpöö/pitch/yaw/thust
219
- self.helper.inputDeviceReader.limiting_updated.add_callback(
220
- self._limiting_updated.emit)
213
+ # Connect callbacks for input device limiting of roll/pitch/yaw/thrust
214
+ self._helper.inputDeviceReader.limiting_updated.add_callback(self._limiting_updated.emit)
221
215
  self._limiting_updated.connect(self._set_limiting_enabled)
222
216
 
223
- def _set_enable_client_xmode(self, name, value):
224
- if eval(value) <= 128:
225
- self.clientXModeCheckbox.setEnabled(True)
226
- else:
227
- self.clientXModeCheckbox.setEnabled(False)
228
- self.clientXModeCheckbox.setChecked(False)
217
+ self._helper.pose_logger.data_received_cb.add_callback(self._pose_data_signal.emit)
218
+
219
+ def _set_limiting_enabled(self, rp_limiting_enabled, yaw_limiting_enabled, thrust_limiting_enabled):
229
220
 
230
- def _set_limiting_enabled(self, rp_limiting_enabled,
231
- yaw_limiting_enabled,
232
- thrust_limiting_enabled):
233
- self.maxAngle.setEnabled(rp_limiting_enabled)
234
221
  self.targetCalRoll.setEnabled(rp_limiting_enabled)
235
222
  self.targetCalPitch.setEnabled(rp_limiting_enabled)
236
- self.maxYawRate.setEnabled(yaw_limiting_enabled)
237
- self.maxThrust.setEnabled(thrust_limiting_enabled)
238
- self.minThrust.setEnabled(thrust_limiting_enabled)
239
- self.slewEnableLimit.setEnabled(thrust_limiting_enabled)
240
- self.thrustLoweringSlewRateLimit.setEnabled(thrust_limiting_enabled)
223
+
224
+ advanced_is_enabled = self.isInCrazyFlightmode
225
+ self.maxAngle.setEnabled(rp_limiting_enabled and advanced_is_enabled)
226
+ self.maxYawRate.setEnabled(yaw_limiting_enabled and advanced_is_enabled)
227
+ self.maxThrust.setEnabled(thrust_limiting_enabled and advanced_is_enabled)
228
+ self.minThrust.setEnabled(thrust_limiting_enabled and advanced_is_enabled)
229
+ self.slewEnableLimit.setEnabled(thrust_limiting_enabled and advanced_is_enabled)
230
+ self.thrustLoweringSlewRateLimit.setEnabled(thrust_limiting_enabled and advanced_is_enabled)
241
231
 
242
232
  def thrustToPercentage(self, thrust):
243
233
  return ((thrust / MAX_THRUST) * 100.0)
244
234
 
245
235
  def uiSetupReady(self):
246
- flightComboIndex = self.flightModeCombo.findText(
247
- Config().get("flightmode"), Qt.MatchFixedString)
236
+ flightComboIndex = self.flightModeCombo.findText(Config().get("flightmode"), Qt.MatchFlag.MatchFixedString)
248
237
  if (flightComboIndex < 0):
249
238
  self.flightModeCombo.setCurrentIndex(0)
250
239
  self.flightModeCombo.currentIndexChanged.emit(0)
@@ -252,86 +241,225 @@ class FlightTab(Tab, flight_tab_class):
252
241
  self.flightModeCombo.setCurrentIndex(flightComboIndex)
253
242
  self.flightModeCombo.currentIndexChanged.emit(flightComboIndex)
254
243
 
244
+ def _flight_command(self, action):
245
+ current_z = self._helper.pose_logger.position[2]
246
+ move_dist = 0.5
247
+ move_vel = 0.5
248
+
249
+ if action == CommanderAction.TAKE_OFF:
250
+ self._helper.cf.param.set_value('commander.enHighLevel', '1')
251
+ z_target = current_z + move_dist
252
+ self._helper.cf.high_level_commander.takeoff(z_target, move_dist / move_vel)
253
+ elif action == CommanderAction.LAND:
254
+ self._helper.cf.high_level_commander.land(0, current_z / move_vel)
255
+ elif action == CommanderAction.LEFT:
256
+ self._helper.cf.high_level_commander.go_to(0, move_dist, 0, 0, move_dist / move_vel, relative=True)
257
+ elif action == CommanderAction.RIGHT:
258
+ self._helper.cf.high_level_commander.go_to(0, -move_dist, 0, 0, move_dist / move_vel, relative=True)
259
+ elif action == CommanderAction.FORWARD:
260
+ self._helper.cf.high_level_commander.go_to(move_dist, 0, 0, 0, move_dist / move_vel, relative=True)
261
+ elif action == CommanderAction.BACK:
262
+ self._helper.cf.high_level_commander.go_to(-move_dist, 0, 0, 0, move_dist / move_vel, relative=True)
263
+ elif action == CommanderAction.UP:
264
+ self._helper.cf.high_level_commander.go_to(0, 0, move_dist, 0, move_dist / move_vel, relative=True)
265
+ elif action == CommanderAction.DOWN:
266
+ self._helper.cf.high_level_commander.go_to(0, 0, -move_dist, 0, move_dist / move_vel, relative=True)
267
+
255
268
  def _logging_error(self, log_conf, msg):
256
269
  QMessageBox.about(self, "Log error",
257
270
  "Error when starting log config [%s]: %s" % (
258
271
  log_conf.name, msg))
259
272
 
260
- def _motor_data_received(self, timestamp, data, logconf):
261
- if self.isVisible():
262
- self.actualM1.setValue(data["motor.m1"])
263
- self.actualM2.setValue(data["motor.m2"])
264
- self.actualM3.setValue(data["motor.m3"])
265
- self.actualM4.setValue(data["motor.m4"])
273
+ def _log_data_received(self, timestamp, data, logconf):
274
+ if self.isVisible() and self._isConnected:
275
+ self.actualM1.setValue(data[self.LOG_NAME_MOTOR_1])
276
+ self.actualM2.setValue(data[self.LOG_NAME_MOTOR_2])
277
+ self.actualM3.setValue(data[self.LOG_NAME_MOTOR_3])
278
+ self.actualM4.setValue(data[self.LOG_NAME_MOTOR_4])
266
279
 
267
- def _baro_data_received(self, timestamp, data, logconf):
280
+ self.estimateThrust.setText(
281
+ "%.2f%%" % self.thrustToPercentage(data[self.LOG_NAME_THRUST]))
282
+
283
+ if data[self.LOG_NAME_CAN_FLY] != self._can_fly_deprecated:
284
+ self._can_fly_deprecated = data[self.LOG_NAME_CAN_FLY]
285
+ self._update_flight_commander(True)
286
+
287
+ if self.LOG_NAME_SUPERVISOR_INFO in data:
288
+ self._supervisor_info_bitfield = data[self.LOG_NAME_SUPERVISOR_INFO]
289
+
290
+ self._update_supervisor_and_arming(True)
291
+
292
+ def _pose_data_received(self, pose_logger, pose):
268
293
  if self.isVisible():
269
- estimated_z = data[LOG_NAME_ESTIMATED_Z]
270
- self.actualHeight.setText(("%.2f" % estimated_z))
294
+ estimated_z = pose[2]
295
+ roll = pose[3]
296
+ pitch = pose[4]
297
+
298
+ self.estimateX.setText(("%.2f" % pose[0]))
299
+ self.estimateY.setText(("%.2f" % pose[1]))
300
+ self.estimateZ.setText(("%.2f" % estimated_z))
301
+ self.estimateRoll.setText(("%.2f" % roll))
302
+ self.estimatePitch.setText(("%.2f" % pitch))
303
+ self.estimateYaw.setText(("%.2f" % pose[5]))
304
+
271
305
  self.ai.setBaro(estimated_z, self.is_visible())
306
+ self.ai.setRollPitch(-roll, pitch, self.is_visible())
272
307
 
273
308
  def _heighthold_input_updated(self, roll, pitch, yaw, height):
274
309
  if (self.isVisible() and
275
- self.helper.inputDeviceReader.get_assisted_control() ==
276
- self.helper.inputDeviceReader.ASSISTED_CONTROL_HEIGHTHOLD):
277
- self.targetHeight.setText(("%.2f" % height))
310
+ (self._helper.inputDeviceReader.get_assisted_control() ==
311
+ self._helper.inputDeviceReader.ASSISTED_CONTROL_HEIGHTHOLD)):
312
+
313
+ self.targetRoll.setText(("%0.2f deg" % roll))
314
+ self.targetPitch.setText(("%0.2f deg" % pitch))
315
+ self.targetYaw.setText(("%0.2f deg/s" % yaw))
316
+ self.targetHeight.setText(("%.2f m" % height))
278
317
  self.ai.setHover(height, self.is_visible())
279
318
 
280
- def _althold_data_received(self, timestamp, data, logconf):
319
+ self._change_input_labels(using_hover_assist=False)
320
+
321
+ def _hover_input_updated(self, vx, vy, yaw, height):
281
322
  if (self.isVisible() and
282
- self.helper.inputDeviceReader.get_assisted_control() !=
283
- self.helper.inputDeviceReader.ASSISTED_CONTROL_HEIGHTHOLD):
284
- target = data[PARAM_NAME_ALT_HOLD_TARGET]
285
- if target > 0:
286
- if not self.targetHeight.isEnabled():
287
- self.targetHeight.setEnabled(True)
288
- self.targetHeight.setText(("%.2f" % target))
289
- self.ai.setHover(target, self.is_visible())
290
- elif self.targetHeight.isEnabled():
291
- self.targetHeight.setEnabled(False)
292
- self.targetHeight.setText("Not set")
293
- self.ai.setHover(0, self.is_visible())
294
-
295
- def _imu_data_received(self, timestamp, data, logconf):
296
- if self.isVisible():
297
- self.actualRoll.setText(("%.2f" % data["stabilizer.roll"]))
298
- self.actualPitch.setText(("%.2f" % data["stabilizer.pitch"]))
299
- self.actualYaw.setText(("%.2f" % data["stabilizer.yaw"]))
300
- self.actualThrust.setText("%.2f%%" %
301
- self.thrustToPercentage(
302
- data["stabilizer.thrust"]))
323
+ (self._helper.inputDeviceReader.get_assisted_control() ==
324
+ self._helper.inputDeviceReader.ASSISTED_CONTROL_HOVER)):
303
325
 
304
- self.ai.setRollPitch(-data["stabilizer.roll"],
305
- data["stabilizer.pitch"], self.is_visible())
326
+ self.targetRoll.setText(("%0.2f m/s" % vy))
327
+ self.targetPitch.setText(("%0.2f m/s" % vx))
328
+ self.targetYaw.setText(("%0.2f deg/s" % yaw))
329
+ self.targetHeight.setText(("%.2f m" % height))
330
+ self.ai.setHover(height, self.is_visible())
306
331
 
307
- def connected(self, linkURI):
308
- # IMU & THRUST
309
- lg = LogConfig("Stabilizer", Config().get("ui_update_period"))
310
- lg.add_variable("stabilizer.roll", "float")
311
- lg.add_variable("stabilizer.pitch", "float")
312
- lg.add_variable("stabilizer.yaw", "float")
313
- lg.add_variable("stabilizer.thrust", "uint16_t")
332
+ self._change_input_labels(using_hover_assist=True)
314
333
 
315
- try:
316
- self.helper.cf.log.add_config(lg)
317
- lg.data_received_cb.add_callback(self._imu_data_signal.emit)
318
- lg.error_cb.add_callback(self._log_error_signal.emit)
319
- lg.start()
320
- except KeyError as e:
321
- logger.warning(str(e))
322
- except AttributeError as e:
323
- logger.warning(str(e))
334
+ def _change_input_labels(self, using_hover_assist):
335
+ if using_hover_assist:
336
+ pitch, roll, yaw = 'Velocity X', 'Velocity Y', 'Velocity Z'
337
+ else:
338
+ pitch, roll, yaw = 'Pitch', 'Roll', 'Yaw'
339
+
340
+ self.inputPitchLabel.setText(pitch)
341
+ self.inputRollLabel.setText(roll)
342
+ self.inputYawLabel.setText(yaw)
343
+
344
+ def _update_supervisor_and_arming(self, connected):
345
+ if not connected:
346
+ self.armButton.setStyleSheet("")
347
+ self.armButton.setText("Arm")
348
+ self.armButton.setEnabled(False)
349
+ self._supervisor_state.setText("")
350
+ self._supervisor_state.setStyleSheet("")
351
+ return
352
+
353
+ self._supervisor_state.setText("")
354
+ if self._is_tumbled():
355
+ self._supervisor_state.setText("Tumbled")
324
356
 
325
- # MOTOR
357
+ if self._is_locked():
358
+ self.armButton.setText("")
359
+ self.armButton.setEnabled(False)
360
+ self.armButton.setStyleSheet("")
361
+ self._supervisor_state.setText("Locked-please reboot")
362
+ self._supervisor_state.setStyleSheet("background-color: red")
363
+ return
364
+ else:
365
+ self._supervisor_state.setStyleSheet("")
366
+
367
+ if self._is_crashed():
368
+ self.armButton.setText("Recover")
369
+ if self._is_tumbled():
370
+ self.armButton.setEnabled(False)
371
+ self.armButton.setStyleSheet("")
372
+ self._supervisor_state.setText("Crashed, flip over to recover")
373
+ else:
374
+ self.armButton.setEnabled(True)
375
+ self.armButton.setStyleSheet("background-color: red")
376
+ self._supervisor_state.setText("Crashed, click Recover")
377
+
378
+ self._supervisor_state.setStyleSheet("background-color: red")
379
+ return
380
+
381
+ if self._is_flying():
382
+ self.armButton.setEnabled(True)
383
+ self.armButton.setText("Emergency stop")
384
+ self.armButton.setStyleSheet("background-color: red")
385
+ self._supervisor_state.setText("Flying")
386
+ return
387
+
388
+ if self._is_armed():
389
+ self.armButton.setStyleSheet("background-color: red")
390
+ if self._auto_arming():
391
+ self.armButton.setEnabled(False)
392
+ self.armButton.setText("Auto armed")
393
+ else:
394
+ self.armButton.setEnabled(True)
395
+ self.armButton.setText("Disarm")
396
+ else:
397
+ self.armButton.setText("Arm")
398
+ if self._can_arm():
399
+ self.armButton.setEnabled(True)
400
+ self.armButton.setStyleSheet("background-color: lightgreen")
401
+ else:
402
+ self.armButton.setStyleSheet("")
403
+ self.armButton.setEnabled(False)
404
+
405
+ def _update_flight_commander(self, connected):
406
+ self.commanderBox.setToolTip(str())
407
+ if not connected:
408
+ self.commanderBox.setEnabled(False)
409
+ return
410
+
411
+ if self._can_fly_deprecated == 0:
412
+ self.commanderBox.setEnabled(False)
413
+ self.commanderBox.setToolTip('The Crazyflie reports that flight is not possible')
414
+ return
415
+
416
+ # We cannot know if we have a positioning deck until we get params
417
+ if not self._helper.cf.param.is_updated:
418
+ self.commanderBox.setEnabled(False)
419
+ return
420
+
421
+ # flowV1 flowV2 LightHouse LPS
422
+ position_decks = ['bcFlow', 'bcFlow2', 'bcLighthouse4', 'bcLoco', 'bcDWM1000']
423
+ for deck in position_decks:
424
+ if int(self._helper.cf.param.values['deck'][deck]) == 1:
425
+ self.commanderBox.setEnabled(True)
426
+ break
427
+ else:
428
+ self.commanderBox.setToolTip('You need a positioning deck to use Command Based Flight')
429
+ self.commanderBox.setEnabled(False)
430
+ return
431
+
432
+ # To prevent conflicting commands from the controller and the flight panel
433
+ if JoystickReader().available_devices():
434
+ self.commanderBox.setToolTip(
435
+ 'Cannot use both a controller and Command Based Flight'
436
+ )
437
+ self.commanderBox.setEnabled(False)
438
+ return
439
+
440
+ def connected(self, linkURI):
441
+ self._isConnected = True
442
+ # MOTOR & THRUST
326
443
  lg = LogConfig("Motors", Config().get("ui_update_period"))
327
- lg.add_variable("motor.m1")
328
- lg.add_variable("motor.m2")
329
- lg.add_variable("motor.m3")
330
- lg.add_variable("motor.m4")
444
+ lg.add_variable(self.LOG_NAME_THRUST, "uint16_t")
445
+ lg.add_variable(self.LOG_NAME_MOTOR_1)
446
+ lg.add_variable(self.LOG_NAME_MOTOR_2)
447
+ lg.add_variable(self.LOG_NAME_MOTOR_3)
448
+ lg.add_variable(self.LOG_NAME_MOTOR_4)
449
+ lg.add_variable(self.LOG_NAME_CAN_FLY)
450
+
451
+ # Add supervisor info if it exists to keep backwards compatibility
452
+ if self._helper.cf.log.toc.get_element_by_complete_name(self.LOG_NAME_SUPERVISOR_INFO):
453
+ lg.add_variable(self.LOG_NAME_SUPERVISOR_INFO)
454
+ # Full supervisor info available after V7, hide supervisor info for earlier versions
455
+ update_supervisor_info = self._helper.cf.platform.get_protocol_version() >= 7
456
+ self._supervisor_state.setVisible(update_supervisor_info)
457
+ self._supervisor_label1.setVisible(update_supervisor_info)
458
+ self._supervisor_label2.setVisible(update_supervisor_info)
331
459
 
332
460
  try:
333
- self.helper.cf.log.add_config(lg)
334
- lg.data_received_cb.add_callback(self._motor_data_signal.emit)
461
+ self._helper.cf.log.add_config(lg)
462
+ lg.data_received_cb.add_callback(self._log_data_signal.emit)
335
463
  lg.error_cb.add_callback(self._log_error_signal.emit)
336
464
  lg.start()
337
465
  except KeyError as e:
@@ -339,78 +467,41 @@ class FlightTab(Tab, flight_tab_class):
339
467
  except AttributeError as e:
340
468
  logger.warning(str(e))
341
469
 
342
- self._populate_assisted_mode_dropdown()
470
+ def _enable_estimators(self, should_enable):
471
+ self.estimateX.setEnabled(should_enable)
472
+ self.estimateY.setEnabled(should_enable)
473
+ self.estimateZ.setEnabled(should_enable)
343
474
 
344
475
  def _set_available_sensors(self, name, available):
345
- logger.info("[%s]: %s", name, available)
476
+ logger.debug("[%s]: %s", name, available)
346
477
  available = eval(available)
347
- if ("HMC5883L" in name):
348
- if (not available):
349
- self.actualHeight.setText("N/A")
350
- self.actualHeight.setEnabled(False)
351
- else:
352
- self.actualHeight.setEnabled(True)
353
- self.helper.inputDeviceReader.set_alt_hold_available(available)
354
- if (not self.logBaro and not self.logAltHold):
355
- # The sensor is available, set up the logging
356
- self.logBaro = LogConfig("Baro", 200)
357
- self.logBaro.add_variable(LOG_NAME_ESTIMATED_Z, "float")
358
-
359
- try:
360
- self.helper.cf.log.add_config(self.logBaro)
361
- self.logBaro.data_received_cb.add_callback(
362
- self._baro_data_signal.emit)
363
- self.logBaro.error_cb.add_callback(
364
- self._log_error_signal.emit)
365
- self.logBaro.start()
366
- except KeyError as e:
367
- logger.warning(str(e))
368
- except AttributeError as e:
369
- logger.warning(str(e))
370
- self.logAltHold = LogConfig("AltHold", 200)
371
- self.logAltHold.add_variable(PARAM_NAME_ALT_HOLD_TARGET,
372
- "float")
373
-
374
- try:
375
- self.helper.cf.log.add_config(self.logAltHold)
376
- self.logAltHold.data_received_cb.add_callback(
377
- self._althold_data_signal.emit)
378
- self.logAltHold.error_cb.add_callback(
379
- self._log_error_signal.emit)
380
- self.logAltHold.start()
381
- except KeyError as e:
382
- logger.warning(str(e))
383
- except AttributeError as e:
384
- logger.warning(str(e))
478
+
479
+ self._enable_estimators(True)
480
+ self._helper.inputDeviceReader.set_alt_hold_available(available)
385
481
 
386
482
  def disconnected(self, linkURI):
483
+ self._isConnected = False
387
484
  self.ai.setRollPitch(0, 0)
388
485
  self.actualM1.setValue(0)
389
486
  self.actualM2.setValue(0)
390
487
  self.actualM3.setValue(0)
391
488
  self.actualM4.setValue(0)
392
- self.actualRoll.setText("")
393
- self.actualPitch.setText("")
394
- self.actualYaw.setText("")
395
- self.actualThrust.setText("")
396
- self.actualHeight.setText("")
489
+
490
+ self.estimateRoll.setText("")
491
+ self.estimatePitch.setText("")
492
+ self.estimateYaw.setText("")
493
+ self.estimateThrust.setText("")
494
+ self.estimateX.setText("")
495
+ self.estimateY.setText("")
496
+ self.estimateZ.setText("")
497
+
397
498
  self.targetHeight.setText("Not Set")
398
499
  self.ai.setHover(0, self.is_visible())
399
500
  self.targetHeight.setEnabled(False)
400
- self.actualHeight.setEnabled(False)
401
- self.clientXModeCheckbox.setEnabled(False)
402
- self.logBaro = None
501
+
502
+ self._enable_estimators(False)
503
+
403
504
  self.logAltHold = None
404
- self._led_ring_effect.setEnabled(False)
405
- self._led_ring_effect.clear()
406
- try:
407
- self._led_ring_effect.currentIndexChanged.disconnect(
408
- self._ring_effect_changed)
409
- except TypeError:
410
- # Signal was not connected
411
- pass
412
- self._led_ring_effect.setCurrentIndex(-1)
413
- self._led_ring_headlight.setEnabled(False)
414
505
 
415
506
  try:
416
507
  self._assist_mode_combo.currentIndexChanged.disconnect(
@@ -421,17 +512,46 @@ class FlightTab(Tab, flight_tab_class):
421
512
  self._assist_mode_combo.setEnabled(False)
422
513
  self._assist_mode_combo.clear()
423
514
 
515
+ self._update_flight_commander(False)
516
+
517
+ self._supervisor_info_bitfield = 0
518
+ self._update_supervisor_and_arming(False)
519
+
520
+ def _can_arm(self):
521
+ return bool(self._supervisor_info_bitfield & 0x0001)
522
+
523
+ def _is_armed(self):
524
+ return bool(self._supervisor_info_bitfield & 0x0002)
525
+
526
+ def _auto_arming(self):
527
+ return bool(self._supervisor_info_bitfield & 0x0004)
528
+
529
+ def _can_fly(self):
530
+ return bool(self._supervisor_info_bitfield & 0x0008)
531
+
532
+ def _is_flying(self):
533
+ return bool(self._supervisor_info_bitfield & 0x0010)
534
+
535
+ def _is_tumbled(self):
536
+ return bool(self._supervisor_info_bitfield & 0x0020)
537
+
538
+ def _is_locked(self):
539
+ return bool(self._supervisor_info_bitfield & 0x0040)
540
+
541
+ def _is_crashed(self):
542
+ return bool(self._supervisor_info_bitfield & 0x0080)
543
+
424
544
  def minMaxThrustChanged(self):
425
- self.helper.inputDeviceReader.min_thrust = self.minThrust.value()
426
- self.helper.inputDeviceReader.max_thrust = self.maxThrust.value()
545
+ self._helper.inputDeviceReader.min_thrust = self.minThrust.value()
546
+ self._helper.inputDeviceReader.max_thrust = self.maxThrust.value()
427
547
  if (self.isInCrazyFlightmode is True):
428
548
  Config().set("min_thrust", self.minThrust.value())
429
549
  Config().set("max_thrust", self.maxThrust.value())
430
550
 
431
551
  def thrustLoweringSlewRateLimitChanged(self):
432
- self.helper.inputDeviceReader.thrust_slew_rate = (
552
+ self._helper.inputDeviceReader.thrust_slew_rate = (
433
553
  self.thrustLoweringSlewRateLimit.value())
434
- self.helper.inputDeviceReader.thrust_slew_limit = (
554
+ self._helper.inputDeviceReader.thrust_slew_limit = (
435
555
  self.slewEnableLimit.value())
436
556
  if (self.isInCrazyFlightmode is True):
437
557
  Config().set("slew_limit", self.slewEnableLimit.value())
@@ -439,24 +559,24 @@ class FlightTab(Tab, flight_tab_class):
439
559
 
440
560
  def maxYawRateChanged(self):
441
561
  logger.debug("MaxYawrate changed to %d", self.maxYawRate.value())
442
- self.helper.inputDeviceReader.max_yaw_rate = self.maxYawRate.value()
562
+ self._helper.inputDeviceReader.max_yaw_rate = self.maxYawRate.value()
443
563
  if (self.isInCrazyFlightmode is True):
444
564
  Config().set("max_yaw", self.maxYawRate.value())
445
565
 
446
566
  def maxAngleChanged(self):
447
567
  logger.debug("MaxAngle changed to %d", self.maxAngle.value())
448
- self.helper.inputDeviceReader.max_rp_angle = self.maxAngle.value()
568
+ self._helper.inputDeviceReader.max_rp_angle = self.maxAngle.value()
449
569
  if (self.isInCrazyFlightmode is True):
450
570
  Config().set("max_rp", self.maxAngle.value())
451
571
 
452
572
  def _trim_pitch_changed(self, value):
453
573
  logger.debug("Pitch trim updated to [%f]" % value)
454
- self.helper.inputDeviceReader.trim_pitch = value
574
+ self._helper.inputDeviceReader.trim_pitch = value
455
575
  Config().set("trim_pitch", value)
456
576
 
457
577
  def _trim_roll_changed(self, value):
458
578
  logger.debug("Roll trim updated to [%f]" % value)
459
- self.helper.inputDeviceReader.trim_roll = value
579
+ self._helper.inputDeviceReader.trim_roll = value
460
580
  Config().set("trim_roll", value)
461
581
 
462
582
  def calUpdateFromInput(self, rollCal, pitchCal):
@@ -466,12 +586,14 @@ class FlightTab(Tab, flight_tab_class):
466
586
  self.targetCalPitch.setValue(pitchCal)
467
587
 
468
588
  def updateInputControl(self, roll, pitch, yaw, thrust):
469
- self.targetRoll.setText(("%0.2f" % roll))
470
- self.targetPitch.setText(("%0.2f" % pitch))
471
- self.targetYaw.setText(("%0.2f" % yaw))
589
+ self.targetRoll.setText(("%0.2f deg" % roll))
590
+ self.targetPitch.setText(("%0.2f deg" % pitch))
591
+ self.targetYaw.setText(("%0.2f deg/s" % yaw))
472
592
  self.targetThrust.setText(("%0.2f %%" %
473
593
  self.thrustToPercentage(thrust)))
474
- self.thrustProgress.setValue(thrust)
594
+ self.thrustProgress.setValue(int(thrust))
595
+
596
+ self._change_input_labels(using_hover_assist=False)
475
597
 
476
598
  def setMotorLabelsEnabled(self, enabled):
477
599
  self.M1label.setEnabled(enabled)
@@ -487,11 +609,21 @@ class FlightTab(Tab, flight_tab_class):
487
609
  def updateEmergencyStop(self, emergencyStop):
488
610
  if emergencyStop:
489
611
  self.setMotorLabelsEnabled(False)
490
- self.emergency_stop_label.setText(
491
- self.emergencyStopStringWithText("Kill switch active"))
612
+ self._helper.cf.loc.send_emergency_stop()
613
+ # TODO krri disarm?
492
614
  else:
493
615
  self.setMotorLabelsEnabled(True)
494
- self.emergency_stop_label.setText("")
616
+
617
+ def updateArm(self, from_controller=False):
618
+ if self._is_flying() and not from_controller:
619
+ self._helper.cf.loc.send_emergency_stop()
620
+ elif self._is_crashed():
621
+ self._helper.cf.platform.send_crash_recovery_request()
622
+ elif self._is_armed():
623
+ self._helper.cf.platform.send_arming_request(False)
624
+ elif self._can_arm():
625
+ self.armButton.setStyleSheet("background-color: orange")
626
+ self._helper.cf.platform.send_arming_request(True)
495
627
 
496
628
  def flightmodeChange(self, item):
497
629
  Config().set("flightmode", str(self.flightModeCombo.itemText(item)))
@@ -536,111 +668,89 @@ class FlightTab(Tab, flight_tab_class):
536
668
  mode = JoystickReader.ASSISTED_CONTROL_POSHOLD
537
669
  if (item == 2): # Position hold
538
670
  mode = JoystickReader.ASSISTED_CONTROL_HEIGHTHOLD
671
+ if (item == 3): # Position hold
672
+ mode = JoystickReader.ASSISTED_CONTROL_HOVER
539
673
 
540
- self.helper.inputDeviceReader.set_assisted_control(mode)
674
+ self._helper.inputDeviceReader.set_assisted_control(mode)
541
675
  Config().set("assistedControl", mode)
542
676
 
543
677
  def _assisted_control_updated(self, enabled):
544
- if self.helper.inputDeviceReader.get_assisted_control() == \
678
+ if self._helper.inputDeviceReader.get_assisted_control() == \
545
679
  JoystickReader.ASSISTED_CONTROL_POSHOLD:
546
680
  self.targetThrust.setEnabled(not enabled)
547
681
  self.targetRoll.setEnabled(not enabled)
548
682
  self.targetPitch.setEnabled(not enabled)
549
- elif self.helper.inputDeviceReader.get_assisted_control() == \
550
- JoystickReader.ASSISTED_CONTROL_HEIGHTHOLD:
683
+ elif ((self._helper.inputDeviceReader.get_assisted_control() ==
684
+ JoystickReader.ASSISTED_CONTROL_HEIGHTHOLD) or
685
+ (self._helper.inputDeviceReader.get_assisted_control() ==
686
+ JoystickReader.ASSISTED_CONTROL_HOVER)):
551
687
  self.targetThrust.setEnabled(not enabled)
688
+ self.targetHeight.setEnabled(enabled)
689
+ print('Chaning enable for target height: %s' % enabled)
552
690
  else:
553
- self.helper.cf.param.set_value("flightmode.althold", str(enabled))
554
-
555
- @pyqtSlot(bool)
556
- def changeXmode(self, checked):
557
- self.helper.cf.commander.set_client_xmode(checked)
558
- Config().set("client_side_xmode", checked)
559
- logger.info("Clientside X-mode enabled: %s", checked)
691
+ self._helper.cf.param.set_value("flightmode.althold", int(enabled))
560
692
 
561
- def alt1_updated(self, state):
562
- if state:
563
- new_index = (self._ring_effect+1) % (self._ledring_nbr_effects+1)
564
- self.helper.cf.param.set_value("ring.effect",
565
- str(new_index))
566
-
567
- def alt2_updated(self, state):
568
- self.helper.cf.param.set_value("ring.headlightEnable", str(state))
693
+ def _all_params_updated(self):
694
+ self._populate_assisted_mode_dropdown()
695
+ self._update_flight_commander(True)
696
+ self._update_supervisor_and_arming(True)
569
697
 
570
- def _ring_populate_dropdown(self):
571
- try:
572
- nbr = int(self.helper.cf.param.values["ring"]["neffect"])
573
- current = int(self.helper.cf.param.values["ring"]["effect"])
574
- except KeyError:
575
- return
698
+ def _populate_assisted_mode_dropdown(self):
699
+ self._assist_mode_combo.addItem("Altitude hold", 0)
700
+ self._assist_mode_combo.addItem("Position hold", 1)
701
+ self._assist_mode_combo.addItem("Height hold", 2)
702
+ self._assist_mode_combo.addItem("Hover", 3)
576
703
 
577
- # Used only in alt1_updated function
578
- self._ring_effect = current
579
- self._ledring_nbr_effects = nbr
580
-
581
- hardcoded_names = {0: "Off",
582
- 1: "White spinner",
583
- 2: "Color spinner",
584
- 3: "Tilt effect",
585
- 4: "Brightness effect",
586
- 5: "Color spinner 2",
587
- 6: "Double spinner",
588
- 7: "Solid color effect",
589
- 8: "Factory test",
590
- 9: "Battery status",
591
- 10: "Boat lights",
592
- 11: "Alert",
593
- 12: "Gravity",
594
- 13: "LED tab"}
595
-
596
- for i in range(nbr + 1):
597
- name = "{}: ".format(i)
598
- if i in hardcoded_names:
599
- name += hardcoded_names[i]
600
- else:
601
- name += "N/A"
602
- self._led_ring_effect.addItem(name, i)
704
+ # Add the tooltips to the assist-mode items.
705
+ self._assist_mode_combo.setItemData(0, TOOLTIP_ALTITUDE_HOLD, Qt.ItemDataRole.ToolTipRole)
706
+ self._assist_mode_combo.setItemData(1, TOOLTIP_POSITION_HOLD, Qt.ItemDataRole.ToolTipRole)
707
+ self._assist_mode_combo.setItemData(2, TOOLTIP_HEIGHT_HOLD, Qt.ItemDataRole.ToolTipRole)
708
+ self._assist_mode_combo.setItemData(3, TOOLTIP_HOVER, Qt.ItemDataRole.ToolTipRole)
603
709
 
604
- self._led_ring_effect.currentIndexChanged.connect(
605
- self._ring_effect_changed)
710
+ heightHoldPossible = False
711
+ hoverPossible = False
606
712
 
607
- self._led_ring_effect.setCurrentIndex(current)
608
- if self.helper.cf.mem.ow_search(vid=0xBC, pid=0x01):
609
- self._led_ring_effect.setEnabled(True)
610
- self._led_ring_headlight.setEnabled(True)
713
+ if int(self._helper.cf.param.values["deck"]["bcZRanger"]) == 1:
714
+ heightHoldPossible = True
715
+ self._helper.inputDeviceReader.set_hover_max_height(1.0)
611
716
 
612
- def _ring_effect_changed(self, index):
613
- self._ring_effect = index
614
- if index > -1:
615
- i = self._led_ring_effect.itemData(index)
616
- logger.info("Changed effect to {}".format(i))
617
- if i != int(self.helper.cf.param.values["ring"]["effect"]):
618
- self.helper.cf.param.set_value("ring.effect", str(i))
717
+ if int(self._helper.cf.param.values["deck"]["bcZRanger2"]) == 1:
718
+ heightHoldPossible = True
719
+ self._helper.inputDeviceReader.set_hover_max_height(2.0)
619
720
 
620
- def _ring_effect_updated(self, name, value):
621
- if self.helper.cf.param.is_updated:
622
- self._led_ring_effect.setCurrentIndex(int(value))
721
+ if int(self._helper.cf.param.values["deck"]["bcFlow"]) == 1:
722
+ heightHoldPossible = True
723
+ hoverPossible = True
724
+ self._helper.inputDeviceReader.set_hover_max_height(1.0)
623
725
 
624
- def _populate_assisted_mode_dropdown(self):
625
- self._assist_mode_combo.addItem("Altitude hold", 0)
626
- self._assist_mode_combo.addItem("Position hold", 1)
627
- self._assist_mode_combo.addItem("Height hold", 2)
628
- heightHoldPossible = False
629
- if self.helper.cf.mem.ow_search(vid=0xBC, pid=0x09):
726
+ if int(self._helper.cf.param.values["deck"]["bcFlow2"]) == 1:
630
727
  heightHoldPossible = True
728
+ hoverPossible = True
729
+ self._helper.inputDeviceReader.set_hover_max_height(2.0)
631
730
 
632
731
  if not heightHoldPossible:
633
732
  self._assist_mode_combo.model().item(2).setEnabled(False)
634
733
  else:
635
734
  self._assist_mode_combo.model().item(0).setEnabled(False)
636
735
 
736
+ if not hoverPossible:
737
+ self._assist_mode_combo.model().item(3).setEnabled(False)
738
+ else:
739
+ self._assist_mode_combo.model().item(0).setEnabled(False)
740
+
637
741
  self._assist_mode_combo.currentIndexChanged.connect(
638
742
  self._assist_mode_changed)
639
743
  self._assist_mode_combo.setEnabled(True)
640
744
 
641
745
  try:
642
746
  assistmodeComboIndex = Config().get("assistedControl")
643
- if assistmodeComboIndex == 2 and not heightHoldPossible:
747
+ if assistmodeComboIndex == 3 and not hoverPossible:
748
+ self._assist_mode_combo.setCurrentIndex(0)
749
+ self._assist_mode_combo.currentIndexChanged.emit(0)
750
+ elif assistmodeComboIndex == 0 and hoverPossible:
751
+ self._assist_mode_combo.setCurrentIndex(3)
752
+ self._assist_mode_combo.currentIndexChanged.emit(3)
753
+ elif assistmodeComboIndex == 2 and not heightHoldPossible:
644
754
  self._assist_mode_combo.setCurrentIndex(0)
645
755
  self._assist_mode_combo.currentIndexChanged.emit(0)
646
756
  elif assistmodeComboIndex == 0 and heightHoldPossible:
@@ -652,7 +762,9 @@ class FlightTab(Tab, flight_tab_class):
652
762
  assistmodeComboIndex)
653
763
  except KeyError:
654
764
  defaultOption = 0
655
- if heightHoldPossible:
765
+ if hoverPossible:
766
+ defaultOption = 3
767
+ elif heightHoldPossible:
656
768
  defaultOption = 2
657
769
  self._assist_mode_combo.setCurrentIndex(defaultOption)
658
770
  self._assist_mode_combo.currentIndexChanged.emit(defaultOption)