diff --git a/locales/en/messages.json b/locales/en/messages.json
index 8f10ebbb..ddb8a937 100644
--- a/locales/en/messages.json
+++ b/locales/en/messages.json
@@ -5598,5 +5598,41 @@
},
"darkTheme": {
"message": "Enable dark theme"
+ },
+ "pidTuningRatesType": {
+ "message": "Rates Type"
+ },
+ "pidTuningRatesTypeTip": {
+ "message": "Changing rates type will change the rates curve and the way you can set it"
+ },
+ "pidTuningRcRateRaceflight": {
+ "message": "Rate"
+ },
+ "pidTuningRcExpoRaceflight": {
+ "message": "Expo"
+ },
+ "pidTuningRateRaceflight": {
+ "message": "Acro+"
+ },
+ "pidTuningRcExpoKISS": {
+ "message": "RC Curve"
+ },
+ "pidTuningRateQuickRates": {
+ "message": "Max Rate"
+ },
+ "pidTuningRcRateActual": {
+ "message": "Center Sensitivity"
+ },
+ "dialogRatesTypeTitle": {
+ "message": "Rates type change"
+ },
+ "dialogRatesTypeNote": {
+ "message": "
Nothing is saved yet and you can still return to your previous values by setting the previous rates type.
Click the Save button to apply the changes."
+ },
+ "dialogRatesTypeConfirm": {
+ "message": "Change"
+ },
+ "dialogRatesTypeCancel": {
+ "message": "Return to saved values"
}
}
diff --git a/src/css/tabs/pid_tuning.css b/src/css/tabs/pid_tuning.css
index 85262cbc..c2a51594 100644
--- a/src/css/tabs/pid_tuning.css
+++ b/src/css/tabs/pid_tuning.css
@@ -908,3 +908,26 @@
.tab-pid_tuning .pid_titlebar .name-helpicon-flex .helpicon {
margin-right: 0;
}
+
+.tab-pid_tuning .cf .rates_logo_bg {
+ background-color: #ebeced;
+}
+
+.tab-pid_tuning .rates_logo_div {
+ margin-top: -10%;
+ text-align: center;
+}
+
+.tab-pid_tuning .rates_logo {
+ width: 80%;
+ height: 80%;
+}
+
+.tab-pid_tuning .rates-tab-warning {
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.tab-pid_tuning .float-left {
+ float: left;
+}
diff --git a/src/images/rate_logos/actual.svg b/src/images/rate_logos/actual.svg
new file mode 100644
index 00000000..ccd10479
--- /dev/null
+++ b/src/images/rate_logos/actual.svg
@@ -0,0 +1,124 @@
+
+
diff --git a/src/images/rate_logos/betaflight.svg b/src/images/rate_logos/betaflight.svg
new file mode 100644
index 00000000..fc9c7a7d
--- /dev/null
+++ b/src/images/rate_logos/betaflight.svg
@@ -0,0 +1,141 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/images/rate_logos/kiss.svg b/src/images/rate_logos/kiss.svg
new file mode 100644
index 00000000..6388a34d
--- /dev/null
+++ b/src/images/rate_logos/kiss.svg
@@ -0,0 +1,85 @@
+
+
+
+
diff --git a/src/images/rate_logos/quickrates.svg b/src/images/rate_logos/quickrates.svg
new file mode 100644
index 00000000..7318771c
--- /dev/null
+++ b/src/images/rate_logos/quickrates.svg
@@ -0,0 +1,87 @@
+
+
+
+
diff --git a/src/images/rate_logos/raceflight.svg b/src/images/rate_logos/raceflight.svg
new file mode 100644
index 00000000..1b91b644
--- /dev/null
+++ b/src/images/rate_logos/raceflight.svg
@@ -0,0 +1,71 @@
+
+
+
+
diff --git a/src/js/RateCurve.js b/src/js/RateCurve.js
index f9d71ff0..c1fda2fa 100644
--- a/src/js/RateCurve.js
+++ b/src/js/RateCurve.js
@@ -56,7 +56,7 @@ var RateCurve = function (useLegacyCurve) {
context.moveTo(0, height);
context.quadraticCurveTo(width * 11 / 20, height - ((rateY / 2) * (1 - rcExpo)), width, height - rateY);
context.stroke();
- }
+ };
this.drawStickPosition = function (rcData, rate, rcRate, rcExpo, superExpoActive, deadband, limit, maxAngularVel, context, stickColor) {
@@ -76,20 +76,15 @@ var RateCurve = function (useLegacyCurve) {
context.restore();
}
return (Math.abs(currentValue)<0.5)?0:currentValue.toFixed(0); // The calculated value in deg/s is returned from the function call for further processing.
- }
+ };
-};
+ this.getBetaflightRates = function (rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo, superExpoActive, limit) {
+ let angularVel;
-RateCurve.prototype.rcCommandRawToDegreesPerSecond = function (rcData, rate, rcRate, rcExpo, superExpoActive, deadband, limit) {
- var angleRate;
- if (rate !== undefined && rcRate !== undefined && rcExpo !== undefined) {
if (rcRate > 2) {
rcRate = rcRate + (rcRate - 2) * 14.54;
}
- var maxRc = 500 * rcRate;
- var rcCommandf = this.rcCommand(rcData, rcRate, deadband) / maxRc;
- var rcCommandfAbs = Math.abs(rcCommandf);
var expoPower;
var rcRateConstant;
@@ -107,13 +102,90 @@ RateCurve.prototype.rcCommandRawToDegreesPerSecond = function (rcData, rate, rcR
if (superExpoActive) {
var rcFactor = 1 / this.constrain(1 - rcCommandfAbs * rate, 0.01, 1);
- angleRate = rcRateConstant * rcRate * rcCommandf; // 200 should be variable checked on version (older versions it's 205,9)
- angleRate = angleRate * rcFactor;
+ angularVel = rcRateConstant * rcRate * rcCommandf; // 200 should be variable checked on version (older versions it's 205,9)
+ angularVel = angularVel * rcFactor;
} else {
- angleRate = (((rate * 100) + 27) * rcCommandf / 16) / 4.1; // Only applies to old versions ?
+ angularVel = (((rate * 100) + 27) * rcCommandf / 16) / 4.1; // Only applies to old versions ?
}
- angleRate = this.constrain(angleRate, -1 * limit, limit); // Rate limit from profile
+ angularVel = this.constrain(angularVel, -1 * limit, limit); // Rate limit from profile
+
+ return angularVel;
+ };
+
+ this.getRaceflightRates = function (rcCommandf, rate, rcRate, rcExpo) {
+ let angularVel = ((1 + 0.01 * rcExpo * (rcCommandf * rcCommandf - 1.0)) * rcCommandf);
+ angularVel = (angularVel * (rcRate + (Math.abs(angularVel) * rcRate * rate * 0.01)));
+ return angularVel;
+ };
+
+ this.getKISSRates = function (rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo) {
+ const kissRpy = 1 - rcCommandfAbs * rate;
+ const kissTempCurve = rcCommandf * rcCommandf;
+ rcCommandf = ((rcCommandf * kissTempCurve) * rcExpo + rcCommandf * (1 - rcExpo)) * (rcRate / 10);
+ return ((2000.0 * (1.0 / kissRpy)) * rcCommandf);
+ };
+
+ this.getActualRates = function (rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo) {
+ let angularVel;
+ const expof = rcCommandfAbs * ((Math.pow(rcCommandf, 5) * rcExpo) + (rcCommandf * (1 - rcExpo)));
+
+ angularVel = Math.max(0, rate-rcRate);
+ angularVel = (rcCommandf * rcRate) + (angularVel * expof);
+
+ return angularVel;
+ };
+
+ this.getQuickRates = function (rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo) {
+ rcRate = rcRate * 200;
+ rate = Math.max(rate, rcRate);
+
+ let angularVel;
+ const superExpoConfig = (((rate / rcRate) - 1) / (rate / rcRate));
+ const curve = Math.pow(rcCommandfAbs, 3) * rcExpo + rcCommandfAbs * (1 - rcExpo);
+
+ angularVel = 1.0 / (1.0 - (curve * superExpoConfig));
+ angularVel = rcCommandf * rcRate * angularVel;
+
+ return angularVel;
+ };
+
+};
+
+RateCurve.prototype.rcCommandRawToDegreesPerSecond = function (rcData, rate, rcRate, rcExpo, superExpoActive, deadband, limit) {
+ var angleRate;
+
+ if (rate !== undefined && rcRate !== undefined && rcExpo !== undefined) {
+ const rcCommandf = this.rcCommand(rcData, 1, deadband) / 500;
+ var rcCommandfAbs = Math.abs(rcCommandf);
+
+ switch(TABS.pid_tuning.currentRatesType) {
+ case TABS.pid_tuning.RATES_TYPE.RACEFLIGHT:
+ angleRate=this.getRaceflightRates(rcCommandf, rate, rcRate, rcExpo);
+
+ break;
+
+ case TABS.pid_tuning.RATES_TYPE.KISS:
+ angleRate=this.getKISSRates(rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo);
+
+ break;
+
+ case TABS.pid_tuning.RATES_TYPE.ACTUAL:
+ angleRate=this.getActualRates(rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo);
+
+ break;
+
+ case TABS.pid_tuning.RATES_TYPE.QUICKRATES:
+ angleRate=this.getQuickRates(rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo);
+
+ break;
+
+ // add future rates types here
+ default: // BetaFlight
+ angleRate=this.getBetaflightRates(rcCommandf, rcCommandfAbs, rate, rcRate, rcExpo, superExpoActive, limit);
+
+ break;
+ }
}
return angleRate;
diff --git a/src/js/msp/MSPHelper.js b/src/js/msp/MSPHelper.js
index 28c29ec9..9e3dd60d 100644
--- a/src/js/msp/MSPHelper.js
+++ b/src/js/msp/MSPHelper.js
@@ -357,6 +357,9 @@ MspHelper.prototype.process_data = function(dataHandler) {
RC_tuning.pitch_rate_limit = data.readU16();
RC_tuning.yaw_rate_limit = data.readU16();
}
+ if (semver.gte(CONFIG.apiVersion, "1.43.0")) {
+ RC_tuning.rates_type = data.readU8();
+ }
break;
case MSPCodes.MSP_PID:
// PID data arrived, we need to scale it and save to appropriate bank / array
@@ -1689,6 +1692,9 @@ MspHelper.prototype.crunch = function(code) {
buffer.push16(RC_tuning.pitch_rate_limit);
buffer.push16(RC_tuning.yaw_rate_limit);
}
+ if (semver.gte(CONFIG.apiVersion, "1.43.0")) {
+ buffer.push8(RC_tuning.rates_type);
+ }
break;
case MSPCodes.MSP_SET_RX_MAP:
for (let i = 0; i < RC_MAP.length; i++) {
diff --git a/src/js/tabs/pid_tuning.js b/src/js/tabs/pid_tuning.js
index 24f28f89..ddd28ea8 100644
--- a/src/js/tabs/pid_tuning.js
+++ b/src/js/tabs/pid_tuning.js
@@ -7,6 +7,14 @@ TABS.pid_tuning = {
dirty: false,
currentProfile: null,
currentRateProfile: null,
+ currentRatesType: null,
+ RATES_TYPE: {
+ BETAFLIGHT: 0,
+ RACEFLIGHT: 1,
+ KISS: 2,
+ ACTUAL: 3,
+ QUICKRATES: 4,
+ },
SETPOINT_WEIGHT_RANGE_LOW: 2.55,
SETPOINT_WEIGHT_RANGE_HIGH: 20,
SETPOINT_WEIGHT_RANGE_LEGACY: 2.54,
@@ -399,6 +407,29 @@ TABS.pid_tuning.initialize = function (callback) {
$('.idleMinRpm').hide();
}
+ if (semver.gte(CONFIG.apiVersion, "1.43.0")) {
+ const ratesTypeListElement = $('select[id="ratesType"]'); // generates list
+ const ratesList = [
+ {name: "Betaflight"},
+ {name: "Raceflight"},
+ {name: "KISS"},
+ {name: "Actual"},
+ {name: "QuickRates"},
+ ];
+ // add future rates types here with CONFIG.apiVersion check
+ for (let i = 0; i < ratesList.length; i++) {
+ ratesTypeListElement.append(``);
+ }
+
+ self.currentRatesType = RC_tuning.rates_type;
+ ratesTypeListElement.val(self.currentRatesType);
+
+ self.changeRatesType(self.currentRatesType); // update rate type code when updating the tab
+
+ } else {
+ $('.rates_type').hide();
+ }
+
$('input[id="useIntegratedYaw"]').change(function() {
var checked = $(this).is(':checked');
$('#pidTuningIntegratedYawCaution').toggle(checked);
@@ -649,16 +680,65 @@ TABS.pid_tuning.initialize = function (callback) {
});
// catch RC_tuning changes
- RC_tuning.RC_RATE = parseFloat($('.pid_tuning input[name="rc_rate"]').val());
+ const pitch_rate_e = $('.pid_tuning input[name="pitch_rate"]');
+ const roll_rate_e = $('.pid_tuning input[name="roll_rate"]');
+ const yaw_rate_e = $('.pid_tuning input[name="yaw_rate"]');
+ const rc_rate_pitch_e = $('.pid_tuning input[name="rc_rate_pitch"]');
+ const rc_rate_e = $('.pid_tuning input[name="rc_rate"]');
+ const rc_rate_yaw_e = $('.pid_tuning input[name="rc_rate_yaw"]');
+ const rc_pitch_expo_e = $('.pid_tuning input[name="rc_pitch_expo"]');
+ const rc_expo_e = $('.pid_tuning input[name="rc_expo"]');
+ const rc_yaw_expo_e = $('.pid_tuning input[name="rc_yaw_expo"]');
+
RC_tuning.roll_pitch_rate = parseFloat($('.pid_tuning input[name="roll_pitch_rate"]').val());
- RC_tuning.roll_rate = parseFloat($('.pid_tuning input[name="roll_rate"]').val());
- RC_tuning.pitch_rate = parseFloat($('.pid_tuning input[name="pitch_rate"]').val());
- RC_tuning.yaw_rate = parseFloat($('.pid_tuning input[name="yaw_rate"]').val());
- RC_tuning.RC_EXPO = parseFloat($('.pid_tuning input[name="rc_expo"]').val());
- RC_tuning.RC_YAW_EXPO = parseFloat($('.pid_tuning input[name="rc_yaw_expo"]').val());
- RC_tuning.rcYawRate = parseFloat($('.pid_tuning input[name="rc_rate_yaw"]').val());
- RC_tuning.rcPitchRate = parseFloat($('.pid_tuning input[name="rc_rate_pitch"]').val());
- RC_tuning.RC_PITCH_EXPO = parseFloat($('.pid_tuning input[name="rc_pitch_expo"]').val());
+ RC_tuning.RC_RATE = parseFloat(rc_rate_e.val());
+ RC_tuning.roll_rate = parseFloat(roll_rate_e.val());
+ RC_tuning.pitch_rate = parseFloat(pitch_rate_e.val());
+ RC_tuning.yaw_rate = parseFloat(yaw_rate_e.val());
+ RC_tuning.RC_EXPO = parseFloat(rc_expo_e.val());
+ RC_tuning.RC_YAW_EXPO = parseFloat(rc_yaw_expo_e.val());
+ RC_tuning.rcYawRate = parseFloat(rc_rate_yaw_e.val());
+ RC_tuning.rcPitchRate = parseFloat(rc_rate_pitch_e.val());
+ RC_tuning.RC_PITCH_EXPO = parseFloat(rc_pitch_expo_e.val());
+
+ if (semver.gte(CONFIG.apiVersion, "1.43.0")) {
+ switch(self.currentRatesType) {
+ case self.RATES_TYPE.RACEFLIGHT:
+ RC_tuning.pitch_rate = parseFloat(pitch_rate_e.val()) / 100;
+ RC_tuning.roll_rate = parseFloat(roll_rate_e.val()) / 100;
+ RC_tuning.yaw_rate = parseFloat(yaw_rate_e.val()) / 100;
+ RC_tuning.rcPitchRate = parseFloat(rc_rate_pitch_e.val()) / 1000;
+ RC_tuning.RC_RATE = parseFloat(rc_rate_e.val()) / 1000;
+ RC_tuning.rcYawRate = parseFloat(rc_rate_yaw_e.val()) / 1000;
+ RC_tuning.RC_PITCH_EXPO = parseFloat(rc_pitch_expo_e.val()) / 100;
+ RC_tuning.RC_EXPO = parseFloat(rc_expo_e.val()) / 100;
+ RC_tuning.RC_YAW_EXPO = parseFloat(rc_yaw_expo_e.val()) / 100;
+
+ break;
+
+ case self.RATES_TYPE.ACTUAL:
+ RC_tuning.pitch_rate = parseFloat(pitch_rate_e.val()) / 1000;
+ RC_tuning.roll_rate = parseFloat(roll_rate_e.val()) / 1000;
+ RC_tuning.yaw_rate = parseFloat(yaw_rate_e.val()) / 1000;
+ RC_tuning.rcPitchRate = parseFloat(rc_rate_pitch_e.val()) / 1000;
+ RC_tuning.RC_RATE = parseFloat(rc_rate_e.val()) / 1000;
+ RC_tuning.rcYawRate = parseFloat(rc_rate_yaw_e.val()) / 1000;
+
+ break;
+
+ case self.RATES_TYPE.QUICKRATES:
+ RC_tuning.pitch_rate = parseFloat(pitch_rate_e.val()) / 1000;
+ RC_tuning.roll_rate = parseFloat(roll_rate_e.val()) / 1000;
+ RC_tuning.yaw_rate = parseFloat(yaw_rate_e.val()) / 1000;
+
+ break;
+
+ // add future rates types here
+ default: // BetaFlight
+
+ break;
+ }
+ }
RC_tuning.throttle_MID = parseFloat($('.throttle input[name="mid"]').val());
RC_tuning.throttle_EXPO = parseFloat($('.throttle input[name="expo"]').val());
@@ -779,6 +859,15 @@ TABS.pid_tuning.initialize = function (callback) {
ADVANCED_TUNING.motorOutputLimit = parseInt($('.pid_tuning input[name="motorLimit"]').val());
ADVANCED_TUNING.autoProfileCellCount = parseInt($('.pid_tuning input[name="cellCount"]').val());
ADVANCED_TUNING.idleMinRpm = parseInt($('input[name="idleMinRpm-number"]').val());
+
+ const selectedRatesType = $('select[id="ratesType"]').val(); // send analytics for rates type
+ let selectedRatesTypeName = null;
+ if (selectedRatesType !== RC_tuning.rates_type) {
+ selectedRatesTypeName = $('select[id="ratesType"]').find('option:selected').text();
+ }
+ self.analyticsChanges['RatesType'] = selectedRatesTypeName;
+
+ RC_tuning.rates_type = selectedRatesType;
}
}
@@ -941,6 +1030,45 @@ TABS.pid_tuning.initialize = function (callback) {
self.currentRates.rc_expo_pitch = self.currentRates.rc_expo;
}
+ if (semver.gte(CONFIG.apiVersion, "1.43.0")) {
+ switch(RC_tuning.rates_type) {
+ case self.RATES_TYPE.RACEFLIGHT:
+ self.currentRates.roll_rate *= 100;
+ self.currentRates.pitch_rate *= 100;
+ self.currentRates.yaw_rate *= 100;
+ self.currentRates.rc_rate *= 1000;
+ self.currentRates.rc_rate_yaw *= 1000;
+ self.currentRates.rc_rate_pitch *= 1000;
+ self.currentRates.rc_expo *= 100;
+ self.currentRates.rc_yaw_expo *= 100;
+ self.currentRates.rc_pitch_expo *= 100;
+
+ break;
+
+ case self.RATES_TYPE.ACTUAL:
+ self.currentRates.roll_rate *= 1000;
+ self.currentRates.pitch_rate *= 1000;
+ self.currentRates.yaw_rate *= 1000;
+ self.currentRates.rc_rate *= 1000;
+ self.currentRates.rc_rate_yaw *= 1000;
+ self.currentRates.rc_rate_pitch *= 1000;
+
+ break;
+
+ case self.RATES_TYPE.QUICKRATES:
+ self.currentRates.roll_rate *= 1000;
+ self.currentRates.pitch_rate *= 1000;
+ self.currentRates.yaw_rate *= 1000;
+
+ break;
+
+ // add future rates types here
+ default: // BetaFlight
+
+ break;
+ }
+ }
+
$('.tab-pid_tuning .tab-container .pid').on('click', () => activateSubtab('pid'));
$('.tab-pid_tuning .tab-container .rates').on('click', () => activateSubtab('rates'));
@@ -1265,6 +1393,11 @@ TABS.pid_tuning.initialize = function (callback) {
targetValue = checkInput(targetElement);
if (self.currentRates.hasOwnProperty(targetElement.attr('name')) && targetValue !== undefined) {
+ const stepValue = parseFloat(targetElement.prop('step')); // adjust value to match step (change only the result, not the the actual value)
+ if (stepValue != null) {
+ targetValue = Math.round(targetValue / stepValue) * stepValue;
+ }
+
self.currentRates[targetElement.attr('name')] = targetValue;
updateNeeded = true;
@@ -1294,6 +1427,12 @@ TABS.pid_tuning.initialize = function (callback) {
if (targetElement.attr('name') === 'rc_expo' && semver.lt(CONFIG.apiVersion, "1.37.0")) {
self.currentRates.rc_pitch_expo = targetValue;
}
+
+ if (targetElement.attr('id') === 'ratesType' && semver.gte(CONFIG.apiVersion, "1.43.0")) {
+ self.changeRatesType(targetValue);
+
+ updateNeeded = true;
+ }
} else { // no event was passed, just force a graph update
updateNeeded = true;
}
@@ -1752,6 +1891,8 @@ TABS.pid_tuning.initialize = function (callback) {
self.setDirty(false);
GUI.log(i18n.getMessage('pidTuningEepromSaved'));
+
+ self.refresh();
});
analytics.sendChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, self.analyticsChanges);
@@ -1765,7 +1906,7 @@ TABS.pid_tuning.initialize = function (callback) {
self.updating = false;
// enable RC data pulling for rates preview
- GUI.interval_add('receiver_pull', self.getRecieverData, true);
+ GUI.interval_add('receiver_pull', self.getReceiverData, true);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
@@ -1776,7 +1917,7 @@ TABS.pid_tuning.initialize = function (callback) {
}
};
-TABS.pid_tuning.getRecieverData = function () {
+TABS.pid_tuning.getReceiverData = function () {
MSP.send_message(MSPCodes.MSP_RC, false, false);
};
@@ -2192,3 +2333,217 @@ TABS.pid_tuning.updatePIDColors = function(clear = false) {
setTuningElementColor($('.pid_tuning .PITCH input[name="f"]'), ADVANCED_TUNING_ACTIVE.feedforwardPitch, ADVANCED_TUNING.feedforwardPitch);
setTuningElementColor($('.pid_tuning .YAW input[name="f"]'), ADVANCED_TUNING_ACTIVE.feedforwardYaw, ADVANCED_TUNING.feedforwardYaw);
};
+
+TABS.pid_tuning.changeRatesType = function(rateTypeID) {
+ let self = this;
+ const dialogRatesType = $('.dialogRatesType')[0];
+ let sameRatesType = true;
+
+ self.currentRatesType = rateTypeID;
+
+ if (self.currentRatesType !== RC_tuning.rates_type) {
+ sameRatesType = false;
+ dialogRatesType.showModal();
+
+ $('.dialogRatesType-cancelbtn').click(function() {
+ sameRatesType = true;
+ self.currentRatesType = RC_tuning.rates_type;
+ $('.rates_type select[id="ratesType"]').val(RC_tuning.rates_type);
+ self.changeRatesTypeLogo();
+ self.changeRatesSystem(sameRatesType);
+ dialogRatesType.close();
+ });
+
+ $('.dialogRatesType-confirmbtn').click(function() {
+ self.changeRatesTypeLogo();
+ self.changeRatesSystem(sameRatesType);
+ dialogRatesType.close();
+ });
+ } else {
+ self.changeRatesTypeLogo();
+ self.changeRatesSystem(sameRatesType);
+ }
+};
+
+TABS.pid_tuning.changeRatesSystem = function(sameType) {
+ let self = this;
+
+ let rcRateMax = 2.55, rcRateMin = 0.01, rcRateStep = 0.01;
+ let rateMax = 1.0, rateMin = 0, rateStep = 0.01;
+ let expoMax = 1.0, expoMin = 0, expoStep = 0.01;
+
+ const pitch_rate_e = $('.pid_tuning input[name="pitch_rate"]');
+ const roll_rate_e = $('.pid_tuning input[name="roll_rate"]');
+ const yaw_rate_e = $('.pid_tuning input[name="yaw_rate"]');
+ const rc_rate_pitch_e = $('.pid_tuning input[name="rc_rate_pitch"]');
+ const rc_rate_e = $('.pid_tuning input[name="rc_rate"]');
+ const rc_rate_yaw_e = $('.pid_tuning input[name="rc_rate_yaw"]');
+ const rc_pitch_expo_e = $('.pid_tuning input[name="rc_pitch_expo"]');
+ const rc_expo_e = $('.pid_tuning input[name="rc_expo"]');
+ const rc_yaw_expo_e = $('.pid_tuning input[name="rc_yaw_expo"]');
+
+ const rcRateLabel = $('#pid-tuning .pid_titlebar .rc_rate');
+ const rateLabel = $('#pid-tuning .pid_titlebar .rate');
+ const rcExpoLabel = $('#pid-tuning .pid_titlebar .rc_expo');
+
+ // default values for betaflight curve. all the default values produce the same betaflight default curve (or at least near enough)
+ let rcRateDefault = (1).toFixed(2), rateDefault = (0.7).toFixed(2), expoDefault = (0).toFixed(2);
+
+ if (sameType) { // if selected rates type is different from the saved one, set values to default instead of reading
+ pitch_rate_e.val(RC_tuning.pitch_rate.toFixed(2));
+ roll_rate_e.val(RC_tuning.roll_rate.toFixed(2));
+ yaw_rate_e.val(RC_tuning.yaw_rate.toFixed(2));
+ rc_rate_pitch_e.val(RC_tuning.rcPitchRate.toFixed(2));
+ rc_rate_e.val(RC_tuning.RC_RATE.toFixed(2));
+ rc_rate_yaw_e.val(RC_tuning.rcYawRate.toFixed(2));
+ rc_pitch_expo_e.val(RC_tuning.RC_PITCH_EXPO.toFixed(2));
+ rc_expo_e.val(RC_tuning.RC_EXPO.toFixed(2));
+ rc_yaw_expo_e.val(RC_tuning.RC_YAW_EXPO.toFixed(2));
+ }
+
+ switch(self.currentRatesType) {
+ case self.RATES_TYPE.RACEFLIGHT:
+ rcRateLabel.text(i18n.getMessage("pidTuningRcRateRaceflight"));
+ rateLabel.text(i18n.getMessage("pidTuningRateRaceflight"));
+ rcExpoLabel.text(i18n.getMessage("pidTuningRcExpoRaceflight"));
+
+ rcRateMax = 2000;
+ rcRateMin = 10;
+ rcRateStep = 10;
+ rateMax = 255;
+ rateStep = 1;
+ expoMax = 100;
+ expoStep = 1;
+
+ if (sameType) {
+ pitch_rate_e.val((RC_tuning.pitch_rate * 100).toFixed(0));
+ roll_rate_e.val((RC_tuning.roll_rate * 100).toFixed(0));
+ yaw_rate_e.val((RC_tuning.yaw_rate * 100).toFixed(0));
+ rc_rate_pitch_e.val((RC_tuning.rcPitchRate * 1000).toFixed(0));
+ rc_rate_e.val((RC_tuning.RC_RATE * 1000).toFixed(0));
+ rc_rate_yaw_e.val((RC_tuning.rcYawRate * 1000).toFixed(0));
+ rc_pitch_expo_e.val((RC_tuning.RC_PITCH_EXPO * 100).toFixed(0));
+ rc_expo_e.val((RC_tuning.RC_EXPO * 100).toFixed(0));
+ rc_yaw_expo_e.val((RC_tuning.RC_YAW_EXPO * 100).toFixed(0));
+ } else {
+ rcRateDefault = (370).toFixed(0);
+ rateDefault = (80).toFixed(0);
+ expoDefault = (50).toFixed(0);
+ }
+
+ break;
+
+ case self.RATES_TYPE.KISS:
+ rcRateLabel.text(i18n.getMessage("pidTuningRcRate"));
+ rateLabel.text(i18n.getMessage("pidTuningRcRateRaceflight"));
+ rcExpoLabel.text(i18n.getMessage("pidTuningRcExpoKISS"));
+
+ rateMax = 0.99;
+
+ break;
+
+ case self.RATES_TYPE.ACTUAL:
+ rcRateLabel.text(i18n.getMessage("pidTuningRcRateActual"));
+ rateLabel.text(i18n.getMessage("pidTuningRateQuickRates"));
+ rcExpoLabel.text(i18n.getMessage("pidTuningRcExpoRaceflight"));
+
+ rateMax = 2000;
+ rateStep = 10;
+ rcRateMax = 2000;
+ rcRateMin = 10;
+ rcRateStep = 10;
+
+ if (sameType) {
+ pitch_rate_e.val((RC_tuning.pitch_rate * 1000).toFixed(0));
+ roll_rate_e.val((RC_tuning.roll_rate * 1000).toFixed(0));
+ yaw_rate_e.val((RC_tuning.yaw_rate * 1000).toFixed(0));
+ rc_rate_pitch_e.val((RC_tuning.rcPitchRate * 1000).toFixed(0));
+ rc_rate_e.val((RC_tuning.RC_RATE * 1000).toFixed(0));
+ rc_rate_yaw_e.val((RC_tuning.rcYawRate * 1000).toFixed(0));
+ } else {
+ rcRateDefault = (200).toFixed(0);
+ rateDefault = (670).toFixed(0);
+ expoDefault = (0.54).toFixed(2);
+ }
+
+ break;
+
+ case self.RATES_TYPE.QUICKRATES:
+ rcRateLabel.text(i18n.getMessage("pidTuningRcRate"));
+ rateLabel.text(i18n.getMessage("pidTuningRateQuickRates"));
+ rcExpoLabel.text(i18n.getMessage("pidTuningRcExpoRaceflight"));
+
+ rateMax = 2000;
+ rateStep = 10;
+
+ if (sameType) {
+ pitch_rate_e.val((RC_tuning.pitch_rate * 1000).toFixed(0));
+ roll_rate_e.val((RC_tuning.roll_rate * 1000).toFixed(0));
+ yaw_rate_e.val((RC_tuning.yaw_rate * 1000).toFixed(0));
+ } else {
+ rateDefault = (670).toFixed(0);
+ }
+
+ break;
+
+ // add future rates types here
+ default: // BetaFlight
+ rcRateLabel.text(i18n.getMessage("pidTuningRcRate"));
+ rateLabel.text(i18n.getMessage("pidTuningRate"));
+ rcExpoLabel.text(i18n.getMessage("pidTuningRcExpo"));
+
+ break;
+ }
+
+ const rc_rate_input_c = $('#pid-tuning input[class="rc_rate_input"]');
+ const rate_input_c = $('#pid-tuning input[class="rate_input"]');
+ const expo_input_c = $('#pid-tuning input[class="expo_input"]');
+
+ if (!sameType) {
+ rate_input_c.val(rateDefault);
+ rc_rate_input_c.val(rcRateDefault);
+ expo_input_c.val(expoDefault);
+ }
+
+ rc_rate_input_c.attr({"max":rcRateMax, "min":rcRateMin, "step":rcRateStep}).change();
+ rate_input_c.attr({"max":rateMax, "min":rateMin, "step":rateStep}).change();
+ expo_input_c.attr({"max":expoMax, "min":expoMin, "step":expoStep}).change();
+
+ if (sameType) {
+ self.setDirty(false);
+ }
+};
+
+TABS.pid_tuning.changeRatesTypeLogo = function() {
+ let self = this;
+
+ const ratesLogoElement = $('.rates_type img[id="ratesLogo"]');
+
+ switch(self.currentRatesType) {
+ case self.RATES_TYPE.RACEFLIGHT:
+ ratesLogoElement.attr("src", "../images/rate_logos/raceflight.svg");
+
+ break;
+
+ case self.RATES_TYPE.KISS:
+ ratesLogoElement.attr("src", "../images/rate_logos/kiss.svg");
+
+ break;
+
+ case self.RATES_TYPE.ACTUAL:
+ ratesLogoElement.attr("src", "../images/rate_logos/actual.svg");
+
+ break;
+
+ case self.RATES_TYPE.QUICKRATES:
+ ratesLogoElement.attr("src", "../images/rate_logos/quickrates.svg");
+
+ break;
+
+ // add future rates types here
+ default: // BetaFlight
+ ratesLogoElement.attr("src", "../images/rate_logos/betaflight.svg");
+
+ break;
+ }
+};
diff --git a/src/js/tabs/receiver.js b/src/js/tabs/receiver.js
index 67f25c6b..f4f7c1ae 100644
--- a/src/js/tabs/receiver.js
+++ b/src/js/tabs/receiver.js
@@ -572,7 +572,7 @@ TABS.receiver.initialize = function (callback) {
tab.renderModel();
// TODO: Combine two polls together
- GUI.interval_add('receiver_pull_for_model_preview', tab.getRecieverData, 33, false);
+ GUI.interval_add('receiver_pull_for_model_preview', tab.getReceiverData, 33, false);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
@@ -583,7 +583,7 @@ TABS.receiver.initialize = function (callback) {
}
};
-TABS.receiver.getRecieverData = function () {
+TABS.receiver.getReceiverData = function () {
MSP.send_message(MSPCodes.MSP_RC, false, false);
};
diff --git a/src/tabs/pid_tuning.html b/src/tabs/pid_tuning.html
index b9715400..145b0322 100644
--- a/src/tabs/pid_tuning.html
+++ b/src/tabs/pid_tuning.html
@@ -107,7 +107,7 @@