JoystickInput.h 12.6 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*=====================================================================

PIXHAWK Micro Air Vehicle Flying Robotics Toolkit

(c) 2009, 2010 PIXHAWK PROJECT  <http://pixhawk.ethz.ch>

This file is part of the PIXHAWK project

    PIXHAWK 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.

    PIXHAWK 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 PIXHAWK. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
26
 * @brief Definition of joystick interface
pixhawk's avatar
pixhawk committed
27
 *
28 29 30 31 32
 * This class defines a new thread to operate the reading of any joystick/controllers
 * via the Simple Directmedia Library (libsdl.org). This relies on polling of the SDL,
 * which is not their recommended method, instead they suggest use event checking. That
 * does not seem to support switching joysticks after their internal event loop has started,
 * so it was abandoned.
pixhawk's avatar
pixhawk committed
33
 *
34 35 36 37 38 39
 * All joystick-related functionality is done in this class, though the JoystickWidget provides
 * a UI around modifying its settings. Additionally controller buttons can be mapped to
 * actions defined by any UASInterface object through the `UASInterface::getActions()` function.
 *
 * @author Lorenz Meier <mavteam@student.ethz.ch>
 * @author Andreas Romer <mavteam@student.ethz.ch>
40
 * @author Julian Oes <julian@oes.ch>
pixhawk's avatar
pixhawk committed
41 42 43 44 45 46 47
 */

#ifndef _JOYSTICKINPUT_H_
#define _JOYSTICKINPUT_H_

#include <QThread>
#include <QList>
48
#include <qmutex.h>
Lorenz Meier's avatar
Lorenz Meier committed
49 50 51
#ifdef Q_OS_MAC
#include <SDL.h>
#else
52
#include <SDL/SDL.h>
Lorenz Meier's avatar
Lorenz Meier committed
53
#endif
pixhawk's avatar
pixhawk committed
54

55
#include "UAS.h"
pixhawk's avatar
pixhawk committed
56

57 58 59
struct JoystickSettings {
    QMap<int, bool> axesInverted; ///< Whether each axis should be used inverted from what was reported.
    QMap<int, bool> axesLimited; ///< Whether each axis should be limited to only the positive range. Currently this only applies to the throttle axis, but is kept generic here to possibly support other axes.
60 61
    QMap<int, float> axesMaxRange; ///< The maximum values per axis
    QMap<int, float> axesMinRange; ///< The minimum values per axis
62 63
    QMap<int, int> buttonActions; ///< The index of the action associated with every button.
};
64
Q_DECLARE_METATYPE(JoystickSettings)
65

pixhawk's avatar
pixhawk committed
66 67 68 69 70 71 72 73 74
/**
 * @brief Joystick input
 */
class JoystickInput : public QThread
{
    Q_OBJECT

public:
    JoystickInput();
75
    ~JoystickInput();
pixhawk's avatar
pixhawk committed
76
    void run();
77
    void shutdown();
pixhawk's avatar
pixhawk committed
78

79 80 81 82 83 84 85 86 87 88 89 90 91
    /**
     * @brief The JOYSTICK_INPUT_MAPPING enum storing the values for each item in the mapping combobox.
     * This should match the order of items in the mapping combobox in JoystickAxis.ui.
     */
    enum JOYSTICK_INPUT_MAPPING
    {
        JOYSTICK_INPUT_MAPPING_NONE     = 0,
        JOYSTICK_INPUT_MAPPING_YAW      = 1,
        JOYSTICK_INPUT_MAPPING_PITCH    = 2,
        JOYSTICK_INPUT_MAPPING_ROLL     = 3,
        JOYSTICK_INPUT_MAPPING_THROTTLE = 4
    };

92 93 94 95 96 97 98 99 100 101 102 103 104
    /**
     * @brief The JOYSTICK_MODE enum stores the values for each item in the mode combobox.
     * This should match the order of items in the mode combobox in JoystickWidget.ui.
     */
    enum JOYSTICK_MODE
    {
        JOYSTICK_MODE_ATTITUDE     = 0,
        JOYSTICK_MODE_POSITION     = 1,
        JOYSTICK_MODE_FORCE        = 2,
        JOYSTICK_MODE_VELOCITY     = 3,
        JOYSTICK_MODE_MANUAL       = 4
    };

Lorenz Meier's avatar
Lorenz Meier committed
105
    /**
106
     * @brief Load joystick-specific settings.
Lorenz Meier's avatar
Lorenz Meier committed
107
     */
108 109 110 111 112
    void loadJoystickSettings();
    /**
     * @brief Load joystick-independent settings.
     */
    void loadGeneralSettings();
Lorenz Meier's avatar
Lorenz Meier committed
113 114

