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": "WARNING: You are changing the rates type. Your rates will be set to a default curve.

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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + 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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + 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 @@
-
+
@@ -142,7 +142,7 @@
-
+
@@ -319,7 +319,7 @@
-
+
@@ -366,7 +366,7 @@
-
+
@@ -376,13 +376,13 @@
-
+
-
+
@@ -656,7 +656,38 @@