initial implementation of accel graph in motors tab

10.3.x-maintenance
cTn 2014-06-25 13:01:21 +02:00
parent 9e15e55e3f
commit d128bd76a5
3 changed files with 387 additions and 91 deletions

View File

@ -1,97 +1,183 @@
.tab-motor_outputs {
.tab-motor_outputs .plot_control {
float: right;
width: 158px;
border: 1px solid silver;
}
.tab-motor_outputs .left.motors {
.tab-motor_outputs .plot_control .title {
line-height: 20px;
font-weight: bold;
text-align: center;
border-bottom: 1px solid silver;
background-color: #ececec;
}
.tab-motor_outputs .plot_control dl {
padding: 5px 0 0 5px;
line-height: 22px;
}
.tab-motor_outputs .plot_control dt {
float: left;
width: 60px;
height: 22px;
font-weight: bold;
}
.tab-motor_outputs .plot_control dd {
margin-left: 20px;
height: 22px;
}
.tab-motor_outputs .plot_control .x {
color: #00A8F0;
}
.tab-motor_outputs .plot_control .y {
color: #C0D800;
}
.tab-motor_outputs .plot_control .z {
color: #CB4B4B;
}
.tab-motor_outputs select {
width: 70px;
border: 1px solid silver;
}
.tab-motor_outputs svg {
float: left;
width: calc(100% - 168px); /* - (plot control, margin)*/
height: 140px;
margin-bottom: 10px;
}
.tab-motor_outputs .grid .tick {
stroke: silver;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.tab-motor_outputs .grid path {
stroke-width: 0;
}
.tab-motor_outputs .data .line {
stroke-width: 2px;
fill: none;
}
.tab-motor_outputs .axis path, .tab-motor_outputs .axis line {
fill: none;
stroke: #000000;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.tab-motor_outputs .line:nth-child(1) {
stroke: #00A8F0;
}
.tab-motor_outputs .line:nth-child(2) {
stroke: #C0D800;
}
.tab-motor_outputs .line:nth-child(3) {
stroke: #CB4B4B;
}
.tab-motor_outputs .left.motors {
float: left;
margin-right: 50px;
width: calc(50% - 50px);
}
.tab-motor_outputs .right.servos {
float: left;
width: 50%;
}
.tab-motor_outputs .titles {
height: 20px;
}
.tab-motor_outputs .titles li {
float: left;
width: calc((100% / 9) - 10px);
margin-right: 10px;
text-align: center;
}
.tab-motor_outputs .servos .titles li {
float: right;
width: calc((100% / 8) - 10px);
margin: 0 0 0 10px;
}
.tab-motor_outputs .titles .active {
color: green;
}
.tab-motor_outputs .m-block {
float: left;
width: calc((100% / 9) - 12px);
height: 80px;
margin-right: 10px;
border: 1px solid silver;
background-color: #e9e9e9;
}
.tab-motor_outputs .servos .m-block {
float: right;
width: calc((100% / 8) - 12px);
margin: 0 0 0 10px;
}
.tab-motor_outputs .indicator {
float: left;
width: 100%;
}
.tab-motor_outputs p {
margin-top: 10px;
margin-bottom: 10px;
padding: 5px;
border: 1px dotted silver;
}
.tab-motor_outputs .motor_testing {
display: none;
}
.tab-motor_outputs .motor_testing .left {
margin-right: 50px;
width: calc(50% - 50px);
}
.tab-motor_outputs .right.servos {
float: left;
.tab-motor_outputs .motor_testing .sliders {
}
.tab-motor_outputs .motor_testing .sliders input {
-webkit-appearance: slider-vertical;
width: 50%;
width: calc((100% / 9) - 3px); /* - (width / 9 elements, not sure about -3 */
height: 80px;
}
.tab-motor_outputs .motor_testing .values {
margin-top: 5px;
}
.tab-motor_outputs .titles {
height: 20px;
}
.tab-motor_outputs .titles li {
.tab-motor_outputs .motor_testing .values li {
float: left;
width: calc((100% / 9) - 10px); /* - (margin) */
margin-right: 10px;
width: calc((100% / 9));
text-align: center;
}
.tab-motor_outputs .servos .titles li {
width: calc((100% / 8) - 10px); /* - (margin) */
}
.tab-motor_outputs .titles .active {
color: green;
}
.tab-motor_outputs .m-block {
float: left;
.tab-motor_outputs .motor_testing .notice {
float: right;
width: calc((100% / 9) - 12px); /* - (margin, border) */
height: 160px;
width: calc(50% - 22px);
margin-right: 10px;
border: 1px solid silver;
background-color: #e9e9e9;
}
.tab-motor_outputs .servos .m-block {
width: calc((100% / 8) - 12px); /* - (margin, border) */
}
.tab-motor_outputs .indicator {
float: left;
width: 100%;
}
.tab-motor_outputs p {
margin-top: 20px;
padding: 5px;
border: 1px dotted silver;
}
.tab-motor_outputs .motor_testing {
display: none;
}
.tab-motor_outputs .motor_testing .left {
margin-right: 50px;
.tab-motor_outputs .motor_testing .notice input[type="checkbox"] {
margin-left: 5px;
width: calc(50% - 50px);
}
.tab-motor_outputs .motor_testing .sliders {
margin-top: 20px;
}
.tab-motor_outputs .motor_testing .sliders input {
-webkit-appearance: slider-vertical;
width: calc((100% / 9) - 3px); /* - (width / 9 elements, not sure about -3 */
}
.tab-motor_outputs .motor_testing .values {
margin-top: 5px;
}
.tab-motor_outputs .motor_testing .values li {
float: left;
width: calc((100% / 9));
text-align: center;
}
.tab-motor_outputs .motor_testing .notice {
float: left;
width: calc(50% - 12px); /* - (padding, border) */
margin-top: 20px;
padding: 5px;
border: 1px dotted silver;
}
.tab-motor_outputs .motor_testing .notice input[type="checkbox"] {
margin-left: 5px;
vertical-align: middle;
}
vertical-align: middle;
}

View File

@ -1,4 +1,44 @@
<div class="tab-motor_outputs">
<div class="wrapper accel">
<div class="plot_control">
<div class="title">Accelerometer - g</div>
<dl>
<dt i18n="sensorsRefresh"></dt>
<dd class="rate">
<select name="accel_refresh_rate">
<option value="10">10 ms</option>
<option value="20" selected="selected">20 ms</option>
<option value="30">30 ms</option>
<option value="40">40 ms</option>
<option value="50">50 ms</option>
<option value="100">100 ms</option>
<option value="250">250 ms</option>
<option value="500">500 ms</option>
<option value="1000">1000 ms</option>
</select>
</dd>
<dt i18n="sensorsScale"></dt>
<dd class="scale">
<select name="accel_scale">
<option value="0.5">0.5</option>
<option value="1">1</option>
<option value="2" selected="selected">2</option>
</select>
</dd>
<dt>X:</dt><dd class="x">0</dd>
<dt>Y:</dt><dd class="y">0</dd>
<dt>Z:</dt><dd class="z">0</dd>
</dl>
</div>
<svg id="accel">
<g class="grid x" transform="translate(20, 120)"></g>
<g class="grid y" transform="translate(20, 10)"></g>
<g class="data" transform="translate(21, 10)"></g>
<g class="axis x" transform="translate(20, 120)"></g>
<g class="axis y" transform="translate(20, 10)"></g>
</svg>
<div class="clear-both"></div>
</div>
<div class="left motors">
<ul class="titles">
<li title="Motor - 1">M - 1</li>
@ -23,24 +63,24 @@
</div>
<div class="right servos">
<ul class="titles">
<li title="Servo - 1">S - 1</li>
<li title="Servo - 2">S - 2</li>
<li title="Servo - 3">S - 3</li>
<li title="Servo - 4">S - 4</li>
<li title="Servo - 5">S - 5</li>
<li title="Servo - 6">S - 6</li>
<li title="Servo - 7">S - 7</li>
<li title="Servo - 8">S - 8</li>
<li title="Servo - 7">S - 7</li>
<li title="Servo - 6">S - 6</li>
<li title="Servo - 5">S - 5</li>
<li title="Servo - 4">S - 4</li>
<li title="Servo - 3">S - 3</li>
<li title="Servo - 2">S - 2</li>
<li title="Servo - 1">S - 1</li>
</ul>
<div class="bar-wrapper">
<div class="m-block servo-0"><div class="indicator"></div></div>
<div class="m-block servo-1"><div class="indicator"></div></div>
<div class="m-block servo-2"><div class="indicator"></div></div>
<div class="m-block servo-3"><div class="indicator"></div></div>
<div class="m-block servo-4"><div class="indicator"></div></div>
<div class="m-block servo-5"><div class="indicator"></div></div>
<div class="m-block servo-6"><div class="indicator"></div></div>
<div class="m-block servo-7"><div class="indicator"></div></div>
<div class="m-block servo-6"><div class="indicator"></div></div>
<div class="m-block servo-5"><div class="indicator"></div></div>
<div class="m-block servo-4"><div class="indicator"></div></div>
<div class="m-block servo-3"><div class="indicator"></div></div>
<div class="m-block servo-2"><div class="indicator"></div></div>
<div class="m-block servo-1"><div class="indicator"></div></div>
<div class="m-block servo-0"><div class="indicator"></div></div>
</div>
</div>
<div class="clear-both"></div>

View File

@ -2,6 +2,124 @@ function tab_initialize_motor_outputs() {
ga_tracker.sendAppView('Motor Outputs Page');
GUI.active_tab = 'motor_outputs';
function initSensorData() {
for (var i = 0; i < 3; i++) {
SENSOR_DATA.accelerometer[i] = 0;
}
}
function initDataArray(length) {
var data = new Array(length);
for (var i = 0; i < length; i++) {
data[i] = new Array();
data[i].min = -1;
data[i].max = 1;
}
return data;
}
function addSampleToData(data, sampleNumber, sensorData) {
for (var i = 0; i < data.length; i++) {
var dataPoint = sensorData[i];
data[i].push([sampleNumber, dataPoint]);
if (dataPoint < data[i].min) {
data[i].min = dataPoint;
}
if (dataPoint > data[i].max) {
data[i].max = dataPoint;
}
}
while (data[0].length > 300) {
for (i = 0; i < data.length; i++) {
data[i].shift();
}
}
return sampleNumber + 1;
}
var margin = {top: 20, right: 10, bottom: 10, left: 20};
function updateGraphHelperSize(helpers) {
helpers.width = helpers.targetElement.width() - margin.left - margin.right;
helpers.height = helpers.targetElement.height() - margin.top - margin.bottom;
helpers.widthScale.range([0, helpers.width]);
helpers.heightScale.range([helpers.height, 0]);
helpers.xGrid.tickSize(-helpers.height, 0, 0);
helpers.yGrid.tickSize(-helpers.width, 0, 0);
}
function initGraphHelpers(selector, sampleNumber, heightDomain) {
var helpers = {selector: selector, targetElement: $(selector), dynamicHeightDomain: !heightDomain};
helpers.widthScale = d3.scale.linear()
.clamp(true)
.domain([(sampleNumber - 299), sampleNumber]);
helpers.heightScale = d3.scale.linear()
.clamp(true)
.domain(heightDomain || [1, -1]);
helpers.xGrid = d3.svg.axis();
helpers.yGrid = d3.svg.axis();
updateGraphHelperSize(helpers);
helpers.xGrid
.scale(helpers.widthScale)
.orient("bottom")
.ticks(5)
.tickFormat("");
helpers.yGrid
.scale(helpers.heightScale)
.orient("left")
.ticks(5)
.tickFormat("");
helpers.xAxis = d3.svg.axis()
.scale(helpers.widthScale)
.ticks(5)
.orient("bottom")
.tickFormat(function(d) {return d;});
helpers.yAxis = d3.svg.axis()
.scale(helpers.heightScale)
.ticks(5)
.orient("left")
.tickFormat(function(d) {return d;});
helpers.line = d3.svg.line()
.x(function(d) { return helpers.widthScale(d[0]); })
.y(function(d) { return helpers.heightScale(d[1]); });
return helpers;
}
function drawGraph(graphHelpers, data, sampleNumber) {
svg = d3.select(graphHelpers.selector);
if (graphHelpers.dynamicHeightDomain) {
var limits = [];
$.each(data, function(idx, datum) {
limits.push(datum.min);
limits.push(datum.max);
});
graphHelpers.heightScale.domain(d3.extent(limits));
}
graphHelpers.widthScale.domain([(sampleNumber - 299), sampleNumber]);
svg.select(".x.grid").call(graphHelpers.xGrid);
svg.select(".y.grid").call(graphHelpers.yGrid);
svg.select(".x.axis").call(graphHelpers.xAxis);
svg.select(".y.axis").call(graphHelpers.yAxis);
var group = svg.select("g.data");
var lines = group.selectAll("path").data(data, function(d, i) { return i; });
var newLines = lines.enter().append("path").attr("class", "line");
lines.attr('d', graphHelpers.line);
}
MSP.send_message(MSP_codes.MSP_MISC, false, false, get_motor_data);
function get_motor_data() {
@ -16,6 +134,58 @@ function tab_initialize_motor_outputs() {
// translate to user-selected language
localize();
// Always start with default/empty sensor data array, clean slate all
initSensorData();
// Setup variables
var samples_accel_i = 0;
var accel_data = initDataArray(3);
var accelHelpers = initGraphHelpers('#accel', samples_accel_i, [-2, 2]);
var raw_data_text_ements = {
x: [],
y: [],
z: [],
};
$('.plot_control .x, .plot_control .y, .plot_control .z').each(function() {
var el = $(this);
if (el.hasClass('x')) {
raw_data_text_ements.x.push(el);
} else if (el.hasClass('y')) {
raw_data_text_ements.y.push(el);
} else {
raw_data_text_ements.z.push(el);
}
});
$('.tab-motor_outputs .rate select, .tab-motor_outputs .scale select').change(function() {
var rate = parseInt($('.tab-motor_outputs select[name="accel_refresh_rate"]').val(), 10);
var scale = parseFloat($('.tab-motor_outputs select[name="accel_scale"]').val());
accelHelpers = initGraphHelpers('#accel', samples_accel_i, [-scale, scale]);
// timer initialization
GUI.interval_kill_all(['motor_pull', 'status_pull']);
GUI.interval_add('IMU_pull', function imu_data_pull() {
MSP.send_message(MSP_codes.MSP_RAW_IMU, false, false, update_accel_graph);
}, rate, true);
function update_accel_graph() {
updateGraphHelperSize(accelHelpers);
samples_accel_i = addSampleToData(accel_data, samples_accel_i, SENSOR_DATA.accelerometer);
drawGraph(accelHelpers, accel_data, samples_accel_i);
raw_data_text_ements.x[0].text(SENSOR_DATA.accelerometer[0].toFixed(2));
raw_data_text_ements.y[0].text(SENSOR_DATA.accelerometer[1].toFixed(2));
raw_data_text_ements.z[0].text(SENSOR_DATA.accelerometer[2].toFixed(2));
}
});
// fire change event to start accel plot
$('.tab-motor_outputs .rate select:first').change();
// if CAP_DYNBALANCE is true
if (bit_check(CONFIG.capability, 2)) {
$('div.motor_testing').show();
@ -141,7 +311,7 @@ function tab_initialize_motor_outputs() {
GUI.interval_add('motor_pull', get_motor_data, 50, true);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function() {
GUI.interval_add('status_pull', function get_status_data() {
MSP.send_message(MSP_codes.MSP_STATUS);
}, 250, true);
}