JoystickInput.cc 9.8 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
/*=====================================================================
======================================================================*/

/**
 * @file
 *   @brief Joystick interface
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *   @author Andreas Romer <mavteam@student.ethz.ch>
 *
 */

#include "JoystickInput.h"

#include <QDebug>
#include <limits.h>
#include "UAS.h"
#include "UASManager.h"
19
#include "QGC.h"
20
#include <QMutexLocker>
Lorenz Meier's avatar
Lorenz Meier committed
21
#include <QSettings>
22
#include <math.h>
pixhawk's avatar
pixhawk committed
23 24 25 26 27 28

/**
 * The coordinate frame of the joystick axis is the aeronautical frame like shown on this image:
 * @image html http://pixhawk.ethz.ch/wiki/_media/standards/body-frame.png Aeronautical frame
 */
JoystickInput::JoystickInput() :
29 30 31 32 33 34 35 36 37 38 39
    sdlJoystickMin(-32768.0f),
    sdlJoystickMax(32767.0f),
    uas(NULL),
    done(false),
    rollAxis(-1),
    pitchAxis(-1),
    yawAxis(-1),
    throttleAxis(-1),
    joystickName(""),
    joystickID(-1),
    joystickNumButtons(0)
pixhawk's avatar
pixhawk committed
40
{
Lorenz Meier's avatar
Lorenz Meier committed
41 42
    loadSettings();

43
    for (int i = 0; i < 10; i++) {
pixhawk's avatar
pixhawk committed
44 45 46 47
        calibrationPositive[i] = sdlJoystickMax;
        calibrationNegative[i] = sdlJoystickMin;
    }

48
    // Listen for when the active UAS changes so we can change who we're sending data to.
pixhawk's avatar
pixhawk committed
49 50
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));

51 52
    // Start this thread. This allows the Joystick Settings window to work correctly even w/o any UASes connected.
    start();
pixhawk's avatar
pixhawk committed
53 54
}

55 56
JoystickInput::~JoystickInput()
{
Lorenz Meier's avatar
Lorenz Meier committed
57
    storeSettings();
LM's avatar
LM committed
58
    done = true;
Lorenz Meier's avatar
Lorenz Meier committed
59 60 61 62
}

void JoystickInput::loadSettings()
{
63 64 65 66 67 68 69 70 71 72 73 74
//    // Load defaults from settings
//    QSettings settings;
//    settings.sync();
//    settings.beginGroup("QGC_JOYSTICK_INPUT");
//    xAxis = (settings.value("X_AXIS_MAPPING", xAxis).toInt());
//    yAxis = (settings.value("Y_AXIS_MAPPING", yAxis).toInt());
//    thrustAxis = (settings.value("THRUST_AXIS_MAPPING", thrustAxis).toInt());
//    yawAxis = (settings.value("YAW_AXIS_MAPPING", yawAxis).toInt());
//    autoButtonMapping = (settings.value("AUTO_BUTTON_MAPPING", autoButtonMapping).toInt());
//    stabilizeButtonMapping = (settings.value("STABILIZE_BUTTON_MAPPING", stabilizeButtonMapping).toInt());
//    manualButtonMapping = (settings.value("MANUAL_BUTTON_MAPPING", manualButtonMapping).toInt());
//    settings.endGroup();
Lorenz Meier's avatar
Lorenz Meier committed
75 76 77 78
}

void JoystickInput::storeSettings()
{
79 80 81 82 83 84 85 86 87 88 89 90
//    // Store settings
//    QSettings settings;
//    settings.beginGroup("QGC_JOYSTICK_INPUT");
//    settings.setValue("X_AXIS_MAPPING", xAxis);
//    settings.setValue("Y_AXIS_MAPPING", yAxis);
//    settings.setValue("THRUST_AXIS_MAPPING", thrustAxis);
//    settings.setValue("YAW_AXIS_MAPPING", yawAxis);
//    settings.setValue("AUTO_BUTTON_MAPPING", autoButtonMapping);
//    settings.setValue("STABILIZE_BUTTON_MAPPING", stabilizeButtonMapping);
//    settings.setValue("MANUAL_BUTTON_MAPPING", manualButtonMapping);
//    settings.endGroup();
//    settings.sync();
91 92 93
}


