import { i18n } from "../localization"; import GUI from '../gui'; import { get as getConfig, set as setConfig } from '../ConfigStorage'; import CryptoES from 'crypto-es'; const receiver = { rateChartHeight: 117, analyticsChanges: {}, needReboot: false, elrsPassphraseEnabled: false, }; receiver.initialize = function (callback) { const tab = this; GUI.active_tab = 'receiver'; function lookup_elrs_passphrase(uidString) { const passphraseMap = getConfig('passphrase_map').passphrase_map || {}; return passphraseMap[uidString] ?? 0; } function save_elrs_passphrase(uidString, passphrase) { const passphraseMap = getConfig('passphrase_map').passphrase_map ?? {}; passphraseMap[uidString] = passphrase; setConfig({'passphrase_map': passphraseMap}); } function elrs_passphrase_to_bytes(text) { let uidBytes = [0,0,0,0,0,0]; if (text) { const bindingPhraseFull = `-DMY_BINDING_PHRASE="${text}"`; const hash = CryptoES.MD5(bindingPhraseFull).toString(); uidBytes = Uint8Array.from(Buffer.from(hash, 'hex')).subarray(0, 6); } return uidBytes; } function get_rc_data() { MSP.send_message(MSPCodes.MSP_RC, false, false, get_rssi_config); } function get_rssi_config() { MSP.send_message(MSPCodes.MSP_RSSI_CONFIG, false, false, get_rc_tuning); } function get_rc_tuning() { MSP.send_message(MSPCodes.MSP_RC_TUNING, false, false, get_rc_map); } function get_rc_map() { MSP.send_message(MSPCodes.MSP_RX_MAP, false, false, load_rc_configs); } function load_rc_configs() { MSP.send_message(MSPCodes.MSP_RC_DEADBAND, false, false, load_rx_config); } function load_rx_config() { MSP.send_message(MSPCodes.MSP_RX_CONFIG, false, false, load_mixer_config); } function load_mixer_config() { MSP.send_message(MSPCodes.MSP_MIXER_CONFIG, false, false, load_html); } function load_html() { $('#content').load("./tabs/receiver.html", process_html); } MSP.send_message(MSPCodes.MSP_FEATURE_CONFIG, false, false, get_rc_data); function process_html() { self.analyticsChanges = {}; const featuresElement = $('.tab-receiver .features'); FC.FEATURE_CONFIG.features.generateElements(featuresElement); // translate to user-selected language i18n.localizePage(); $('.deadband input[name="yaw_deadband"]').val(FC.RC_DEADBAND_CONFIG.yaw_deadband); $('.deadband input[name="deadband"]').val(FC.RC_DEADBAND_CONFIG.deadband); $('.deadband input[name="3ddeadbandthrottle"]').val(FC.RC_DEADBAND_CONFIG.deadband3d_throttle); $('.sticks input[name="stick_min"]').val(FC.RX_CONFIG.stick_min); $('.sticks input[name="stick_center"]').val(FC.RX_CONFIG.stick_center); $('.sticks input[name="stick_max"]').val(FC.RX_CONFIG.stick_max); $('select[name="rcInterpolation-select"]').val(FC.RX_CONFIG.rcInterpolation); $('input[name="rcInterpolationInterval-number"]').val(FC.RX_CONFIG.rcInterpolationInterval); $('select[name="rcInterpolation-select"]').change(function () { tab.updateRcInterpolationParameters(); }).change(); // generate bars const bar_names = [ i18n.getMessage('controlAxisRoll'), i18n.getMessage('controlAxisPitch'), i18n.getMessage('controlAxisYaw'), i18n.getMessage('controlAxisThrottle'), ]; const barContainer = $('.tab-receiver .bars'); let auxIndex = 1; const numBars = (FC.RC.active_channels > 0) ? FC.RC.active_channels : 8; for (let i = 0; i < numBars; i++) { let name; if (i < bar_names.length) { name = bar_names[i]; } else { name = i18n.getMessage(`controlAxisAux${auxIndex++}`); } barContainer.append(`\ \ `); } // we could probably use min and max throttle for the range, will see const meterScale = { 'min': 800, 'max': 2200, }; const meterFillArray = []; $('.meter .fill', barContainer).each(function () { meterFillArray.push($(this)); }); const meterLabelArray = []; $('.meter', barContainer).each(function () { meterLabelArray.push($('.label' , this)); }); // correct inner label margin on window resize (i don't know how we could do this in css) tab.resize = function () { const containerWidth = $('.meter:first', barContainer).width(), labelWidth = $('.meter .label:first', barContainer).width(), margin = (containerWidth / 2) - (labelWidth / 2); for (let i = 0; i < meterLabelArray.length; i++) { meterLabelArray[i].css('margin-left', margin); } }; $(window).on('resize', tab.resize).resize(); // trigger so labels get correctly aligned on creation // handle rcmap & rssi aux channel let rcMapLetters = ['A', 'E', 'R', 'T', '1', '2', '3', '4']; let strBuffer = []; for (let i = 0; i < FC.RC_MAP.length; i++) { strBuffer[FC.RC_MAP[i]] = rcMapLetters[i]; } // reconstruct const str = strBuffer.join(''); // set current value $('input[name="rcmap"]').val(str); // validation / filter const lastValid = str; $('input[name="rcmap"]').on('input', function () { let 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 () { const val = $(this).val(); strBuffer = val.split(''); const duplicityBuffer = []; if (val.length !== 8) { $(this).val(lastValid); return false; } // check if characters inside are all valid, also check for duplicity for (let i = 0; i < val.length; i++) { if (rcMapLetters.indexOf(strBuffer[i]) < 0) { $(this).val(lastValid); return false; } if (duplicityBuffer.indexOf(strBuffer[i]) < 0) { duplicityBuffer.push(strBuffer[i]); } else { $(this).val(lastValid); 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()); }); // rssi const rssi_channel_e = $('select[name="rssi_channel"]'); rssi_channel_e.append(``); //1-4 reserved for Roll Pitch Yaw & Throttle, starting at 5 for (let i = 5; i < FC.RC.active_channels + 1; i++) { const messageKey = `controlAxisAux${i-4}`; rssi_channel_e.append(``); } $('select[name="rssi_channel"]').val(FC.RSSI_CONFIG.channel); const serialRxSelectElement = $('select.serialRX'); FC.getSerialRxTypes().forEach((serialRxType, index) => { serialRxSelectElement.append(``); }); serialRxSelectElement.change(function () { const serialRxValue = parseInt($(this).val()); let newValue; if (serialRxValue !== FC.RX_CONFIG.serialrx_provider) { newValue = $(this).find('option:selected').text(); updateSaveButton(true); } tab.analyticsChanges['SerialRx'] = newValue; FC.RX_CONFIG.serialrx_provider = serialRxValue; }); // select current serial RX type serialRxSelectElement.val(FC.RX_CONFIG.serialrx_provider); // Convert to select2 and order alphabetic if (!GUI.isCordova()) { serialRxSelectElement.sortSelect().select2(); } const spiRxTypes = [ 'NRF24_V202_250K', 'NRF24_V202_1M', 'NRF24_SYMA_X', 'NRF24_SYMA_X5C', 'NRF24_CX10', 'CX10A', 'NRF24_H8_3D', 'NRF24_INAV', 'FRSKY_D', 'FRSKY_X', 'A7105_FLYSKY', 'A7105_FLYSKY_2A', 'NRF24_KN', 'SFHSS', 'SPEKTRUM', 'FRSKY_X_LBT', ]; if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) { spiRxTypes.push( 'REDPINE', ); } if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { spiRxTypes.push( 'FRSKY_X_V2', 'FRSKY_X_LBT_V2', 'EXPRESSLRS', ); } const spiRxElement = $('select.spiRx'); for (let i = 0; i < spiRxTypes.length; i++) { spiRxElement.append(``); } spiRxElement.change(function () { const value = parseInt($(this).val()); let newValue = undefined; if (value !== FC.RX_CONFIG.rxSpiProtocol) { newValue = $(this).find('option:selected').text(); updateSaveButton(true); } tab.analyticsChanges['SPIRXProtocol'] = newValue; FC.RX_CONFIG.rxSpiProtocol = value; }); // select current serial RX type spiRxElement.val(FC.RX_CONFIG.rxSpiProtocol); if (!GUI.isCordova()) { // Convert to select2 and order alphabetic spiRxElement.sortSelect().select2(); } if (FC.FEATURE_CONFIG.features.isEnabled('RX_SPI') && FC.RX_CONFIG.rxSpiProtocol == 19 && semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45)) { tab.elrsPassphraseEnabled = true; const elrsUid = $('span.elrsUid'); const elrsUidString = FC.RX_CONFIG.elrsUid.join(','); elrsUid.text(elrsUidString); const elrsPassphrase = $('input.elrsPassphrase'); const passphraseString = lookup_elrs_passphrase(elrsUidString); if (passphraseString) { elrsPassphrase.val(passphraseString); } elrsPassphrase.on('keyup', function() { const passphrase = elrsPassphrase.val(); if (passphrase) { elrsUid.text(elrs_passphrase_to_bytes(passphrase)); } else { elrsUid.text("0.0.0.0.0.0"); } updateSaveButton(true); }); } else { tab.elrsPassphraseEnabled = false; } // UI Hooks function updateSaveButton(reboot=false) { if (reboot) { tab.needReboot = true; } if (tab.needReboot) { $('.update_btn').hide(); $('.save_btn').show(); } else { $('.update_btn').show(); $('.save_btn').hide(); } } $('input.feature', featuresElement).change(function () { const element = $(this); FC.FEATURE_CONFIG.features.updateData(element); updateTabList(FC.FEATURE_CONFIG.features); if (element.attr('name') === "RSSI_ADC" || element.attr('name') === "TELEMETRY") { updateSaveButton(true); } }); function checkShowSerialRxBox() { if (FC.FEATURE_CONFIG.features.isEnabled('RX_SERIAL')) { $('div.serialRXBox').show(); } else { $('div.serialRXBox').hide(); } } function checkShowSpiRxBox() { if (FC.FEATURE_CONFIG.features.isEnabled('RX_SPI')) { $('div.spiRxBox').show(); } else { $('div.spiRxBox').hide(); } } function checkShowElrsPassphrase() { $('#elrsContainer').toggle(tab.elrsPassphraseEnabled); $('input.elrsUid').toggle(tab.elrsPassphraseEnabled); } $(featuresElement).filter('select').change(function () { const element = $(this); FC.FEATURE_CONFIG.features.updateData(element); updateTabList(FC.FEATURE_CONFIG.features); if (element.attr('name') === 'rxMode') { checkShowSerialRxBox(); checkShowSpiRxBox(); checkShowElrsPassphrase(); updateSaveButton(true); } }); checkShowSerialRxBox(); checkShowSpiRxBox(); checkShowElrsPassphrase(); updateSaveButton(); $('a.refresh').click(function () { tab.refresh(function () { GUI.log(i18n.getMessage('receiverDataRefreshed')); }); }); function saveConfiguration(boot=false) { FC.RX_CONFIG.stick_max = parseInt($('.sticks input[name="stick_max"]').val()); FC.RX_CONFIG.stick_center = parseInt($('.sticks input[name="stick_center"]').val()); FC.RX_CONFIG.stick_min = parseInt($('.sticks input[name="stick_min"]').val()); FC.RC_DEADBAND_CONFIG.yaw_deadband = parseInt($('.deadband input[name="yaw_deadband"]').val()); FC.RC_DEADBAND_CONFIG.deadband = parseInt($('.deadband input[name="deadband"]').val()); FC.RC_DEADBAND_CONFIG.deadband3d_throttle = ($('.deadband input[name="3ddeadbandthrottle"]').val()); // catch rc map rcMapLetters = ['A', 'E', 'R', 'T', '1', '2', '3', '4']; strBuffer = $('input[name="rcmap"]').val().split(''); for (let i = 0; i < FC.RC_MAP.length; i++) { FC.RC_MAP[i] = strBuffer.indexOf(rcMapLetters[i]); } // catch rssi aux FC.RSSI_CONFIG.channel = parseInt($('select[name="rssi_channel"]').val()); FC.RX_CONFIG.rcInterpolation = parseInt($('select[name="rcInterpolation-select"]').val()); FC.RX_CONFIG.rcInterpolationInterval = parseInt($('input[name="rcInterpolationInterval-number"]').val()); FC.RX_CONFIG.rcSmoothingSetpointCutoff = parseInt($('input[name="rcSmoothingSetpointHz-number"]').val()); FC.RX_CONFIG.rcSmoothingFeedforwardCutoff = parseInt($('input[name="rcSmoothingFeedforwardCutoff-number"]').val()); FC.RX_CONFIG.rcSmoothingDerivativeType = parseInt($('select[name="rcSmoothingFeedforwardType-select"]').val()); FC.RX_CONFIG.rcInterpolationChannels = parseInt($('select[name="rcSmoothingChannels-select"]').val()); FC.RX_CONFIG.rcSmoothingInputType = parseInt($('select[name="rcSmoothingSetpointType-select"]').val()); if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42)) { FC.RX_CONFIG.rcSmoothingAutoFactor = parseInt($('input[name="rcSmoothingAutoFactor-number"]').val()); } if (tab.elrsPassphraseEnabled) { const elrsUidChars = $('span.elrsUid')[0].innerText.split(',').map(uidChar => parseInt(uidChar, 10)); if (elrsUidChars.length === 6) { FC.RX_CONFIG.elrsUid = elrsUidChars; const elrsUid = $('span.elrsUid')[0].innerText; const elrsPassphrase = $('input.elrsPassphrase').val(); save_elrs_passphrase(elrsUid, elrsPassphrase); } else { FC.RX_CONFIG.elrsUid = [0, 0, 0, 0, 0, 0]; } } function save_rssi_config() { MSP.send_message(MSPCodes.MSP_SET_RSSI_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_RSSI_CONFIG), false, save_rc_configs); } function save_rc_configs() { MSP.send_message(MSPCodes.MSP_SET_RC_DEADBAND, mspHelper.crunch(MSPCodes.MSP_SET_RC_DEADBAND), false, save_rx_config); } function save_rx_config() { const nextCallback = (boot) ? save_feature_config : save_to_eeprom; MSP.send_message(MSPCodes.MSP_SET_RX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_RX_CONFIG), false, nextCallback); } function save_feature_config() { MSP.send_message(MSPCodes.MSP_SET_FEATURE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FEATURE_CONFIG), false, save_to_eeprom); } function save_to_eeprom() { MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, reboot); } function reboot() { GUI.log(i18n.getMessage('configurationEepromSaved')); if (boot) { GUI.tab_switch_cleanup(function() { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitializeConnection); }); } } analytics.sendSaveAndChangeEvents(analytics.EVENT_CATEGORIES.FLIGHT_CONTROLLER, tab.analyticsChanges, 'receiver'); tab.analyticsChanges = {}; MSP.send_message(MSPCodes.MSP_SET_RX_MAP, mspHelper.crunch(MSPCodes.MSP_SET_RX_MAP), false, save_rssi_config); } $('a.update').click(function () { saveConfiguration(false); }); $('a.save').click(function () { saveConfiguration(true); tab.needReboot = false; }); $("a.sticks").click(function() { const windowWidth = 370; const windowHeight = 510; chrome.app.window.create("/tabs/receiver_msp.html", { id: "receiver_msp", innerBounds: { minWidth: windowWidth, minHeight: windowHeight, width: windowWidth, height: windowHeight, 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') { mspHelper.setRawRx(channels); return true; } else { return false; } }; DarkTheme.isDarkThemeEnabled(function(isEnabled) { windowWatcherUtil.passValue(createdWindow, 'darkTheme', isEnabled); }); }); }); let showBindButton = false; if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) { showBindButton = bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.SUPPORTS_RX_BIND); $("a.bind").click(function() { MSP.send_message(MSPCodes.MSP2_BETAFLIGHT_BIND); GUI.log(i18n.getMessage('receiverButtonBindMessage')); }); } $(".bind_btn").toggle(showBindButton); // RC Smoothing const smoothingOnOff = semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44) ? FC.RX_CONFIG.rcSmoothingMode : FC.RX_CONFIG.rcSmoothingType; $('.tab-receiver .rcSmoothing').show(); const rc_smoothing_protocol_e = $('select[name="rcSmoothing-select"]'); rc_smoothing_protocol_e.change(function () { if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { FC.RX_CONFIG.rcSmoothingMode = parseFloat($(this).val()); } else { FC.RX_CONFIG.rcSmoothingType = parseFloat($(this).val()); } updateInterpolationView(); }); rc_smoothing_protocol_e.val(smoothingOnOff); const rcSmoothingNumberElement = $('input[name="rcSmoothingSetpointHz-number"]'); const rcSmoothingFeedforwardNumberElement = $('input[name="rcSmoothingFeedforwardCutoff-number"]'); rcSmoothingNumberElement.val(FC.RX_CONFIG.rcSmoothingSetpointCutoff); rcSmoothingFeedforwardNumberElement.val(FC.RX_CONFIG.rcSmoothingFeedforwardCutoff); $('.tab-receiver .rcSmoothing-setpoint-cutoff').show(); $('select[name="rcSmoothing-setpoint-manual-select"]').val("1"); if (FC.RX_CONFIG.rcSmoothingSetpointCutoff === 0) { $('.tab-receiver .rcSmoothing-setpoint-cutoff').hide(); $('select[name="rcSmoothing-setpoint-manual-select"]').val("0"); } $('select[name="rcSmoothing-setpoint-manual-select"]').change(function () { if ($(this).val() === "0") { rcSmoothingNumberElement.val(0); $('.tab-receiver .rcSmoothing-setpoint-cutoff').hide(); } if ($(this).val() === "1") { rcSmoothingNumberElement.val(FC.RX_CONFIG.rcSmoothingSetpointCutoff); $('.tab-receiver .rcSmoothing-setpoint-cutoff').show(); } }).change(); $('.tab-receiver .rcSmoothing-feedforward-cutoff').show(); $('select[name="rcSmoothing-feedforward-select"]').val("1"); if (FC.RX_CONFIG.rcSmoothingFeedforwardCutoff === 0) { $('select[name="rcSmoothing-feedforward-select"]').val("0"); $('.tab-receiver .rcSmoothing-feedforward-cutoff').hide(); } $('select[name="rcSmoothing-feedforward-select"]').change(function () { if ($(this).val() === "0") { $('.tab-receiver .rcSmoothing-feedforward-cutoff').hide(); rcSmoothingFeedforwardNumberElement.val(0); } if ($(this).val() === "1") { $('.tab-receiver .rcSmoothing-feedforward-cutoff').show(); rcSmoothingFeedforwardNumberElement.val(FC.RX_CONFIG.rcSmoothingFeedforwardCutoff); } }).change(); const rcSmoothingFeedforwardType = $('select[name="rcSmoothingFeedforwardType-select"]'); if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) { rcSmoothingFeedforwardType.append($(``)); } rcSmoothingFeedforwardType.val(FC.RX_CONFIG.rcSmoothingDerivativeType); const rcSmoothingChannels = $('select[name="rcSmoothingChannels-select"]'); rcSmoothingChannels.val(FC.RX_CONFIG.rcInterpolationChannels); const rcSmoothingSetpointType = $('select[name="rcSmoothingSetpointType-select"]'); rcSmoothingSetpointType.val(FC.RX_CONFIG.rcSmoothingInputType); if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42)) { $('select[name="rcSmoothing-setpoint-manual-select"], select[name="rcSmoothing-feedforward-select"]').change(function() { if ($('select[name="rcSmoothing-setpoint-manual-select"]').val() === "0" || $('select[name="rcSmoothing-feedforward-select"]').val() === "0") { $('.tab-receiver .rcSmoothing-auto-factor').show(); } else { $('.tab-receiver .rcSmoothing-auto-factor').hide(); } }); $('select[name="rcSmoothing-setpoint-manual-select"]').change(); const rcSmoothingAutoFactor = $('input[name="rcSmoothingAutoFactor-number"]'); rcSmoothingAutoFactor.val(FC.RX_CONFIG.rcSmoothingAutoFactor); } else { $('.tab-receiver .rcSmoothing-auto-factor').hide(); } if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { $('.receiverRcSmoothingAutoFactorHelp').attr('title', i18n.getMessage("receiverRcSmoothingAutoFactorHelp2")); } updateInterpolationView(); // Only show the MSP control sticks if the MSP Rx feature is enabled $(".sticks_btn").toggle(FC.FEATURE_CONFIG.features.isEnabled('RX_MSP')); const labelsChannelData = { ch1: [], ch2: [], ch3: [], ch4: [], }; $(`.plot_control .ch1, .plot_control .ch2, .plot_control .ch3, .plot_control .ch4`).each(function (){ const element = $(this); if (element.hasClass('ch1')){ labelsChannelData.ch1.push(element); } else if (element.hasClass('ch2')){ labelsChannelData.ch2.push(element); } else if (element.hasClass('ch3')){ labelsChannelData.ch3.push(element); } else if (element.hasClass('ch4')){ labelsChannelData.ch4.push(element); } }); let plotUpdateRate; const rxRefreshRate = $('select[name="rx_refresh_rate"]'); $('a.reset_rate').click(function () { plotUpdateRate = 50; rxRefreshRate.val(plotUpdateRate).change(); }); rxRefreshRate.change(function () { plotUpdateRate = parseInt($(this).val(), 10); // save update rate setConfig({'rx_refresh_rate': plotUpdateRate}); function get_rc_refresh_data() { MSP.send_message(MSPCodes.MSP_RC, false, false, update_ui); } // setup plot const rxPlotData = new Array(FC.RC.active_channels); for (let i = 0; i < rxPlotData.length; i++) { rxPlotData[i] = []; } let samples = 0; const svg = d3.select("svg"); const RX_plot_e = $('#RX_plot'); const margin = {top: 20, right: 0, bottom: 10, left: 40}; let width, height, widthScale, heightScale; function update_receiver_plot_size() { width = RX_plot_e.width() - margin.left - margin.right; height = RX_plot_e.height() - margin.top - margin.bottom; widthScale.range([0, width]); heightScale.range([height, 0]); } function update_ui() { if (FC.RC.active_channels > 0) { // update bars with latest data for (let i = 0; i < FC.RC.active_channels; i++) { meterFillArray[i].css('width', `${((FC.RC.channels[i] - meterScale.min) / (meterScale.max - meterScale.min) * 100).clamp(0, 100)}%`); meterLabelArray[i].text(FC.RC.channels[i]); } labelsChannelData.ch1[0].text(FC.RC.channels[0]); labelsChannelData.ch2[0].text(FC.RC.channels[1]); labelsChannelData.ch3[0].text(FC.RC.channels[2]); labelsChannelData.ch4[0].text(FC.RC.channels[3]); // push latest data to the main array for (let i = 0; i < FC.RC.active_channels; i++) { rxPlotData[i].push([samples, FC.RC.channels[i]]); } // Remove old data from array while (rxPlotData[0].length > 300) { for (let i = 0; i < rxPlotData.length; i++) { rxPlotData[i].shift(); } } } // update required parts of the plot widthScale = d3.scale.linear(). domain([(samples - 299), samples]); heightScale = d3.scale.linear(). domain([800, 2200]); update_receiver_plot_size(); const xGrid = d3.svg.axis(). scale(widthScale). orient("bottom"). tickSize(-height, 0, 0). tickFormat(""); const yGrid = d3.svg.axis(). scale(heightScale). orient("left"). tickSize(-width, 0, 0). tickFormat(""); const xAxis = d3.svg.axis(). scale(widthScale). orient("bottom"). tickFormat(function (d) {return d;}); const yAxis = d3.svg.axis(). scale(heightScale). orient("left"). tickFormat(function (d) {return d;}); const line = d3.svg.line(). x(function (d) {return widthScale(d[0]);}). y(function (d) {return heightScale(d[1]);}); svg.select(".x.grid").call(xGrid); svg.select(".y.grid").call(yGrid); svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); const data = svg.select("g.data"); const lines = data.selectAll("path").data(rxPlotData, function (d, i) {return i;}); lines.enter().append("path").attr("class", "line"); lines.attr('d', line); samples++; } // timer initialization GUI.interval_remove('receiver_pull'); // enable RC data pulling GUI.interval_add('receiver_pull', get_rc_refresh_data, plotUpdateRate, true); }); const result = getConfig('rx_refresh_rate'); if (result.rxRefreshRate) { rxRefreshRate.val(result.rxRefreshRate).change(); } else { rxRefreshRate.change(); // start with default value } // Setup model for preview tab.initModelPreview(); tab.renderModel(); // TODO: Combine two polls together GUI.interval_add('receiver_pull_for_model_preview', tab.getReceiverData, 33, false); // status data pulled via separate timer with static speed GUI.interval_add('status_pull', function status_pull() { MSP.send_message(MSPCodes.MSP_STATUS); }, 250, true); GUI.content_ready(callback); } }; receiver.getReceiverData = function () { MSP.send_message(MSPCodes.MSP_RC, false, false); }; receiver.initModelPreview = function () { this.keepRendering = true; this.model = new Model($('.model_preview'), $('.model_preview canvas')); this.rateCurve = new RateCurve(false); this.currentRates = this.rateCurve.getCurrentRates(); $(window).on('resize', $.bind(this.model.resize, this.model)); }; receiver.renderModel = function () { if (this.keepRendering) { requestAnimationFrame(this.renderModel.bind(this)); } if (!this.clock) { this.clock = new THREE.Clock(); } if (FC.RC.channels[0] && FC.RC.channels[1] && FC.RC.channels[2]) { const delta = this.clock.getDelta(); const roll = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(FC.RC.channels[0], this.currentRates.roll_rate, this.currentRates.rc_rate, this.currentRates.rc_expo, this.currentRates.superexpo, this.currentRates.deadband, this.currentRates.roll_rate_limit); const pitch = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(FC.RC.channels[1], this.currentRates.pitch_rate, this.currentRates.rc_rate_pitch, this.currentRates.rc_pitch_expo, this.currentRates.superexpo, this.currentRates.deadband, this.currentRates.pitch_rate_limit); const yaw = delta * this.rateCurve.rcCommandRawToDegreesPerSecond(FC.RC.channels[2], this.currentRates.yaw_rate, this.currentRates.rc_rate_yaw, this.currentRates.rc_yaw_expo, this.currentRates.superexpo, this.currentRates.yawDeadband, this.currentRates.yaw_rate_limit); this.model.rotateBy(-degToRad(pitch), -degToRad(yaw), -degToRad(roll)); } }; receiver.cleanup = function (callback) { $(window).off('resize', this.resize); if (this.model) { $(window).off('resize', $.proxy(this.model.resize, this.model)); this.model.dispose(); } this.keepRendering = false; if (callback) callback(); }; receiver.refresh = function (callback) { const self = this; GUI.tab_switch_cleanup(function () { self.initialize(); if (callback) { callback(); } }); }; receiver.updateRcInterpolationParameters = function () { if ($('select[name="rcInterpolation-select"]').val() === '3') { $('.tab-receiver .rc-interpolation-manual').show(); } else { $('.tab-receiver .rc-interpolation-manual').hide(); } }; function updateInterpolationView() { const smoothingOnOff = ((semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) ? FC.RX_CONFIG.rcSmoothingMode : FC.RX_CONFIG.rcSmoothingType); $('.tab-receiver .rcInterpolation').hide(); $('.tab-receiver .rcSmoothing-feedforward-cutoff').show(); $('.tab-receiver .rcSmoothing-setpoint-cutoff').show(); $('.tab-receiver .rcSmoothing-feedforward-type').show(); $('.tab-receiver .rcSmoothing-setpoint-type').show(); $('.tab-receiver .rcSmoothing-feedforward-manual').show(); $('.tab-receiver .rcSmoothing-setpoint-manual').show(); if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42)) { if (FC.RX_CONFIG.rcSmoothingFeedforwardCutoff === 0 || FC.RX_CONFIG.rcSmoothingSetpointCutoff === 0) { $('.tab-receiver .rcSmoothing-auto-factor').show(); } } if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { $('.tab-receiver .rcSmoothing-feedforward-type').hide(); $('.tab-receiver .rcSmoothing-setpoint-type').hide(); $('.tab-receiver .rc-smoothing-channels').hide(); $('.tab-receiver input[name="rcSmoothingAutoFactor-number"]').attr("max", "250"); $('.tab-receiver .rcSmoothingType').hide(); $('.tab-receiver .rcSmoothingOff').text(i18n.getMessage('off')); $('.tab-receiver .rcSmoothingOn').text(i18n.getMessage('on')); } else { $('.tab-receiver .rcSmoothingMode').hide(); } if (smoothingOnOff === 0) { if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) { $('.tab-receiver .rcSmoothing-feedforward-cutoff').hide(); $('.tab-receiver .rcSmoothing-setpoint-cutoff').hide(); $('.tab-receiver .rcSmoothing-feedforward-manual').hide(); $('.tab-receiver .rcSmoothing-setpoint-manual').hide(); $('.tab-receiver .rcSmoothing-auto-factor').hide(); } else { $('.tab-receiver .rcInterpolation').show(); $('.tab-receiver .rcSmoothing-feedforward-cutoff').hide(); $('.tab-receiver .rcSmoothing-setpoint-cutoff').hide(); $('.tab-receiver .rcSmoothing-feedforward-type').hide(); $('.tab-receiver .rcSmoothing-setpoint-type').hide(); $('.tab-receiver .rcSmoothing-feedforward-manual').hide(); $('.tab-receiver .rcSmoothing-setpoint-manual').hide(); $('.tab-receiver .rcSmoothing-auto-factor').hide(); } } if (FC.RX_CONFIG.rcSmoothingFeedforwardCutoff === 0) { $('.tab-receiver .rcSmoothing-feedforward-cutoff').hide(); } if (FC.RX_CONFIG.rcSmoothingSetpointCutoff === 0) { $('.tab-receiver .rcSmoothing-setpoint-cutoff').hide(); } } window.TABS.receiver = receiver; export { receiver, };