betaflight-configurator/tabs/receiver.js

438 lines
16 KiB
JavaScript
Raw Normal View History

'use strict';
2015-10-23 13:42:32 +00:00
TABS.receiver = {
2015-11-08 15:52:07 +00:00
rateChartHeight: 117
2015-10-23 13:42:32 +00:00
};
2014-08-12 14:20:26 +00:00
TABS.receiver.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'receiver') {
GUI.active_tab = 'receiver';
}
function get_rc_data() {
2016-07-06 22:13:07 +00:00
MSP.send_message(MSP_codes.MSP_RC, false, false, get_rc_tuning_data);
}
function get_rc_tuning_data() {
MSP.send_message(MSP_codes.MSP_RC_TUNING, false, false, get_bt_config_data);
}
function get_bt_config_data() {
MSP.send_message(MSP_codes.MSP_BF_CONFIG, false, false, get_rc_map);
}
function get_rc_map() {
2015-10-23 13:42:32 +00:00
MSP.send_message(MSP_codes.MSP_RX_MAP, false, false, load_config);
}
2015-10-23 13:42:32 +00:00
// Fetch features so we can check if RX_MSP is enabled:
function load_config() {
MSP.send_message(MSP_codes.MSP_BF_CONFIG, false, false, load_rc_configs);
}
2016-07-04 21:34:49 +00:00
function load_rc_configs() {
var next_callback = load_html;
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
MSP.send_message(MSP_codes.MSP_RC_DEADBAND, false, false, next_callback);
} else {
next_callback();
}
}
function load_html() {
$('#content').load("./tabs/receiver.html", process_html);
}
2016-06-28 13:17:33 +00:00
MSP.send_message(MSP_codes.MSP_MISC, false, false, get_rc_data);
function process_html() {
// translate to user-selected language
localize();
2014-08-12 14:20:26 +00:00
chrome.storage.local.get('rx_refresh_rate', function (result) {
2014-08-14 14:41:49 +00:00
if (result.rx_refresh_rate) {
$('select[name="rx_refresh_rate"]').val(result.rx_refresh_rate).change();
} else {
$('select[name="rx_refresh_rate"]').change(); // start with default value
}
});
if (semver.lt(CONFIG.apiVersion, "1.15.0")) {
$('.deadband').hide();
} else {
$('.deadband input[name="yaw_deadband"]').val(RC_deadband.yaw_deadband);
$('.deadband input[name="deadband"]').val(RC_deadband.deadband);
}
2014-05-19 21:36:04 +00:00
// generate bars
2015-10-23 13:42:32 +00:00
var bar_names = [
chrome.i18n.getMessage('controlAxisRoll'),
chrome.i18n.getMessage('controlAxisPitch'),
chrome.i18n.getMessage('controlAxisYaw'),
chrome.i18n.getMessage('controlAxisThrottle')
],
2014-08-12 14:20:26 +00:00
bar_container = $('.tab-receiver .bars'),
aux_index = 1;
2014-05-19 21:36:04 +00:00
for (var i = 0; i < RC.active_channels; i++) {
var name;
if (i < bar_names.length) {
name = bar_names[i];
} else {
2015-10-23 13:42:32 +00:00
name = chrome.i18n.getMessage("controlAxisAux" + (aux_index++));
}
2014-05-19 21:36:04 +00:00
bar_container.append('\
<ul>\
<li class="name">' + name + '</li>\
<li class="meter">\
<div class="meter-bar">\
<div class="label"></div>\
<div class="fill">\
<div class="label"></div>\
</div>\
</div>\
</li>\
2014-05-19 21:36:04 +00:00
</ul>\
');
}
// we could probably use min and max throttle for the range, will see
var meter_scale = {
'min': 800,
'max': 2200
};
var meter_fill_array = [];
$('.meter .fill', bar_container).each(function () {
meter_fill_array.push($(this));
2014-05-19 21:36:04 +00:00
});
var meter_label_array = [];
$('.meter', bar_container).each(function () {
meter_label_array.push($('.label' , this));
2014-05-19 21:36:04 +00:00
});
// correct inner label margin on window resize (i don't know how we could do this in css)
self.resize = function () {
var containerWidth = $('.meter:first', bar_container).width(),
labelWidth = $('.meter .label:first', bar_container).width(),
margin = (containerWidth / 2) - (labelWidth / 2);
for (var i = 0; i < meter_label_array.length; i++) {
meter_label_array[i].css('margin-left', margin);
}
};
$(window).on('resize', self.resize).resize(); // trigger so labels get correctly aligned on creation
// handle rcmap & rssi aux channel
var RC_MAP_Letters = ['A', 'E', 'R', 'T', '1', '2', '3', '4'];
var strBuffer = [];
for (var i = 0; i < RC_MAP.length; i++) {
strBuffer[RC_MAP[i]] = RC_MAP_Letters[i];
}
// reconstruct
var str = strBuffer.join('');
// set current value
$('input[name="rcmap"]').val(str);
// validation / filter
var last_valid = str;
$('input[name="rcmap"]').on('input', function () {
var val = $(this).val();
// limit length to max 8
if (val.length > 8) {
val = val.substr(0, 8);
$(this).val(val);
}
});
$('input[name="rcmap"]').focusout(function () {
var val = $(this).val(),
strBuffer = val.split(''),
duplicityBuffer = [];
if (val.length != 8) {
$(this).val(last_valid);
return false;
}
// check if characters inside are all valid, also check for duplicity
for (var i = 0; i < val.length; i++) {
if (RC_MAP_Letters.indexOf(strBuffer[i]) < 0) {
$(this).val(last_valid);
return false;
}
if (duplicityBuffer.indexOf(strBuffer[i]) < 0) {
duplicityBuffer.push(strBuffer[i]);
} else {
$(this).val(last_valid);
return false;
}
}
});
// handle helper
$('select[name="rcmap_helper"]').val(0); // go out of bounds
$('select[name="rcmap_helper"]').change(function () {
$('input[name="rcmap"]').val($(this).val());
});
2015-01-07 13:50:24 +00:00
// rssi
var rssi_channel_e = $('select[name="rssi_channel"]');
2015-01-07 13:50:24 +00:00
rssi_channel_e.append('<option value="0">Disabled</option>');
for (var i = 1; i < RC.active_channels + 1; i++) {
2015-01-07 13:50:24 +00:00
rssi_channel_e.append('<option value="' + i + '">' + i + '</option>');
}
$('select[name="rssi_channel"]').val(MISC.rssi_channel);
2015-10-23 13:42:32 +00:00
var rateHeight = TABS.receiver.rateChartHeight;
// UI Hooks
2014-08-12 14:20:26 +00:00
$('a.refresh').click(function () {
2016-06-28 13:17:33 +00:00
// Todo: refresh data here
});
2014-03-08 05:25:15 +00:00
2014-08-12 14:20:26 +00:00
$('a.update').click(function () {
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
RC_deadband.yaw_deadband = parseInt($('.deadband input[name="yaw_deadband"]').val());
RC_deadband.deadband = parseInt($('.deadband input[name="deadband"]').val());
}
2016-07-04 21:34:49 +00:00
// catch rc map
var RC_MAP_Letters = ['A', 'E', 'R', 'T', '1', '2', '3', '4'];
var strBuffer = $('input[name="rcmap"]').val().split('');
for (var i = 0; i < RC_MAP.length; i++) {
RC_MAP[i] = strBuffer.indexOf(RC_MAP_Letters[i]);
}
// catch rssi aux
2015-01-07 13:50:24 +00:00
MISC.rssi_channel = parseInt($('select[name="rssi_channel"]').val());
function save_misc() {
MSP.send_message(MSP_codes.MSP_SET_MISC, MSP.crunch(MSP_codes.MSP_SET_MISC), false, save_rc_configs);
}
2016-07-04 21:34:49 +00:00
function save_rc_configs() {
var next_callback = save_to_eeprom;
if (semver.gte(CONFIG.apiVersion, "1.15.0")) {
MSP.send_message(MSP_codes.MSP_SET_RC_DEADBAND, MSP.crunch(MSP_codes.MSP_SET_RC_DEADBAND), false, next_callback);
} else {
next_callback();
}
}
function save_to_eeprom() {
2014-08-12 14:20:26 +00:00
MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, function () {
2014-05-06 21:33:47 +00:00
GUI.log(chrome.i18n.getMessage('receiverEepromSaved'));
});
}
2016-06-28 13:17:33 +00:00
MSP.send_message(MSP_codes.MSP_SET_RX_MAP, MSP.crunch(MSP_codes.MSP_SET_RX_MAP), false, save_misc);
});
2015-10-23 13:42:32 +00:00
$("a.sticks").click(function() {
var
windowWidth = 370,
windowHeight = 510;
2015-10-23 13:42:32 +00:00
chrome.app.window.create("/tabs/receiver_msp.html", {
id: "receiver_msp",
innerBounds: {
minWidth: windowWidth, minHeight: windowHeight,
width: windowWidth, height: windowHeight,
2015-10-23 13:42:32 +00:00
maxWidth: windowWidth, maxHeight: windowHeight
},
alwaysOnTop: true
}, function(createdWindow) {
// Give the window a callback it can use to send the channels (otherwise it can't see those objects)
createdWindow.contentWindow.setRawRx = function(channels) {
if (CONFIGURATOR.connectionValid && GUI.active_tab != 'cli') {
MSP.setRawRx(channels);
return true;
} else {
return false;
}
}
});
});
2015-10-23 13:42:32 +00:00
// Only show the MSP control sticks if the MSP Rx feature is enabled
$(".sticks_btn").toggle(bit_check(BF_CONFIG.features, 14 /* RX_MSP */));
2014-03-08 05:25:15 +00:00
2014-08-12 14:20:26 +00:00
$('select[name="rx_refresh_rate"]').change(function () {
2014-04-12 15:20:43 +00:00
var plot_update_rate = parseInt($(this).val(), 10);
// save update rate
chrome.storage.local.set({'rx_refresh_rate': plot_update_rate});
function get_rc_data() {
MSP.send_message(MSP_codes.MSP_RC, false, false, update_ui);
}
2014-03-29 18:21:13 +00:00
// setup plot
var RX_plot_data = new Array(RC.active_channels);
for (var i = 0; i < RX_plot_data.length; i++) {
2014-03-29 18:21:13 +00:00
RX_plot_data[i] = [];
}
2014-08-12 14:20:26 +00:00
var samples = 0,
svg = d3.select("svg"),
RX_plot_e = $('#RX_plot'),
margin = {top: 20, right: 0, bottom: 10, left: 40},
width, height, widthScale, heightScale;
2014-03-29 18:21:13 +00:00
2014-04-21 10:14:03 +00:00
function update_receiver_plot_size() {
2014-05-05 09:34:20 +00:00
width = RX_plot_e.width() - margin.left - margin.right;
height = RX_plot_e.height() - margin.top - margin.bottom;
2014-04-21 10:14:03 +00:00
widthScale.range([0, width]);
heightScale.range([height, 0]);
}
function update_ui() {
// update bars with latest data
for (var i = 0; i < RC.active_channels; i++) {
2014-10-05 17:46:27 +00:00
meter_fill_array[i].css('width', ((RC.channels[i] - meter_scale.min) / (meter_scale.max - meter_scale.min) * 100).clamp(0, 100) + '%');
meter_label_array[i].text(RC.channels[i]);
}
// push latest data to the main array
for (var i = 0; i < RC.active_channels; i++) {
RX_plot_data[i].push([samples, RC.channels[i]]);
}
// Remove old data from array
while (RX_plot_data[0].length > 300) {
for (var i = 0; i < RX_plot_data.length; i++) {
RX_plot_data[i].shift();
}
2014-04-12 15:20:43 +00:00
}
2014-03-29 18:21:13 +00:00
// update required parts of the plot
2014-04-21 10:14:03 +00:00
widthScale = d3.scale.linear().
domain([(samples - 299), samples]);
2014-04-12 15:20:43 +00:00
2014-04-21 10:14:03 +00:00
heightScale = d3.scale.linear().
domain([800, 2200]);
update_receiver_plot_size();
2014-04-12 15:20:43 +00:00
var xGrid = d3.svg.axis().
scale(widthScale).
orient("bottom").
tickSize(-height, 0, 0).
tickFormat("");
var yGrid = d3.svg.axis().
scale(heightScale).
orient("left").
tickSize(-width, 0, 0).
tickFormat("");
var xAxis = d3.svg.axis().
scale(widthScale).
orient("bottom").
2014-08-12 14:20:26 +00:00
tickFormat(function (d) {return d;});
2014-04-12 15:20:43 +00:00
var yAxis = d3.svg.axis().
scale(heightScale).
orient("left").
2014-08-12 14:20:26 +00:00
tickFormat(function (d) {return d;});
2014-04-12 15:20:43 +00:00
var line = d3.svg.line().
2014-08-12 14:20:26 +00:00
x(function (d) {return widthScale(d[0]);}).
y(function (d) {return heightScale(d[1]);});
2014-04-12 15:20:43 +00:00
svg.select(".x.grid").call(xGrid);
svg.select(".y.grid").call(yGrid);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
2014-08-12 14:20:26 +00:00
var data = svg.select("g.data"),
lines = data.selectAll("path").data(RX_plot_data, function (d, i) {return i;}),
newLines = lines.enter().append("path").attr("class", "line");
2014-04-12 15:20:43 +00:00
lines.attr('d', line);
2014-03-29 18:21:13 +00:00
samples++;
}
// timer initialization
GUI.interval_remove('receiver_pull');
// enable RC data pulling
GUI.interval_add('receiver_pull', get_rc_data, plot_update_rate, true);
});
2016-07-04 21:34:49 +00:00
// Setup model for preview
self.initModelPreview();
self.renderModel();
// TODO: Combine two polls together
GUI.interval_add('receiver_pull_for_model_preview', self.getRecieverData, 33, false);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSP_codes.MSP_STATUS);
}, 250, true);
2014-07-10 16:20:09 +00:00
GUI.content_ready(callback);
}
2014-07-10 16:20:09 +00:00
};
2016-07-04 21:34:49 +00:00
TABS.receiver.getRecieverData = function () {
MSP.send_message(MSP_codes.MSP_RC, false, false);
};
TABS.receiver.initModelPreview = function () {
this.keepRendering = true;
this.model = new Model($('.model_preview'), $('.model_preview canvas'));
2016-07-06 22:13:07 +00:00
this.useSuperExpo = false;
if (CONFIG.flightControllerIdentifier === 'BTFL' && semver.gte(CONFIG.flightControllerVersion, '2.8.0')) {
this.useSuperExpo = bit_check(BF_CONFIG.features, SUPEREXPO_FEATURE_BIT);
}
2016-07-04 21:34:49 +00:00
2016-07-06 22:13:07 +00:00
this.rateCurve = new RateCurve(CONFIG.flightControllerIdentifier !== 'BTFL' || semver.lt(CONFIG.flightControllerVersion, '2.8.0'));
this.degreeToRadianRatio = Math.PI / 180;
2016-07-04 21:34:49 +00:00
$(window).on('resize', $.proxy(this.model.resize, this.model));
};
TABS.receiver.renderModel = function () {
if (this.keepRendering) { requestAnimationFrame(this.renderModel.bind(this)); }
if (!this.clock) { this.clock = new THREE.Clock(); }
if (RC.channels[0] && RC.channels[1] && RC.channels[2]) {
2016-07-06 22:13:07 +00:00
var delta = this.clock.getDelta();
var roll = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(RC.channels[0], RC_tuning.roll_rate * 100, RC_tuning.RC_RATE * 100, RC_tuning.RC_EXPO * 100, this.useSuperExpo),
pitch = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(RC.channels[1], RC_tuning.pitch_rate * 100, RC_tuning.RC_RATE * 100, RC_tuning.RC_EXPO * 100, this.useSuperExpo),
yaw = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(RC.channels[2], RC_tuning.yaw_rate * 100, SPECIAL_PARAMETERS.RC_RATE_YAW * 100, RC_tuning.RC_YAW_EXPO * 100, this.useSuperExpo);
2016-07-04 21:34:49 +00:00
2016-07-06 22:13:07 +00:00
this.model.rotateBy(-pitch * this.degreeToRadianRatio, -yaw * this.degreeToRadianRatio, -roll * this.degreeToRadianRatio);
2016-07-04 21:34:49 +00:00
}
};
2014-08-12 14:20:26 +00:00
TABS.receiver.cleanup = function (callback) {
$(window).off('resize', this.resize);
2016-07-04 21:34:49 +00:00
$(window).off('resize', $.proxy(this.model.resize, this.model));
this.keepRendering = false;
2014-07-10 16:20:09 +00:00
if (callback) callback();
};