    /**
115 116 117 118 119
     * @brief Store joystick-specific settings.
     */
    void storeJoystickSettings() const;
    /**
     * @brief Store joystick-independent settings.
Lorenz Meier's avatar
Lorenz Meier committed
120
     */
121 122 123 124 125 126
    void storeGeneralSettings() const;

    bool enabled() const
    {
        return isEnabled;
    }
Lorenz Meier's avatar
Lorenz Meier committed
127

128 129 130 131 132
    bool calibrating() const
    {
        return isCalibrating;
    }

133
    int getMappingThrottleAxis() const
Lorenz Meier's avatar
Lorenz Meier committed
134
    {
135
        return throttleAxis;
Lorenz Meier's avatar
Lorenz Meier committed
136 137
    }

138
    int getMappingRollAxis() const
Lorenz Meier's avatar
Lorenz Meier committed
139
    {
140
        return rollAxis;
Lorenz Meier's avatar
Lorenz Meier committed
141 142
    }

143
    int getMappingPitchAxis() const
Lorenz Meier's avatar
Lorenz Meier committed
144
    {
145
        return pitchAxis;
Lorenz Meier's avatar
Lorenz Meier committed
146 147
    }

148
    int getMappingYawAxis() const
Lorenz Meier's avatar
Lorenz Meier committed
149 150 151 152
    {
        return yawAxis;
    }

153 154
    int getJoystickNumButtons() const
    {
155
        return joystickNumButtons;
156 157
    }

158 159
    int getJoystickNumAxes() const
    {
160
        return joystickNumAxes;
161 162
    }

163 164 165 166 167
    int getJoystickID() const
    {
        return joystickID;
    }

168 169 170 171 172
    const QString& getName() const
    {
        return joystickName;
    }

173 174
    int getNumJoysticks() const
    {
175
        return numJoysticks;
176 177
    }

178 179 180 181 182
    JOYSTICK_MODE getMode() const
    {
        return mode;
    }

183 184 185 186 187
    QString getJoystickNameById(int id) const
    {
        return QString(SDL_JoystickName(id));
    }

188 189 190
    float getCurrentValueForAxis(int axis) const;
    bool getInvertedForAxis(int axis) const;
    bool getRangeLimitForAxis(int axis) const;
191 192
    float getAxisRangeLimitMinForAxis(int axis) const;
    float getAxisRangeLimitMaxForAxis(int axis) const;
193
    int getActionForButton(int button) const;
194

pixhawk's avatar
pixhawk committed
195 196 197 198
    const double sdlJoystickMin;
    const double sdlJoystickMax;

protected:
199 200

    bool isEnabled; ///< Track whether the system should emit the higher-level signals: joystickChanged & actionTriggered.
201
    bool isCalibrating; ///< Track if calibration in progress
202 203
    bool done;

pixhawk's avatar
pixhawk committed
204
    SDL_Joystick* joystick;
205
    UAS* uas; ///< Track the current UAS.
206 207
    int autopilotType; ///< Cache the autopilotType
    int systemType; ///< Cache the systemType
208
    bool uasCanReverse; ///< Track whether the connect UAS can drive a reverse speed.
pixhawk's avatar
pixhawk committed
209

210
    // Store the mapping between axis numbers and the roll/pitch/yaw/throttle configuration.
211
    // Value is one of JoystickAxis::JOYSTICK_INPUT_MAPPING.
212 213
    int rollAxis;
    int pitchAxis;
pixhawk's avatar
pixhawk committed
214
    int yawAxis;
215 216 217
    int throttleAxis;

