1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-11-29 07:24:13 +01:00

Merged in f5soh/librepilot/LP-454_Boat (pull request #369)

LP-454 boat
This commit is contained in:
Philippe Renon 2017-01-14 17:52:06 +01:00
commit a0a2f30788
21 changed files with 6339 additions and 2823 deletions

View File

@ -364,6 +364,8 @@ FrameType_t GetCurrentFrameType()
case SYSTEMSETTINGS_AIRFRAMETYPE_GROUNDVEHICLECAR:
case SYSTEMSETTINGS_AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIAL:
case SYSTEMSETTINGS_AIRFRAMETYPE_GROUNDVEHICLEMOTORCYCLE:
case SYSTEMSETTINGS_AIRFRAMETYPE_GROUNDVEHICLEBOAT:
case SYSTEMSETTINGS_AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIALBOAT:
return FRAME_TYPE_GROUND;
case SYSTEMSETTINGS_AIRFRAMETYPE_VTOL:

View File

@ -1061,7 +1061,7 @@ margin:1px;
</size>
</property>
<property name="title">
<string>Motor outputs</string>
<string>Motor Outputs</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
@ -1206,7 +1206,7 @@ margin:1px;
</size>
</property>
<property name="title">
<string>Swashplate outputs</string>
<string>Swashplate Outputs</string>
</property>
<layout class="QGridLayout" name="gridLayout_19">
<property name="leftMargin">

View File

@ -84,7 +84,7 @@
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -102,7 +102,7 @@
</size>
</property>
<property name="title">
<string>Output channel assignments</string>
<string>Output Channel Assignments</string>
</property>
<layout class="QFormLayout" name="formLayout_5">
<property name="fieldGrowthPolicy">
@ -206,7 +206,7 @@
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -365,7 +365,7 @@ margin:1px;</string>
</size>
</property>
<property name="title">
<string>Vehicle frame</string>
<string>Vehicle Frame</string>
</property>
<layout class="QVBoxLayout" name="vehicleframeBox">
<property name="leftMargin">

View File

@ -509,7 +509,7 @@ Typical value is 50% for + or X configuration on quads.</string>
</size>
</property>
<property name="title">
<string>Motor output channels</string>
<string>Motor Output Channels</string>
</property>
<layout class="QGridLayout" name="gridLayout_7" columnstretch="1,1,0,0,1,0,0">
<property name="leftMargin">

View File

@ -1,9 +1,9 @@
/**
******************************************************************************
*
* @file configgroundvehiclemwidget.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
* @author K. Sebesta & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @file configgroundvehiclewidget.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* K. Sebesta & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
@ -81,15 +81,15 @@ ConfigGroundVehicleWidget::ConfigGroundVehicleWidget(QWidget *parent) :
populateChannelComboBoxes();
QStringList groundVehicleTypes;
groundVehicleTypes << "Turnable (car)" << "Differential (tank)" << "Motorcycle";
groundVehicleTypes << "Car (Turnable)" << "Tank (Differential)" << "Motorcycle" << "Boat (Turnable)" << "Boat (Differential)";
m_aircraft->groundVehicleType->addItems(groundVehicleTypes);
m_aircraft->groundShape->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_aircraft->groundShape->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// Set default model to "Turnable (car)"
// Set default model to "Car (Turnable)"
connect(m_aircraft->groundVehicleType, SIGNAL(currentIndexChanged(QString)), this, SLOT(setupUI(QString)));
m_aircraft->groundVehicleType->setCurrentIndex(m_aircraft->groundVehicleType->findText("Turnable (car)"));
m_aircraft->groundVehicleType->setCurrentIndex(m_aircraft->groundVehicleType->findText("Car (Turnable)"));
setupUI(m_aircraft->groundVehicleType->currentText());
}
@ -122,20 +122,65 @@ void ConfigGroundVehicleWidget::setupUI(QString frameType)
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(true);
m_aircraft->groundVehicleThrottle1->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_PITCH);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
initMixerCurves(frameType);
if (frameType == "GroundVehicleBoat" || frameType == "Boat (Turnable)") {
// Boat
m_vehicleImg->setElementId("boat");
setComboCurrentIndex(m_aircraft->groundVehicleType, m_aircraft->groundVehicleType->findText("Boat (Turnable)"));
m_aircraft->gvMotor1ChannelBox->setEnabled(true);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
m_aircraft->gvMotor1Label->setText("First motor");
m_aircraft->gvMotor2Label->setText("Second motor");
m_aircraft->gvSteering1ChannelBox->setEnabled(true);
m_aircraft->gvSteering2ChannelBox->setEnabled(true);
m_aircraft->gvSteering1Label->setText("First rudder");
m_aircraft->gvSteering2Label->setText("Second rudder");
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Throttle Curve 1");
m_aircraft->gvThrottleCurve1GroupBox->setEnabled(true);
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Throttle Curve 2");
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(false);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
m_aircraft->groundVehicleThrottle1->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
initMixerCurves(frameType);
// If new setup, set curves values
if (frameTypeSaved->getValue().toString() != "GroundVehicleBoat") {
m_aircraft->groundVehicleThrottle1->initLinearCurve(5, 1.0, 0.0);
m_aircraft->groundVehicleThrottle2->initLinearCurve(5, 1.0, 0.0);
}
} else if ((frameType == "GroundVehicleDifferential") || (frameType == "Tank (Differential)") ||
(frameType == "GroundVehicleDifferentialBoat") || (frameType == "Boat (Differential)")) {
bool is_Boat = frameType.contains("Boat");
if (is_Boat) {
// Boat differential
m_vehicleImg->setElementId("boat_diff");
setComboCurrentIndex(m_aircraft->groundVehicleType,
m_aircraft->groundVehicleType->findText("Boat (Differential)"));
m_aircraft->gvSteering1Label->setText("First rudder");
m_aircraft->gvSteering2Label->setText("Second rudder");
} else {
// Tank
m_vehicleImg->setElementId("tank");
setComboCurrentIndex(m_aircraft->groundVehicleType,
m_aircraft->groundVehicleType->findText("Tank (Differential)"));
m_aircraft->gvSteering1Label->setText("Front steering");
m_aircraft->gvSteering2Label->setText("Rear steering");
}
if (frameType == "GroundVehicleDifferential" || frameType == "Differential (tank)") {
// Tank
m_vehicleImg->setElementId("tank");
setComboCurrentIndex(m_aircraft->groundVehicleType,
m_aircraft->groundVehicleType->findText("Differential (tank)"));
m_aircraft->gvMotor1ChannelBox->setEnabled(true);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
m_aircraft->gvThrottleCurve1GroupBox->setEnabled(false);
m_aircraft->gvMotor1Label->setText("Left motor");
m_aircraft->gvMotor2Label->setText("Right motor");
@ -143,34 +188,34 @@ void ConfigGroundVehicleWidget::setupUI(QString frameType)
m_aircraft->gvSteering1ChannelBox->setEnabled(false);
m_aircraft->gvSteering2ChannelBox->setEnabled(false);
m_aircraft->gvSteering1Label->setText("Front steering");
m_aircraft->gvSteering2Label->setText("Rear steering");
m_aircraft->differentialSteeringSlider1->setEnabled(true);
m_aircraft->differentialSteeringSlider2->setEnabled(true);
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Throttle curve1");
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Throttle curve2 ");
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Throttle Curve 1");
m_aircraft->gvThrottleCurve1GroupBox->setEnabled(true);
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Throttle Curve 2 ");
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(false);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_PITCH);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
m_aircraft->groundVehicleThrottle1->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
initMixerCurves(frameType);
// If new setup, set sliders to defaults and set curves values
if (frameTypeSaved->getValue().toString() != "GroundVehicleDifferential") {
if ((!is_Boat && (frameTypeSaved->getValue().toString() != "GroundVehicleDifferential")) ||
(is_Boat && (frameTypeSaved->getValue().toString() != "GroundVehicleDifferentialBoat"))) {
m_aircraft->differentialSteeringSlider1->setValue(100);
m_aircraft->differentialSteeringSlider2->setValue(100);
m_aircraft->groundVehicleThrottle1->initLinearCurve(5, 1.0, 0.0);
m_aircraft->groundVehicleThrottle2->initLinearCurve(5, 1.0, 0.0);
m_aircraft->groundVehicleThrottle1->initLinearCurve(5, 0.8, 0.0);
m_aircraft->groundVehicleThrottle2->initLinearCurve(5, 0.8, 0.0);
}
} else if (frameType == "GroundVehicleMotorcycle" || frameType == "Motorcycle") {
// Motorcycle
m_vehicleImg->setElementId("motorbike");
setComboCurrentIndex(m_aircraft->groundVehicleType, m_aircraft->groundVehicleType->findText("Motorcycle"));
m_aircraft->gvMotor1ChannelBox->setEnabled(true);
m_aircraft->gvMotor2ChannelBox->setEnabled(false);
m_aircraft->gvMotor1ChannelBox->setEnabled(false);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
m_aircraft->gvMotor2Label->setText("Rear motor");
@ -181,10 +226,10 @@ void ConfigGroundVehicleWidget::setupUI(QString frameType)
m_aircraft->gvSteering2Label->setText("Balancing");
// Curve1 for Motorcyle
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Throttle curve1");
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Throttle Curve 1");
m_aircraft->gvThrottleCurve1GroupBox->setEnabled(true);
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Throttle curve2");
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(true);
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Throttle Curve 2");
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(false);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
m_aircraft->groundVehicleThrottle1->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
@ -199,7 +244,7 @@ void ConfigGroundVehicleWidget::setupUI(QString frameType)
} else {
// Car
m_vehicleImg->setElementId("car");
setComboCurrentIndex(m_aircraft->groundVehicleType, m_aircraft->groundVehicleType->findText("Turnable (car)"));
setComboCurrentIndex(m_aircraft->groundVehicleType, m_aircraft->groundVehicleType->findText("Car (Turnable)"));
m_aircraft->gvMotor1ChannelBox->setEnabled(true);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
@ -213,12 +258,12 @@ void ConfigGroundVehicleWidget::setupUI(QString frameType)
m_aircraft->gvSteering1Label->setText("Front steering");
m_aircraft->gvSteering2Label->setText("Rear steering");
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Throttle curve2");
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(true);
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Throttle curve1");
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Front Motor Throttle Curve");
m_aircraft->gvThrottleCurve1GroupBox->setEnabled(true);
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Rear Motor Throttle Curve");
m_aircraft->gvThrottleCurve2GroupBox->setEnabled(true);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_PITCH);
m_aircraft->groundVehicleThrottle2->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
m_aircraft->groundVehicleThrottle1->setMixerType(MixerCurve::MIXERCURVE_THROTTLE);
initMixerCurves(frameType);
@ -288,7 +333,7 @@ void ConfigGroundVehicleWidget::refreshWidgetsValues(QString frameType)
setComboCurrentIndex(m_aircraft->gvSteering1ChannelBox, config.ground.GroundVehicleSteering1);
setComboCurrentIndex(m_aircraft->gvSteering2ChannelBox, config.ground.GroundVehicleSteering2);
if (frameType == "GroundVehicleDifferential") {
if (frameType.contains("GroundVehicleDifferential")) {
// Find the channel number for Motor1
int channel = m_aircraft->gvMotor1ChannelBox->currentIndex() - 1;
if (channel > -1) {
@ -322,7 +367,7 @@ void ConfigGroundVehicleWidget::initMixerCurves(QString frameType)
m_aircraft->groundVehicleThrottle1->initCurve(&curveValues);
} else {
// no, init a straight curve
if (frameType == "GroundVehicleDifferential") {
if (frameType.contains("GroundVehicleDifferential")) {
m_aircraft->groundVehicleThrottle1->initLinearCurve(curveValues.count(), 0.8, 0.0);
} else if (frameType == "GroundVehicleCar") {
m_aircraft->groundVehicleThrottle1->initLinearCurve(curveValues.count(), 1.0, 0.0);
@ -338,7 +383,7 @@ void ConfigGroundVehicleWidget::initMixerCurves(QString frameType)
m_aircraft->groundVehicleThrottle2->initCurve(&curveValues);
} else {
// no, init a straight curve
if (frameType == "GroundVehicleDifferential") {
if (frameType.contains("GroundVehicleDifferential")) {
m_aircraft->groundVehicleThrottle2->initLinearCurve(curveValues.count(), 0.8, 0.0);
} else if (frameType == "GroundVehicleCar") {
m_aircraft->groundVehicleThrottle2->initLinearCurve(curveValues.count(), 1.0, 0.0);
@ -363,10 +408,16 @@ QString ConfigGroundVehicleWidget::updateConfigObjectsFromWidgets()
setThrottleCurve(mixer, VehicleConfig::MIXER_THROTTLECURVE2, m_aircraft->groundVehicleThrottle2->getCurve());
// All airframe types must start with "GroundVehicle"
if (m_aircraft->groundVehicleType->currentText() == "Turnable (car)") {
if (m_aircraft->groundVehicleType->currentText() == "Boat (Differential)") {
airframeType = "GroundVehicleDifferentialBoat";
setupGroundVehicleDifferential(airframeType);
} else if (m_aircraft->groundVehicleType->currentText() == "Boat (Turnable)") {
airframeType = "GroundVehicleBoat";
setupGroundVehicleTurnable(airframeType);
} else if (m_aircraft->groundVehicleType->currentText() == "Car (Turnable)") {
airframeType = "GroundVehicleCar";
setupGroundVehicleCar(airframeType);
} else if (m_aircraft->groundVehicleType->currentText() == "Differential (tank)") {
setupGroundVehicleTurnable(airframeType);
} else if (m_aircraft->groundVehicleType->currentText() == "Tank (Differential)") {
airframeType = "GroundVehicleDifferential";
setupGroundVehicleDifferential(airframeType);
} else {
@ -460,13 +511,13 @@ bool ConfigGroundVehicleWidget::setupGroundVehicleDifferential(QString airframeT
// left motor
int channel = m_aircraft->gvMotor1ChannelBox->currentIndex() - 1;
setMixerType(mixer, channel, VehicleConfig::MIXERTYPE_REVERSABLEMOTOR);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE2, 127);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE1, 127);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_YAW, yawmotor1);
// right motor
channel = m_aircraft->gvMotor2ChannelBox->currentIndex() - 1;
setMixerType(mixer, channel, VehicleConfig::MIXERTYPE_REVERSABLEMOTOR);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE2, 127);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE1, 127);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_YAW, -yawmotor2);
// Output success message
@ -480,7 +531,7 @@ bool ConfigGroundVehicleWidget::setupGroundVehicleDifferential(QString airframeT
Returns False if impossible to create the mixer.
*/
bool ConfigGroundVehicleWidget::setupGroundVehicleCar(QString airframeType)
bool ConfigGroundVehicleWidget::setupGroundVehicleTurnable(QString airframeType)
{
// Check coherence:
// Show any config errors in GUI
@ -517,8 +568,13 @@ bool ConfigGroundVehicleWidget::setupGroundVehicleCar(QString airframeType)
channel = m_aircraft->gvMotor2ChannelBox->currentIndex() - 1;
setMixerType(mixer, channel, VehicleConfig::MIXERTYPE_REVERSABLEMOTOR);
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE1, 127);
if (airframeType == "GroundVehicleCar") {
// Car: Throttle2 curve for 2nd motor
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE2, 127);
} else {
// Boat: Throttle1 curve for both motors
setMixerVectorValue(mixer, channel, VehicleConfig::MIXERVECTOR_THROTTLECURVE1, 127);
}
// Output success message
m_aircraft->gvStatusLabel->setText("Mixer generated");
@ -539,7 +595,7 @@ bool ConfigGroundVehicleWidget::throwConfigError(QString airframeType)
pixmap.fill(QColor("red"));
if (airframeType == "GroundVehicleCar") { // Car
if ((airframeType == "GroundVehicleCar") || (airframeType == "GroundVehicleBoat")) { // Car
if (m_aircraft->gvMotor1ChannelBox->currentText() == "None"
&& m_aircraft->gvMotor2ChannelBox->currentText() == "None") {
m_aircraft->gvMotor1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole); // Set color palettes
@ -559,7 +615,7 @@ bool ConfigGroundVehicleWidget::throwConfigError(QString airframeType)
m_aircraft->gvSteering1ChannelBox->setItemData(0, 0, Qt::DecorationRole); // Reset color palettes
m_aircraft->gvSteering2ChannelBox->setItemData(0, 0, Qt::DecorationRole); // Reset color palettes
}
} else if (airframeType == "GroundVehicleDifferential") { // Tank
} else if (airframeType.contains("GroundVehicleDifferential")) { // differential Tank and Boat
if (m_aircraft->gvMotor1ChannelBox->currentText() == "None"
|| m_aircraft->gvMotor2ChannelBox->currentText() == "None") {
m_aircraft->gvMotor1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole); // Set color palettes

View File

@ -2,7 +2,8 @@
******************************************************************************
*
* @file configgroundvehiclewidget.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
@ -64,7 +65,7 @@ private:
virtual void registerWidgets(ConfigTaskWidget &parent);
virtual void resetActuators(GUIConfigDataUnion *configData);
bool setupGroundVehicleCar(QString airframeType);
bool setupGroundVehicleTurnable(QString airframeType);
bool setupGroundVehicleDifferential(QString airframeType);
bool setupGroundVehicleMotorcycle(QString airframeType);

View File

@ -882,10 +882,18 @@ void ConfigInputWidget::wizardTearDownStep(enum wizardSteps step)
transmitterType = acro;
} else if (wizardUi->typeGround->isChecked()) {
transmitterType = ground;
/* Make sure to tell controller, this is really a ground vehicle. */
systemSettingsData = systemSettingsObj->getData();
systemSettingsData.AirframeType = SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR;
systemSettingsObj->setData(systemSettingsData);
/* Make sure to tell controller, this is really a ground vehicle. */
if ((systemSettingsData.AirframeType != SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR) ||
(systemSettingsData.AirframeType != SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIAL) ||
(systemSettingsData.AirframeType != SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEMOTORCYCLE) ||
(systemSettingsData.AirframeType != SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEBOAT) ||
(systemSettingsData.AirframeType != SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIALBOAT)) {
// Apply default ground vehicle airframe
systemSettingsData.AirframeType = SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR;
systemSettingsObj->setData(systemSettingsData);
}
} else {
transmitterType = heli;
}
@ -1990,7 +1998,11 @@ void ConfigInputWidget::simpleCalibration(bool enable)
manualSettingsData = manualSettingsObj->getData();
systemSettingsData = systemSettingsObj->getData();
if (systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR) {
if ((systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR) ||
(systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIAL) ||
(systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEMOTORCYCLE) ||
(systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEBOAT) ||
(systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIALBOAT)) {
QMessageBox::warning(this, tr("Ground Vehicle"),
tr("<p>Please <b>center</b> throttle control and press OK when ready.</p>"));

View File

@ -2,7 +2,7 @@
******************************************************************************
*
* @file configvehicletypewidget.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* E. Lafargue, K. Sebesta & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012
* @addtogroup GCSPlugins GCS Plugins
* @{
@ -99,6 +99,8 @@ QStringList ConfigVehicleTypeWidget::getChannelDescriptions()
case SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR:
case SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIAL:
case SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEMOTORCYCLE:
case SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEBOAT:
case SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIALBOAT:
// ground
channelDesc = ConfigGroundVehicleWidget::getChannelDescriptions();
break;
@ -282,9 +284,11 @@ int ConfigVehicleTypeWidget::frameCategory(QString frameType)
return ConfigVehicleTypeWidget::MULTIROTOR;
} else if (frameType == "HeliCP") {
return ConfigVehicleTypeWidget::HELICOPTER;
} else if (frameType == "GroundVehicleCar" || frameType == "Turnable (car)"
|| frameType == "GroundVehicleDifferential" || frameType == "Differential (tank)"
|| frameType == "GroundVehicleMotorcycle" || frameType == "Motorcycle") {
} else if (frameType == "GroundVehicleCar" || frameType == "Car (Turnable)"
|| frameType == "GroundVehicleDifferential" || frameType == "Tank (Differential)"
|| frameType == "GroundVehicleMotorcycle" || frameType == "Motorcycle"
|| frameType == "GroundVehicleBoat" || frameType == "Boat (Turnable)"
|| frameType == "GroundVehicleDifferentialBoat" || frameType == "Boat (Differential)") {
return ConfigVehicleTypeWidget::GROUND;
} else {
return ConfigVehicleTypeWidget::CUSTOM;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 360 KiB

After

Width:  |  Height:  |  Size: 503 KiB

View File

@ -2,7 +2,7 @@
******************************************************************************
*
* @file connectiondiagram.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup
* @{
@ -159,6 +159,12 @@ void ConnectionDiagram::setupGraphicsScene()
case VehicleConfigurationSource::GROUNDVEHICLE_MOTORCYCLE:
elementsToShow << "motorbike";
break;
case VehicleConfigurationSource::GROUNDVEHICLE_BOAT:
elementsToShow << "boat";
break;
case VehicleConfigurationSource::GROUNDVEHICLE_DIFFERENTIAL_BOAT:
elementsToShow << "boat_diff";
break;
default:
break;
}

View File

@ -82,9 +82,11 @@ void OutputCalibrationPage::setupActuatorMinMaxAndNeutral(int motorChannelStart,
m_actuatorSettings[servoid].channelNeutral = LOW_OUTPUT_RATE_MILLISECONDS;
m_actuatorSettings[servoid].channelMax = getHighOutputRate();
m_actuatorSettings[servoid].isReversableMotor = false;
// Car and Tank should use reversable Esc/motors
// Car, Tank, Boat and Boat differential should use reversable Esc/motors
if ((getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_CAR)
|| (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL)) {
|| (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL)
|| (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_BOAT)
|| (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL_BOAT)) {
m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
m_actuatorSettings[servoid].isReversableMotor = true;
// Set initial output value
@ -290,6 +292,30 @@ void OutputCalibrationPage::setupVehicle()
setupActuatorMinMaxAndNeutral(3, 3, 2);
getWizard()->setActuatorSettings(m_actuatorSettings);
break;
case SetupWizard::GROUNDVEHICLE_BOAT:
loadSVGFile(GROUND_SVG_FILE);
m_wizardIndexes << 0 << 1 << 2;
m_vehicleElementIds << "boat" << "boat-frame" << "boat-motor" << "boat-rudder";
m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO;
m_vehicleHighlightElementIndexes << 0 << 1 << 2;
m_channelIndex << 0 << 3 << 0;
setupActuatorMinMaxAndNeutral(3, 3, 2);
getWizard()->setActuatorSettings(m_actuatorSettings);
break;
case SetupWizard::GROUNDVEHICLE_DIFFERENTIAL_BOAT:
loadSVGFile(GROUND_SVG_FILE);
m_wizardIndexes << 0 << 1 << 1;
m_vehicleElementIds << "boat_diff" << "boat_diff-frame" << "boat_diff-left-motor" << "boat_diff-right-motor";
m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR;
m_vehicleHighlightElementIndexes << 0 << 1 << 2;
m_channelIndex << 0 << 0 << 1;
setupActuatorMinMaxAndNeutral(0, 1, 2);
getWizard()->setActuatorSettings(m_actuatorSettings);
break;

View File

@ -2,7 +2,8 @@
******************************************************************************
*
* @file surfacepage.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
* @addtogroup
* @{
* @addtogroup SurfacePage
@ -56,12 +57,12 @@ void SurfacePage::setupSelection(Selection *selection)
"Please select the type of ground vehicle you want to create a configuration for below:"));
selection->addItem(tr("Car"),
tr("This setup expects a traditional car with a rear motor and a front streering servo"),
tr("This setup expects a traditional car with a rear motor and a front steering servo."),
"car",
SetupWizard::GROUNDVEHICLE_CAR);
selection->addItem(tr("Tank"),
tr("This setup expects a traditional vehicle using only two motors and differential steering"),
tr("This setup expects a traditional vehicle using only two motors and differential steering."),
"tank",
SetupWizard::GROUNDVEHICLE_DIFFERENTIAL);
@ -69,4 +70,14 @@ void SurfacePage::setupSelection(Selection *selection)
tr("This setup currently expects a motorcyle setup, using one motor and one servo for steering."),
"motorbike",
SetupWizard::GROUNDVEHICLE_MOTORCYCLE);
selection->addItem(tr("Boat"),
tr("This setup currently expects a boat setup, using one motor and one servo for rudder/helm."),
"boat",
SetupWizard::GROUNDVEHICLE_BOAT);
selection->addItem(tr("Boat differential"),
tr("This setup expects a boat using only two motors and differential steering."),
"boat_diff",
SetupWizard::GROUNDVEHICLE_DIFFERENTIAL_BOAT);
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.6 MiB

After

Width:  |  Height:  |  Size: 4.9 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 252 KiB

After

Width:  |  Height:  |  Size: 371 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 492 KiB

After

Width:  |  Height:  |  Size: 630 KiB

View File

@ -2,7 +2,7 @@
******************************************************************************
*
* @file setupwizard.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @addtogroup GCSPlugins GCS Plugins
* @{
@ -138,7 +138,8 @@ int SetupWizard::nextId() const
case PAGE_FIXEDWING:
case PAGE_SURFACE:
if (getVehicleSubType() == GROUNDVEHICLE_DIFFERENTIAL) {
if ((getVehicleSubType() == GROUNDVEHICLE_DIFFERENTIAL) ||
(getVehicleSubType() == GROUNDVEHICLE_DIFFERENTIAL_BOAT)) {
return PAGE_ESC;
} else {
return PAGE_SERVO;
@ -360,6 +361,12 @@ QString SetupWizard::getSummaryText()
case SetupWizard::GROUNDVEHICLE_MOTORCYCLE:
summary.append(tr("Motorcycle"));
break;
case SetupWizard::GROUNDVEHICLE_BOAT:
summary.append(tr("Boat"));
break;
case SetupWizard::GROUNDVEHICLE_DIFFERENTIAL_BOAT:
summary.append(tr("Boat differential"));
break;
default:
summary.append(tr("Unknown"));
break;
@ -422,7 +429,8 @@ QString SetupWizard::getSummaryText()
// If Tricopter show tail servo speed
if (getVehicleSubType() == MULTI_ROTOR_TRI_Y || getVehicleType() == VEHICLE_FIXEDWING
|| getVehicleSubType() == GROUNDVEHICLE_MOTORCYCLE || getVehicleSubType() == GROUNDVEHICLE_CAR) {
|| getVehicleSubType() == GROUNDVEHICLE_MOTORCYCLE || getVehicleSubType() == GROUNDVEHICLE_CAR
|| getVehicleSubType() == GROUNDVEHICLE_BOAT) {
summary.append("<br>");
summary.append("<b>").append(tr("Servo type: ")).append("</b>");
switch (getServoType()) {

View File

@ -2,7 +2,7 @@
***********************************************************************************
*
* @file vehicleconfigurationhelper.cpp
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup
* @{
@ -304,11 +304,21 @@ void VehicleConfigurationHelper::applyHardwareConfiguration()
case VehicleConfigurationSource::GPS_UBX:
gpsData.DataProtocol = GPSSettings::DATAPROTOCOL_UBX;
gpsData.UbxAutoConfig = GPSSettings::UBXAUTOCONFIG_AUTOBAUDANDCONFIGURE;
if (m_configSource->getVehicleType() == VehicleConfigurationSource::VEHICLE_SURFACE) {
gpsData.UbxDynamicModel = GPSSettings::UBXDYNAMICMODEL_AUTOMOTIVE;
} else {
gpsData.UbxDynamicModel = GPSSettings::UBXDYNAMICMODEL_AIRBORNE1G;
}
break;
case VehicleConfigurationSource::GPS_PLATINUM:
{
gpsData.DataProtocol = GPSSettings::DATAPROTOCOL_UBX;
gpsData.UbxAutoConfig = GPSSettings::UBXAUTOCONFIG_AUTOBAUDANDCONFIGURE;
if (m_configSource->getVehicleType() == VehicleConfigurationSource::VEHICLE_SURFACE) {
gpsData.UbxDynamicModel = GPSSettings::UBXDYNAMICMODEL_AUTOMOTIVE;
} else {
gpsData.UbxDynamicModel = GPSSettings::UBXDYNAMICMODEL_AIRBORNE1G;
}
AuxMagSettings *magSettings = AuxMagSettings::GetInstance(m_uavoManager);
Q_ASSERT(magSettings);
AuxMagSettings::DataFields magsData = magSettings->getData();
@ -339,6 +349,11 @@ void VehicleConfigurationHelper::applyHardwareConfiguration()
gpsData.DataProtocol = GPSSettings::DATAPROTOCOL_UBX;
gpsData.UbxAutoConfig = GPSSettings::UBXAUTOCONFIG_AUTOBAUDANDCONFIGURE;
if (m_configSource->getVehicleType() == VehicleConfigurationSource::VEHICLE_SURFACE) {
gpsData.UbxDynamicModel = GPSSettings::UBXDYNAMICMODEL_AUTOMOTIVE;
} else {
gpsData.UbxDynamicModel = GPSSettings::UBXDYNAMICMODEL_AIRBORNE1G;
}
if (m_configSource->getControllerType() == VehicleConfigurationSource::CONTROLLER_SPARKY2) {
data.SPK2_FlexiPort = HwSettings::SPK2_FLEXIPORT_I2C;
} else {
@ -478,6 +493,12 @@ void VehicleConfigurationHelper::applyVehicleConfiguration()
case VehicleConfigurationSource::GROUNDVEHICLE_MOTORCYCLE:
setupMotorcycle();
break;
case VehicleConfigurationSource::GROUNDVEHICLE_BOAT:
setupBoat();
break;
case VehicleConfigurationSource::GROUNDVEHICLE_DIFFERENTIAL_BOAT:
setupBoatDiff();
break;
default:
break;
}
@ -923,7 +944,6 @@ void VehicleConfigurationHelper::applyMixerConfiguration(mixerChannelSettings ch
mSettings->setMixerValueRoll((qint8)100);
mSettings->setMixerValuePitch((qint8)100);
mSettings->setMixerValueYaw((qint8)100);
maxThrottle = 1;
break;
case VehicleConfigurationSource::VEHICLE_HELI:
break;
@ -934,14 +954,11 @@ void VehicleConfigurationHelper::applyMixerConfiguration(mixerChannelSettings ch
mSettings->setMixerValueRoll((qint8)100);
mSettings->setMixerValuePitch((qint8)100);
mSettings->setMixerValueYaw((qint8)100);
maxThrottle = 1;
break;
case VehicleConfigurationSource::GROUNDVEHICLE_CAR:
mSettings->setMixerValueRoll((qint8)100);
mSettings->setMixerValuePitch((qint8)100);
mSettings->setMixerValueYaw((qint8)100);
maxThrottle = 1;
minThrottle = 0;
break;
case VehicleConfigurationSource::GROUNDVEHICLE_DIFFERENTIAL:
mSettings->setMixerValueRoll((qint8)100);
@ -950,6 +967,18 @@ void VehicleConfigurationHelper::applyMixerConfiguration(mixerChannelSettings ch
maxThrottle = 0.8;
minThrottle = 0;
break;
case VehicleConfigurationSource::GROUNDVEHICLE_BOAT:
mSettings->setMixerValueRoll((qint8)100);
mSettings->setMixerValuePitch((qint8)100);
mSettings->setMixerValueYaw((qint8)100);
break;
case VehicleConfigurationSource::GROUNDVEHICLE_DIFFERENTIAL_BOAT:
mSettings->setMixerValueRoll((qint8)100);
mSettings->setMixerValuePitch((qint8)100);
mSettings->setMixerValueYaw((qint8)100);
maxThrottle = 0.8;
minThrottle = 0;
break;
default:
break;
}
@ -960,7 +989,7 @@ void VehicleConfigurationHelper::applyMixerConfiguration(mixerChannelSettings ch
break;
}
// Apply Throttle curve max 90% for Multis, 100% for FixedWing/car/Motorbike, 80% for Tank
// Apply Throttle curve 0-100% for all vehicles except differential vehicles at 0-80%
QString throttlePattern = "ThrottleCurve%1";
for (int i = 1; i <= 2; i++) {
UAVObjectField *field = mSettings->getField(throttlePattern.arg(i));
@ -1181,7 +1210,7 @@ void VehicleConfigurationHelper::resetVehicleConfig()
for (int i = 1; i <= 2; i++) {
UAVObjectField *field = mSettings->getField(throttlePattern.arg(i));
Q_ASSERT(field);
// Set default curve at 90% max for Multirotors
// Set default 0 -100% Throttle curve
for (quint32 i = 0; i < field->getNumElements(); i++) {
field->setValue(i * (1.0f / (field->getNumElements() - 1)), i);
}
@ -2326,3 +2355,69 @@ void VehicleConfigurationHelper::setupMotorcycle()
applyMixerConfiguration(channels);
applyMultiGUISettings(SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEMOTORCYCLE, guiSettings);
}
void VehicleConfigurationHelper::setupBoat()
{
// Typical vehicle setup
// 1. Setup mixer data
// 2. Setup GUI data
// 3. Apply changes
mixerChannelSettings channels[ActuatorSettings::CHANNELADDR_NUMELEM];
GUIConfigDataUnion guiSettings = getGUIConfigData();
// Rudder Servo (Chan 1)
channels[0].type = MIXER_TYPE_SERVO;
channels[0].throttle1 = 0;
channels[0].throttle2 = 0;
channels[0].roll = 0;
channels[0].pitch = 0;
channels[0].yaw = 100;
// Motor (Chan 4)
channels[3].type = MIXER_TYPE_REVERSABLEMOTOR;
channels[3].throttle1 = 100;
channels[3].throttle2 = 0;
channels[3].roll = 0;
channels[3].pitch = 0;
channels[3].yaw = 0;
guiSettings.ground.GroundVehicleSteering1 = 1;
guiSettings.ground.GroundVehicleThrottle2 = 4;
applyMixerConfiguration(channels);
applyMultiGUISettings(SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEBOAT, guiSettings);
}
void VehicleConfigurationHelper::setupBoatDiff()
{
// Typical vehicle setup
// 1. Setup mixer data
// 2. Setup GUI data
// 3. Apply changes
mixerChannelSettings channels[ActuatorSettings::CHANNELADDR_NUMELEM];
GUIConfigDataUnion guiSettings = getGUIConfigData();
// Left Motor (Chan 1)
channels[0].type = MIXER_TYPE_REVERSABLEMOTOR;
channels[0].throttle1 = 100;
channels[0].throttle2 = 0;
channels[0].roll = 0;
channels[0].pitch = 0;
channels[0].yaw = 100;
// Right Motor (Chan 2)
channels[1].type = MIXER_TYPE_REVERSABLEMOTOR;
channels[1].throttle1 = 100;
channels[1].throttle2 = 0;
channels[1].roll = 0;
channels[1].pitch = 0;
channels[1].yaw = -100;
guiSettings.ground.GroundVehicleThrottle1 = 1;
guiSettings.ground.GroundVehicleThrottle2 = 2;
applyMixerConfiguration(channels);
applyMultiGUISettings(SystemSettings::AIRFRAMETYPE_GROUNDVEHICLEDIFFERENTIALBOAT, guiSettings);
}

View File

@ -2,7 +2,8 @@
******************************************************************************
*
* @file vehicleconfigurationhelper.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup
* @{
* @addtogroup VehicleConfigurationHelper
@ -121,6 +122,8 @@ private:
void setupCar();
void setupTank();
void setupMotorcycle();
void setupBoat();
void setupBoatDiff();
private slots:
void uAVOTransactionCompleted(UAVObject *object, bool success);

View File

@ -2,7 +2,8 @@
******************************************************************************
*
* @file vehicleconfigurationsource.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup
* @{
* @addtogroup VehicleConfigurationSource
@ -63,7 +64,7 @@ public:
MULTI_ROTOR_HEXA, MULTI_ROTOR_HEXA_H, MULTI_ROTOR_HEXA_X, MULTI_ROTOR_HEXA_COAX_Y, MULTI_ROTOR_OCTO,
MULTI_ROTOR_OCTO_X, MULTI_ROTOR_OCTO_V, MULTI_ROTOR_OCTO_COAX_X, MULTI_ROTOR_OCTO_COAX_PLUS,
FIXED_WING_DUAL_AILERON, FIXED_WING_AILERON, FIXED_WING_ELEVON, FIXED_WING_VTAIL, HELI_CCPM,
GROUNDVEHICLE_MOTORCYCLE, GROUNDVEHICLE_CAR, GROUNDVEHICLE_DIFFERENTIAL };
GROUNDVEHICLE_MOTORCYCLE, GROUNDVEHICLE_CAR, GROUNDVEHICLE_DIFFERENTIAL, GROUNDVEHICLE_BOAT, GROUNDVEHICLE_DIFFERENTIAL_BOAT };
enum ESC_TYPE { ESC_ONESHOT, ESC_SYNCHED, ESC_RAPID, ESC_STANDARD, ESC_UNKNOWN };
enum SERVO_TYPE { SERVO_ANALOG, SERVO_DIGITAL, SERVO_UNKNOWN };
enum INPUT_TYPE { INPUT_PWM, INPUT_PPM, INPUT_SBUS, INPUT_DSM, INPUT_SRXL, INPUT_HOTT_SUMD, INPUT_EXBUS, INPUT_IBUS, INPUT_UNKNOWN };

View File

@ -155,7 +155,7 @@ function isCC3D() {
function frameType() {
var frameTypeText = ["FixedWing", "FixedWingElevon", "FixedWingVtail", "VTOL", "HeliCP", "QuadX", "QuadP",
"Hexa+", "Octo+", "Custom", "HexaX", "HexaH", "OctoV", "OctoCoaxP", "OctoCoaxX", "OctoX", "HexaCoax",
"Tricopter", "GroundVehicleCar", "GroundVehicleDiff", "GroundVehicleMoto"];
"Tricopter", "GroundCar", "GroundDiff", "GroundMoto", "GroundBoat", "GroundDiffBoat"];
if (frameTypeText.length != SystemSettings.SystemSettingsConstants.AirframeTypeCount) {
console.log("uav.js: frameType() do not match systemSettings.airframeType uavo");

View File

@ -1,7 +1,7 @@
<xml>
<object name="SystemSettings" singleinstance="true" settings="true" category="System">
<description>Select airframe type. Currently used by @ref ActuatorModule to choose mixing from @ref ActuatorDesired to @ref ActuatorCommand</description>
<field name="AirframeType" units="" type="enum" elements="1" options="FixedWing,FixedWingElevon,FixedWingVtail,VTOL,HeliCP,QuadX,QuadP,Hexa,Octo,Custom,HexaX,HexaH,OctoV,OctoCoaxP,OctoCoaxX,OctoX,HexaCoax,Tri,GroundVehicleCar,GroundVehicleDifferential,GroundVehicleMotorcycle" defaultvalue="QuadX"/>
<field name="AirframeType" units="" type="enum" elements="1" options="FixedWing,FixedWingElevon,FixedWingVtail,VTOL,HeliCP,QuadX,QuadP,Hexa,Octo,Custom,HexaX,HexaH,OctoV,OctoCoaxP,OctoCoaxX,OctoX,HexaCoax,Tri,GroundVehicleCar,GroundVehicleDifferential,GroundVehicleMotorcycle,GroundVehicleBoat,GroundVehicleDifferentialBoat" defaultvalue="QuadX"/>
<field name="VehicleName" units="char" type="uint8" elements="20" defaultvalue="0"/>
<field name="ThrustControl" units="" type="enum" elements="1" options="Throttle,Collective,None" defaultvalue="Throttle" />
<!-- Which way the vehicle controls its thrust. Can be through