pixhawk's avatar
pixhawk committed
94 95 96 97
void JoystickInput::setActiveUAS(UASInterface* uas)
{
    // Only connect / disconnect is the UAS is of a controllable UAS class
    UAS* tmp = 0;
LM's avatar
LM committed
98 99
    if (this->uas)
    {
pixhawk's avatar
pixhawk committed
100
        tmp = dynamic_cast<UAS*>(this->uas);
LM's avatar
LM committed
101 102
        if(tmp)
        {
103
            disconnect(this, SIGNAL(joystickChanged(double,double,double,double,int,int,int)), tmp, SLOT(setManualControlCommands(double,double,double,double,int,int,int)));
pixhawk's avatar
pixhawk committed
104 105 106 107 108 109
            disconnect(this, SIGNAL(buttonPressed(int)), tmp, SLOT(receiveButton(int)));
        }
    }

    this->uas = uas;

110
    if (this->uas)
LM's avatar
LM committed
111
    {
112 113 114 115 116
        tmp = dynamic_cast<UAS*>(this->uas);
        if(tmp) {
            connect(this, SIGNAL(joystickChanged(double,double,double,double,int,int,int)), tmp, SLOT(setManualControlCommands(double,double,double,double,int,int,int)));
            connect(this, SIGNAL(buttonPressed(int)), tmp, SLOT(receiveButton(int)));
        }
117
    }
pixhawk's avatar
pixhawk committed
118 119 120 121 122
}

void JoystickInput::init()
{
    // INITIALIZE SDL Joystick support
123
    if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) {
pixhawk's avatar
pixhawk committed
124 125 126 127
        printf("Couldn't initialize SimpleDirectMediaLayer: %s\n", SDL_GetError());
    }

    // Enumerate joysticks and select one
128
    numJoysticks = SDL_NumJoysticks();
pixhawk's avatar
pixhawk committed
129

130
    // Wait for joysticks if none are connected
131
    while (numJoysticks == 0 && !done)
LM's avatar
LM committed
132
    {
Lorenz Meier's avatar
Lorenz Meier committed
133
        QGC::SLEEP::msleep(400);
pixhawk's avatar
pixhawk committed
134
        // INITIALIZE SDL Joystick support
LM's avatar
LM committed
135 136
        if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0)
        {
pixhawk's avatar
pixhawk committed
137 138
            printf("Couldn't initialize SimpleDirectMediaLayer: %s\n", SDL_GetError());
        }
139
        numJoysticks = SDL_NumJoysticks();
pixhawk's avatar
pixhawk committed
140
    }
141 142 143 144
    if (done)
    {
        return;
    }
pixhawk's avatar
pixhawk committed
145

146 147
    qDebug() << QString("%1 Input devices found:").arg(numJoysticks);
    for(int i=0; i < numJoysticks; i++ )
LM's avatar
LM committed
148
    {
149 150 151 152 153
        qDebug() << QString("\t- %1").arg(SDL_JoystickName(i));
        SDL_Joystick* x = SDL_JoystickOpen(i);
        qDebug() << QString("Number of Axes: %1").arg(QString::number(SDL_JoystickNumAxes(x)));
        qDebug() << QString("Number of Buttons: %1").arg(QString::number(SDL_JoystickNumButtons(x)));
        SDL_JoystickClose(x);
pixhawk's avatar
pixhawk committed
154 155
    }

156 157
    // And attach to the first joystick found to start.
    setActiveJoystick(0);
158 159 160

    // Make sure active UAS is set
    setActiveUAS(UASManager::instance()->getActiveUAS());
pixhawk's avatar
pixhawk committed
161
}
162

163 164 165 166
void JoystickInput::shutdown()
{
    done = true;
}
pixhawk's avatar
pixhawk committed
167 168 169

/**
 * @brief Execute the Joystick process
170 171
 * Note that the SDL procedure is polled. This is because connecting and disconnecting while the event checker is running
 * fails as of SDL 1.2. It is therefore much easier to just poll for the joystick we want to sample.
pixhawk's avatar
pixhawk committed
172 173 174 175 176
 */