    // Cache information on the joystick instead of polling the SDL everytime.
218
    int numJoysticks; ///< Total number of joysticks detected by the SDL.
219
    QString joystickName;
220
    int joystickID;
221 222
    int joystickNumAxes;
    int joystickNumButtons;
223

224 225 226
    // mode of joystick (attitude, position, force, ... (see JOYSTICK_MODE enum))
    JOYSTICK_MODE mode;

227 228 229 230 231 232 233 234 235
    // Track axis/button settings based on a Joystick/AutopilotType/SystemType triplet.
    // This is only a double-map, because settings are stored/loaded based on joystick
    // name first, so only the settings for the current joystick need to be stored at any given time.
    // Pointers are kept to the various settings field to reduce lookup times.
    // Note that the mapping (0,0) corresponds to when no UAS is connected. Since this corresponds
    // to a generic vehicle type and a generic autopilot, this is a pretty safe default.
    QMap<int, QMap<int, JoystickSettings> > joystickSettings;

    // Track the last state of the axes, buttons, and hats for only emitting change signals.
236
    QList<float> joystickAxes; ///< The values of every axes during the last sample.
237
    quint16 joystickButtons;   ///< The state of every button. Bitfield supporting 16 buttons with 1s indicating that the button is down.
238
    qint8 xHat, yHat;            ///< The horizontal/vertical hat directions. Values are -1, 0, 1, with (-1,-1) indicating bottom-left.
pixhawk's avatar
pixhawk committed
239

240 241 242
    /**
     * @brief Called before main run() event loop starts. Waits for joysticks to be connected.
     */
pixhawk's avatar
pixhawk committed
243 244
    void init();

245
signals:
pixhawk's avatar
pixhawk committed
246 247 248 249

    /**
     * @brief Signal containing all joystick raw positions
     *
250 251 252 253
     * @param roll forward / pitch / x axis, front: 1, center: 0, back: -1. If the roll axis isn't defined, NaN is transmit instead.
     * @param pitch left / roll / y axis, left: -1, middle: 0, right: 1. If the roll axis isn't defined, NaN is transmit instead.
     * @param yaw turn axis, left-turn: -1, centered: 0, right-turn: 1. If the roll axis isn't defined, NaN is transmit instead.
     * @param throttle Throttle, -100%:-1.0, 0%: 0.0, 100%: 1.0. If the roll axis isn't defined, NaN is transmit instead.
pixhawk's avatar
pixhawk committed
254 255
     * @param xHat hat vector in forward-backward direction, +1 forward, 0 center, -1 backward
     * @param yHat hat vector in left-right direction, -1 left, 0 center, +1 right
256
     * @param mode (setpoint type) see JOYSTICK_MODE enum
pixhawk's avatar
pixhawk committed
257
     */
258
    void joystickChanged(float roll, float pitch, float yaw, float throttle, qint8 xHat, qint8 yHat, quint16 buttons, quint8 mode);
pixhawk's avatar
pixhawk committed
259 260

