diff --git a/CMakeLists.txt b/CMakeLists.txt
index dcfc67c2b..61b3c54ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -610,7 +610,9 @@ else()
MultimediaWidgets
Positioning
Charts
- SerialPort)
+ SerialPort
+ OPTIONAL_COMPONENTS
+ Gamepad)
endif()
# for the server we don't need OpenGL/Qt Quick components
diff --git a/plugins/feature/gs232controller/CMakeLists.txt b/plugins/feature/gs232controller/CMakeLists.txt
index cfab468b1..2d3a24fd4 100644
--- a/plugins/feature/gs232controller/CMakeLists.txt
+++ b/plugins/feature/gs232controller/CMakeLists.txt
@@ -38,17 +38,27 @@ if(NOT SERVER_MODE)
gs232controllergui.ui
dfmstatusdialog.cpp
dfmstatusdialog.ui
+ inputcontroller.cpp
)
set(gs232controller_HEADERS
${gs232controller_HEADERS}
gs232controllergui.h
dfmstatusdialog.h
+ inputcontroller.h
)
set(TARGET_NAME featuregs232controller)
set(TARGET_LIB Qt::Widgets)
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
+
+ if(Qt${QT_DEFAULT_MAJOR_VERSION}Gamepad_FOUND)
+ add_compile_definitions(QT_GAMEPAD_FOUND)
+ set(TARGET_LIB ${TARGET_LIB} Qt::Gamepad)
+ set(gs232controller_SOURCES ${gs232controller_SOURCES} gamepadinputcontroller.cpp)
+ set(gs232controller_HEADERS ${gs232controller_HEADERS} gamepadinputcontroller.h)
+ endif()
+
else()
set(TARGET_NAME featuregs232controllersrv)
set(TARGET_LIB "")
diff --git a/plugins/feature/gs232controller/gamepadinputcontroller.cpp b/plugins/feature/gs232controller/gamepadinputcontroller.cpp
new file mode 100644
index 000000000..7dc8c78d9
--- /dev/null
+++ b/plugins/feature/gs232controller/gamepadinputcontroller.cpp
@@ -0,0 +1,139 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2023 Jon Beniston, M7RCE //
+// //
+// This program 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 as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+
+#include "gamepadinputcontroller.h"
+
+GamepadInputController::GamepadInputController(int deviceId) :
+ m_gamepad(deviceId),
+ m_rightX(0.0),
+ m_rightY(0.0),
+ m_leftX(0.0),
+ m_leftY(0.0)
+{
+ connect(&m_gamepad, &QGamepad::axisRightXChanged, this, &GamepadInputController::axisRightXChanged);
+ connect(&m_gamepad, &QGamepad::axisRightYChanged, this, &GamepadInputController::axisRightYChanged);
+ connect(&m_gamepad, &QGamepad::axisLeftXChanged, this, &GamepadInputController::axisLeftXChanged);
+ connect(&m_gamepad, &QGamepad::axisLeftYChanged, this, &GamepadInputController::axisLeftYChanged);
+}
+
+double GamepadInputController::getAxisValue(int axis)
+{
+ switch (axis)
+ {
+ case 0:
+ return m_rightX;
+ case 1:
+ return m_rightY;
+ case 2:
+ return m_leftX;
+ case 3:
+ return m_leftY;
+ }
+ return 0.0;
+}
+
+int GamepadInputController::getNumberOfAxes() const
+{
+ return 4;
+}
+
+void GamepadInputController::axisRightXChanged(double value)
+{
+ m_rightX = value;
+}
+
+void GamepadInputController::axisRightYChanged(double value)
+{
+ m_rightY = value;
+}
+
+void GamepadInputController::axisLeftXChanged(double value)
+{
+ m_leftX = value;
+}
+
+void GamepadInputController::axisLeftYChanged(double value)
+{
+ m_leftY = value;
+}
+
+QStringList GamepadInputController::getAllControllers()
+{
+ QStringList names;
+ QGamepadManager *gamepadManager = QGamepadManager::instance();
+
+ if (gamepadManager)
+ {
+ const QList gamepads = gamepadManager->connectedGamepads();
+ for (const auto gamepad : gamepads)
+ {
+ QString name;
+ if (gamepadManager->gamepadName(gamepad).isEmpty()) {
+ name = QString("Gamepad %1").arg(gamepad);
+ } else {
+ name = gamepadManager->gamepadName(gamepad);
+ }
+ qDebug() << "GamepadInputController::getAllControllers: Gamepad: " << gamepad << "name:" << gamepadManager->gamepadName(gamepad) << " connected " << gamepadManager->isGamepadConnected(gamepad);
+ names.append(name);
+ }
+ if (gamepads.size() == 0) {
+ qDebug() << "GamepadInputController::getAllControllers: No gamepads";
+ }
+ }
+ else
+ {
+ qDebug() << "GamepadInputController::getAllControllers: No gamepad manager";
+ }
+ return names;
+}
+
+GamepadInputController* GamepadInputController::open(const QString& name)
+{
+ GamepadInputController *inputController = nullptr;
+ QGamepadManager *gamepadManager = QGamepadManager::instance();
+
+ if (gamepadManager)
+ {
+ const QList gamepads = gamepadManager->connectedGamepads();
+ for (const auto gamepad : gamepads)
+ {
+ QString gamepadName;
+ if (gamepadManager->gamepadName(gamepad).isEmpty()) {
+ gamepadName = QString("Gamepad %1").arg(gamepad);
+ } else {
+ gamepadName = gamepadManager->gamepadName(gamepad);
+ }
+ if (name == gamepadName)
+ {
+ inputController = new GamepadInputController(gamepad);
+ if (inputController)
+ {
+ qDebug() << "GamepadInputController::open: Opened gamepad " << name;
+ }
+ else
+ {
+ qDebug() << "GamepadInputController::open: Failed to open gamepad: " << gamepad;
+ }
+ }
+ }
+ }
+
+ return inputController;
+}
diff --git a/plugins/feature/gs232controller/gamepadinputcontroller.h b/plugins/feature/gs232controller/gamepadinputcontroller.h
new file mode 100644
index 000000000..d881b03d2
--- /dev/null
+++ b/plugins/feature/gs232controller/gamepadinputcontroller.h
@@ -0,0 +1,53 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2023 Jon Beniston, M7RCE //
+// //
+// This program 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 as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_GAMEPADINPUTCONTROLLER_H_
+#define INCLUDE_FEATURE_GAMEPADINPUTCONTROLLER_H_
+
+#include "inputcontroller.h"
+
+#include
+
+class GamepadInputController : public InputController {
+
+public:
+
+ GamepadInputController(int deviceId);
+ double getAxisValue(int axis) override;
+ int getNumberOfAxes() const override;
+
+ static QStringList getAllControllers();
+ static GamepadInputController* open(const QString& name);
+
+private slots:
+
+ void axisRightXChanged(double value);
+ void axisRightYChanged(double value);
+ void axisLeftXChanged(double value);
+ void axisLeftYChanged(double value);
+
+private:
+
+ QGamepad m_gamepad;
+ double m_rightX;
+ double m_rightY;
+ double m_leftX;
+ double m_leftY;
+};
+
+#endif // INCLUDE_FEATURE_GAMEPADINPUTCONTROLLER_H_
+
diff --git a/plugins/feature/gs232controller/gs232controller.cpp b/plugins/feature/gs232controller/gs232controller.cpp
index 6f58478be..ba8fda383 100644
--- a/plugins/feature/gs232controller/gs232controller.cpp
+++ b/plugins/feature/gs232controller/gs232controller.cpp
@@ -491,6 +491,8 @@ void GS232Controller::webapiFormatFeatureSettings(
response.getGs232ControllerSettings()->setProtocol(settings.m_protocol);
response.getGs232ControllerSettings()->setPrecision(settings.m_precision);
response.getGs232ControllerSettings()->setCoordinates((int)settings.m_coordinates);
+ response.getGs232ControllerSettings()->setInputController(new QString(settings.m_inputController));
+ response.getGs232ControllerSettings()->setInputSensitivity(settings.m_inputSensitivity);
if (response.getGs232ControllerSettings()->getTitle()) {
*response.getGs232ControllerSettings()->getTitle() = settings.m_title;
@@ -585,6 +587,12 @@ void GS232Controller::webapiUpdateFeatureSettings(
if (featureSettingsKeys.contains("coordinates")) {
settings.m_coordinates = (GS232ControllerSettings::Coordinates)response.getGs232ControllerSettings()->getCoordinates();
}
+ if (featureSettingsKeys.contains("inputController")) {
+ settings.m_inputController = *response.getGs232ControllerSettings()->getInputController();
+ }
+ if (featureSettingsKeys.contains("inputSensitivity")) {
+ settings.m_inputSensitivity = response.getGs232ControllerSettings()->getInputSensitivity();
+ }
if (featureSettingsKeys.contains("title")) {
settings.m_title = *response.getGs232ControllerSettings()->getTitle();
}
@@ -676,6 +684,12 @@ void GS232Controller::webapiReverseSendSettings(const QList& featureSet
if (featureSettingsKeys.contains("coordinates") || force) {
swgGS232ControllerSettings->setCoordinates(settings.m_coordinates);
}
+ if (featureSettingsKeys.contains("inputController") || force) {
+ swgGS232ControllerSettings->setInputController(new QString(settings.m_inputController));
+ }
+ if (featureSettingsKeys.contains("inputSensitivity") || force) {
+ swgGS232ControllerSettings->setInputSensitivity(settings.m_inputSensitivity);
+ }
if (featureSettingsKeys.contains("title") || force) {
swgGS232ControllerSettings->setTitle(new QString(settings.m_title));
}
diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp
index 10450a9ea..4dd44c30a 100644
--- a/plugins/feature/gs232controller/gs232controllergui.cpp
+++ b/plugins/feature/gs232controller/gs232controllergui.cpp
@@ -216,7 +216,13 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu
m_doApplySettings(true),
m_lastFeatureState(0),
m_lastOnTarget(false),
- m_dfmStatusDialog()
+ m_dfmStatusDialog(),
+ m_inputController(nullptr),
+ m_inputCoord1(0.0),
+ m_inputCoord2(0.0),
+ m_inputAzOffset(0.0),
+ m_inputElOffset(0.0),
+ m_inputUpdate(false)
{
m_feature = feature;
setAttribute(Qt::WA_DeleteOnClose, true);
@@ -246,6 +252,10 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu
m_settings.setRollupState(&m_rollupState);
+ updateInputControllerList();
+ connect(InputControllerManager::instance(), &InputControllerManager::controllersChanged, this, &GS232ControllerGUI::updateInputControllerList);
+ connect(&m_inputTimer, &QTimer::timeout, this, &GS232ControllerGUI::checkInputController);
+
displaySettings();
applySettings(true);
makeUIConnections();
@@ -256,6 +266,123 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu
new DialogPositioner(&m_dfmStatusDialog, true);
}
+void GS232ControllerGUI::updateInputControllerList()
+{
+ ui->inputController->blockSignals(true);
+ ui->inputController->clear();
+ ui->inputController->addItem("None");
+
+ QStringList controllers = InputControllerManager::getAllControllers();
+ for (const auto& controller : controllers) {
+ ui->inputController->addItem(controller);
+ }
+ ui->inputController->blockSignals(false);
+ int index = ui->inputController->findText(m_settings.m_inputController);
+ ui->inputController->setCurrentIndex(index);
+}
+
+void GS232ControllerGUI::updateInputController()
+{
+ delete m_inputController;
+ m_inputController = nullptr;
+
+ bool enabled = false;
+ if (m_settings.m_inputController != "None")
+ {
+ m_inputController = InputControllerManager::open(m_settings.m_inputController);
+ if (m_inputController)
+ {
+ m_inputTimer.start(20);
+ enabled = true;
+ }
+ }
+ else
+ {
+ m_inputTimer.stop();
+ }
+
+ ui->inputSensitivityLabel->setEnabled(enabled);
+ ui->inputSensitivity->setEnabled(enabled);
+ ui->inputSensitivityText->setEnabled(enabled);
+}
+
+void GS232ControllerGUI::checkInputController()
+{
+ if (m_inputController)
+ {
+ // If our input device has two sticks (four axes), we use one for target and one for offset
+ // If only one stick (two axes), it's used both for target when not tracking and offset, when tracking
+ // Use separate variables rather than values in UI, to allow for higher precision
+
+ if (!m_settings.m_track)
+ {
+ m_inputCoord1 += m_settings.m_inputSensitivity * m_inputController->getAxisValue(0);
+ m_inputCoord2 += m_settings.m_inputSensitivity * -m_inputController->getAxisValue(1);
+
+ if (m_settings.m_coordinates == GS232ControllerSettings::AZ_EL)
+ {
+ m_inputCoord1 = std::max(m_inputCoord1, (double) m_settings.m_azimuthMin);
+ m_inputCoord1 = std::min(m_inputCoord1, (double) m_settings.m_azimuthMax);
+ m_inputCoord2 = std::max(m_inputCoord2, (double) m_settings.m_elevationMin);
+ m_inputCoord2 = std::min(m_inputCoord2, (double) m_settings.m_elevationMax);
+ }
+ else
+ {
+ m_inputCoord1 = std::max(m_inputCoord1, -90.0);
+ m_inputCoord1 = std::min(m_inputCoord1, 90.0);
+ m_inputCoord2 = std::max(m_inputCoord2, -90.0);
+ m_inputCoord2 = std::min(m_inputCoord2, 90.0);
+ }
+ }
+
+ if ((m_inputController->getNumberOfAxes() < 4) && m_settings.m_track)
+ {
+ m_inputAzOffset += m_settings.m_inputSensitivity * m_inputController->getAxisValue(0);
+ m_inputElOffset += m_settings.m_inputSensitivity * -m_inputController->getAxisValue(1);
+ }
+ else if (m_inputController->getNumberOfAxes() >= 4)
+ {
+ m_inputAzOffset += m_settings.m_inputSensitivity * m_inputController->getAxisValue(2);
+ m_inputElOffset += m_settings.m_inputSensitivity * -m_inputController->getAxisValue(3);
+ }
+ m_inputAzOffset = std::max(m_inputAzOffset, -360.0);
+ m_inputAzOffset = std::min(m_inputAzOffset, 360.0);
+ m_inputElOffset = std::max(m_inputElOffset, -180.0);
+ m_inputElOffset = std::min(m_inputElOffset, 180.0);
+
+ m_inputUpdate = true;
+ if (!m_settings.m_track)
+ {
+ ui->coord1->setValue(m_inputCoord1);
+ ui->coord2->setValue(m_inputCoord2);
+ }
+ if (((m_inputController->getNumberOfAxes() < 4) && m_settings.m_track) || (m_inputController->getNumberOfAxes() >= 4))
+ {
+ ui->azimuthOffset->setValue(m_inputAzOffset);
+ ui->elevationOffset->setValue(m_inputElOffset);
+ }
+ m_inputUpdate = false;
+ }
+}
+
+void GS232ControllerGUI::on_inputController_currentIndexChanged(int index)
+{
+ // Don't update settings if set to -1
+ if (index >= 0)
+ {
+ m_settings.m_inputController = ui->inputController->currentText();
+ applySettings();
+ updateInputController();
+ }
+}
+
+void GS232ControllerGUI::on_inputSensitivty_valueChanged(int value)
+{
+ m_settings.m_inputSensitivity = value / 1000.0;
+ ui->inputSensitivityText->setText(QString("%1%").arg(m_settings.m_inputSensitivity * 100.0));
+ applySettings();
+}
+
GS232ControllerGUI::~GS232ControllerGUI()
{
m_dfmStatusDialog.close();
@@ -302,6 +429,9 @@ void GS232ControllerGUI::displaySettings()
ui->elevationMin->setValue(m_settings.m_elevationMin);
ui->elevationMax->setValue(m_settings.m_elevationMax);
ui->tolerance->setValue(m_settings.m_tolerance);
+ ui->inputController->setCurrentText(m_settings.m_inputController);
+ ui->inputSensitivity->setValue((int) (m_settings.m_inputSensitivity * 1000.0));
+ ui->inputSensitivityText->setText(QString("%1%").arg(m_settings.m_inputSensitivity * 100.0));
ui->dfmTrack->setChecked(m_settings.m_dfmTrackOn);
ui->dfmLubePumps->setChecked(m_settings.m_dfmLubePumpsOn);
ui->dfmBrakes->setChecked(m_settings.m_dfmBrakesOn);
@@ -543,18 +673,27 @@ void GS232ControllerGUI::on_port_valueChanged(int value)
void GS232ControllerGUI::on_coord1_valueChanged(double value)
{
+ if (!m_inputUpdate) {
+ m_inputCoord1 = value;
+ }
displayToAzEl(value, ui->coord2->value());
ui->targetName->setText("");
}
void GS232ControllerGUI::on_coord2_valueChanged(double value)
{
+ if (!m_inputUpdate) {
+ m_inputCoord2 = value;
+ }
displayToAzEl(ui->coord1->value(), value);
ui->targetName->setText("");
}
void GS232ControllerGUI::on_azimuthOffset_valueChanged(int value)
{
+ if (!m_inputUpdate) {
+ m_inputAzOffset = value;
+ }
m_settings.m_azimuthOffset = value;
m_settingsKeys.append("azimuthOffset");
applySettings();
@@ -562,6 +701,9 @@ void GS232ControllerGUI::on_azimuthOffset_valueChanged(int value)
void GS232ControllerGUI::on_elevationOffset_valueChanged(int value)
{
+ if (!m_inputUpdate) {
+ m_inputElOffset = value;
+ }
m_settings.m_elevationOffset = value;
m_settingsKeys.append("elevationOffset");
applySettings();
@@ -805,6 +947,8 @@ void GS232ControllerGUI::makeUIConnections()
QObject::connect(ui->tolerance, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_tolerance_valueChanged);
QObject::connect(ui->precision, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_precision_valueChanged);
QObject::connect(ui->coordinates, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_coordinates_currentIndexChanged);
+ QObject::connect(ui->inputController, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_inputController_currentIndexChanged);
+ QObject::connect(ui->inputSensitivity, qOverload(&QSlider::valueChanged), this, &GS232ControllerGUI::on_inputSensitivty_valueChanged);
QObject::connect(ui->dfmTrack, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmTrack_clicked);
QObject::connect(ui->dfmLubePumps, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmLubePumps_clicked);
QObject::connect(ui->dfmBrakes, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmBrakes_clicked);
diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h
index 2005d363b..55bf01612 100644
--- a/plugins/feature/gs232controller/gs232controllergui.h
+++ b/plugins/feature/gs232controller/gs232controllergui.h
@@ -27,6 +27,7 @@
#include "gs232controllersettings.h"
#include "dfmstatusdialog.h"
+#include "inputcontroller.h"
class PluginAPI;
class FeatureUISet;
@@ -68,6 +69,14 @@ private:
DFMStatusDialog m_dfmStatusDialog;
+ InputController *m_inputController;
+ QTimer m_inputTimer;
+ double m_inputCoord1;
+ double m_inputCoord2;
+ double m_inputAzOffset;
+ double m_inputElOffset;
+ bool m_inputUpdate;
+
explicit GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~GS232ControllerGUI();
@@ -84,6 +93,7 @@ private:
void makeUIConnections();
void azElToDisplay(float az, float el, float& coord1, float& coord2) const;
void displayToAzEl(float coord1, float coord2);
+ void updateInputController();
private slots:
void onMenuDialogCalled(const QPoint &p);
@@ -115,6 +125,10 @@ private slots:
void on_dfmDrives_clicked(bool checked=false);
void on_dfmShowStatus_clicked();
void updateStatus();
+ void on_inputController_currentIndexChanged(int index);
+ void on_inputSensitivty_valueChanged(int value);
+ void updateInputControllerList();
+ void checkInputController();
};
#endif // INCLUDE_FEATURE_GS232CONTROLLERGUI_H_
diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui
index 4a26a83c6..6edfed6f0 100644
--- a/plugins/feature/gs232controller/gs232controllergui.ui
+++ b/plugins/feature/gs232controller/gs232controllergui.ui
@@ -306,7 +306,7 @@
10
140
341
- 191
+ 201
@@ -330,6 +330,84 @@
-
+
-
+
+
+ Baud rate
+
+
+
+ -
+
+
+ Gamepad / joystick to use
+
+
-
+
+ None
+
+
+
+
+ -
+
+
+ 450
+
+
+
+ -
+
+
+ Elevation offset
+
+
+
+ -
+
+
+ Azimuth max
+
+
+
+ -
+
+
+ Coordinates
+
+
+
+ -
+
+
+ Azimuth min
+
+
+
+ -
+
+
+ 180
+
+
+
+ -
+
+
+ false
+
+
+ Sensitivity
+
+
+
+ -
+
+
+ Elevation max
+
+
+
-
@@ -337,6 +415,56 @@
+ -
+
+
+ Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment
+
+
+ -180
+
+
+ 180
+
+
+ 1
+
+
+
+ -
+
+
+ Command protocol
+
+
-
+
+ GS-232
+
+
+ -
+
+ SPID
+
+
+ -
+
+ rotctld
+
+
+ -
+
+ DFM
+
+
+
+
+ -
+
+
+ Port
+
+
+
-
@@ -353,64 +481,6 @@
- -
-
-
- Hostname / IP address of computer to connect to
-
-
-
- -
-
-
- 450
-
-
-
- -
-
-
- Tolerance
-
-
-
- -
-
-
- Name of serial port to use to connect to the rotator
-
-
- true
-
-
-
- -
-
-
- Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment
-
-
- -360
-
-
- 360
-
-
-
- -
-
-
- Azimuth max
-
-
-
- -
-
-
- Connection
-
-
-
-
@@ -421,110 +491,13 @@
- -
-
-
- Baud rate
-
-
-
- -
-
-
- The type of connection to use to the rotator
-
-
-
-
- Serial
-
-
- -
-
- TCP
-
-
-
-
- -
-
-
- Serial Port
-
-
-
- -
-
-
- Tolerance in degrees
-
-
- 0
-
-
-
- -
-
+
-
+
450
- -
-
-
- Host
-
-
-
- -
-
-
- Port
-
-
-
- -
-
-
- Elevation max
-
-
-
- -
-
-
- Elevation min
-
-
-
- -
-
-
- 180
-
-
-
- -
-
-
- Azimuth min
-
-
-
- -
-
-
- Elevation offset
-
-
-
- -
-
-
- 180
-
-
-
-
@@ -585,6 +558,61 @@
+ -
+
+
+ Connection
+
+
+
+ -
+
+
+ The type of connection to use to the rotator
+
+
-
+
+ Serial
+
+
+ -
+
+ TCP
+
+
+
+
+ -
+
+
+ Name of serial port to use to connect to the rotator
+
+
+ true
+
+
+
+ -
+
+
+ Host
+
+
+
+ -
+
+
+ 180
+
+
+
+ -
+
+
+ Hostname / IP address of computer to connect to
+
+
+
-
@@ -595,49 +623,6 @@
- -
-
-
- Command protocol
-
-
-
-
- GS-232
-
-
- -
-
- SPID
-
-
- -
-
- rotctld
-
-
- -
-
- DFM
-
-
-
-
- -
-
-
- Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment
-
-
- -180
-
-
- 180
-
-
- 1
-
-
-
-
@@ -645,6 +630,50 @@
+ -
+
+
+ Elevation min
+
+
+
+ -
+
+
+ Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment
+
+
+ -360
+
+
+ 360
+
+
+
+ -
+
+
+ Tolerance in degrees
+
+
+ 0
+
+
+
+ -
+
+
+ Serial Port
+
+
+
+ -
+
+
+ Tolerance
+
+
+
-
@@ -667,13 +696,52 @@
- -
-
+
-
+
- Coordinates
+ Input Control
+ -
+
+
-
+
+
+ false
+
+
+ Input controller sensitivity
+
+
+ 1
+
+
+ 2000
+
+
+ 10
+
+
+ 25
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ false
+
+
+ 100%
+
+
+
+
+
diff --git a/plugins/feature/gs232controller/gs232controllersettings.cpp b/plugins/feature/gs232controller/gs232controllersettings.cpp
index 8311a957b..2a822286d 100644
--- a/plugins/feature/gs232controller/gs232controllersettings.cpp
+++ b/plugins/feature/gs232controller/gs232controllersettings.cpp
@@ -64,6 +64,8 @@ void GS232ControllerSettings::resetToDefaults()
m_connection = SERIAL;
m_precision = 0;
m_coordinates = AZ_EL;
+ m_inputController = "None";
+ m_inputSensitivity = 0.25;
m_dfmTrackOn = false;
m_dfmLubePumpsOn = false;
m_dfmBrakesOn = false;
@@ -119,6 +121,8 @@ QByteArray GS232ControllerSettings::serialize() const
s.writeBool(32, m_dfmLubePumpsOn);
s.writeBool(33, m_dfmBrakesOn);
s.writeBool(34, m_dfmDrivesOn);
+ s.writeString(35, m_inputController);
+ s.writeFloat(36, m_inputSensitivity);
return s.final();
}
@@ -187,6 +191,8 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data)
d.readBool(32, &m_dfmLubePumpsOn);
d.readBool(33, &m_dfmBrakesOn);
d.readBool(34, &m_dfmDrivesOn);
+ d.readString(35, &m_inputController, "None");
+ d.readFloat(36, &m_inputSensitivity, 0.25);
return true;
}
@@ -271,6 +277,12 @@ void GS232ControllerSettings::applySettings(const QStringList& settingsKeys, con
if (settingsKeys.contains("coordinates")) {
m_coordinates = settings.m_coordinates;
}
+ if (settingsKeys.contains("inputController")) {
+ m_inputController = settings.m_inputController;
+ }
+ if (settingsKeys.contains("inputSensitivity")) {
+ m_inputSensitivity = settings.m_inputSensitivity;
+ }
if (settingsKeys.contains("dfmTrackOn")) {
m_dfmTrackOn = settings.m_dfmTrackOn;
}
@@ -370,6 +382,12 @@ QString GS232ControllerSettings::getDebugString(const QStringList& settingsKeys,
if (settingsKeys.contains("coordinates") || force) {
ostr << " m_coordinates: " << m_precision;
}
+ if (settingsKeys.contains("inputController") || force) {
+ ostr << " m_inputController: " << m_inputController.toStdString();
+ }
+ if (settingsKeys.contains("inputSensitivity") || force) {
+ ostr << " m_inputSensitivity: " << m_inputSensitivity;
+ }
if (settingsKeys.contains("title") || force) {
ostr << " m_title: " << m_title.toStdString();
}
@@ -397,4 +415,3 @@ QString GS232ControllerSettings::getDebugString(const QStringList& settingsKeys,
return QString(ostr.str().c_str());
}
-
diff --git a/plugins/feature/gs232controller/gs232controllersettings.h b/plugins/feature/gs232controller/gs232controllersettings.h
index eff7863f1..1d116b05d 100644
--- a/plugins/feature/gs232controller/gs232controllersettings.h
+++ b/plugins/feature/gs232controller/gs232controllersettings.h
@@ -62,6 +62,8 @@ struct GS232ControllerSettings
enum Connection { SERIAL, TCP } m_connection;
int m_precision;
enum Coordinates { AZ_EL, X_Y_85, X_Y_30 } m_coordinates;
+ QString m_inputController;
+ float m_inputSensitivity;
bool m_dfmTrackOn;
bool m_dfmLubePumpsOn;
diff --git a/plugins/feature/gs232controller/inputcontroller.cpp b/plugins/feature/gs232controller/inputcontroller.cpp
new file mode 100644
index 000000000..e7f8f8df4
--- /dev/null
+++ b/plugins/feature/gs232controller/inputcontroller.cpp
@@ -0,0 +1,63 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2023 Jon Beniston, M7RCE //
+// //
+// This program 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 as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifdef QT_GAMEPAD_FOUND
+#include
+#include "gamepadinputcontroller.h"
+#endif
+
+#include "inputcontroller.h"
+
+InputControllerManager* InputControllerManager::m_instance = nullptr;
+
+QStringList InputControllerManager::getAllControllers()
+{
+#ifdef QT_GAMEPAD_FOUND
+ return GamepadInputController::getAllControllers();
+#else
+ return {};
+#endif
+}
+
+InputController* InputControllerManager::open(const QString& name)
+{
+#ifdef QT_GAMEPAD_FOUND
+ return GamepadInputController::open(name);
+#else
+ return nullptr;
+#endif
+}
+
+InputControllerManager* InputControllerManager::instance()
+{
+ if (!m_instance) {
+ m_instance = new InputControllerManager();
+ }
+ return m_instance;
+}
+
+InputControllerManager::InputControllerManager()
+{
+#ifdef QT_GAMEPAD_FOUND
+ connect(QGamepadManager::instance(), &QGamepadManager::connectedGamepadsChanged, this, &InputControllerManager::connectedGamepadsChanged);
+#endif
+}
+
+void InputControllerManager::connectedGamepadsChanged()
+{
+ emit controllersChanged();
+}
diff --git a/plugins/feature/gs232controller/inputcontroller.h b/plugins/feature/gs232controller/inputcontroller.h
new file mode 100644
index 000000000..d35306c4b
--- /dev/null
+++ b/plugins/feature/gs232controller/inputcontroller.h
@@ -0,0 +1,58 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2023 Jon Beniston, M7RCE //
+// //
+// This program 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 as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_INPUTCONTROLLER_H_
+#define INCLUDE_FEATURE_INPUTCONTROLLER_H_
+
+#include
+
+class InputController : public QObject {
+ Q_OBJECT
+public:
+
+ // Called every ~50ms
+ // axis 0-3. 0=Az/X, 1=El/Y, 2=Az Offset, 3=El Offset
+ // value returned should be current axis position in range [-1,1]
+ virtual double getAxisValue(int axis) = 0;
+ virtual int getNumberOfAxes() const = 0;
+
+};
+
+class InputControllerManager : public QObject {
+ Q_OBJECT
+public:
+
+ static QStringList getAllControllers();
+ static InputController* open(const QString& name);
+ static InputControllerManager* instance();
+
+signals:
+
+ void controllersChanged();
+
+private slots:
+ void connectedGamepadsChanged();
+
+private:
+ InputControllerManager();
+
+ static InputControllerManager *m_instance;
+
+};
+
+
+#endif // INCLUDE_FEATURE_INPUTCONTROLLER_H_
diff --git a/plugins/feature/gs232controller/readme.md b/plugins/feature/gs232controller/readme.md
index 34cd7794f..34770c183 100644
--- a/plugins/feature/gs232controller/readme.md
+++ b/plugins/feature/gs232controller/readme.md
@@ -2,10 +2,10 @@
Introduction
-The Rotator Controller feature plugin allows SDRangel to send commands to GS-232 and SPID rotators as well as hamlib's rotctld, via a serial or TCP connection.
+The Rotator Controller feature plugin allows SDRangel to send commands to GS-232 and SPID rotators as well as hamlib's rotctld, via a serial or TCP connection.
This allows SDRangel to point antennas mounted on a rotator to a specified azimuth and elevation.
-Azimuth and elevation can be set manually by a user in the GUI, via the REST API, or via another plugin, such as the Map Feature, the ADS-B Demodulator, or the Star Tracker.
+Azimuth and elevation can be set manually by a user in the GUI, via the REST API, via another plugin, such as the Map Feature, the ADS-B Demodulator, or the Star Tracker, or by controller/gamepads (such as an XBox Controller).
Interface
@@ -107,6 +107,20 @@ Specifies the coordinate system used by the GUI for entry and display of the pos
Equations for translating between these coordinate systems can be found [here](https://ntrs.nasa.gov/citations/19670030005).
+22: Input Control
+
+Specifies a controller/gamepad (such as an XBox Wireless Controller) that can be used to specify target coordinates or azimuth and elevation offset.
+
+When a controller with 2 sticks (4 axes) such as the XBox Wireless Controller is used, the right stick is used for controlling target coordinates,
+while the left stick is for controlling azimuth and elevation offset.
+If a controller only has 2 axes, target coordinates will be controlled when not tracking (6) and offset will be controlled when tracking.
+
+The [Qt Gamepad](https://doc.qt.io/qt-5/qtgamepad-index.html) library is used to implement gamepad support.
+
+23: Sensitivity
+
+Specifies the sensitivity on the input controls (22). The higher the value, the faster coordinates will change for a given control stick movement.
+
Protocol Implementations
GS-232 Protocol Implementation Notes
diff --git a/swagger/sdrangel/api/swagger/include/GS232Controller.yaml b/swagger/sdrangel/api/swagger/include/GS232Controller.yaml
index b555a959e..8e47d320e 100644
--- a/swagger/sdrangel/api/swagger/include/GS232Controller.yaml
+++ b/swagger/sdrangel/api/swagger/include/GS232Controller.yaml
@@ -58,6 +58,13 @@ GS232ControllerSettings:
coordinates:
description: (0 Az/El, 1 X/Y 85, 2 X/Y 30)
type: integer
+ inputController:
+ description: Name of input controller
+ type: string
+ inputSensitivity:
+ description: Input controller sensitivity
+ type: number
+ format: float
title:
type: string
rgbColor:
diff --git a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp
index 954e40380..309e2ee1e 100644
--- a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp
+++ b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp
@@ -64,6 +64,10 @@ SWGGS232ControllerSettings::SWGGS232ControllerSettings() {
m_precision_isSet = false;
coordinates = 0;
m_coordinates_isSet = false;
+ input_controller = nullptr;
+ m_input_controller_isSet = false;
+ input_sensitivity = 0.0f;
+ m_input_sensitivity_isSet = false;
title = nullptr;
m_title_isSet = false;
rgb_color = 0;
@@ -124,6 +128,10 @@ SWGGS232ControllerSettings::init() {
m_precision_isSet = false;
coordinates = 0;
m_coordinates_isSet = false;
+ input_controller = new QString("");
+ m_input_controller_isSet = false;
+ input_sensitivity = 0.0f;
+ m_input_sensitivity_isSet = false;
title = new QString("");
m_title_isSet = false;
rgb_color = 0;
@@ -168,6 +176,10 @@ SWGGS232ControllerSettings::cleanup() {
+ if(input_controller != nullptr) {
+ delete input_controller;
+ }
+
if(title != nullptr) {
delete title;
}
@@ -231,6 +243,10 @@ SWGGS232ControllerSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&coordinates, pJson["coordinates"], "qint32", "");
+ ::SWGSDRangel::setValue(&input_controller, pJson["inputController"], "QString", "QString");
+
+ ::SWGSDRangel::setValue(&input_sensitivity, pJson["inputSensitivity"], "float", "");
+
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
@@ -317,6 +333,12 @@ SWGGS232ControllerSettings::asJsonObject() {
if(m_coordinates_isSet){
obj->insert("coordinates", QJsonValue(coordinates));
}
+ if(input_controller != nullptr && *input_controller != QString("")){
+ toJsonValue(QString("inputController"), input_controller, obj, QString("QString"));
+ }
+ if(m_input_sensitivity_isSet){
+ obj->insert("inputSensitivity", QJsonValue(input_sensitivity));
+ }
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
@@ -525,6 +547,26 @@ SWGGS232ControllerSettings::setCoordinates(qint32 coordinates) {
this->m_coordinates_isSet = true;
}
+QString*
+SWGGS232ControllerSettings::getInputController() {
+ return input_controller;
+}
+void
+SWGGS232ControllerSettings::setInputController(QString* input_controller) {
+ this->input_controller = input_controller;
+ this->m_input_controller_isSet = true;
+}
+
+float
+SWGGS232ControllerSettings::getInputSensitivity() {
+ return input_sensitivity;
+}
+void
+SWGGS232ControllerSettings::setInputSensitivity(float input_sensitivity) {
+ this->input_sensitivity = input_sensitivity;
+ this->m_input_sensitivity_isSet = true;
+}
+
QString*
SWGGS232ControllerSettings::getTitle() {
return title;
@@ -664,6 +706,12 @@ SWGGS232ControllerSettings::isSet(){
if(m_coordinates_isSet){
isObjectUpdated = true; break;
}
+ if(input_controller && *input_controller != QString("")){
+ isObjectUpdated = true; break;
+ }
+ if(m_input_sensitivity_isSet){
+ isObjectUpdated = true; break;
+ }
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
diff --git a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h
index eff95dede..3cc129009 100644
--- a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h
+++ b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h
@@ -97,6 +97,12 @@ public:
qint32 getCoordinates();
void setCoordinates(qint32 coordinates);
+ QString* getInputController();
+ void setInputController(QString* input_controller);
+
+ float getInputSensitivity();
+ void setInputSensitivity(float input_sensitivity);
+
QString* getTitle();
void setTitle(QString* title);
@@ -179,6 +185,12 @@ private:
qint32 coordinates;
bool m_coordinates_isSet;
+ QString* input_controller;
+ bool m_input_controller_isSet;
+
+ float input_sensitivity;
+ bool m_input_sensitivity_isSet;
+
QString* title;
bool m_title_isSet;