void JoystickInput::run()
{
    init();

177
    forever
LM's avatar
LM committed
178 179 180
    {
        if (done)
        {
181 182 183
            done = false;
            exit();
            return;
LM's avatar
LM committed
184
        }
pixhawk's avatar
pixhawk committed
185

186 187
        // Poll the joystick for new values.
        SDL_JoystickUpdate();
pixhawk's avatar
pixhawk committed
188

189
        // Emit all necessary signals for all axes.
190
        for (int i = 0; i < joystickNumAxes; i++)
191 192 193 194 195 196 197 198 199 200
        {
            // First emit the uncalibrated values for each axis based on their ID.
            // This is generally not used for controlling a vehicle, but a UI representation, so it being slightly off is fine.
            float axisValue = (SDL_JoystickGetAxis(joystick, i) - calibrationNegative[i]) / (calibrationPositive[i] - calibrationNegative[i]);
            axisValue = 1.0f - axisValue;
            axisValue = axisValue * 2.0f - 1.0f;

            // Bound rounding errors
            if (axisValue > 1.0f) axisValue = 1.0f;
            if (axisValue < -1.0f) axisValue = -1.0f;
201 202 203 204 205
            if (joystickAxes[i] != axisValue)
            {
                joystickAxes[i] = axisValue;
                emit axisValueChanged(i, axisValue);
            }
206
        }
pixhawk's avatar
pixhawk committed
207 208

        // Build up vectors describing the hat position
209
        int hatPosition = SDL_JoystickGetHat(joystick, 0);
210 211 212 213 214 215 216 217 218 219
        int newYHat = 0;
        if ((SDL_HAT_UP & hatPosition) > 0) newYHat = 1;
        if ((SDL_HAT_DOWN & hatPosition) > 0) newYHat = -1;
        int newXHat = 0;
        if ((SDL_HAT_LEFT & hatPosition) > 0) newXHat = -1;
        if ((SDL_HAT_RIGHT & hatPosition) > 0) newXHat = 1;
        if (newYHat != yHat || newXHat != xHat)
        {
            xHat = newXHat;
            yHat = newYHat;
220
            emit hatDirectionChanged(newXHat, newYHat);
221
        }
pixhawk's avatar
pixhawk committed
222

223
        // Emit signals for each button individually
224
        for (int i = 0; i < joystickNumButtons; i++)
LM's avatar
LM committed
225
        {
226
            // If the button was down, but now it's up, trigger a buttonPressed event
227
            quint16 lastButtonState = joystickButtons & (1 << i);
228
            if (SDL_JoystickGetButton(joystick, i) && !lastButtonState)
LM's avatar
LM committed
229
            {
pixhawk's avatar
pixhawk committed
230
                emit buttonPressed(i);
231
                joystickButtons |= 1 << i;
232 233 234 235
            }
            else if (!SDL_JoystickGetButton(joystick, i) && lastButtonState)
            {
                emit buttonReleased(i);
236
                joystickButtons &= ~(1 << i);
pixhawk's avatar
pixhawk committed
237 238
            }
        }
239 240

        // Now signal an update for all UI together.
241 242 243 244 245
        float roll = rollAxis > -1?joystickAxes[rollAxis]:NAN;
        float pitch = pitchAxis > -1?joystickAxes[pitchAxis]:NAN;
        float yaw = yawAxis > -1?joystickAxes[yawAxis]:NAN;
        float throttle = throttleAxis > -1?joystickAxes[throttleAxis]:NAN;
        emit joystickChanged(roll, pitch, yaw, throttle, xHat, yHat, joystickButtons);
pixhawk's avatar
pixhawk committed
246 247

        // Sleep, update rate of joystick is approx. 50 Hz (1000 ms / 50 = 20 ms)
248
        QGC::SLEEP::msleep(20);
pixhawk's avatar
pixhawk committed
249 250
    }
}
251

252 253
void JoystickInput::setActiveJoystick(int id)
{
254 255 256 257 258 259 260
    if (joystick && SDL_JoystickOpened(joystickID))
    {
        SDL_JoystickClose(joystick);
        joystick = NULL;
        joystickID = -1;
    }

261 262
    joystickID = id;
    joystick = SDL_JoystickOpen(joystickID);
263
    if (joystick && SDL_JoystickOpened(joystickID))
264
    {
265
        SDL_JoystickUpdate();
266
        // Update joystick configuration.
267
        joystickName = QString(SDL_JoystickName(joystickID));
268 269 270
        joystickNumButtons = SDL_JoystickNumButtons(joystick);
        joystickNumAxes = SDL_JoystickNumAxes(joystick);

271
        // Update cached joystick values
272
        joystickAxes.clear();
273
        for (int i = 0; i < joystickNumAxes; i++)
274
        {
275 276 277
            int axisValue = SDL_JoystickGetAxis(joystick, i);
            joystickAxes.append(axisValue);
            emit axisValueChanged(i, axisValue);
278 279
        }
        joystickButtons = 0;
280 281 282 283 284 285 286 287
        for (int i = 0; i < joystickNumButtons; i++)
        {
            if (SDL_JoystickGetButton(joystick, i))
            {
                emit buttonPressed(i);
                joystickButtons |= 1 << i;
            }
        }
288 289 290 291 292 293
        qDebug() << QString("Switching to joystick '%1' with %2 buttons/%3 axes").arg(joystickName, QString::number(joystickNumButtons), QString::number(joystickNumAxes));
    }
    else
    {
        joystickNumButtons = 0;
        joystickNumAxes = 0;
294 295 296
    }
}

297
float JoystickInput::getCurrentValueForAxis(int axisID)
298
{
299
    if (axisID < joystickAxes.size())
300
    {
301
        return joystickAxes[axisID];
302 303
    }
    return 0.0f;
304
}