    /**
261
      * @brief Emit a new value for an axis
262
      *
263
      * @param value Value of the axis, between -1.0 and 1.0.
264
      */
265
    void axisValueChanged(int axis, float value);
pixhawk's avatar
pixhawk committed
266

267
    /**
268
      * @brief Joystick button has changed state from unpressed to pressed.
269 270
      * @param key index of the pressed key
      */
pixhawk's avatar
pixhawk committed
271 272
    void buttonPressed(int key);

273 274 275 276 277 278 279
    /**
      * @brief Joystick button has changed state from pressed to unpressed.
      *
      * @param key index of the released key
      */
    void buttonReleased(int key);

280 281 282 283 284 285
    /**
      * @brief A joystick button was pressed that had a corresponding action.
      * @param action The index of the action to trigger. Dependent on UAS.
      */
    void actionTriggered(int action);

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
    /**
      * @brief Hat (8-way switch on the top) has changed position
      *
      * Coordinate frame for joystick hat:
      *
      *    y
      *    ^
      *    |
      *    |
      *    0 ----> x
      *
      *
      * @param x vector in left-right direction
      * @param y vector in forward-backward direction
      */
301
    void hatDirectionChanged(qint8 x, qint8 y);
pixhawk's avatar
pixhawk committed
302

303 304 305
    /** @brief Signal that the UAS has been updated for this JoystickInput
     * Note that any UI updates should NOT query this object for joystick details. That should be done in response to the joystickSettingsChanged signal.
     */
306
    void activeVehicleChanged(Vehicle* vehicle);
307

308 309 310
    /** @brief Signals that new joystick-specific settings were changed. Useful for triggering updates that at dependent on the current joystick. */
    void joystickSettingsChanged();

311 312 313
    /** @brief The JoystickInput has switched to a different joystick. UI should be adjusted accordingly. */
    void newJoystickSelected();

314
public slots:
315 316
    /** @brief Enable or disable emitting the high-level control signals from the joystick. */
    void setEnabled(bool enable);
317
    /** @brief Switch to a new joystick by ID number. Both buttons and axes are updated with the proper signals emitted. */
318
    void setActiveJoystick(int id);
319 320
    /** @brief Switch calibration mode active */
    void setCalibrating(bool active);
321 322 323 324 325 326
    /**
     * @brief Change the control mapping for a given joystick axis.
     * @param axisID The axis to modify (0-indexed)
     * @param newMapping The mapping to use.
     * @see JOYSTICK_INPUT_MAPPING
     */
327
    void setAxisMapping(int axis, JoystickInput::JOYSTICK_INPUT_MAPPING newMapping);
328 329 330 331 332 333
    /**
     * @brief Specify if an axis should be inverted.
     * @param axis The ID of the axis.
     * @param inverted True indicates inverted from normal. Varies by controller.
     */
    void setAxisInversion(int axis, bool inverted);
334

335 336 337 338 339 340
    /**
     * @brief Specify that an axis should only transmit the positive values. Useful for controlling throttle from auto-centering axes.
     * @param axis Which axis has its range limited.
     * @param limitRange If true only the positive half of this axis will be read.
     */
    void setAxisRangeLimit(int axis, bool limitRange);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

    /**
     * @brief Specify minimum value for axis.
     * @param axis Which axis should be set.
     * @param min Value to be set.
     */
    void setAxisRangeLimitMin(int axis, float min);

    /**
     * @brief Specify maximum value for axis.
     * @param axis Which axis should be set.
     * @param max Value to be set.
     */
    void setAxisRangeLimitMax(int axis, float max);

356 357 358 359 360 361 362 363
    /**
     * @brief Specify a button->action mapping for the given uas.
     * This mapping is applied based on UAS autopilot type and UAS system type.
     * Connects the buttonEmitted signal for the corresponding button to the corresponding action for the current UAS.
     * @param button The numeric ID for the button
     * @param action The numeric ID of the action for this UAS to map to.
     */
    void setButtonAction(int button, int action);
364 365 366 367 368 369 370 371 372

    /**
     * @brief Specify which setpoints should be sent to the UAS when moving the joystick
     * @param newMode the mode (setpoint type) see the JOYSTICK_MODE enum
     */
    void setMode(int newMode)
    {
        mode = (JOYSTICK_MODE)newMode;
    }
373 374 375
    
private slots:
    void _activeVehicleChanged(Vehicle* vehicle);
pixhawk's avatar
pixhawk committed
376 377 378
};

#endif // _JOYSTICKINPUT_H_