Added support for new rate profiles.

10.3.x-maintenance
mikeller 2016-07-04 03:02:15 +12:00 committed by Michael Keller
parent 21af5f6278
commit e885979d89
4 changed files with 249 additions and 162 deletions

View File

@ -793,11 +793,8 @@
"pidTuningFilterFrequency": {
"message": "Frequency"
},
"pidTuningRcCurve": {
"message": "RC Curve"
},
"pidTuningRcYawCurve": {
"message": "RC Yaw Curve"
"pidTuningRatesCurve": {
"message": "Rates"
},
"throttle": {
"message": "Throttle"
@ -1576,6 +1573,12 @@
"pidTuningYaw": {
"message": "Yaw (Hz)"
},
"pidTuningShowRatesWithSuperExpo": {
"message": "Show rates with SuperExpo"
},
"pidTuningRatesSuperExpoHelp": {
"message": "To enable SuperExpo, enable 'SUPEREXPO_RATES' in 'Other Features' on the 'Configuration' tab."
},
"configHelp2": {
"message": "Arbitrary board rotation in degrees, to allow mounting it sideways / upside down / rotated etc. When running external sensors, use the sensor alignments (Gyro, Acc, Mag) to define sensor position independent from board orientation. "
},

View File

@ -201,40 +201,7 @@
border-top-right-radius: 3px;
}
.tab-pid_tuning .pitch_roll_curve {
margin: 0 4px 0px 0;
height: 100%;
border: 1px solid silver;
border-radius: 3px;
background-image: url(../images/paper.jpg);
background-size: 200%;
background-position: center;
}
.tab-pid_tuning .rc_yaw_curve {
float: right;
width: calc(100% - 2px); /* - ( "virtual" margin) */
}
.tab-pid_tuning .rc_yaw_curve th {
background-color: #828885;
padding: 4px;
border-left: 0px solid #ccc;
border-bottom: 1px solid #ccc;
font-weight: bold;
color: white;
text-align: left;
}
.tab-pid_tuning .rc_yaw_curve th:first-child {
border-top-left-radius: 3px;
}
.tab-pid_tuning .rc_yaw_curve th:last-child {
border-top-right-radius: 3px;
}
.tab-pid_tuning .yaw_curve {
.tab-pid_tuning .rate_curve {
margin: 0 4px 0px 0;
height: 100%;
border: 1px solid silver;

View File

@ -60,7 +60,7 @@
</tr>
<tr class="ROLL">
<!-- 0 -->
<td></td>
<td bgcolor="#FF8080"></td>
<td><input type="number" name="p" step="1" min="0" max="255" /></td>
<td><input type="number" name="i" step="1" min="0" max="255" /></td>
<td><input type="number" name="d" step="1" min="0" max="255" /></td>
@ -77,7 +77,7 @@
</tr>
<tr class="PITCH">
<!-- 1 -->
<td></td>
<td bgcolor="#80FF80"></td>
<td><input type="number" name="p" step="1" min="0" max="255" /></td>
<td><input type="number" name="i" step="1" min="0" max="255" /></td>
<td><input type="number" name="d" step="1" min="0" max="255" /></td>
@ -85,7 +85,7 @@
</tr>
<tr class="YAW">
<!-- 2 -->
<td></td>
<td bgcolor="#8080FF"></td>
<td><input type="number" name="p" step="1" min="0" max="255" /></td>
<td><input type="number" name="i" step="1" min="0" max="255" /></td>
<td><input type="number" name="d" step="1" min="0" max="255" /></td>
@ -221,56 +221,11 @@
</tr>
</table>
</div>
<div class="cf_column half topspacer">
<div>
<table class="rc_curve cf">
<div class="gui_box grey topspacer">
<table class="pid_titlebar">
<thead>
<tr>
<th i18n="pidTuningRcCurve"></th>
</tr>
</thead>
</tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="pitch_roll_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="clear-both"></div>
</div>
<div class="topspacer">
<table class="rc_yaw_curve cf">
<thead>
<tr>
<th i18n="pidTuningRcYawCurve"></th>
</tr>
</thead>
</tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="yaw_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="clear-both"></div>
</div>
</div>
<div class="cf_column half topspacer">
<div class="spacer_left">
<table class="tpa cf">
<thead>
<tr>
<th i18n="pidTuningRatesPreview" style="font-weight: bold;"></th>
<th i18n="pidTuningRatesPreview"></th>
</tr>
</thead>
<tbody>
@ -285,9 +240,57 @@
</table>
</div>
</div>
</div>
<div class="cf_column third_right">
<div class="spacer_left throttle">
<div class="spacer_left rc_curve">
<table class="cf">
<thead>
<tr>
<th i18n="pidTuningRatesCurve"></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="rate_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div class="checkbox super_expo_checkbox">
<div style="float: left; margin-right: 5px; margin-left: 3px; margin-bottom: 5px;">
<input type="checkbox" name="show_superexpo_rates" class="toggle" />
</div>
<label for="showSuperExpoRates">
<span i18n="pidTuningShowRatesWithSuperExpo"></span>
</label>
<div class="helpicon cf_tip" i18n_title="pidTuningRatesSuperExpoHelp"></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="spacer_left topspacer throttle">
<table class="cf">
<thead>
<tr>
<th i18n="receiverThrottleMid"></th>
<th i18n="receiverThrottleExpo"></th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="number" name="mid" step="0.01" min="0" max="1" /></td>
<td><input type="number" name="expo" step="0.01" min="0" max="1" /></td>
</tr>
</tbody>
</table>
</div>
<div class="spacer_left topspacer throttle">
<table class="cf">
<thead>
<tr>
@ -307,24 +310,8 @@
</tbody>
</table>
</div>
<div class="spacer_left topspacer throttle">
<table class="tpa cf">
<thead>
<tr>
<th i18n="receiverThrottleMid"></th>
<th i18n="receiverThrottleExpo"></th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="number" name="mid" step="0.01" min="0" max="1" /></td>
<td><input type="number" name="expo" step="0.01" min="0" max="1" /></td>
</tr>
</tbody>
</table>
</div>
<div class="spacer_left topspacer">
<table class="tpa cf">
<div class="spacer_left topspacer tpa">
<table class="cf">
<thead>
<tr>
<th i18n="pidTuningTPA"></th>

View File

@ -311,6 +311,121 @@ TABS.pid_tuning.initialize = function (callback) {
}
}
function drawAxes(curveContext, width, height) {
curveContext.strokeStyle = '#000000';
curveContext.lineWidth = 1;
// Horizontal
curveContext.beginPath();
curveContext.moveTo(0, height / 2);
curveContext.lineTo(width, height / 2);
curveContext.stroke();
// Vertical
curveContext.beginPath();
curveContext.moveTo(width / 2, 0);
curveContext.lineTo(width / 2, height);
curveContext.stroke();
}
function constrain(value, min, max) {
return Math.max(min, Math.min(value, max));
}
function rcCommand(rcData, rcRate, rcExpo, midRc) {
var tmp = Math.min(Math.abs(rcData - midRc), 500) / 100;
var result = ((2500 + rcExpo * (tmp * tmp - 25)) * tmp * rcRate / 2500).toFixed(0);
if (rcData < midRc) {
result = -result;
}
return result;
}
function rcCommandRawToDegreesPerSecond(value, rate, rcRate, superExpoActive) {
var angleRate;
if (superExpoActive) {
var rcFactor = Math.abs(value) / (500 * rcRate / 100);
rcFactor = 1 / constrain(1 - rcFactor * rate / 100, 0.01, 1);
angleRate = rcFactor * 27 * value / 16;
} else {
angleRate = (rate + 27) * value / 16;
}
angleRate = constrain(angleRate, -8190, 8190); // Rate limit protection
return angleRate >> 2; // the shift by 2 is to counterbalance the divide by 4 that occurs on the gyro to calculate the error
};
function drawRateCurve(rcRateElement, rateElement, rcExpoElement, curveContext, width, height, superExpoActive) {
var rcRate = parseFloat(rcRateElement.val());
var rate = parseFloat(rateElement.val());
var rcExpo = parseFloat(rcExpoElement.val());
// local validation to deal with input event
if (rcRate >= parseFloat(rcRateElement.prop('min')) &&
rcRate <= parseFloat(rcRateElement.prop('max')) &&
rate >= parseFloat(rateElement.prop('min')) &&
rate <= parseFloat(rateElement.prop('max')) &&
rcExpo >= parseFloat(rcExpoElement.prop('min')) &&
rcExpo <= parseFloat(rcExpoElement.prop('max'))) {
rcRate = rcRate * 100;
rate = rate * 100;
rcExpo = rcExpo * 100;
var minRc = 1000;
var midRc = 1500;
var maxRc = 2000;
var canvasHeightScale = height / Math.abs(
rcCommandRawToDegreesPerSecond(rcCommand(maxRc, rcRate, rcExpo, midRc), rate, rcRate, superExpoActive)
- rcCommandRawToDegreesPerSecond(rcCommand(minRc, rcRate, rcExpo, midRc), rate, rcRate, superExpoActive));
curveContext.save();
curveContext.translate(width / 2, height / 2);
curveContext.beginPath();
var rcData = minRc;
curveContext.moveTo(-500, -canvasHeightScale * rcCommandRawToDegreesPerSecond(rcCommand(rcData, rcRate, rcExpo, midRc), rate, rcRate, superExpoActive));
rcData = rcData + 1;
while (rcData <= maxRc) {
curveContext.lineTo(rcData - midRc, -canvasHeightScale * rcCommandRawToDegreesPerSecond(rcCommand(rcData, rcRate, rcExpo, midRc), rate, rcRate, superExpoActive));
rcData = rcData + 1;
}
curveContext.stroke();
curveContext.restore();
}
}
function drawLegacyRateCurve(rcRateElement, rateElement, rcExpoElement, curveContext, width, height) {
var rcRate = parseFloat(rcRateElement.val());
var rate = parseFloat(rateElement.val());
var rcExpo = parseFloat(rcExpoElement.val());
// local validation to deal with input event
if (rcRate >= parseFloat(rcRateElement.prop('min')) &&
rcRate <= parseFloat(rcRateElement.prop('max')) &&
rate >= parseFloat(rateElement.prop('min')) &&
rate <= parseFloat(rateElement.prop('max')) &&
rcExpo >= parseFloat(rcExpoElement.prop('min')) &&
rcExpo <= parseFloat(rcExpoElement.prop('max'))) {
// math magic by englishman
var rateY = height * rcRate;
rateY = rateY + (1 / (1 - ((rateY / height) * rate)))
// draw
curveContext.beginPath();
curveContext.moveTo(0, height);
curveContext.quadraticCurveTo(width * 11 / 20, height - ((rateY / 2) * (1 - rcExpo)), width, height - rateY);
curveContext.stroke();
}
}
function process_html() {
// translate to user-selected language
localize();
@ -403,64 +518,79 @@ TABS.pid_tuning.initialize = function (callback) {
$('.pid_tuning .roll_pitch_rate').hide();
}
function drawRateCurve(rateElement, sRateElement, expoElement, canvasElement) {
var rate = parseFloat(rateElement.val()),
sRate = parseFloat(sRateElement.val()),
expo = parseFloat(expoElement.val()),
context = canvasElement.getContext("2d");
// local validation to deal with input event
if (rate >= parseFloat(rateElement.prop('min')) &&
rate <= parseFloat(rateElement.prop('max')) &&
expo >= parseFloat(expoElement.prop('min')) &&
expo <= parseFloat(expoElement.prop('max'))) {
var rateHeight = canvasElement.height;
var rateWidth = canvasElement.width;
// math magic by englishman
var ratey = rateHeight * rate;
ratey = ratey + (1 / (1 - ((ratey / rateHeight) * sRate)))
// draw
context.clearRect(0, 0, rateWidth, rateHeight);
context.beginPath();
context.moveTo(0, rateHeight);
context.quadraticCurveTo(rateWidth * 11 / 20, rateHeight - ((ratey / 2) * (1 - expo)), rateWidth, rateHeight - ratey);
context.lineWidth = 2;
context.strokeStyle = '#ffbb00';
context.stroke();
// Getting the DOM elements for curve display
var rcRateElement = $('.pid_tuning input[name="rc_rate"]');
var rcRateElementYaw;
if (CONFIG.flightControllerIdentifier == "BTFL" && semver.gte(CONFIG.flightControllerVersion, "2.8.1")) {
rcRateElementYaw = $('.pid_tuning input[name="rc_rate_yaw"]');
} else {
rcRateElementYaw = rcRateElement;
}
var rateElementRoll = $('.pid_tuning input[name="roll_rate"]');
var rateElementPitch = $('.pid_tuning input[name="pitch_rate"]');
var rateElementYaw = $('.pid_tuning input[name="yaw_rate"]');
var rcExpoElement = $('.pid_tuning input[name="rc_expo"]');
var yawExpoElement = $('.pid_tuning input[name="rc_yaw_expo"]');
var rcCurveElement = $('.rate_curve canvas').get(0);
var curveContext = rcCurveElement.getContext("2d");
rcCurveElement.width = 1000;
rcCurveElement.height = 1000;
var superExpoElement = $('.rc_curve input[name="show_superexpo_rates"]');
if (CONFIG.flightControllerIdentifier !== "BTFL" || semver.lt(CONFIG.flightControllerVersion, "2.8.0")) {
$('.pid_tuning .super_expo_checkbox').hide();
}
// UI Hooks
// curves
$('.pid_tuning').on('input change', function () {
function redrawRateCurves() {
setTimeout(function () { // let global validation trigger and adjust the values first
var rateElement = $('.pid_tuning input[name="rc_rate"]'),
sRateElementRoll = $('.pid_tuning input[name="roll_rate"]'),
sRateElementPitch = $('.pid_tuning input[name="pitch_rate"]'),
sRateElementYaw = $('.pid_tuning input[name="yaw_rate"]'),
expoElement = $('.pid_tuning input[name="rc_expo"]'),
yawExpoElement = $('.pid_tuning input[name="rc_yaw_expo"]'),
rcCurveElement = $('.pitch_roll_curve canvas').get(0),
rcYawCurveElement = $('.yaw_curve canvas').get(0);
var curveHeight = rcCurveElement.height;
var curveWidth = rcCurveElement.width;
var rateElementYaw = 1;
if (CONFIG.flightControllerIdentifier == "BTFL" && semver.gte(CONFIG.flightControllerVersion, "2.8.1")) {
rateElementYaw = $('.pid_tuning input[name="rc_rate_yaw"]');
var useSuperExpo = superExpoElement.is(':checked');
curveContext.clearRect(0, 0, curveWidth, curveHeight);
var drawingFunc;
if (CONFIG.flightControllerIdentifier !== "BTFL" || semver.lt(CONFIG.flightControllerVersion, "2.8.0")) {
drawingFunc = drawLegacyRateCurve;
} else {
rateElementYaw = rateElement;
drawAxes(curveContext, curveWidth, curveHeight);
drawingFunc = drawRateCurve;
}
drawRateCurve(rateElement, sRateElementRoll, expoElement, rcCurveElement);
//drawRateCurve(rateElement, sRateElementPitch, expoElement, rcCurveElement); // Add Pitch Curve
drawRateCurve(rateElementYaw, sRateElementYaw, yawExpoElement, rcYawCurveElement);
curveContext.lineWidth = 4;
curveContext.save();
curveContext.strokeStyle = '#ff0000';
drawingFunc(rcRateElement, rateElementRoll, rcExpoElement, curveContext, curveWidth, curveHeight, useSuperExpo);
curveContext.restore();
curveContext.save();
curveContext.translate(0, -4);
curveContext.strokeStyle = '#00ff00';
drawingFunc(rcRateElement, rateElementPitch, rcExpoElement, curveContext, curveWidth, curveHeight, useSuperExpo);
curveContext.restore();
curveContext.save();
curveContext.strokeStyle = '#0000ff';
curveContext.translate(0, -4);
drawingFunc(rcRateElementYaw, rateElementYaw, yawExpoElement, curveContext, curveWidth, curveHeight, useSuperExpo);
curveContext.restore();
}, 0);
}).trigger('input');
};
$('.pid_tuning').on('input change', redrawRateCurves).trigger('input');
$('.super_expo_checkbox').on('input change', redrawRateCurves).trigger('input');
$('.throttle input').on('input change', function () {
setTimeout(function () { // let global validation trigger and adjust the values firs
setTimeout(function () { // let global validation trigger and adjust the values first
var throttleMidE = $('.throttle input[name="mid"]'),
throttleExpoE = $('.throttle input[name="expo"]'),
mid = parseFloat(throttleMidE.val()),