Merge pull request #2638 from haslinghuis/rfc_filters

10.8-maintenance
J Blackman 2021-11-26 06:35:26 +11:00 committed by GitHub
commit ddf817cfb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1015 additions and 533 deletions

View File

@ -1827,6 +1827,9 @@
"pidTuningOptionOff": {
"message": "OFF"
},
"pidTuningOptionOn": {
"message": "ON"
},
"pidTuningFeedforwardAveragingOption2Point": {
"message": "2 Point"
},
@ -3626,6 +3629,10 @@
"message": "Raises or Lowers the default Gyro Lowpass Filters in proportion to each-other. The gyro filtering is applied before the PID loop.<br />General motor noise ranges per quad class:<br /><br />6\"+ quads - generally within 100hz to 330hz<br />5\" quads - generally within 220hz to 500hz<br />Whoop to 3\" quads - generally within 300hz to 850hz<br /><br />Generally, you want to set the slider to have the Gyro Lowpass 1 Dynamic Min/Max Cutoff range cover the above.<br />However, for smoother flights use More Filtering on the slider. To get a more aggressive filter tune, use Less Filtering on the slider.<br /><br />With Less Filtering BE CAREFUL to not get radical as to cause a fly-away or burn out motors.<br />Note frame resonance issues, bad bearings, and beat up props may cause you to need more filtering.",
"description": "Gyro filtering tuning slider helpicon message"
},
"pidTuningGyroSliderEnabled": {
"message": "Use Gyro Slider",
"description": "Disable or enable Gyro Filter Tuning Slider"
},
"pidTuningDTermFilterSlider": {
"message": "D Term Filter Multiplier:",
"description": "D Term filter tuning slider label"
@ -3634,6 +3641,10 @@
"message": "Changes the D-term Lowpass Filter cutoffs.<br />Moving the slider to the left gives stronger D filtering (lower cutoff frequency); to the right gives less filtering (higher cutoff frequency).<br /><br />D-term is the most sensitive PID element to noise and resonance. It can amplify any high frequency noise by 10x to 100x plus. That's why the cutoffs for the D filters are much lower than the gyro filters.<br /><br />D-term filtering is applied after - in addition to - the gyro filtering. Strong D filtering will reduce motor heat on noisy quads and can be useful with high PID 'D' values to minimise D resonance at medium to high frequencies. However, strong D filtering may delay the D signal, worsening propwash handling and encouraging lower frequency D resonances. The default D filtering is optimal for most quads. Larger machines with high D may do better with stronger D filtering than defaults. Moving the D lowpass filter slider to the right is generally not required. On very clean builds, going to the right can attenuate propwash. It should be done very cautiously since having less filtering on D can result in sudden resonances (including motor grinding or taking off on arming) and extreme motor heat.",
"description": "D Term filtering tuning slider helpicon message"
},
"pidTuningDTermSliderEnabled": {
"message": "Use D Term Slider",
"description": "Disable or enable D Term Filter Tuning Slider"
},
"pidTuningPidSlidersHelp": {
"message": "Sliders to adjust the quad flight characteristics (PID gains)<br /><br />Damping (D gain): Resists fast movement, minimises P oscillation.<br /><br />Tracking (P and I gain): Enchances the responsiveness of the quad, if too high may cause trilling or oscillation.<br /><br />Stick Response (Feedforward): Increases the responsiveness of the quad to faster stick movements.<br /><br />Drift - Wobble (I gain, expert): Fine adjustment of I.<br /><br />Dynamic D (D Max, expert): Sets the maximum amount that D can be boosted to during fast movements.<br /><br />Pitch Damping (Pitch:Roll D ratio, expert): Increases the amount of damping on pitch relative to roll.<br /><br />Pitch Tracking (Pitch:Roll P, I and F ratio, expert): Increases stabilising strenght on pitch relative to roll.<br /><br />Master Multiplier (all gains, expert): Raises or Lowers all the PID gains, keeping their proportions constant.",
"description": "Overall helpicon message for PID tuning sliders"
@ -3646,6 +3657,14 @@
"message": "<strong>Note:</strong> Sliders are disabled because values were changed manually. Clicking the '$t(pidTuningSliderEnableButton.message)' button will activate them again. This will reset the values and any unsaved changes will be lost.",
"description": "Tuning sliders disabled note when manual changes are detected"
},
"pidTuningGyroSliderDisabled": {
"message": "<strong>Note:</strong> Gyro Slider is disabled because values were changed manually. Clicking the '$t(pidTuningGyroSliderEnableButton.message)' button will activate them again. This will reset the values and any unsaved changes will be lost.",
"description": "Gyro Tuning sliders disabled note when manual changes are detected"
},
"pidTuningDTermSliderDisabled": {
"message": "<strong>Note:</strong> DTerm Slider is disabled because values were changed manually. Clicking the '$t(pidTuningDTermSliderEnableButton.message)' button will activate them again. This will reset the values and any unsaved changes will be lost.",
"description": "DTerm Tuning sliders disabled note when manual changes are detected"
},
"pidTuningPidSlidersDisabled": {
"message": "<strong>Note:</strong> Sliders are disabled. Clicking the '$t(pidTuningSliderEnableButton.message)' button will change the PID values to match your previously saved slider position.",
"description": "Tuning sliders disabled note"
@ -3774,26 +3793,44 @@
"pidTuningGyroLowpassFiltersGroup": {
"message": "Gyro Lowpass Filters"
},
"pidTuningGyroLowpassFrequency": {
"message": "Gyro Lowpass 1 Cutoff Frequency [Hz]"
},
"pidTuningGyroLowpassType": {
"message": "Gyro Lowpass 1 Filter Type"
},
"pidTuningGyroLowpassFrequency": {
"message": "Cutoff Frequency [Hz]"
},
"pidTuningGyroLowpass": {
"message": "Gyro Lowpass 1"
},
"pidTuningGyroLowpassMode": {
"message": "Mode"
},
"pidTuningLowpassStatic": {
"message": "STATIC"
},
"pidTuningLowpassDynamic": {
"message": "DYNAMIC"
},
"pidTuningLowpassFilterType": {
"message": "Filter Type"
},
"pidTuningGyroLowpassDyn": {
"message": "Gyro Lowpass Dynamic Filter"
},
"pidTuningGyroLowpassDynMinFrequency": {
"message": "Gyro Lowpass 1 Dynamic Min Cutoff Frequency [Hz]"
"message": "Min Cutoff Frequency [Hz]"
},
"pidTuningGyroLowpassDynMaxFrequency": {
"message": "Gyro Lowpass 1 Dynamic Max Cutoff Frequency [Hz]"
"message": "Max Cutoff Frequency [Hz]"
},
"pidTuningGyroLowpassDynType": {
"message": "Gyro Lowpass 1 Dynamic Filter Type"
},
"pidTuningGyroLowpass2Frequency": {
"message": "Gyro Lowpass 2 Cutoff Frequency [Hz]"
"pidTuningGyroLowpass2": {
"message": "Gyro Lowpass 2"
},
"pidTuningGyroLowpass2Type": {
"message": "Gyro Lowpass 2 Filter Type"
"pidTuningGyroLowpass2Frequency": {
"message": "Cutoff Frequency [Hz]"
},
"pidTuningGyroLowpassFilterHelp": {
"message": "Gyro lowpass filters attenuate higher frequency noise to keep it out of the PID loop. There are two independently configurable gyro filters; by default both are active.<br /><br />The first D lowpass can be static (fixed cutoff) or dynamic; the second D lowpass is always static. When a lowpass is in dynamic mode, filter will be stronger at low throttle, and the cutoff will go higher (less filtering) as throttle increases.<br /><br />Without RPM filtering, both PT1 filters should be enabled at default (or stronger) cutoffs, with lowpass 1 in dynamic mode.<br /><br />With RPM filtering, the gyro filter slider can often be moved some way to the right. On clean quads it can go all the way right, or alternatively a single static gyro lowpass filter at 500hz may be sufficient.<br /><br />A quad will have less propwash with the least gyro filter delay (sliders to the right, higher cutoff values).<br /><br />Always check for motor heat when shifting to less gyro filtering (sliders to the right). With minimal gyro filtering, it is essential to have enough D filtering! Take care!"
@ -3804,6 +3841,12 @@
"pidTuningGyroNotchFiltersGroup": {
"message": "Gyro Notch Filters"
},
"pidTuningGyroNotchFilter": {
"message": "Gyro Notch Filter 1"
},
"pidTuningGyroNotchFilter2": {
"message": "Gyro Notch Filter 2"
},
"pidTuningGyroNotch1Frequency": {
"message": "Gyro Notch Filter 1 Center Frequency [Hz]"
},
@ -3864,6 +3907,9 @@
"pidTuningDynamicNotchMaxHzHelp": {
"message": "Set this to the highest incoming noise frequency that is needed to be controlled by the dynamic notch."
},
"pidTuningGyroLowpassType": {
"message": "Gyro Lowpass 1 Filter Type"
},
"pidTuningDynamicNotchCountHelp": {
"message": "Sets the number of dynamic notches per axis. With RPM filter enabled a value of 1 or 2 is recommended. Without RPM filter a value of 4 or 5 is recommended. Lower numbers will reduce filter delay, however it may increase motor temperature."
},
@ -3894,6 +3940,15 @@
"pidTuningFilterSettings": {
"message": "Profile dependent Filter Settings"
},
"pidTuningDTermLowpass": {
"message": "D Term Lowpass 1"
},
"pidTuningDTermLowpassMode": {
"message": "Mode"
},
"pidTuningDTermLowpass2": {
"message": "D Term Lowpass 2"
},
"pidTuningDTermLowpassFiltersGroup": {
"message": "D Term Lowpass Filters"
},
@ -3901,26 +3956,29 @@
"message": "D Term Lowpass 1 Filter Type"
},
"pidTuningDTermLowpassFrequency": {
"message": "D Term Lowpass 1 Cutoff Frequency [Hz]"
"message": "D Term Lowpass 1 Static Cutoff Frequency [Hz]"
},
"pidTuningDTermLowpass2Frequency": {
"message": "D Term Lowpass 2 Cutoff Frequency [Hz]"
"message": "D Term Lowpass 2 Static Cutoff Frequency [Hz]"
},
"pidTuningDTermLowpass2Type": {
"message": "D Term Lowpass 2 Filter Type"
},
"pidTuningDTermLowpassDyn": {
"message": "D Term Lowpass Dynamic Filter"
},
"pidTuningDTermLowpassDynMinFrequency": {
"message": "D Term Lowpass 1 Dynamic Min Cutoff Frequency [Hz]"
},
"pidTuningDTermLowpassDynMaxFrequency": {
"message": "D Term Lowpass 1 Dynamic Max Cutoff Frequency [Hz]"
},
"pidTuningDTermLowpassDynType": {
"message": "D Term Lowpass 1 Dynamic Filter Type"
},
"pidTuningDTermLowpassDynExpo": {
"message": "D Term Lowpass 1 Dynamic Curve Expo"
},
"pidTuningDTermLowpassDynType": {
"message": "D Term Lowpass 1 Dynamic Filter Type"
},
"pidTuningDTermNotchFiltersGroup": {
"message": "D Term Notch Filters"
},
@ -3930,7 +3988,7 @@
"pidTuningDTermNotchCutoff": {
"message": "D Term Notch Filter Cutoff Frequency [Hz]"
},
"pidTuningYawLospassFiltersGroup": {
"pidTuningYawLowpassFiltersGroup": {
"message": "Yaw Lowpass Filters"
},
"pidTuningYawLowpassFrequency": {

View File

@ -163,13 +163,17 @@
padding-right: 5px;
}
.tab-pid_tuning table.compensation td:first-child {
.tab-pid_tuning table.compensation td:first-child:not(.filterTable) {
width: 65px;
text-align: center;
vertical-align: top;
padding-top: 4px;
}
.tab-pid_tuning table.filterTable.compensation td:first-child {
width: 5%;
}
.tab-pid_tuning table.compensation td:last-child {
width: 100%;
}
@ -914,10 +918,6 @@
table-layout: auto;
}
.tab-pid_tuning table.filterTable td:first-child {
width: 25%;
}
@media only screen and (max-width: 1205px) {
.tab-pid_tuning .subtab-pid .spacer_left {

View File

@ -37,6 +37,9 @@ const TuningSliders = {
cachedGyroSliderValues: false,
cachedDTermSliderValues: false,
sliderGyroFilterDisabled: false,
sliderDTermFilterDisabled: false,
expertMode: false,
};
@ -72,7 +75,25 @@ TuningSliders.initialize = function() {
this.cachedDTermSliderValues = false;
this.updatePidSlidersDisplay();
this.updateFilterSlidersDisplay();
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
// if sliders are enabled on initialization tuning has not yet updated values according to sliders.
if (!FC.TUNING_SLIDERS.slider_gyro_filter) {
this.updateFilterSlidersDisplay();
}
if (!FC.TUNING_SLIDERS.slider_dterm_filter) {
this.updateFilterSlidersDisplay();
}
this.updateFilterSlidersWarning();
$('.subtab-filter .slidersDisabled').hide();
} else {
this.updateFilterSlidersDisplay();
$('select[id="sliderGyroFilterModeSelect"]').hide();
$('select[id="sliderDTermFilterModeSelect"]').hide();
}
};
TuningSliders.updateExpertModeSlidersDisplay = function() {
@ -109,6 +130,8 @@ TuningSliders.updateExpertModeSlidersDisplay = function() {
$('#sliderPitchPIGain').prop('disabled', !this.expertMode);
$('#sliderMasterMultiplier').prop('disabled', !this.expertMode);
$('.sliderGyroFilter').prop('disabled', gyro && !this.expertMode);
$('.sliderDTermFilter').prop('disabled', dterm && !this.expertMode);
$('#sliderGyroFilterMultiplier').prop('disabled', gyro && !this.expertMode);
$('#sliderDTermFilterMultiplier').prop('disabled', dterm && !this.expertMode);
@ -118,9 +141,6 @@ TuningSliders.updateExpertModeSlidersDisplay = function() {
$('.advancedSlider').toggleClass('disabledSliders', !this.expertMode);
$('.sliderGyroFilter').toggleClass('disabledSliders', gyro && !this.expertMode);
$('.sliderDtermFilter').toggleClass('disabledSliders', dterm && !this.expertMode);
$('.advancedSliderDmaxGain').toggle(dMaxGain || this.expertMode);
$('.advancedSliderIGain').toggle(iGain || this.expertMode);
$('.advancedSliderRollPitchRatio').toggle(rpRatio || this.expertMode);
@ -227,8 +247,11 @@ TuningSliders.initGyroFilterSliderPosition = function() {
this.sliderGyroFilter = FC.TUNING_SLIDERS.slider_gyro_filter;
this.sliderGyroFilterMultiplier = FC.TUNING_SLIDERS.slider_gyro_filter_multiplier / 100;
} else {
this.sliderGyroFilterMultiplier = Math.floor((FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz + FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz + FC.FILTER_CONFIG.gyro_lowpass2_hz) /
(this.FILTER_DEFAULT.gyro_lowpass_dyn_min_hz + this.FILTER_DEFAULT.gyro_lowpass_dyn_max_hz + this.FILTER_DEFAULT.gyro_lowpass2_hz) * 100) / 100;
this.sliderGyroFilterMultiplier =
Math.floor(
(FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz + FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz + FC.FILTER_CONFIG.gyro_lowpass_hz + FC.FILTER_CONFIG.gyro_lowpass2_hz) /
(this.FILTER_DEFAULT.gyro_lowpass_dyn_min_hz + this.FILTER_DEFAULT.gyro_lowpass_dyn_max_hz + this.FILTER_DEFAULT.gyro_lowpass_hz +
this.FILTER_DEFAULT.gyro_lowpass2_hz) * 100) / 100;
}
$('#sliderGyroFilterMultiplier').val(this.sliderGyroFilterMultiplier);
@ -236,16 +259,19 @@ TuningSliders.initGyroFilterSliderPosition = function() {
};
TuningSliders.initDTermFilterSliderPosition = function() {
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
this.sliderDTermFilterMultiplier = Math.floor((FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz + FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz + FC.FILTER_CONFIG.dterm_lowpass2_hz) /
(this.FILTER_DEFAULT.dterm_lowpass_dyn_min_hz + this.FILTER_DEFAULT.dterm_lowpass_dyn_max_hz + this.FILTER_DEFAULT.dterm_lowpass2_hz) * 100) / 100;
} else {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
this.sliderDTermFilter = FC.TUNING_SLIDERS.slider_dterm_filter;
this.sliderDTermFilterMultiplier = FC.TUNING_SLIDERS.slider_dterm_filter_multiplier / 100;
} else {
this.sliderDTermFilterMultiplier =
Math.floor(
(FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz + FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz + FC.FILTER_CONFIG.dterm_lowpass_hz + FC.FILTER_CONFIG.dterm_lowpass2_hz) /
(this.FILTER_DEFAULT.dterm_lowpass_dyn_min_hz + this.FILTER_DEFAULT.dterm_lowpass_dyn_max_hz + this.FILTER_DEFAULT.dterm_lowpass_hz +
this.FILTER_DEFAULT.dterm_lowpass2_hz) * 100) / 100;
}
$('output[name="sliderDTermFilterMultiplier-number"]').val(this.sliderDTermFilterMultiplier);
$('#sliderDTermFilterMultiplier').val(this.sliderDTermFilterMultiplier);
$('output[name="sliderDTermFilterMultiplier-number"]').val(this.sliderDTermFilterMultiplier);
};
TuningSliders.resetPidSliders = function() {
@ -282,43 +308,17 @@ TuningSliders.resetPidSliders = function() {
};
TuningSliders.resetGyroFilterSlider = function() {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
if (!this.cachedGyroSliderValues) {
this.sliderGyroFilterMultiplier = 1;
}
this.sliderGyroFilter = 1;
FC.TUNING_SLIDERS.slider_gyro_filter = 1;
this.initGyroFilterSliderPosition();
} else {
this.sliderGyroFilterMultiplier = 1;
$('#sliderGyroFilterMultiplier').val(this.sliderGyroFilterMultiplier);
}
this.FilterReset = true;
this.sliderGyroFilterMultiplier = 1;
$('#sliderGyroFilterMultiplier').val(this.sliderGyroFilterMultiplier);
this.calculateNewGyroFilters();
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
this.updateFilterSlidersDisplay();
}
this.updateFilterSlidersDisplay();
};
TuningSliders.resetDTermFilterSlider = function() {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
if (!this.cachedDTermSliderValues) {
this.sliderDTermFilterMultiplier = 1;
}
this.sliderDtermFilter = 1;
FC.TUNING_SLIDERS.slider_dterm_filter = 1;
this.initDTermFilterSliderPosition();
} else {
this.sliderDTermFilterMultiplier = 1;
$('#sliderDTermFilterMultiplier').val(this.sliderDTermFilterMultiplier);
}
this.FilterReset = true;
this.sliderDTermFilterMultiplier = 1;
$('#sliderDTermFilterMultiplier').val(this.sliderDTermFilterMultiplier);
this.calculateNewDTermFilters();
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
this.updateFilterSlidersDisplay();
}
this.updateFilterSlidersDisplay();
};
TuningSliders.legacyUpdateFilterSlidersDisplay = function() {
@ -327,6 +327,8 @@ TuningSliders.legacyUpdateFilterSlidersDisplay = function() {
parseInt($('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_max_hz * this.sliderGyroFilterMultiplier) ||
parseInt($('.pid_filter select[name="gyroLowpassDynType"]').val()) !== this.FILTER_DEFAULT.gyro_lowpass_type ||
parseInt($('.pid_filter input[name="gyroLowpassFrequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.gyro_lowpass_hz * this.sliderGyroFilterMultiplier) ||
parseInt($('.pid_filter input[name="gyroLowpass2Frequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.gyro_lowpass2_hz * this.sliderGyroFilterMultiplier) ||
parseInt($('.pid_filter select[name="gyroLowpass2Type"]').val()) !== this.FILTER_DEFAULT.gyro_lowpass2_type) {
@ -340,6 +342,8 @@ TuningSliders.legacyUpdateFilterSlidersDisplay = function() {
parseInt($('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').val()) !==
Math.round(this.FILTER_DEFAULT.dterm_lowpass_dyn_max_hz * this.sliderDTermFilterMultiplier) ||
parseInt($('.pid_filter select[name="dtermLowpassDynType"]').val()) !== this.FILTER_DEFAULT.dterm_lowpass_type ||
parseInt($('.pid_filter input[name="dtermLowpassFrequency"]').val()) !==
Math.round(this.FILTER_DEFAULT.dterm_lowpass_hz * this.sliderDTermFilterMultiplier) ||
parseInt($('.pid_filter input[name="dtermLowpass2Frequency"]').val()) !==
Math.round(this.FILTER_DEFAULT.dterm_lowpass2_hz * this.sliderDTermFilterMultiplier) ||
parseInt($('.pid_filter select[name="dtermLowpass2Type"]').val()) !== this.FILTER_DEFAULT.dterm_lowpass2_type) {
@ -347,6 +351,12 @@ TuningSliders.legacyUpdateFilterSlidersDisplay = function() {
} else {
this.cachedDTermSliderValues = true;
}
$('.tuningFilterSliders .sliderLabels tr:nth-child(2)').toggle(!this.GyroSliderUnavailable);
$('.tuningFilterSliders .sliderLabels tr:last-child').toggle(!this.DTermSliderUnavailable);
$('.tuningFilterSliders').toggle(!(this.GyroSliderUnavailable && this.DTermSliderUnavailable));
$('.subtab-filter .slidersDisabled').toggle(this.GyroSliderUnavailable || this.DTermSliderUnavailable);
};
TuningSliders.updateSlidersWarning = function(slidersUnavailable = false) {
@ -366,20 +376,20 @@ TuningSliders.updateSlidersWarning = function(slidersUnavailable = false) {
$('.subtab-pid .slidersWarning').toggle(enableWarning && !slidersUnavailable);
};
TuningSliders.updateFilterSlidersWarning = function(gyroSliderUnavailable = false, DTermSliderUnavailable = false) {
TuningSliders.updateFilterSlidersWarning = function() {
let WARNING_FILTER_GYRO_LOW_GAIN = 0.7;
let WARNING_FILTER_GYRO_HIGH_GAIN = 1.25;
let WARNING_FILTER_GYRO_HIGH_GAIN = 1.4;
let WARNING_FILTER_DTERM_LOW_GAIN = 0.7;
const WARNING_FILTER_DTERM_HIGH_GAIN = 1.25;
let WARNING_FILTER_DTERM_HIGH_GAIN = 1.4;
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
WARNING_FILTER_GYRO_LOW_GAIN = 0.45;
WARNING_FILTER_GYRO_HIGH_GAIN = 1.55;
WARNING_FILTER_DTERM_LOW_GAIN = 0.75;
WARNING_FILTER_DTERM_HIGH_GAIN = 1.25;
}
$('.subtab-filter .slidersWarning').toggle(((this.sliderGyroFilterMultiplier >= WARNING_FILTER_GYRO_HIGH_GAIN ||
this.sliderGyroFilterMultiplier <= WARNING_FILTER_GYRO_LOW_GAIN) && !gyroSliderUnavailable) ||
((this.sliderDTermFilterMultiplier >= WARNING_FILTER_DTERM_HIGH_GAIN ||
this.sliderDTermFilterMultiplier <= WARNING_FILTER_DTERM_LOW_GAIN) && !DTermSliderUnavailable));
$('.subtab-filter .slidersWarning').toggle((this.sliderGyroFilterMultiplier >= WARNING_FILTER_GYRO_HIGH_GAIN ||
this.sliderGyroFilterMultiplier <= WARNING_FILTER_GYRO_LOW_GAIN) ||
(this.sliderDTermFilterMultiplier >= WARNING_FILTER_DTERM_HIGH_GAIN || this.sliderDTermFilterMultiplier <= WARNING_FILTER_DTERM_LOW_GAIN));
};
TuningSliders.updatePidSlidersDisplay = function() {
@ -426,65 +436,116 @@ TuningSliders.updatePidSlidersDisplay = function() {
this.updateSlidersWarning(this.pidSlidersUnavailable);
};
TuningSliders.updateGyroFilterSliderDisplay = function() {
// check if enabled filters were changed manually by comparing current value and those based on slider position
const lp1DynMin = parseInt($('.pid_filter input[name="gyroLowpassDynMinFrequency"]').val());
const lp1DynMax = parseInt($('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').val());
const lp1Static = parseInt($('.pid_filter input[name="gyroLowpassFrequency"]').val());
const lp2Freq = parseInt($('.pid_filter input[name="gyroLowpass2Frequency"]').val());
const lp1DynamicMinChanged = (lp1DynMin > 0) && (lp1DynMin !== Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_min_hz * this.sliderGyroFilterMultiplier));
const lp1DynamicMaxChanged = (lp1DynMax > 0) && (lp1DynMax !== Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_max_hz * this.sliderGyroFilterMultiplier));
const lp1Changed = (lp1Static > 0) && (lp1Static !== Math.floor(this.FILTER_DEFAULT.gyro_lowpass_hz * this.sliderGyroFilterMultiplier));
const lp2Changed = (lp2Freq > 0) && (lp2Freq !== Math.floor(this.FILTER_DEFAULT.gyro_lowpass2_hz * this.sliderGyroFilterMultiplier));
const hideSlider = (lp1DynMin && (lp1DynamicMinChanged || lp1DynamicMaxChanged)) || (lp1Static && lp1Changed) || (lp2Freq && lp2Changed) || this.sliderGyroFilterDisabled;
if (hideSlider) {
this.GyroSliderUnavailable = true;
this.sliderGyroFilter = 0;
} else {
this.GyroSliderUnavailable = false;
this.sliderGyroFilter = 1;
this.cachedGyroSliderValues = true;
}
// update Gyro mode and slider
$('select[id="sliderGyroFilterModeSelect"]').val(this.sliderGyroFilter);
$('.sliderGyroFilter').toggleClass('disabledSliders', !this.sliderGyroFilter || !(lp1Static || lp1DynMin || lp2Freq));
$('#sliderGyroFilterMultiplier').prop('disabled', !this.sliderGyroFilter);
};
TuningSliders.updateDTermFilterSliderDisplay = function() {
// check if enabled filters were changed manually by comparing current value and those based on slider position
const lp1DynMin = parseInt($('.pid_filter input[name="dtermLowpassDynMinFrequency"]').val());
const lp1DynMax = parseInt($('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').val());
const lp1Static = parseInt($('.pid_filter input[name="dtermLowpassFrequency"]').val());
const lp2Freq = parseInt($('.pid_filter input[name="dtermLowpass2Frequency"]').val());
const lp1DynamicMinChanged = (lp1DynMin > 0) && (lp1DynMin !== Math.floor(this.FILTER_DEFAULT.dterm_lowpass_dyn_min_hz * this.sliderDTermFilterMultiplier));
const lp1DynamicMaxChanged = (lp1DynMax > 0) && (lp1DynMax !== Math.floor(this.FILTER_DEFAULT.dterm_lowpass_dyn_max_hz * this.sliderDTermFilterMultiplier));
const lp1Changed = (lp1Static > 0) && (lp1Static !== Math.floor(this.FILTER_DEFAULT.dterm_lowpass_hz * this.sliderDTermFilterMultiplier));
const lp2Changed = (lp2Freq > 0) && (lp2Freq !== Math.floor(this.FILTER_DEFAULT.dterm_lowpass2_hz * this.sliderDTermFilterMultiplier));
const hideSlider = (lp1DynMin && (lp1DynamicMinChanged || lp1DynamicMaxChanged)) || (lp1Static && lp1Changed) || (lp2Freq && lp2Changed) || this.sliderDTermFilterDisabled;
if (hideSlider) {
this.DTermSliderUnavailable = true;
this.sliderDTermFilter = 0;
} else {
this.DTermSliderUnavailable = false;
this.sliderDTermFilter = 1;
this.cachedDTermSliderValues = true;
}
// update DTerm mode and slider
$('select[id="sliderDTermFilterModeSelect"]').val(this.sliderDTermFilter);
$('.sliderDTermFilter').toggleClass('disabledSliders', !this.sliderDTermFilter || !(lp1Static || lp1DynMin || lp2Freq));
$('#sliderDTermFilterMultiplier').prop('disabled', !this.sliderDTermFilter);
};
TuningSliders.updateFilterSlidersDisplay = function() {
// check if filters changed manually by comparing current value and those based on slider position
this.GyroSliderUnavailable = false;
this.DTermSliderUnavailable = false;
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
this.GyroSliderUnavailable = !FC.TUNING_SLIDERS.slider_gyro_filter;
this.DTermSliderUnavailable = !FC.TUNING_SLIDERS.slider_dterm_filter;
if (parseInt($('.pid_filter input[name="gyroLowpassDynMinFrequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_min_hz * this.sliderGyroFilterMultiplier) ||
parseInt($('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_max_hz * this.sliderGyroFilterMultiplier) ||
parseInt($('.pid_filter select[name="gyroLowpassDynType"]').val()) !== this.FILTER_DEFAULT.gyro_lowpass_type ||
parseInt($('.pid_filter input[name="gyroLowpass2Frequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.gyro_lowpass2_hz * this.sliderGyroFilterMultiplier) ||
parseInt($('.pid_filter select[name="gyroLowpass2Type"]').val()) !== this.FILTER_DEFAULT.gyro_lowpass2_type) {
this.GyroSliderUnavailable = true;
this.sliderGyroFilter = 0;
} else {
this.cachedGyroSliderValues = true;
this.sliderGyroFilter = 1;
}
if (parseInt($('.pid_filter input[name="dtermLowpassDynMinFrequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.dterm_lowpass_dyn_min_hz * this.sliderDTermFilterMultiplier) ||
Math.abs(parseInt($('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').val()) - this.FILTER_DEFAULT.dterm_lowpass_dyn_max_hz * this.sliderDTermFilterMultiplier) > 1 ||
parseInt($('.pid_filter select[name="dtermLowpassDynType"]').val()) !== this.FILTER_DEFAULT.dterm_lowpass_type ||
parseInt($('.pid_filter input[name="dtermLowpass2Frequency"]').val()) !==
Math.floor(this.FILTER_DEFAULT.dterm_lowpass2_hz * this.sliderDTermFilterMultiplier) ||
parseInt($('.pid_filter select[name="dtermLowpass2Type"]').val()) !== this.FILTER_DEFAULT.dterm_lowpass2_type) {
this.DTermSliderUnavailable = true;
this.sliderDTermFilter = 0;
} else {
this.cachedDTermSliderValues = true;
this.sliderDTermFilter = 1;
}
this.updateGyroFilterSliderDisplay();
this.updateDTermFilterSliderDisplay();
} else {
this.legacyUpdateFilterSlidersDisplay();
}
if (this.GyroSliderUnavailable) {
$('.tuningFilterSliders .sliderLabels tr:nth-child(2)').hide();
} else {
$('.tuningFilterSliders .sliderLabels tr:nth-child(2)').show();
}
if (this.DTermSliderUnavailable) {
$('.tuningFilterSliders .sliderLabels tr:last-child').hide();
} else {
$('.tuningFilterSliders .sliderLabels tr:last-child').show();
}
$('.tuningFilterSliders').toggle(!(this.GyroSliderUnavailable && this.DTermSliderUnavailable));
$('.subtab-filter .slidersDisabled').toggle(this.GyroSliderUnavailable || this.DTermSliderUnavailable);
$('.subtab-filter .nonExpertModeSlidersNote').toggle((!this.GyroSliderUnavailable || !this.DTermSliderUnavailable) && !this.expertMode);
this.updateFilterSlidersWarning(this.GyroSliderUnavailable, this.DTermSliderUnavailable);
};
TuningSliders.gyroFilterSliderEnable = function() {
this.sliderGyroFilter = true;
this.sliderGyroFilterDisabled = false;
this.FilterReset = true;
FC.TUNING_SLIDERS.slider_gyro_filter = 1;
this.writeFilterSliders();
this.updateLowpassValues();
this.updateFilterSlidersDisplay();
};
TuningSliders.gyroFilterSliderDisable = function() {
this.sliderGyroFilter = false;
this.sliderGyroFilterDisabled = true;
this.FilterReset = true;
FC.TUNING_SLIDERS.slider_gyro_filter = 0;
this.writeFilterSliders();
};
TuningSliders.dtermFilterSliderEnable = function() {
this.sliderDTermFilter = true;
this.sliderDTermFilterDisabled = false;
this.FilterReset = true;
FC.TUNING_SLIDERS.slider_dterm_filter = 1;
this.writeFilterSliders();
this.updateLowpassValues();
this.updateFilterSlidersDisplay();
};
TuningSliders.dtermFilterSliderDisable = function() {
this.sliderDTermFilter = false;
this.sliderDTermFilterDisabled = true;
this.FilterReset = true;
FC.TUNING_SLIDERS.slider_dterm_filter = 0;
this.writeFilterSliders();
};
TuningSliders.updateFormPids = function(updateSlidersOnly = false) {
if (!updateSlidersOnly) {
FC.PID_NAMES.forEach(function (elementPid, indexPid) {
@ -611,6 +672,7 @@ TuningSliders.calculateLegacyGyroFilters = function() {
// calculate, set and display new values in forms based on slider position
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz = Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_min_hz * this.sliderGyroFilterMultiplier);
FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz = Math.floor(this.FILTER_DEFAULT.gyro_lowpass_dyn_max_hz * this.sliderGyroFilterMultiplier);
FC.FILTER_CONFIG.gyro_lowpass_hz = Math.floor(this.FILTER_DEFAULT.gyro_lowpass_hz * this.sliderGyroFilterMultiplier);
FC.FILTER_CONFIG.gyro_lowpass2_hz = Math.floor(this.FILTER_DEFAULT.gyro_lowpass2_hz * this.sliderGyroFilterMultiplier);
FC.FILTER_CONFIG.gyro_lowpass_type = this.FILTER_DEFAULT.gyro_lowpass_type;
FC.FILTER_CONFIG.gyro_lowpass2_type = this.FILTER_DEFAULT.gyro_lowpass2_type;
@ -623,6 +685,7 @@ TuningSliders.calculateLegacyDTermFilters = function() {
// calculate, set and display new values in forms based on slider position
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz = Math.round(this.FILTER_DEFAULT.dterm_lowpass_dyn_min_hz * this.sliderDTermFilterMultiplier);
FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz = Math.round(this.FILTER_DEFAULT.dterm_lowpass_dyn_max_hz * this.sliderDTermFilterMultiplier);
FC.FILTER_CONFIG.dterm_lowpass_hz = Math.round(this.FILTER_DEFAULT.dterm_lowpass_hz * this.sliderDTermFilterMultiplier);
FC.FILTER_CONFIG.dterm_lowpass2_hz = Math.round(this.FILTER_DEFAULT.dterm_lowpass2_hz * this.sliderDTermFilterMultiplier);
FC.FILTER_CONFIG.dterm_lowpass_type = this.FILTER_DEFAULT.dterm_lowpass_type;
FC.FILTER_CONFIG.dterm_lowpass2_type = this.FILTER_DEFAULT.dterm_lowpass2_type;
@ -635,7 +698,6 @@ TuningSliders.calculateNewGyroFilters = function() {
// this is the main calculation for Gyro Filter slider, inputs are in form of slider position values
// values get set both into forms and their respective variables
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
FC.TUNING_SLIDERS.slider_gyro_filter = this.sliderGyroFilter;
//rounds slider values to nearies multiple of 5 and passes to the FW. Avoid dividing calc by (* x 100)/5 = 20
FC.TUNING_SLIDERS.slider_gyro_filter_multiplier = Math.round(this.sliderGyroFilterMultiplier * 20) * 5;
this.writeFilterSliders();
@ -645,10 +707,9 @@ TuningSliders.calculateNewGyroFilters = function() {
};
TuningSliders.calculateNewDTermFilters = function() {
// this is the main calculation for Gyro Filter slider, inputs are in form of slider position values
// this is the main calculation for DTerm Filter slider, inputs are in form of slider position values
// values get set both into forms and their respective variables
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
FC.TUNING_SLIDERS.slider_dterm_filter = this.sliderDTermFilter;
//rounds slider values to nearies multiple of 5 and passes to the FW. Avoid divide by ROUND[(* x 100)/5 = 20]
FC.TUNING_SLIDERS.slider_dterm_filter_multiplier = Math.round(this.sliderDTermFilterMultiplier * 20) * 5;
this.writeFilterSliders();
@ -657,8 +718,20 @@ TuningSliders.calculateNewDTermFilters = function() {
}
};
// We need to write filter config to switch filters without having to save.
TuningSliders.updateFiltersInFirmware = function() {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
MSP.promise(MSPCodes.MSP_SET_FILTER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FILTER_CONFIG))
.then(() => MSPCodes.MSP_SET_TUNING_SLIDERS, mspHelper.crunch(MSPCodes.MSP_SET_TUNING_SLIDERS))
.then(() => MSP.promise(MSPCodes.MSP_FILTER_CONFIG))
.then(() => this.updateLowpassValues());
}
};
TuningSliders.writeFilterSliders = function () {
// send sliders to firmware
MSP.promise(MSPCodes.MSP_SET_TUNING_SLIDERS, mspHelper.crunch(MSPCodes.MSP_SET_TUNING_SLIDERS))
// pulls values from firmware
.then(() => MSP.promise(MSPCodes.MSP_FILTER_CONFIG))
.then(() => {
TuningSliders.updateLowpassValues();
@ -673,15 +746,19 @@ TuningSliders.writeFilterSliders = function () {
TuningSliders.updateLowpassValues = function() {
$('.pid_filter input[name="gyroLowpassDynMinFrequency"]').val(FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz);
$('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').val(FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz);
$('.pid_filter select[name="gyroLowpassDynType"]').val(FC.FILTER_CONFIG.gyro_lowpass_type);
$('.pid_filter input[name="gyroLowpassFrequency"]').val(FC.FILTER_CONFIG.gyro_lowpass_hz);
$('.pid_filter select[name="gyroLowpassType"]').val(FC.FILTER_CONFIG.gyro_lowpass_type);
$('.pid_filter input[name="gyroLowpass2Frequency"]').val(FC.FILTER_CONFIG.gyro_lowpass2_hz);
$('.pid_filter select[name="gyroLowpassDynType]"').val(FC.FILTER_CONFIG.gyro_lowpass_type);
$('.pid_filter select[name="gyroLowpass2Type"]').val(FC.FILTER_CONFIG.gyro_lowpass2_type);
$('output[name="sliderGyroFilterMultiplier-number"]').val(this.sliderGyroFilterMultiplier);
$('.pid_filter input[name="dtermLowpassDynMinFrequency"]').val(FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz);
$('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').val(FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz);
$('.pid_filter input[name="dtermLowpass2Frequency"]').val(FC.FILTER_CONFIG.dterm_lowpass2_hz);
$('.pid_filter select[name="dtermLowpassDynType"]').val(FC.FILTER_CONFIG.dterm_lowpass_type);
$('.pid_filter input[name="dtermLowpassFrequency"]').val(FC.FILTER_CONFIG.dterm_lowpass_hz);
$('.pid_filter select[name="dtermLowpassType"]').val(FC.FILTER_CONFIG.dterm_lowpass_type);
$('.pid_filter input[name="dtermLowpass2Frequency"]').val(FC.FILTER_CONFIG.dterm_lowpass2_hz);
$('.pid_filter select[name="dtermLowpass2Type"]').val(FC.FILTER_CONFIG.dterm_lowpass2_type);
$('output[name="sliderDTermFilterMultiplier-number"]').val(this.sliderDTermFilterMultiplier);
};

View File

@ -853,6 +853,7 @@ const FC = {
versionFilterDefaults.gyro_lowpass_hz = 250;
versionFilterDefaults.gyro_lowpass_dyn_min_hz = 250;
versionFilterDefaults.gyro_lowpass2_hz = 500;
versionFilterDefaults.dterm_lowpass_hz = 75;
versionFilterDefaults.dterm_lowpass_dyn_min_hz = 75;
versionFilterDefaults.dterm_lowpass_dyn_max_hz = 150;
}

View File

@ -1503,7 +1503,6 @@ MspHelper.prototype.process_data = function(dataHandler) {
FC.TUNING_SLIDERS.slider_dterm_filter_multiplier = data.readU8();
FC.TUNING_SLIDERS.slider_gyro_filter = data.readU8();
FC.TUNING_SLIDERS.slider_gyro_filter_multiplier = data.readU8();
break;
case MSPCodes.MSP_SET_VTXTABLE_POWERLEVEL:
@ -1569,7 +1568,7 @@ MspHelper.prototype.process_data = function(dataHandler) {
console.log('Name set');
break;
case MSPCodes.MSP_SET_FILTER_CONFIG:
console.log('Filter config set');
// removed as this fires a lot with firmware sliders console.log('Filter config set');
break;
case MSPCodes.MSP_SET_ADVANCED_CONFIG:
console.log('Advanced config parameters set');
@ -2315,19 +2314,20 @@ MspHelper.prototype.crunch = function(code) {
break;
case MSPCodes.MSP_SET_TUNING_SLIDERS:
buffer.push8(FC.TUNING_SLIDERS.slider_pids_mode)
.push8(FC.TUNING_SLIDERS.slider_master_multiplier)
.push8(FC.TUNING_SLIDERS.slider_roll_pitch_ratio)
.push8(FC.TUNING_SLIDERS.slider_i_gain)
.push8(FC.TUNING_SLIDERS.slider_d_gain)
.push8(FC.TUNING_SLIDERS.slider_pi_gain)
.push8(FC.TUNING_SLIDERS.slider_dmax_gain)
.push8(FC.TUNING_SLIDERS.slider_feedforward_gain)
.push8(FC.TUNING_SLIDERS.slider_pitch_pi_gain)
.push8(FC.TUNING_SLIDERS.slider_dterm_filter)
.push8(FC.TUNING_SLIDERS.slider_dterm_filter_multiplier)
.push8(FC.TUNING_SLIDERS.slider_gyro_filter)
.push8(FC.TUNING_SLIDERS.slider_gyro_filter_multiplier);
buffer
.push8(FC.TUNING_SLIDERS.slider_pids_mode)
.push8(FC.TUNING_SLIDERS.slider_master_multiplier)
.push8(FC.TUNING_SLIDERS.slider_roll_pitch_ratio)
.push8(FC.TUNING_SLIDERS.slider_i_gain)
.push8(FC.TUNING_SLIDERS.slider_d_gain)
.push8(FC.TUNING_SLIDERS.slider_pi_gain)
.push8(FC.TUNING_SLIDERS.slider_dmax_gain)
.push8(FC.TUNING_SLIDERS.slider_feedforward_gain)
.push8(FC.TUNING_SLIDERS.slider_pitch_pi_gain)
.push8(FC.TUNING_SLIDERS.slider_dterm_filter)
.push8(FC.TUNING_SLIDERS.slider_dterm_filter_multiplier)
.push8(FC.TUNING_SLIDERS.slider_gyro_filter)
.push8(FC.TUNING_SLIDERS.slider_gyro_filter_multiplier);
break;
default:

View File

@ -235,6 +235,7 @@ TABS.pid_tuning.initialize = function (callback) {
$('.pid_filter select[name="gyroLowpassType"]').val(FC.FILTER_CONFIG.gyro_lowpass_type);
$('.pid_filter select[name="gyroLowpass2Type"]').val(FC.FILTER_CONFIG.gyro_lowpass2_type);
$('.pid_filter input[name="dtermLowpass2Frequency"]').val(FC.FILTER_CONFIG.dterm_lowpass2_hz);
$('.pid_filter select[name="dtermLowpass2Type"]').val(FC.FILTER_CONFIG.dterm_lowpass2_type);
// We load it again because the limits are now bigger than in 1.16.0
$('.pid_filter input[name="gyroLowpassFrequency"]').attr("max","16000");
@ -343,16 +344,13 @@ TABS.pid_tuning.initialize = function (callback) {
$('select[id="throttleLimitType"]').val(FC.RC_TUNING.throttleLimitType);
$('.throttle_limit input[name="throttleLimitPercent"]').val(FC.RC_TUNING.throttleLimitPercent);
$('.pid_filter select[name="dtermLowpass2Type"]').val(FC.FILTER_CONFIG.dterm_lowpass2_type);
$('.pid_filter input[name="gyroLowpassDynMinFrequency"]').val(FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz);
$('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').val(FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz);
$('.pid_filter select[name="gyroLowpassDynType"]').val(FC.FILTER_CONFIG.gyro_lowpass_type);
$('.pid_filter input[name="dtermLowpassDynMinFrequency"]').val(FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz);
$('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').val(FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz);
$('.pid_filter select[name="dtermLowpassDynType"]').val(FC.FILTER_CONFIG.dterm_lowpass_type);
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
$('.pid_filter input[name="dtermLowpassDynExpo"]').val(FC.FILTER_CONFIG.dyn_lpf_curve_expo);
}
$('.pid_tuning input[name="dMinRoll"]').val(FC.ADVANCED_TUNING.dMinRoll);
$('.pid_tuning input[name="dMinPitch"]').val(FC.ADVANCED_TUNING.dMinPitch);
@ -366,8 +364,8 @@ TABS.pid_tuning.initialize = function (callback) {
} else {
$('.throttle_limit').hide();
$('.gyroLowpassDyn').hide();
$('.dtermLowpassDyn').hide();
$('.gyroLowpassDynLegacy').hide();
$('.dtermLowpassDynLegacy').hide();
$('.dtermLowpass2TypeGroup').hide();
$('.dminGroup').hide();
@ -444,7 +442,7 @@ TABS.pid_tuning.initialize = function (callback) {
dynamicNotchQ_e.val(FC.FILTER_CONFIG.dyn_notch_q);
dynamicNotchWidthPercent_e.val(FC.FILTER_CONFIG.dyn_notch_width_percent);
}
$('.rpmFilter span.suboption').toggle(checked);
}).prop('checked', FC.FILTER_CONFIG.gyro_rpm_notch_harmonics != 0).change();
} else {
@ -488,7 +486,19 @@ TABS.pid_tuning.initialize = function (callback) {
$('.rates_type').hide();
}
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
// hide legacy filter switches
$('.gyroLowpassLegacy').hide();
$('.gyroLowpassDynLegacy').hide();
$('.dtermLowpassLegacy').hide();
$('.dtermLowpassDynLegacy').hide();
$('.pid_filter input[name="dtermLowpassExpo"]').val(FC.FILTER_CONFIG.dyn_lpf_curve_expo);
} else {
// hide firmware filter switches
$('.gyroLowpass').hide();
$('.dtermLowpass').hide();
// Previous html attributes for legacy sliders
$('.pid_tuning .ROLL input[name="p"]').attr("max", "200");
$('.pid_tuning .ROLL input[name="i"]').attr("max", "200");
@ -684,6 +694,8 @@ TABS.pid_tuning.initialize = function (callback) {
$('.pid_filter input[name="gyroNotch1Frequency"]').val(checked ? hz : 0).attr('disabled', !checked)
.attr("min", checked ? 1 : 0).change();
$('.pid_filter input[name="gyroNotch1Cutoff"]').attr('disabled', !checked).change();
$('.gyroNotch1 span.suboption').toggle(checked);
});
$('input[id="gyroNotch2Enabled"]').change(function() {
@ -693,6 +705,8 @@ TABS.pid_tuning.initialize = function (callback) {
$('.pid_filter input[name="gyroNotch2Frequency"]').val(checked ? hz : 0).attr('disabled', !checked)
.attr("min", checked ? 1 : 0).change();
$('.pid_filter input[name="gyroNotch2Cutoff"]').attr('disabled', !checked).change();
$('.gyroNotch2 span.suboption').toggle(checked);
});
$('input[id="dtermNotchEnabled"]').change(function() {
@ -702,114 +716,354 @@ TABS.pid_tuning.initialize = function (callback) {
$('.pid_filter input[name="dTermNotchFrequency"]').val(checked ? hz : 0).attr('disabled', !checked)
.attr("min", checked ? 1 : 0).change();
$('.pid_filter input[name="dTermNotchCutoff"]').attr('disabled', !checked).change();
$('.dtermNotch span.suboption').toggle(checked);
});
$('input[id="gyroLowpassEnabled"]').change(function() {
const checked = $(this).is(':checked');
const disabledByDynamicLowpass = $('input[id="gyroLowpassDynEnabled"]').is(':checked');
// gyro filter selectors
const gyroLowpassDynMinFrequency = $('.pid_filter input[name="gyroLowpassDynMinFrequency"]');
const gyroLowpassDynMaxFrequency = $('.pid_filter input[name="gyroLowpassDynMaxFrequency"]');
const gyroLowpassFrequency = $('.pid_filter input[name="gyroLowpassFrequency"]');
const gyroLowpass2Frequency = $('.pid_filter input[name="gyroLowpass2Frequency"]');
const gyroLowpassType = $('.pid_filter select[name="gyroLowpassType"]');
const gyroLowpass2Type = $('.pid_filter select[name="gyroLowpass2Type"]');
const gyroLowpassDynType = $('.pid_filter select[name="gyroLowpassDynType"]');
const cutoff = FC.FILTER_CONFIG.gyro_lowpass_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass_hz : FILTER_DEFAULT.gyro_lowpass_hz;
const type = FC.FILTER_CONFIG.gyro_lowpass_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass_type : FILTER_DEFAULT.gyro_lowpass_type;
const gyroLowpassDynEnabled = $('.pid_filter input[id="gyroLowpassDynEnabled"]');
const gyroLowpassEnabled = $('.pid_filter input[id="gyroLowpassEnabled"]');
const gyroLowpass2Enabled = $('.pid_filter input[id="gyroLowpass2Enabled"]');
$('.pid_filter input[name="gyroLowpassFrequency"]').val((checked || disabledByDynamicLowpass) ? cutoff : 0).attr('disabled', !checked);
$('.pid_filter select[name="gyroLowpassType"]').val(type).attr('disabled', !checked);
const gyroLowpassOption = $('.gyroLowpass span.suboption');
const gyroLowpassOptionStatic = $('.gyroLowpass span.suboption.static');
const gyroLowpassOptionDynamic = $('.gyroLowpass span.suboption.dynamic');
const gyroLowpass2Option = $('.gyroLowpass2 span.suboption');
if (checked) {
$('input[id="gyroLowpassDynEnabled"]').prop('checked', false).change();
}
self.updateFilterWarning();
});
const gyroLowpassFilterMode = $('.pid_filter select[name="gyroLowpassFilterMode"]');
$('input[id="gyroLowpassDynEnabled"]').change(function() {
const checked = $(this).is(':checked');
let cutoff_min = FILTER_DEFAULT.gyro_lowpass_dyn_min_hz;
let type = FILTER_DEFAULT.gyro_lowpass_type;
if (FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz < FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz) {
cutoff_min = FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz;
type = FC.FILTER_CONFIG.gyro_lowpass_type;
}
// dterm filter selectors
const dtermLowpassDynMinFrequency = $('.pid_filter input[name="dtermLowpassDynMinFrequency"]');
const dtermLowpassDynMaxFrequency = $('.pid_filter input[name="dtermLowpassDynMaxFrequency"]');
const dtermLowpassFrequency = $('.pid_filter input[name="dtermLowpassFrequency"]');
const dtermLowpass2Frequency = $('.pid_filter input[name="dtermLowpass2Frequency"]');
const dtermLowpassType = $('.pid_filter select[name="dtermLowpassType"]');
const dtermLowpass2Type = $('.pid_filter select[name="dtermLowpass2Type"]');
const dtermLowpassDynType = $('.pid_filter select[name="dtermLowpassDynType"]');
$('.pid_filter input[name="gyroLowpassDynMinFrequency"]').val(checked ? cutoff_min : 0).attr('disabled', !checked);
$('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').attr('disabled', !checked);
$('.pid_filter select[name="gyroLowpassDynType"]').val(type).attr('disabled', !checked);
const dtermLowpassDynEnabled = $('.pid_filter input[id="dtermLowpassDynEnabled"]');
const dtermLowpassEnabled = $('input[id="dtermLowpassEnabled"]');
const dtermLowpass2Enabled = $('input[id="dtermLowpass2Enabled"]');
if (checked) {
$('input[id="gyroLowpassEnabled"]').prop('checked', false).change();
} else if (FC.FILTER_CONFIG.gyro_lowpass_hz > 0 && !$('input[id="gyroLowpassEnabled"]').is(':checked')) {
$('input[id="gyroLowpassEnabled"]').prop('checked', true).change();
}
self.updateFilterWarning();
});
const dtermLowpassOption = $('.dtermLowpass span.suboption');
const dtermLowpassOptionStatic = $('.dtermLowpass span.suboption.static');
const dtermLowpassOptionDynamic = $('.dtermLowpass span.suboption.dynamic');
const dtermLowpass2Option = $('.dtermLowpass2 span.suboption');
$('input[id="gyroLowpass2Enabled"]').change(function() {
const checked = $(this).is(':checked');
const cutoff = FC.FILTER_CONFIG.gyro_lowpass2_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass2_hz : FILTER_DEFAULT.gyro_lowpass2_hz;
const type = FC.FILTER_CONFIG.gyro_lowpass2_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass2_type : FILTER_DEFAULT.gyro_lowpass2_type;
const dtermLowpassFilterMode = $('.pid_filter select[name="dtermLowpassFilterMode"]');
$('.pid_filter input[name="gyroLowpass2Frequency"]').val(checked ? cutoff : 0).attr('disabled', !checked);
$('.pid_filter select[name="gyroLowpass2Type"]').val(type).attr('disabled', !checked);
});
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
$('input[id="dtermLowpassEnabled"]').change(function() {
const checked = $(this).is(':checked');
const disabledByDynamicLowpass = $('input[id="dtermLowpassDynEnabled"]').is(':checked');
// Legacy filter selectors for lowpass 1 and 2
gyroLowpassEnabled.change(function() {
const checked = $(this).is(':checked');
const disabledByDynamicLowpass = gyroLowpassDynEnabled.is(':checked');
const cutoff = FC.FILTER_CONFIG.dterm_lowpass_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass_hz : FILTER_DEFAULT.dterm_lowpass_hz;
const type = FC.FILTER_CONFIG.dterm_lowpass_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass_type : FILTER_DEFAULT.dterm_lowpass_type;
const cutoff = FC.FILTER_CONFIG.gyro_lowpass_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass_hz : FILTER_DEFAULT.gyro_lowpass_hz;
const type = FC.FILTER_CONFIG.gyro_lowpass_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass_type : FILTER_DEFAULT.gyro_lowpass_type;
$('.pid_filter input[name="dtermLowpassFrequency"]').val((checked || disabledByDynamicLowpass) ? cutoff : 0).attr('disabled', !checked);
$('.pid_filter select[name="dtermLowpassType"]').val(type).attr('disabled', !checked);
gyroLowpassFrequency.val((checked || disabledByDynamicLowpass) ? cutoff : 0).attr('disabled', !checked);
gyroLowpassType.each((i, el) => $(el).val(type).attr('disabled', !checked));
if (checked) {
$('input[id="dtermLowpassDynEnabled"]').prop('checked', false).change();
}
self.updateFilterWarning();
});
if (checked) {
gyroLowpassDynEnabled.prop('checked', false).change();
}
self.updateFilterWarning();
});
$('.dynLpfCurveExpo').toggle(semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44));
$('input[id="dtermLowpassDynEnabled"]').change(function() {
const checked = $(this).is(':checked');
let cutoff_min = FILTER_DEFAULT.dterm_lowpass_dyn_min_hz;
let type = FILTER_DEFAULT.dterm_lowpass_type;
if (FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz < FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz) {
cutoff_min = FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz;
type = FC.FILTER_CONFIG.dterm_lowpass_type;
}
gyroLowpassDynEnabled.change(function() {
const checked = $(this).is(':checked');
let cutoff_min = FILTER_DEFAULT.gyro_lowpass_dyn_min_hz;
let type = FILTER_DEFAULT.gyro_lowpass_type;
if (FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz < FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz) {
cutoff_min = FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz;
type = FC.FILTER_CONFIG.gyro_lowpass_type;
}
$('.pid_filter input[name="dtermLowpassDynMinFrequency"]').val(checked ? cutoff_min : 0).attr('disabled', !checked);
$('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').attr('disabled', !checked);
$('.pid_filter select[name="dtermLowpassDynType"]').val(type).attr('disabled', !checked);
gyroLowpassDynMinFrequency.val(checked ? cutoff_min : 0).attr('disabled', !checked);
gyroLowpassDynMaxFrequency.attr('disabled', !checked);
gyroLowpassDynType.each((i, el) => $(el).val(type).attr('disabled', !checked));
if (checked) {
$('input[id="dtermLowpassEnabled"]').prop('checked', false).change();
} else if (FC.FILTER_CONFIG.dterm_lowpass_hz > 0 && !$('input[id="dtermLowpassEnabled"]').is(':checked')) {
$('input[id="dtermLowpassEnabled"]').prop('checked', true).change();
$('.pid_filter input[id="dtermLowpassDynExpoEnabled"]').prop('checked', false).change();
}
self.updateFilterWarning();
});
if (checked) {
gyroLowpassEnabled.prop('checked', false).change();
} else if (FC.FILTER_CONFIG.gyro_lowpass_hz > 0 && !gyroLowpassEnabled.is(':checked')) {
gyroLowpassEnabled.prop('checked', true).change();
}
self.updateFilterWarning();
});
$('input[id="dtermLowpassDynExpoEnabled"]').change(function() {
const checked = $(this).is(':checked');
const curveExpo = FC.FILTER_CONFIG.dyn_lpf_curve_expo > 0 ? FC.FILTER_CONFIG.dyn_lpf_curve_expo : FILTER_DEFAULT.dyn_lpf_curve_expo;
gyroLowpass2Enabled.change(function() {
const checked = $(this).is(':checked');
const cutoff = FC.FILTER_CONFIG.gyro_lowpass2_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass2_hz : FILTER_DEFAULT.gyro_lowpass2_hz;
const type = FC.FILTER_CONFIG.gyro_lowpass2_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass2_type : FILTER_DEFAULT.gyro_lowpass2_type;
$('.pid_filter input[name="dtermLowpassDynExpo"]').val(checked ? curveExpo : 0).attr('disabled', !checked);
});
gyroLowpass2Frequency.val(checked ? cutoff : 0).attr('disabled', !checked);
gyroLowpass2Type.each((i, el) => $(el).val(type).attr('disabled', !checked));
});
$('input[id="dtermLowpass2Enabled"]').change(function() {
const checked = $(this).is(':checked');
const cutoff = FC.FILTER_CONFIG.dterm_lowpass2_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass2_hz : FILTER_DEFAULT.dterm_lowpass2_hz;
const type = FC.FILTER_CONFIG.dterm_lowpass2_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass2_type : FILTER_DEFAULT.dterm_lowpass2_type;
dtermLowpassEnabled.change(function() {
const checked = $(this).is(':checked');
const disabledByDynamicLowpass = dtermLowpassDynEnabled.is(':checked');
$('.pid_filter input[name="dtermLowpass2Frequency"]').val(checked ? cutoff : 0).attr('disabled', !checked);
$('.pid_filter select[name="dtermLowpass2Type"]').val(type).attr('disabled', !checked);
});
const cutoff = FC.FILTER_CONFIG.dterm_lowpass_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass_hz : FILTER_DEFAULT.dterm_lowpass_hz;
const type = FC.FILTER_CONFIG.dterm_lowpass_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass_type : FILTER_DEFAULT.dterm_lowpass_type;
dtermLowpassFrequency.val((checked || disabledByDynamicLowpass) ? cutoff : 0).attr('disabled', !checked);
dtermLowpassType.each((i, el) => $(el).val(type).attr('disabled', !checked));
if (checked) {
dtermLowpassDynEnabled.prop('checked', false).change();
}
self.updateFilterWarning();
});
dtermLowpassDynEnabled.change(function() {
const checked = $(this).is(':checked');
let cutoff_min = FILTER_DEFAULT.dterm_lowpass_dyn_min_hz;
let type = FILTER_DEFAULT.dterm_lowpass_type;
if (FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz < FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz) {
cutoff_min = FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz;
type = FC.FILTER_CONFIG.dterm_lowpass_type;
}
dtermLowpassDynMinFrequency.val(checked ? cutoff_min : 0).attr('disabled', !checked);
dtermLowpassDynMaxFrequency.attr('disabled', !checked);
dtermLowpassDynType.each((i, el) => $(el).val(type).attr('disabled', !checked));
if (checked) {
dtermLowpassEnabled.prop('checked', false).change();
} else if (FC.FILTER_CONFIG.dterm_lowpass_hz > 0 && !dtermLowpassEnabled.is(':checked')) {
dtermLowpassEnabled.prop('checked', true).change();
}
self.updateFilterWarning();
});
dtermLowpass2Enabled.change(function() {
const checked = $(this).is(':checked');
const cutoff = FC.FILTER_CONFIG.dterm_lowpass2_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass2_hz : FILTER_DEFAULT.dterm_lowpass2_hz;
const type = FC.FILTER_CONFIG.dterm_lowpass2_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass2_type : FILTER_DEFAULT.dterm_lowpass2_type;
dtermLowpass2Frequency.val(checked ? cutoff : 0).attr('disabled', !checked);
dtermLowpass2Type.each((i, el) => $(el).val(type).attr('disabled', !checked));
});
} else {
// firmware 4.3 filter selectors for lowpass 1 and 2
gyroLowpassEnabled.change(function() {
const checked = $(this).is(':checked');
let cutoffMin = FILTER_DEFAULT.gyro_lowpass_dyn_min_hz;
let cutoffMax = FILTER_DEFAULT.gyro_lowpass_dyn_max_hz;
if (FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz > 0 || FC.FILTER_CONFIG.gyro_lowpass_hz > 0) {
// lowpass1 is enabled, set the master switch on, show the label, mode selector and type fields
if (checked) {
gyroLowpassFilterMode.val(FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz > 0 ? 1 : 0).change();
} else {
// the user is disabling Lowpass 1 so set everything to zero
gyroLowpassDynMinFrequency.val(0);
gyroLowpassDynMaxFrequency.val(0);
gyroLowpassFrequency.val(0);
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz = 0;
FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz = 0;
FC.FILTER_CONFIG.gyro_lowpass_hz = 0;
TuningSliders.updateFiltersInFirmware();
}
} else {
// lowpass 1 is disabled, set the master switch off, only show label
if (checked) {
// user is trying to enable the lowpass filter, but it was off (both cutoffs are zero)
// initialise in dynamic mode with values at sliders, or use defaults
gyroLowpassFilterMode.val(1).change();
cutoffMin = Math.floor(cutoffMin * TuningSliders.sliderGyroFilterMultiplier);
cutoffMax = Math.floor(cutoffMax * TuningSliders.sliderGyroFilterMultiplier);
gyroLowpassDynMinFrequency.val(cutoffMin);
gyroLowpassDynMaxFrequency.val(cutoffMax);
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz = cutoffMin;
FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz = cutoffMax;
TuningSliders.updateFiltersInFirmware();
}
}
gyroLowpassOption.toggle(checked);
gyroLowpassOptionStatic.toggle(FC.FILTER_CONFIG.gyro_lowpass_hz !== 0);
gyroLowpassOptionDynamic.toggle(FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz !== 0);
});
gyroLowpassFilterMode.change(function() {
const dynMode = parseInt($(this).val());
let cutoff = FILTER_DEFAULT.gyro_lowpass_hz;
let cutoffMin = FILTER_DEFAULT.gyro_lowpass_dyn_min_hz;
let cutoffMax = FILTER_DEFAULT.gyro_lowpass_dyn_max_hz;
if (dynMode) {
// dynamic mode, set the static field min to zero
gyroLowpassFrequency.val(0);
FC.FILTER_CONFIG.gyro_lowpass_hz = 0;
// if dyn min is zero, set dyn min to sliders or default
if (!FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz) {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44) && TuningSliders.sliderGyroFilter) {
cutoffMin = Math.floor(cutoffMin * TuningSliders.sliderGyroFilterMultiplier);
cutoffMax = Math.floor(cutoffMax * TuningSliders.sliderGyroFilterMultiplier);
}
gyroLowpassDynMinFrequency.val(cutoffMin);
gyroLowpassDynMaxFrequency.val(cutoffMax);
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz = cutoffMin;
FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz = cutoffMax;
}
gyroLowpassOptionStatic.hide();
gyroLowpassOptionDynamic.show();
TuningSliders.updateFiltersInFirmware();
} else {
// static, set the dynamic field min to zero
gyroLowpassDynMinFrequency.val(0);
gyroLowpassDynMaxFrequency.val(0);
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz = 0;
FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz = 0;
// If static is zero, set the dynamic cutoff field according to sliders or default
if (!FC.FILTER_CONFIG.gyro_lowpass_hz) {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44) && TuningSliders.sliderGyroFilter) {
cutoff = Math.floor(FILTER_DEFAULT.gyro_lowpass_hz * TuningSliders.sliderGyroFilterMultiplier);
}
gyroLowpassFrequency.val(cutoff);
FC.FILTER_CONFIG.gyro_lowpass_hz = cutoff;
}
gyroLowpassOptionStatic.show();
gyroLowpassOptionDynamic.hide();
TuningSliders.updateFiltersInFirmware();
}
});
// switch gyro lpf2
gyroLowpass2Enabled.change(function() {
const checked = $(this).is(':checked');
let cutoff = FC.FILTER_CONFIG.gyro_lowpass2_hz > 0 ? FC.FILTER_CONFIG.gyro_lowpass2_hz : FILTER_DEFAULT.gyro_lowpass2_hz;
if (TuningSliders.sliderGyroFilter) {
cutoff = checked ? Math.floor(FILTER_DEFAULT.gyro_lowpass2_hz * TuningSliders.sliderGyroFilterMultiplier) : 0;
FC.FILTER_CONFIG.gyro_lowpass2_hz = cutoff;
TuningSliders.updateFiltersInFirmware();
}
gyroLowpass2Frequency.val(checked ? cutoff : 0).attr('disabled', !checked);
gyroLowpass2Option.toggle(checked);
self.updateFilterWarning();
});
dtermLowpassEnabled.change(function() {
const checked = $(this).is(':checked');
let cutoffMin = FILTER_DEFAULT.dterm_lowpass_dyn_min_hz;
let cutoffMax = FILTER_DEFAULT.dterm_lowpass_dyn_max_hz;
if (FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz > 0 || FC.FILTER_CONFIG.dterm_lowpass_hz > 0) {
// lowpass1 is enabled, set the master switch on, show the label, mode selector and type fields
if (checked) {
dtermLowpassFilterMode.val(FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz > 0 ? 1 : 0).change();
} else {
// the user is disabling Lowpass 1 so set everything to zero
dtermLowpassDynMinFrequency.val(0);
dtermLowpassDynMaxFrequency.val(0);
dtermLowpassFrequency.val(0);
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz = 0;
FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz = 0;
FC.FILTER_CONFIG.dterm_lowpass_hz = 0;
TuningSliders.updateFiltersInFirmware();
}
} else {
// lowpass 1 is disabled, set the master switch off, only show label
if (checked) {
// user is trying to enable the lowpass filter, but it was off (both cutoffs are zero)
// initialise in dynamic mode with values at sliders, or use defaults
dtermLowpassFilterMode.val(1).change();
if (TuningSliders.sliderDTermFilter) {
cutoffMin = Math.floor(cutoffMin * TuningSliders.sliderDTermFilterMultiplier);
cutoffMax = Math.floor(cutoffMax * TuningSliders.sliderDTermFilterMultiplier);
}
dtermLowpassDynMinFrequency.val(cutoffMin);
dtermLowpassDynMaxFrequency.val(cutoffMax);
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz = cutoffMin;
FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz = cutoffMax;
TuningSliders.updateFiltersInFirmware();
}
}
dtermLowpassOption.toggle(checked);
dtermLowpassOptionStatic.toggle(FC.FILTER_CONFIG.dterm_lowpass_hz !== 0);
dtermLowpassOptionDynamic.toggle(FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz !== 0);
});
dtermLowpassFilterMode.change(function() {
const dynMode = parseInt($(this).val());
let cutoff = FILTER_DEFAULT.dterm_lowpass_hz;
let cutoffMin = FILTER_DEFAULT.dterm_lowpass_dyn_min_hz;
let cutoffMax = FILTER_DEFAULT.dterm_lowpass_dyn_max_hz;
if (dynMode) {
// dynamic mode, set the static field min to zero
dtermLowpassFrequency.val(0);
FC.FILTER_CONFIG.dterm_lowpass_hz = 0;
// if dyn min is zero, set dyn min to sliders or default
if (!FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz) {
if (TuningSliders.sliderDTermFilter) {
cutoffMin = Math.floor(cutoffMin * TuningSliders.sliderDTermFilterMultiplier);
cutoffMax = Math.floor(cutoffMax * TuningSliders.sliderDTermFilterMultiplier);
}
dtermLowpassDynMinFrequency.val(cutoffMin);
dtermLowpassDynMaxFrequency.val(cutoffMax);
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz = cutoffMin;
FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz = cutoffMax;
}
dtermLowpassOptionStatic.hide();
dtermLowpassOptionDynamic.show();
TuningSliders.updateFiltersInFirmware();
} else {
// static, set the dynamic field min to zero
dtermLowpassDynMinFrequency.val(0);
dtermLowpassDynMaxFrequency.val(0);
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz = 0;
FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz = 0;
// If static is zero, set the dynamic cutoff field according to sliders or default
if (!FC.FILTER_CONFIG.dterm_lowpass_hz) {
if (TuningSliders.sliderDTermFilter) {
cutoff = Math.floor(FILTER_DEFAULT.dterm_lowpass_hz * TuningSliders.sliderDTermFilterMultiplier);
}
dtermLowpassFrequency.val(cutoff);
FC.FILTER_CONFIG.dterm_lowpass_hz = cutoff;
}
dtermLowpassOptionStatic.show();
dtermLowpassOptionDynamic.hide();
TuningSliders.updateFiltersInFirmware();
}
});
dtermLowpass2Enabled.change(function() {
const checked = $(this).is(':checked');
let cutoff = FC.FILTER_CONFIG.dterm_lowpass2_hz > 0 ? FC.FILTER_CONFIG.dterm_lowpass2_hz : FILTER_DEFAULT.dterm_lowpass2_hz;
if (TuningSliders.sliderDTermFilter) {
cutoff = checked ? Math.floor(FILTER_DEFAULT.dterm_lowpass2_hz * TuningSliders.sliderDTermFilterMultiplier) : 0;
FC.FILTER_CONFIG.dterm_lowpass2_hz = cutoff;
TuningSliders.updateFiltersInFirmware();
}
dtermLowpass2Frequency.val(checked ? cutoff : 0).attr('disabled', !checked);
dtermLowpass2Option.toggle(checked);
self.updateFilterWarning();
});
}
$('input[id="yawLowpassEnabled"]').change(function() {
const checked = $(this).is(':checked');
const cutoff = FC.FILTER_CONFIG.yaw_lowpass_hz > 0 ? FC.FILTER_CONFIG.yaw_lowpass_hz : FILTER_DEFAULT.yaw_lowpass_hz;
$('.pid_filter input[name="yawLowpassFrequency"]').val(checked ? cutoff : 0).attr('disabled', !checked);
$('.yawLowpass span.suboption').toggle(checked);
});
// The notch cutoff must be smaller than the notch frecuency
@ -838,17 +1092,37 @@ TABS.pid_tuning.initialize = function (callback) {
}).change();
// Initial state of the filters: enabled or disabled
$('input[id="gyroNotch1Enabled"]').prop('checked', FC.FILTER_CONFIG.gyro_notch_hz != 0).change();
$('input[id="gyroNotch2Enabled"]').prop('checked', FC.FILTER_CONFIG.gyro_notch2_hz != 0).change();
$('input[id="dtermNotchEnabled"]').prop('checked', FC.FILTER_CONFIG.dterm_notch_hz != 0).change();
$('input[id="gyroLowpassEnabled"]').prop('checked', FC.FILTER_CONFIG.gyro_lowpass_hz != 0).change();
$('input[id="gyroLowpassDynEnabled"]').prop('checked', FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz != 0 && FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz < FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz).change();
$('input[id="dtermLowpassDynExpoEnabled"]').prop('checked', FC.FILTER_CONFIG.dyn_lpf_curve_expo != 0).change();
$('input[id="gyroLowpass2Enabled"]').prop('checked', FC.FILTER_CONFIG.gyro_lowpass2_hz != 0).change();
$('input[id="dtermLowpassEnabled"]').prop('checked', FC.FILTER_CONFIG.dterm_lowpass_hz != 0).change();
$('input[id="dtermLowpassDynEnabled"]').prop('checked', FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz != 0 && FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz < FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz).change();
$('input[id="dtermLowpass2Enabled"]').prop('checked', FC.FILTER_CONFIG.dterm_lowpass2_hz != 0).change();
$('input[id="yawLowpassEnabled"]').prop('checked', FC.FILTER_CONFIG.yaw_lowpass_hz != 0).change();
$('input[id="gyroNotch1Enabled"]').prop('checked', FC.FILTER_CONFIG.gyro_notch_hz !== 0).change();
$('input[id="gyroNotch2Enabled"]').prop('checked', FC.FILTER_CONFIG.gyro_notch2_hz !== 0).change();
$('input[id="dtermNotchEnabled"]').prop('checked', FC.FILTER_CONFIG.dterm_notch_hz !== 0).change();
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
gyroLowpassEnabled.prop('checked', FC.FILTER_CONFIG.gyro_lowpass_hz !== 0 || FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz !== 0).change();
dtermLowpassEnabled.prop('checked', FC.FILTER_CONFIG.dterm_lowpass_hz !== 0 || FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz !== 0).change();
if (FC.FILTER_CONFIG.gyro_lowpass_hz > 0) {
gyroLowpassFilterMode.val(0).change();
} else if (FC.FILTER_CONFIG.gyroLowpassDynMinFrequency > 0) {
gyroLowpassFilterMode.val(1).change();
}
if (FC.FILTER_CONFIG.dterm_lowpass_hz > 0) {
dtermLowpassFilterMode.val(0).change();
} else if (FC.FILTER_CONFIG.dtermLowpassDynMinFrequency > 0) {
dtermLowpassFilterMode.val(1).change();
}
} else {
gyroLowpassEnabled.prop('checked', FC.FILTER_CONFIG.gyro_lowpass_hz !== 0).change();
gyroLowpassDynEnabled.prop('checked', FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz !== 0 &&
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz < FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz).change();
dtermLowpassEnabled.prop('checked', FC.FILTER_CONFIG.dterm_lowpass_hz !== 0).change();
dtermLowpassDynEnabled.prop('checked', FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz !== 0 &&
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz < FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz).change();
}
gyroLowpass2Enabled.prop('checked', FC.FILTER_CONFIG.gyro_lowpass2_hz !== 0).change();
dtermLowpass2Enabled.prop('checked', FC.FILTER_CONFIG.dterm_lowpass2_hz !== 0).change();
$('input[id="yawLowpassEnabled"]').prop('checked', FC.FILTER_CONFIG.yaw_lowpass_hz !== 0).change();
self.updatePIDColors();
}
@ -977,7 +1251,7 @@ TABS.pid_tuning.initialize = function (callback) {
}
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_36)) {
FC.FILTER_CONFIG.dterm_lowpass_type = $('.pid_filter select[name="dtermLowpassType"]').val();
FC.FILTER_CONFIG.dterm_lowpass_type = parseInt($('.pid_filter select[name="dtermLowpassType"]').val());
FC.ADVANCED_TUNING.itermThrottleThreshold = parseInt($('.antigravity input[name="itermThrottleThreshold"]').val());
FC.ADVANCED_TUNING.itermAcceleratorGain = parseInt($('.antigravity input[name="itermAcceleratorGain"]').val() * 1000);
}
@ -987,6 +1261,7 @@ TABS.pid_tuning.initialize = function (callback) {
FC.FILTER_CONFIG.gyro_lowpass_type = parseInt($('.pid_filter select[name="gyroLowpassType"]').val());
FC.FILTER_CONFIG.gyro_lowpass2_type = parseInt($('.pid_filter select[name="gyroLowpass2Type"]').val());
FC.FILTER_CONFIG.dterm_lowpass2_hz = parseInt($('.pid_filter input[name="dtermLowpass2Frequency"]').val());
FC.FILTER_CONFIG.dterm_lowpass2_type = parseInt($('.pid_filter select[name="dtermLowpass2Type"]').val());
}
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_40)) {
@ -1017,17 +1292,18 @@ TABS.pid_tuning.initialize = function (callback) {
FC.RC_TUNING.throttleLimitType = $('select[id="throttleLimitType"]').val();
FC.RC_TUNING.throttleLimitPercent = parseInt($('.throttle_limit input[name="throttleLimitPercent"]').val());
FC.FILTER_CONFIG.dterm_lowpass2_type = $('.pid_filter select[name="dtermLowpass2Type"]').val();
FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz = parseInt($('.pid_filter input[name="gyroLowpassDynMinFrequency"]').val());
FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz = parseInt($('.pid_filter input[name="gyroLowpassDynMaxFrequency"]').val());
FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz = parseInt($('.pid_filter input[name="dtermLowpassDynMinFrequency"]').val());
FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz = parseInt($('.pid_filter input[name="dtermLowpassDynMaxFrequency"]').val());
if (FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz < FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz ) {
FC.FILTER_CONFIG.gyro_lowpass_type = $('.pid_filter select[name="gyroLowpassDynType"]').val();
}
if (FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz < FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz ) {
FC.FILTER_CONFIG.dterm_lowpass_type = $('.pid_filter select[name="dtermLowpassDynType"]').val();
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
if (FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.gyro_lowpass_dyn_min_hz < FC.FILTER_CONFIG.gyro_lowpass_dyn_max_hz ) {
FC.FILTER_CONFIG.gyro_lowpass_type = $('.pid_filter select[name="gyroLowpassDynType"]').val();
}
if (FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz > 0 && FC.FILTER_CONFIG.dterm_lowpass_dyn_min_hz < FC.FILTER_CONFIG.dterm_lowpass_dyn_max_hz ) {
FC.FILTER_CONFIG.dterm_lowpass_type = $('.pid_filter select[name="dtermLowpassDynType"]').val();
}
}
FC.ADVANCED_TUNING.dMinRoll = parseInt($('.pid_tuning input[name="dMinRoll"]').val());
@ -1070,15 +1346,14 @@ TABS.pid_tuning.initialize = function (callback) {
FC.ADVANCED_TUNING.feedforward_averaging = $('select[id="feedforwardAveraging"]').val();
FC.ADVANCED_TUNING.feedforward_smooth_factor = parseInt($('input[name="feedforwardSmoothFactor"]').val());
FC.ADVANCED_TUNING.feedforward_boost = parseInt($('input[name="feedforwardBoost"]').val());
FC.ADVANCED_TUNING.feedforward_max_rate_limit = parseInt($('input[name="feedforwardMaxRateLimit"]').val());
FC.ADVANCED_TUNING.feedforward_jitter_factor = parseInt($('input[name="feedforwardJitterFactor"]').val());
FC.FILTER_CONFIG.dyn_lpf_curve_expo = parseInt($('.pid_filter input[name="dtermLowpassDynExpo"]').val());
FC.ADVANCED_TUNING.vbat_sag_compensation = $('input[id="vbatSagCompensation"]').is(':checked') ? parseInt($('input[name="vbatSagValue"]').val()) : 0;
FC.ADVANCED_TUNING.thrustLinearization = $('input[id="thrustLinearization"]').is(':checked') ? parseInt($('input[name="thrustLinearValue"]').val()) : 0;
FC.FILTER_CONFIG.dyn_lpf_curve_expo = parseInt($('.pid_filter input[name="dtermLowpassExpo"]').val());
FC.FILTER_CONFIG.dyn_notch_count = parseInt($('.pid_filter input[name="dynamicNotchCount"]').val());
FC.TUNING_SLIDERS.slider_pids_mode = TuningSliders.sliderPidsMode;
//rounds slider values to nearies multiple of 5 and passes to the FW. Avoid dividing calc by (* x 100)/5 = 20
//round slider values to nearest multiple of 5 and passes to the FW. Avoid dividing calc by (* x 100)/5 = 20
FC.TUNING_SLIDERS.slider_master_multiplier = Math.round(TuningSliders.sliderMasterMultiplier * 20) * 5;
FC.TUNING_SLIDERS.slider_d_gain = Math.round(TuningSliders.sliderDGain * 20) * 5;
FC.TUNING_SLIDERS.slider_pi_gain = Math.round(TuningSliders.sliderPIGain * 20) * 5;
@ -1494,11 +1769,14 @@ TABS.pid_tuning.initialize = function (callback) {
}
populateFilterTypeSelector('gyroLowpassType', loadFilterTypeValues());
populateFilterTypeSelector('gyroLowpassDynType', loadFilterTypeValues());
populateFilterTypeSelector('gyroLowpass2Type', loadFilterTypeValues());
populateFilterTypeSelector('dtermLowpassType', loadFilterTypeValues());
populateFilterTypeSelector('dtermLowpass2Type', loadFilterTypeValues());
populateFilterTypeSelector('dtermLowpassDynType', loadFilterTypeValues());
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
populateFilterTypeSelector('gyroLowpassDynType', loadFilterTypeValues());
populateFilterTypeSelector('dtermLowpassDynType', loadFilterTypeValues());
}
pid_and_rc_to_form();
@ -1906,6 +2184,8 @@ TABS.pid_tuning.initialize = function (callback) {
const SLIDER_STEP_UPPER = semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44) ? 0.05 : 0.1;
const sliderPidsModeSelect = $('#sliderPidsModeSelect');
const sliderGyroFilterModeSelect = $('#sliderGyroFilterModeSelect');
const sliderDTermFilterModeSelect = $('#sliderDTermFilterModeSelect');
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
if (self.retainConfiguration) {
@ -1914,6 +2194,8 @@ TABS.pid_tuning.initialize = function (callback) {
self.saveInitialSettings();
}
sliderPidsModeSelect.val(FC.TUNING_SLIDERS.slider_pids_mode);
sliderGyroFilterModeSelect.val(FC.TUNING_SLIDERS.slider_gyro_filter);
sliderDTermFilterModeSelect.val(FC.TUNING_SLIDERS.slider_dterm_filter);
} else {
$('#dMinSwitch').change(function() {
TuningSliders.setDMinFeatureEnabled($(this).is(':checked'));
@ -1936,9 +2218,9 @@ TABS.pid_tuning.initialize = function (callback) {
// disable slides if Integrated Yaw is enabled or Slider PID mode is set to OFF
$('input[id="useIntegratedYaw"]').change(() => TuningSliders.updatePidSlidersDisplay());
// trigger Slider Display update when PID mode is changed
// trigger Slider Display update when PID / Filter mode is changed
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
$('select[id="sliderPidsModeSelect"]').on('change', function () {
sliderPidsModeSelect.on('change', function () {
const setMode = parseInt($(this).val());
TuningSliders.sliderPidsMode = setMode;
@ -1962,6 +2244,30 @@ TABS.pid_tuning.initialize = function (callback) {
}
}).trigger('change');
sliderGyroFilterModeSelect.change(function() {
const mode = parseInt($(this).find(':selected').val());
if (mode === 0) {
TuningSliders.gyroFilterSliderEnable();
} else {
TuningSliders.gyroFilterSliderDisable();
}
});
sliderDTermFilterModeSelect.change(function() {
const mode = parseInt($(this).find(':selected').val());
if (mode === 0) {
TuningSliders.dtermFilterSliderEnable();
} else {
TuningSliders.dtermFilterSliderDisable();
}
});
// initial gyro mode
sliderGyroFilterModeSelect.val(TuningSliders.sliderGyroFilter);
// initial dterm mode
sliderDTermFilterModeSelect.val(TuningSliders.sliderDTermFilter);
}
let allPidTuningSliders;
@ -2170,13 +2476,10 @@ TABS.pid_tuning.initialize = function (callback) {
TuningSliders.calculateNewDTermFilters();
}
});
// enable Filter sliders button
// enable Filter sliders button (legacy sliders)
$('a.buttonFilterTuningSliders').click(function() {
if (TuningSliders.GyroSliderUnavailable) {
//set Slider mode to ON when re-enabling Sliders
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
FC.TUNING_SLIDERS.slider_gyro_filter = 1;
}
// update switchery dynamically based on defaults
$('input[id="gyroLowpassDynEnabled"]').prop('checked', false).click();
$('input[id="gyroLowpassEnabled"]').prop('checked', true).click();
@ -2185,11 +2488,6 @@ TABS.pid_tuning.initialize = function (callback) {
self.analyticsChanges['GyroFilterTuningSlider'] = "On";
}
if (TuningSliders.DTermSliderUnavailable) {
//set Slider mode to ON when re-enabling Sliders
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
FC.TUNING_SLIDERS.slider_dterm_filter = 1;
}
// update switchery dynamically based on defaults
$('input[id="dtermLowpassDynEnabled"]').prop('checked', false).click();
$('input[id="dtermLowpassEnabled"]').prop('checked', true).click();
$('input[id="dtermLowpass2Enabled"]').prop('checked', false).click();
@ -2206,7 +2504,15 @@ TABS.pid_tuning.initialize = function (callback) {
}
});
// update on filter value or type changes
$('.pid_filter tr:not(.newFilter) input, .pid_filter tr:not(.newFilter) select').on('input', function() {
$('.pid_filter tr:not(.newFilter) input, .pid_filter tr:not(.newFilter) select').on('input', function(e) {
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
// because legacy / firmware slider inputs for lowpass1 are duplicate the value isn't updated so set it here.
if (e.target.type === 'number') {
$(`.pid_filter input[name="${e.target.name}"]`).val(e.target.value);
} else if (e.target.type === 'select-one') {
$(`.pid_filter select[name="${e.target.name}"]`).val(e.target.value);
}
}
TuningSliders.updateFilterSlidersDisplay();
if (TuningSliders.GyroSliderUnavailable) {
self.analyticsChanges['GyroFilterTuningSlider'] = "Off";
@ -2747,10 +3053,12 @@ TABS.pid_tuning.updateRatesLabels = function() {
};
TABS.pid_tuning.updateFilterWarning = function() {
const gyroDynamicLowpassEnabled = $('input[id="gyroLowpassDynEnabled"]').is(':checked');
const gyroLowpass1Enabled = $('input[id="gyroLowpassEnabled"]').is(':checked');
const dtermDynamicLowpassEnabled = $('input[id="dtermLowpassDynEnabled"]').is(':checked');
const dtermLowpass1Enabled = $('input[id="dtermLowpassEnabled"]').is(':checked');
const gyroLowpassFilterMode = parseInt($('.pid_filter select[name="gyroLowpassFilterMode"]').val());
const gyroDynamicLowpassEnabled = gyroLowpassFilterMode === 1;
const gyroLowpass1Enabled = !gyroLowpassFilterMode;
const dtermLowpassFilterMode = parseInt($('.pid_filter select[name="dtermLowpassFilterMode"]').val());
const dtermDynamicLowpassEnabled = dtermLowpassFilterMode === 1;
const dtermLowpass1Enabled = !dtermLowpassFilterMode;
const warningE = $('#pid-tuning .filterWarning');
const warningDynamicNotchE = $('#pid-tuning .dynamicNotchWarning');
if (!(gyroDynamicLowpassEnabled || gyroLowpass1Enabled) || !(dtermDynamicLowpassEnabled || dtermLowpass1Enabled)) {

View File

@ -1155,6 +1155,7 @@
</th>
</tr>
</table>
<table class="sliderLabels">
<tr class="xs sliderHeaders">
<td colspan="5">
@ -1169,7 +1170,7 @@
<output type="number" name="sliderGyroFilterMultiplier-number"></output>
</td>
<td colspan="3">
<input type="range" min="0.0" max="2.0" step="0.05" class="tuningSlider" id="sliderGyroFilterMultiplier" />
<input type="range" min="0.1" max="2.0" step="0.05" class="tuningSlider" id="sliderGyroFilterMultiplier" />
</td>
<td>
<div class="helpicon cf_tip" i18n_title="pidTuningGyroFilterSliderHelp"></div>
@ -1180,7 +1181,7 @@
<span i18n="pidTuningDTermFilterSlider"></span>
</td>
</tr>
<tr class="sliderDtermFilter">
<tr class="sliderDTermFilter">
<td class="sm-min">
<span i18n="pidTuningDTermFilterSlider"></span>
</td>
@ -1188,7 +1189,7 @@
<output type="number" name="sliderDTermFilterMultiplier-number"></output>
</td>
<td colspan="3">
<input type="range" min="0.0" max="2.0" step="0.05" class="tuningSlider" id="sliderDTermFilterMultiplier" />
<input type="range" min="0.1" max="2.0" step="0.05" class="tuningSlider" id="sliderDTermFilterMultiplier" />
</td>
<td>
<div class="helpicon cf_tip" i18n_title="pidTuningDTermFilterSliderHelp"></div>
@ -1210,123 +1211,161 @@
<table class="pid_titlebar new_rates">
<tr>
<th i18n="pidTuningNonProfileFilterSettings"></th>
<td>
<select id="sliderGyroFilterModeSelect" class="sliderMode">
<option value="0" i18n="pidTuningOptionOff"></option>
<option value="1" i18n="pidTuningOptionOn"></option>
</select>
</td>
</tr>
</table>
<table class="filterTable">
<table class="filterTable compensation">
<tr>
<th colspan="2">
<div class="pid_mode">
<div i18n="pidTuningGyroLowpassFiltersGroup" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningGyroLowpassFilterHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningGyroLowpassFilterHelp"></div>
</div>
</th>
</tr>
<tr class="gyroLowpassDyn">
<!-- legacy filter switches -->
<tr class="gyroLowpassDynLegacy">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="gyroLowpassDynEnabled" class="toggle" /></span>
<span class="inputValue"><input type="number" name="gyroLowpassDynMinFrequency" step="1" min="1" max="1000"/></span>
</span>
<span class="inputSwitch"><input type="checkbox" id="gyroLowpassDynEnabled" class="toggle" /></span>
</td>
<td>
<div>
<td colspan="2">
<div class="helpicon cf_tip" i18n_title="pidTuningGyroLowpassDyn"></div>
<span i18n="pidTuningGyroLowpassDyn"></span>
<span class="suboption">
<input type="number" name="gyroLowpassDynMinFrequency" step="1" min="1" max="1000"/>
<label>
<span i18n="pidTuningGyroLowpassDynMinFrequency"></span>
</label>
</div>
</td>
</tr>
<tr class="gyroLowpassDyn">
<td>
<span class="inputValue"><input type="number" name="gyroLowpassDynMaxFrequency" step="1" min="1" max="1000"/></span>
</td>
<td>
<div>
</span>
<span class="suboption">
<input type="number" name="gyroLowpassDynMaxFrequency" step="1" min="1" max="1000"/>
<label>
<span i18n="pidTuningGyroLowpassDynMaxFrequency"></span>
</label>
</div>
</td>
</tr>
<tr class="gyroLowpassDyn">
<td>
<select name="gyroLowpassDynType">
<!-- Populated on execution -->
</select>
</td>
<td>
<div>
<label>
<span i18n="pidTuningGyroLowpassDynType"></span>
</label>
</div>
</td>
</tr>
<tr>
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="gyroLowpassEnabled" class="toggle" /></span>
<span class="inputValue"><input type="number" class="nonProfile" name="gyroLowpassFrequency" step="1" min="1" max="255"/></span>
</span>
<span class="suboption">
<select name="gyroLowpassDynType">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningGyroLowpassDynType"></span>
</label>
</span>
</td>
</tr>
<tr class="gyroLowpassLegacy">
<td>
<div>
<span class="inputSwitch"><input type="checkbox" id="gyroLowpassEnabled" class="toggle" /></span>
</td>
<td colspan="2">
<div class="helpicon cf_tip" i18n_title="pidTuningGyroLowpass"></div>
<span i18n="pidTuningGyroLowpass"></span>
<span class="suboption">
<input type="number" class="nonProfile" name="gyroLowpassFrequency" step="1" min="1" max="255"/>
<label>
<span i18n="pidTuningGyroLowpassFrequency"></span>
</label>
</div>
</td>
</tr>
<tr>
<td>
<select name="gyroLowpassType">
<!-- Populated on execution -->
</select>
</td>
<td>
<div>
</span>
<span class="suboption">
<select name="gyroLowpassType">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningGyroLowpassType"></span>
</label>
</div>
</span>
</td>
</tr>
<!-- firmware filter switches -->
<tr class="gyroLowpass">
<td>
<span class="inputSwitch"><input type="checkbox" id="gyroLowpassEnabled" class="toggle" /></span>
</td>
<td colspan="2">
<div class="helpicon cf_tip" i18n_title="pidTuningGyroLowpass"></div>
<span i18n="pidTuningGyroLowpass"></span>
<span class="suboption gyroLowpassFilterModeGroup">
<span class="inputValue">
<select name="gyroLowpassFilterMode">
<option value="0" i18n="pidTuningLowpassStatic"></option>
<option value="1" i18n="pidTuningLowpassDynamic"></option>
</select>
</span>
<label>
<span i18n="pidTuningGyroLowpassMode"></span>
</label>
</span>
<span class="suboption static">
<input type="number" class="nonProfile" name="gyroLowpassFrequency" step="1" min="1" max="255"/>
<label>
<span i18n="pidTuningGyroLowpassFrequency"></span>
</label>
</span>
<span class="suboption dynamic">
<input type="number" name="gyroLowpassDynMinFrequency" step="1" min="1" max="1000"/>
<label>
<span i18n="pidTuningGyroLowpassDynMinFrequency"></span>
</label>
</span>
<span class="suboption dynamic">
<input type="number" name="gyroLowpassDynMaxFrequency" step="1" min="1" max="1000"/>
<label>
<span i18n="pidTuningGyroLowpassDynMaxFrequency"></span>
</label>
</span>
<span class="suboption">
<select name="gyroLowpassType">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningLowpassFilterType"></span>
</label>
</span>
</td>
</tr>
<tr class="gyroLowpass2">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="gyroLowpass2Enabled" class="toggle" /></span>
<span class="inputValue"><input type="number" class="nonProfile" name="gyroLowpass2Frequency" step="1" min="1" max="16000"/></span>
</span>
<span class="inputSwitch"><input type="checkbox" id="gyroLowpass2Enabled" class="toggle" /></span>
</td>
<td>
<div>
<td colspan="2">
<div class="helpicon cf_tip" i18n_title="pidTuningGyroLowpass2"></div>
<span i18n="pidTuningGyroLowpass2"></span>
<span class="suboption">
<input type="number" class="nonProfile" name="gyroLowpass2Frequency" step="1" min="1" max="16000"/>
<label>
<span i18n="pidTuningGyroLowpass2Frequency"></span>
</label>
</div>
</td>
</tr>
</span>
<tr class="gyroLowpass2Type">
<td>
<select name="gyroLowpass2Type">
<!-- Populated on execution -->
</select>
</td>
<td>
<div>
<span class="suboption">
<select name="gyroLowpass2Type">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningGyroLowpass2Type"></span>
<span i18n="pidTuningLowpassFilterType"></span>
</label>
</div>
</span>
</td>
</tr>
@ -1339,107 +1378,88 @@
</th>
</tr>
<tr class="newFilter">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="gyroNotch1Enabled" class="toggle" /></span>
<span class="inputValue"><input type="number" class="nonProfile" name="gyroNotch1Frequency" step="1" min="1" max="16000"/></span>
</span>
</td>
<td>
<div>
<tr class="newFilter gyroNotch1">
<td><span class="inputSwitch"><input type="checkbox" id="gyroNotch1Enabled" class="toggle" /></span></td>
<td colspan="2">
<div class="helpicon cf_tip" i18n_title="pidTuningGyroNotchFilter"></div>
<span i18n="pidTuningGyroNotchFilter"></span>
<span class="suboption">
<input type="number" class="nonProfile" name="gyroNotch1Frequency" step="1" min="1" max="16000"/>
<label>
<span i18n="pidTuningGyroNotch1Frequency"></span>
</label>
</div>
</td>
</tr>
</span>
<tr class="newFilter">
<td>
<input type="number" class="nonProfile" name="gyroNotch1Cutoff" step="1" min="0" max="16000"/>
</td>
<td>
<div>
<span class="suboption">
<input type="number" class="nonProfile" name="gyroNotch1Cutoff" step="1" min="0" max="16000"/>
<label>
<span i18n="pidTuningGyroNotch1Cutoff"></span>
</label>
</div>
</span>
</td>
</tr>
<tr class="newFilter gyroNotch2">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="gyroNotch2Enabled" class="toggle" /></span>
<span class="inputValue"><input type="number" class="nonProfile" name="gyroNotch2Frequency" step="1" min="1" max="16000"/></span>
</span>
</td>
<td>
<div>
<td><span class="inputSwitch"><input type="checkbox" id="gyroNotch2Enabled" class="toggle" /></span></td>
<td colspan="2">
<div class="helpicon cf_tip" i18n_title="pidTuningGyroNotchFilter2"></div>
<span i18n="pidTuningGyroNotchFilter2"></span>
<span class="suboption">
<input type="number" class="nonProfile" name="gyroNotch2Frequency" step="1" min="1" max="16000"/>
<label>
<span i18n="pidTuningGyroNotch2Frequency"></span>
</label>
</div>
</td>
</tr>
</span>
<tr class="newFilter gyroNotch2">
<td>
<input type="number" class="nonProfile" name="gyroNotch2Cutoff" step="1" min="0" max="16000"/>
</td>
<td>
<div>
<span class="suboption">
<input type="number" class="nonProfile" name="gyroNotch2Cutoff" step="1" min="0" max="16000"/>
<label>
<span i18n="pidTuningGyroNotch2Cutoff"></span>
</label>
</div>
</span>
</td>
</tr>
<tr>
<th class="rpmFilter" colspan="2">
<div class="pid_mode rpmFilter">
<div i18n="pidTuningRpmFilterGroup" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningRpmFilterHelp" ></div>
<div i18n="pidTuningRpmFilterGroup"></div>
<div class="helpicon cf_tip" i18n_title="pidTuningRpmFilterHelp"></div>
</div>
</th>
</tr>
<tr class="newFilter rpmFilter">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="rpmFilterEnabled" class="toggle" /></span>
<span class="inputValue"><input type="number" class="nonProfile" name="rpmFilterHarmonics" step="1" min="1" max="3"/></span>
</span>
</td>
<td>
<div>
<td><span class="inputSwitch"><input type="checkbox" id="rpmFilterEnabled" class="toggle" /></span></td>
<td colspan="2">
<span i18n="pidTuningRpmFilterGroup"></span>
<span class="suboption">
<input type="number" class="nonProfile" name="rpmFilterHarmonics" step="1" min="1" max="3"/>
<label>
<span i18n="pidTuningRpmHarmonics"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningRpmHarmonicsHelp" ></div>
</div>
</td>
</tr>
<tr class="newFilter rpmFilter">
<td>
<input type="number" name="rpmFilterMinHz" step="1" min="50" max="200"/>
</td>
<td>
<div>
<div class="helpicon cf_tip" i18n_title="pidTuningRpmHarmonicsHelp"></div>
</span>
<span class="suboption">
<input type="number" name="rpmFilterMinHz" step="1" min="50" max="200"/>
<label>
<span i18n="pidTuningRpmMinHz"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningRpmMinHzHelp" ></div>
</div>
<div class="helpicon cf_tip" i18n_title="pidTuningRpmMinHzHelp"></div>
</span>
</td>
</tr>
<tr>
<th class="dynamicNotch" colspan="2">
<div class="pid_mode dynamicNotch">
<div i18n="pidTuningDynamicNotchFilterGroup" ></div>
<div class="helpicon cf_tip dynamicNotchHelp" i18n_title="pidTuningDynamicNotchFilterHelp" ></div>
<div i18n="pidTuningDynamicNotchFilterGroup"></div>
<div class="helpicon cf_tip dynamicNotchHelp" i18n_title="pidTuningDynamicNotchFilterHelp"></div>
</div>
</th>
</tr>
@ -1454,7 +1474,7 @@
<label>
<span i18n="pidTuningDynamicNotchRange"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchRangeHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchRangeHelp"></div>
</div>
</td>
</tr>
@ -1467,7 +1487,7 @@
<label>
<span i18n="pidTuningDynamicNotchWidthPercent"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchWidthPercentHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchWidthPercentHelp"></div>
</div>
</td>
</tr>
@ -1480,7 +1500,7 @@
<label>
<span i18n="pidTuningDynamicNotchQ"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchQHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchQHelp"></div>
</div>
</td>
</tr>
@ -1493,7 +1513,7 @@
<label>
<span i18n="pidTuningDynamicNotchCount"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchCountHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchCountHelp"></div>
</div>
</td>
</tr>
@ -1506,7 +1526,7 @@
<label>
<span i18n="pidTuningDynamicNotchMinHz"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchMinHzHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchMinHzHelp"></div>
</div>
</td>
</tr>
@ -1519,7 +1539,7 @@
<label>
<span i18n="pidTuningDynamicNotchMaxHz"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchMaxHzHelp" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDynamicNotchMaxHzHelp"></div>
</div>
</td>
</tr>
@ -1530,201 +1550,219 @@
<table class="pid_titlebar new_rates">
<tr>
<th i18n="pidTuningFilterSettings"></th>
<td>
<select id="sliderDTermFilterModeSelect" class="sliderMode">
<option value="0" i18n="pidTuningOptionOff"></option>
<option value="1" i18n="pidTuningOptionOn"></option>
</select>
</td>
</tr>
</table>
<table class="filterTable">
<table class="filterTable compensation">
<tr>
<th colspan="2">
<th colspan="3">
<div class="pid_mode">
<div i18n="pidTuningDTermLowpassFiltersGroup" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningDTermLowpassFilterHelp" ></div>
</div>
<div class="helpicon cf_tip" i18n_title="pidTuningDTermLowpassFilterHelp"></div>
</div>
</th>
</tr>
<tr class="dtermLowpassDyn">
<!-- Legacy filter switches -->
<tr class="dtermLowpassDynLegacy">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="dtermLowpassDynEnabled" class="toggle" /></span>
<span class="inputValue"><input type="number" name="dtermLowpassDynMinFrequency" step="1" min="1" max="1000"/></span>
</span>
<span class="inputSwitch"><input type="checkbox" id="dtermLowpassDynEnabled" class="toggle" /></span>
</td>
<td>
<div>
<td colspan="3">
<div class="helpicon cf_tip" i18n_title="pidTuningDTermLowpassDyn"></div>
<span i18n="pidTuningDTermLowpassDyn"></span>
<span class="suboption">
<span class="inputValue"><input type="number" name="dtermLowpassDynMinFrequency" step="1" min="1" max="1000"/></span>
<label>
<span i18n="pidTuningDTermLowpassDynMinFrequency"></span>
</label>
</div>
</td>
</tr>
</span>
<tr class="dtermLowpassDyn">
<td>
<span class="inputValue"><input type="number" name="dtermLowpassDynMaxFrequency" step="1" min="1" max="1000"/></span>
</td>
<td>
<div>
<span class="suboption">
<span class="inputValue"><input type="number" name="dtermLowpassDynMaxFrequency" step="1" min="1" max="1000"/></span>
<label>
<span i18n="pidTuningDTermLowpassDynMaxFrequency"></span>
</label>
</div>
</td>
</tr>
</span>
<tr class="dtermLowpassDyn">
<td>
<select name="dtermLowpassDynType">
<!-- Populated on execution -->
</select>
</td>
<td>
<div>
<span class="suboption">
<select name="dtermLowpassDynType">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningDTermLowpassDynType"></span>
</label>
</div>
</span>
</td>
</tr>
<tr class="dtermLowpassDyn dynLpfCurveExpo">
<tr class="dtermLowpassLegacy">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="dtermLowpassDynExpoEnabled" class="toggle" /></span>
<span class="inputValue"><input type="number" name="dtermLowpassDynExpo" step="1" min="1" max="10"/></span>
</span>
<span class="inputSwitch"><input type="checkbox" id="dtermLowpassEnabled" class="toggle" /></span>
</td>
<td>
<div>
<label>
<span i18n="pidTuningDTermLowpassDynExpo"></span>
</label>
</div>
</td>
</tr>
<td colspan="3">
<div class="helpicon cf_tip" i18n_title="pidTuningDTermLowpass"></div>
<span i18n="pidTuningDTermLowpass"></span>
<tr>
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="dtermLowpassEnabled" class="toggle" /></span>
<span class="suboption">
<span class="inputValue"><input type="number" name="dtermLowpassFrequency" step="1" min="1" max="16000"/></span>
</span>
</td>
<td>
<div>
<label>
<span i18n="pidTuningDTermLowpassFrequency"></span>
</label>
</div>
</td>
</tr>
<tr>
<td>
<select name="dtermLowpassType">
<!-- Populated on execution -->
</select>
</td>
<td>
<div>
</span>
<span class="suboption">
<select name="dtermLowpassType">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningDTermLowpassType"></span>
</label>
</div>
</span>
</td>
</tr>
<!-- firmware filter switches -->
<tr class="dtermLowpass">
<td>
<span class="inputSwitch"><input type="checkbox" id="dtermLowpassEnabled" class="toggle" /></span>
</td>
<td colspan="3">
<div class="helpicon cf_tip" i18n_title="pidTuningDTermLowpass"></div>
<span i18n="pidTuningDTermLowpass"></span>
<span class="suboption dtermLowpassFilterModeGroup">
<span class="inputValue">
<select name="dtermLowpassFilterMode">
<option value="0" i18n="pidTuningLowpassStatic"></option>
<option value="1" i18n="pidTuningLowpassDynamic"></option>
</select>
</span>
<label>
<span i18n="pidTuningDTermLowpassMode"></span>
</label>
</span>
<span class="suboption static">
<span class="inputValue"><input type="number" name="dtermLowpassFrequency" step="1" min="1" max="16000"/></span>
<label>
<span i18n="pidTuningDTermLowpassFrequency"></span>
</label>
</span>
<span class="suboption dynamic">
<span class="inputValue"><input type="number" name="dtermLowpassDynMinFrequency" step="1" min="1" max="1000"/></span>
<label>
<span i18n="pidTuningDTermLowpassDynMinFrequency"></span>
</label>
</span>
<span class="suboption dynamic">
<span class="inputValue"><input type="number" name="dtermLowpassDynMaxFrequency" step="1" min="1" max="1000"/></span>
<label>
<span i18n="pidTuningDTermLowpassDynMaxFrequency"></span>
</label>
</span>
<span class="suboption dynamic">
<span class="inputValue"><input type="number" name="dtermLowpassExpo" step="1" min="1" max="10"/></span>
<label>
<span i18n="pidTuningDTermLowpassDynExpo"></span>
</label>
</span>
<span class="suboption">
<select name="dtermLowpassType">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningLowpassFilterType"></span>
</label>
</span>
</span>
</td>
</tr>
<tr class="dtermLowpass2">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="dtermLowpass2Enabled" class="toggle" /></span>
<span class="inputValue"><input type="number" name="dtermLowpass2Frequency" step="1" min="1" max="16000"/></span>
</span>
<span class="inputSwitch"><input type="checkbox" id="dtermLowpass2Enabled" class="toggle" /></span>
</td>
<td>
<div>
<td colspan="3">
<div class="helpicon cf_tip" i18n_title="pidTuningDTermLowpass2"></div>
<span i18n="pidTuningDTermLowpass2"></span>
<span class="suboption">
<span class="inputValue"><input type="number" name="dtermLowpass2Frequency" step="1" min="1" max="16000"/></span>
<label>
<span i18n="pidTuningDTermLowpass2Frequency"></span>
</label>
</div>
</td>
</tr>
</span>
<tr class="dtermLowpass2 dtermLowpass2TypeGroup">
<td>
<select name="dtermLowpass2Type">
<!-- Populated on execution -->
</select>
</td>
<td>
<div>
<span class="suboption dtermLowpass2TypeGroup">
<select name="dtermLowpass2Type">
<!-- Populated on execution -->
</select>
<label>
<span i18n="pidTuningDTermLowpass2Type"></span>
<span i18n="pidTuningLowpassFilterType"></span>
</label>
</div>
</span>
</td>
</tr>
<tr>
<th colspan="2">
<th colspan="3">
<div class="pid_mode">
<div i18n="pidTuningDTermNotchFiltersGroup" ></div>
<div class="helpicon cf_tip" i18n_title="pidTuningNotchFilterHelp" ></div>
</div>
</th>
</tr>
<tr class="newFilter">
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="dtermNotchEnabled" class="toggle" /></span>
<tr class="newFilter dtermNotch">
<td><span class="inputSwitch"><input type="checkbox" id="dtermNotchEnabled" class="toggle" /></span>
<td colspan="3">
<div class="helpicon cf_tip" i18n_title="pidTuningDTermNotchFiltersGroup"></div>
<span i18n="pidTuningDTermNotchFiltersGroup"></span>
<span class="suboption">
<span class="inputValue"><input type="number" name="dTermNotchFrequency" step="1" min="1" max="16000"/></span>
<label><span i18n="pidTuningDTermNotchFrequency"></span></label>
</span>
</td>
<td>
<div>
<label>
<span i18n="pidTuningDTermNotchFrequency"></span>
</label>
</div>
</td>
</tr>
<tr class="newFilter">
<td>
<input type="number" name="dTermNotchCutoff" step="1" min="0" max="16000"/>
</td>
<td>
<div>
<label>
<span i18n="pidTuningDTermNotchCutoff"></span>
</label>
</div>
<span class="suboption">
<input type="number" name="dTermNotchCutoff" step="1" min="0" max="16000"/>
<label><span i18n="pidTuningDTermNotchCutoff"></span></label>
</span>
</td>
</tr>
<tr>
<th colspan="2">
<div class="pid_mode" i18n="pidTuningYawLospassFiltersGroup" ></div>
<th colspan="3">
<div class="pid_mode" i18n="pidTuningYawLowpassFiltersGroup" ></div>
</th>
</tr>
<tr>
<td>
<span class="groupSwitchValue">
<span class="inputSwitch"><input type="checkbox" id="yawLowpassEnabled" class="toggle" /></span>
<tr class="yawLowpass">
<td><span class="inputSwitch"><input type="checkbox" id="yawLowpassEnabled" class="toggle" /></span>
<td colspan="3">
<div class="helpicon cf_tip" i18n_title="pidTuningYawLowpassFiltersGroup"></div>
<span i18n="pidTuningYawLowpassFiltersGroup"></span>
<span class="suboption">
<span class="inputValue"><input type="number" name="yawLowpassFrequency" step="1" min="1" max="500"/></span>
</span>
</td>
<td>
<div>
<label>
<span i18n="pidTuningYawLowpassFrequency"></span>
</label>
</div>
</span>
</td>
</tr>
</table>