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 56

#include "UASInterface.h"

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
    };

Thomas Gubler's avatar
Thomas Gubler committed
92 93 94 95 96 97 98
    /**
     * @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,
99 100
        JOYSTICK_MODE_POSITION     = 1,
        JOYSTICK_MODE_FORCE        = 2
Thomas Gubler's avatar
Thomas Gubler committed
101 102
    };

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

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

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

126 127 128 129 130
    bool calibrating() const
    {
        return isCalibrating;
    }

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

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

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

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

151 152
    int getJoystickNumButtons() const
    {
153
        return joystickNumButtons;
154 155
    }

156 157
    int getJoystickNumAxes() const
    {
158
        return joystickNumAxes;
159 160
    }

161 162 163 164 165
    int getJoystickID() const
    {
        return joystickID;
    }

166 167 168 169 170
    const QString& getName() const
    {
        return joystickName;
    }

171 172
    int getNumJoysticks() const
    {
173
        return numJoysticks;
174 175
    }

Thomas Gubler's avatar
Thomas Gubler committed
176 177 178 179 180
    JOYSTICK_MODE getMode() const
    {
        return mode;
    }

181 182 183 184 185
    QString getJoystickNameById(int id) const
    {
        return QString(SDL_JoystickName(id));
    }

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

pixhawk's avatar
pixhawk committed
193 194 195 196
    const double sdlJoystickMin;
    const double sdlJoystickMax;

protected:
197 198

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

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

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

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

Thomas Gubler's avatar
Thomas Gubler committed
222 223 224
    // mode of joystick (attitude, position, force, ... (see JOYSTICK_MODE enum))
    JOYSTICK_MODE mode;

225 226 227 228 229 230 231 232 233
    // 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.
234
    QList<float> joystickAxes; ///< The values of every axes during the last sample.
235
    quint16 joystickButtons;   ///< The state of every button. Bitfield supporting 16 buttons with 1s indicating that the button is down.
236
    qint8 xHat, yHat;            ///< The horizontal/vertical hat directions. Values are -1, 0, 1, with (-1,-1) indicating bottom-left.
pixhawk's avatar
pixhawk committed
237

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

243
signals:
pixhawk's avatar
pixhawk committed
244 245 246 247

    /**
     * @brief Signal containing all joystick raw positions
     *
248 249 250 251
     * @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
252 253
     * @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
254
     * @param mode (setpoint type) see JOYSTICK_MODE enum
pixhawk's avatar
pixhawk committed
255
     */
256
    void joystickChanged(float roll, float pitch, float yaw, float throttle, qint8 xHat, qint8 yHat, quint16 buttons, quint8 mode);
pixhawk's avatar
pixhawk committed
257 258

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

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

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

278 279 280 281 282 283
    /**
      * @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);

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
    /**
      * @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
      */
299
    void hatDirectionChanged(qint8 x, qint8 y);
pixhawk's avatar
pixhawk committed
300

301 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.
     */
    void activeUASSet(UASInterface*);

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

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

312
public slots:
313 314
    /** @brief Enable or disable emitting the high-level control signals from the joystick. */
    void setEnabled(bool enable);
315
    /** @brief Specify the UAS that this input should forward joystickChanged signals and buttonPresses to. */
pixhawk's avatar
pixhawk committed
316
    void setActiveUAS(UASInterface* uas);
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);
Thomas Gubler's avatar
Thomas Gubler committed
364 365 366 367 368

    /**
     * @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
     */
369
    void setMode(quint8 newMode)
Thomas Gubler's avatar
Thomas Gubler committed
370 371 372
    {
        mode = (JOYSTICK_MODE)newMode;
    }
pixhawk's avatar
pixhawk committed
373 374 375
};

#endif // _JOYSTICKINPUT_H_