Merge remote-tracking branch 'origin/master' into mmosca-led-reassignment

pull/2069/head
Marcelo Bezerra 4 months ago
commit 9ce5716a0a

@ -0,0 +1,729 @@
'use strict';
var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var minimist = require('minimist');
var archiver = require('archiver');
var del = require('del');
var NwBuilder = require('nw-builder');
var semver = require('semver');
var gulp = require('gulp');
var concat = require('gulp-concat');
const commandExistsSync = require('command-exists').sync;
// Each key in the *sources* variable must be an array of
// the source files that will be combined into a single
// file and stored in *outputDir*. Each key in *sources*
// must be also present in *output*, whose value indicates
// the filename for the output file which combines the
// contents of the source files.
//
// Keys must be camel cased and end with either 'Css' or
// 'Js' (e.g. someSourcesCss or someSourcesJs). For each
// key, a build task will be generated named by prepending
// 'build-' and converting the key to dash-separated words
// (e.g. someSourcesCss will generate build-some-sources-css).
//
// Tasks with names ending with '-js' will be executed by the
// build-all-js task, while the ones ending with '-css' will
// be done by build-all-css. There's also a build task which
// runs both build-all-css and build-all-js.
//
// The watch task will monitor any files mentioned in the *sources*
// variable and regenerate the corresponding output file when
// they change.
//
// See README.md for details on the other tasks.
var sources = {};
sources.css = [
'./main.css',
'./js/libraries/jquery.nouislider.min.css',
'./js/libraries/jquery.nouislider.pips.min.css',
'./js/libraries/flightindicators.css',
'./src/css/tabs/*.css',
'./src/css/opensans_webfontkit/fonts.css',
'./src/css/font-awesome/css/font-awesome.css',
'./src/css/dropdown-lists/css/style_lists.css',
'./js/libraries/switchery/switchery.css',
'./js/libraries/jbox/jBox.css',
'./node_modules/openlayers/dist/ol.css',
'./src/css/logic.css',
'./src/css/defaults_dialog.css',
];
sources.js = [
'./js/libraries/google-analytics-bundle.js',
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/jquery-ui-npm/jquery-ui.min.js',
'./node_modules/marked/lib/marked.js',
'./js/libraries/d3.min.js',
'./js/libraries/jquery.nouislider.all.min.js',
'./node_modules/three/build/three.min.js',
'./node_modules/three/examples/js/loaders/GLTFLoader.js',
'./node_modules/three/examples/js/controls/OrbitControls.js',
'./js/libraries/nw-dialog.js',
'./js/libraries/bundle_xml2js.js',
'./js/libraries/Projector.js',
'./js/libraries/CanvasRenderer.js',
'./js/libraries/jquery.flightindicators.js',
'./js/libraries/semver.js',
'./js/libraries/jbox/jBox.min.js',
'./js/libraries/switchery/switchery.js',
'./js/libraries/jquery.ba-throttle-debounce.js',
'./js/helpers.js',
'./node_modules/inflection/inflection.min.js',
'./node_modules/bluebird/js/browser/bluebird.min.js',
'./js/injected_methods.js',
'./js/intervals.js',
'./js/timeouts.js',
'./js/pid_controller.js',
'./js/simple_smooth_filter.js',
'./js/walking_average_filter.js',
'./js/gui.js',
'./js/msp/MSPCodes.js',
'./js/msp/MSPHelper.js',
'./js/msp/MSPchainer.js',
'./js/port_handler.js',
'./js/connection/connection.js',
'./js/connection/connectionBle.js',
'./js/connection/connectionSerial.js',
'./js/connection/connectionTcp.js',
'./js/connection/connectionUdp.js',
'./js/servoMixRule.js',
'./js/motorMixRule.js',
'./js/logicCondition.js',
'./js/settings.js',
'./js/outputMapping.js',
'./js/model.js',
'./js/serial_backend.js',
'./js/data_storage.js',
'./js/fc.js',
'./js/msp.js',
'./js/protocols/stm32.js',
'./js/protocols/stm32usbdfu.js',
'./js/localization.js',
'./js/boards.js',
'./js/servoMixerRuleCollection.js',
'./js/motorMixerRuleCollection.js',
'./js/logicConditionsCollection.js',
'./js/logicConditionsStatus.js',
'./js/globalVariablesStatus.js',
'./js/programmingPid.js',
'./js/programmingPidCollection.js',
'./js/programmingPidStatus.js',
'./js/vtx.js',
'./main.js',
'./js/tabs.js',
'./tabs/*.js',
'./js/eventFrequencyAnalyzer.js',
'./js/periodicStatusUpdater.js',
'./js/serial_queue.js',
'./js/msp_balanced_interval.js',
'./tabs/advanced_tuning.js',
'./tabs/ez_tune.js',
'./js/peripherals.js',
'./js/appUpdater.js',
'./js/feature_framework.js',
'./js/defaults_dialog_entries.js',
'./js/defaults_dialog.js',
'./js/safehomeCollection.js',
'./js/safehome.js',
'./js/waypointCollection.js',
'./js/waypoint.js',
'./node_modules/openlayers/dist/ol.js',
'./js/libraries/plotly-latest.min.js',
'./js/sitl.js',
'./js/CliAutoComplete.js',
'./node_modules/jquery-textcomplete/dist/jquery.textcomplete.js'
];
sources.receiverCss = [
'./src/css/tabs/receiver_msp.css',
'./src/css/opensans_webfontkit/fonts.css',
'./js/libraries/jquery.nouislider.min.css',
'./js/libraries/jquery.nouislider.pips.min.css',
];
sources.receiverJs = [
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/jquery-ui-npm/jquery-ui.min.js',
'./js/libraries/jquery.nouislider.all.min.js',
'./tabs/receiver_msp.js'
];
sources.debugTraceJs = [
'./js/debug_trace.js'
];
sources.hexParserJs = [
'./js/workers/hex_parser.js',
];
var output = {
css: 'styles.css',
js: 'script.js',
receiverCss: 'receiver-msp.css',
receiverJs: 'receiver-msp.js',
debugTraceJs: 'debug-trace.js',
hexParserJs: 'hex_parser.js',
};
var outputDir = './build/';
var distDir = './dist/';
var appsDir = './apps/';
function get_task_name(key) {
return 'build-' + key.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
}
function getArguments() {
return minimist(process.argv.slice(2));
}
function getPlatforms() {
const defaultPlatforms = ['win32', 'win64', 'osx64', 'linux32', 'linux64'];
const platform = getArguments().platform;
if (platform) {
if (defaultPlatforms.indexOf(platform) < 0) {
throw new Error(`Invalid platform "${platform}". Available ones are: ${defaultPlatforms}`)
}
return [platform];
}
return defaultPlatforms;
}
function execSync() {
const cmd = arguments[0];
const args = Array.prototype.slice.call(arguments, 1);
const result = child_process.spawnSync(cmd, args, {stdio: 'inherit'});
if (result.error) {
throw result.error;
}
}
// Define build tasks dynamically based on the sources
// and output variables.
var buildCssTasks = [];
var buildJsTasks = [];
(function() {
// Convers fooBarBaz to foo-bar-baz
for (var k in output) {
(function (key) {
var name = get_task_name(key);
if (name.endsWith('-css')) {
buildCssTasks.push(name);
} else if (name.endsWith('-js')) {
buildJsTasks.push(name);
} else {
throw 'Invalid task name: "' + name + '": must end with -css or -js';
}
gulp.task(name, function() {
return gulp.src(sources[key])
.pipe(concat(output[key]))
.pipe(gulp.dest(outputDir));
});
})(k);
}
})();
gulp.task('build-all-js', gulp.parallel(buildJsTasks))
gulp.task('build-all-css', gulp.parallel(buildCssTasks));
gulp.task('build', gulp.parallel('build-all-css', 'build-all-js'));
gulp.task('clean', function() { return del(['./build/**', './dist/**'], {force: true}); });
// Real work for dist task. Done in another task to call it via
// run-sequence.
gulp.task('dist-build', gulp.series('build', function() {
var distSources = [
'./package.json', // For NW.js
'./manifest.json', // For Chrome app
'./eventPage.js',
'./*.html',
'./tabs/*.html',
'./images/**/*',
'./_locales/**/*',
'./build/*',
'./src/css/font-awesome/webfonts/*',
'./src/css/opensans_webfontkit/*.{eot,svg,ttf,woff,woff2}',
'./resources/*.json',
'./resources/models/*',
'./resources/osd/analogue/*.mcm',
'./resources/motor_order/*.svg',
'./resources/sitl/windows/*',
'./resources/sitl/linux/*'
];
return gulp.src(distSources, { base: '.' })
.pipe(gulp.dest(distDir));
}));
gulp.task('dist', gulp.series('clean', 'dist-build'));
// Create app directories in ./apps
gulp.task('apps', gulp.series('dist', function(done) {
var builder = new NwBuilder({
files: './dist/**/*',
buildDir: appsDir,
platforms: getPlatforms(),
flavor: 'normal',
macIcns: './images/inav.icns',
winIco: './images/inav.ico',
version: get_nw_version(),
zip: false
});
builder.on('log', console.log);
builder.build(function (err) {
if (err) {
console.log("Error building NW apps:" + err);
done();
return;
}
// Package apps as .zip files
done();
});
}));
function get_nw_version() {
return semver.valid(semver.coerce(require('./package.json').dependencies.nw));
}
function get_release_filename_base(platform) {
return 'INAV-Configurator_' + platform;
}
function get_release_filename(platform, ext, addition = '') {
var pkg = require('./package.json');
return get_release_filename_base(platform) + addition + '_' + pkg.version + '.' + ext;
}
function build_win_zip(arch) {
return function build_win_zip_proc(done) {
var pkg = require('./package.json');
// Create ZIP
console.log(`Creating ${arch} ZIP file...`);
var src = path.join(appsDir, pkg.name, arch);
var output = fs.createWriteStream(path.join(appsDir, get_release_filename(arch, 'zip')));
var archive = archiver('zip', {
zlib: { level: 9 }
});
archive.on('warning', function(err) { throw err; });
archive.on('error', function(err) { throw err; });
archive.pipe(output);
archive.directory(src, 'INAV Configurator');
return archive.finalize();
}
}
function build_win_iss(arch) {
return function build_win_iss_proc(done) {
if (!getArguments().installer) {
done();
return null;
}
// Create Installer
console.log(`Creating ${arch} Installer...`);
const innoSetup = require('@quanle94/innosetup');
const APPS_DIR = './apps/';
const pkg = require('./package.json');
// Parameters passed to the installer script
const parameters = [];
// Extra parameters to replace inside the iss file
parameters.push(`/Dversion=${pkg.version}`);
parameters.push(`/DarchName=${arch}`);
parameters.push(`/DarchAllowed=${(arch === 'win32') ? 'x86 x64' : 'x64'}`);
parameters.push(`/DarchInstallIn64bit=${(arch === 'win32') ? '' : 'x64'}`);
parameters.push(`/DsourceFolder=${APPS_DIR}`);
parameters.push(`/DtargetFolder=${APPS_DIR}`);
// Show only errors in console
parameters.push(`/Q`);
// Script file to execute
parameters.push("assets/windows/installer.iss");
innoSetup(parameters, {},
function(error) {
if (error != null) {
console.error(`Installer for platform ${arch} finished with error ${error}`);
} else {
console.log(`Installer for platform ${arch} finished`);
}
done();
});
}
}
gulp.task('release-win32', gulp.series(build_win_zip('win32'), build_win_iss('win32')));
gulp.task('release-win64', gulp.series(build_win_zip('win64'), build_win_iss('win64')));
gulp.task('release-osx64', function(done) {
var pkg = require('./package.json');
var src = path.join(appsDir, pkg.name, 'osx64', pkg.name + '.app');
// Check if we want to sign the .app bundle
if (getArguments().codesign) {
// macapptool can be downloaded from
// https://github.com/fiam/macapptool
//
// Make sure the bundle is well formed
execSync('macapptool', '-v', '1', 'fix', src);
// Sign
const codesignArgs = ['macapptool', '-v', '1', 'sign'];
const codesignIdentity = getArguments()['codesign-identity'];
if (codesignIdentity) {
codesignArgs.push('-i', codesignIdentity);
}
codesignArgs.push('-e', 'entitlements.plist');
codesignArgs.push(src)
execSync.apply(this, codesignArgs);
// Check if the bundle is signed
const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', src ];
execSync.apply(this, codesignCheckArgs);
}
// 'old' .zip mode
if (!getArguments().installer) {
const zipFilename = path.join(appsDir, get_release_filename('macOS', 'zip'));
console.log('Creating ZIP file: ' + zipFilename);
var output = fs.createWriteStream(zipFilename);
var archive = archiver('zip', {
zlib: { level: 9 }
});
archive.on('warning', function(err) { throw err; });
archive.on('error', function(err) { throw err; });
archive.pipe(output);
archive.directory(src, 'INAV Configurator.app');
output.on('close', function() {
if (getArguments().notarize) {
console.log('Notarizing DMG file: ' + zipFilename);
const notarizeArgs = ['macapptool', '-v', '1', 'notarize'];
const notarizationUsername = getArguments()['notarization-username'];
if (notarizationUsername) {
notarizeArgs.push('-u', notarizationUsername)
}
const notarizationPassword = getArguments()['notarization-password'];
if (notarizationPassword) {
notarizeArgs.push('-p', notarizationPassword)
}
notarizeArgs.push(zipFilename)
execSync.apply(this, notarizeArgs);
}
done();
});
archive.finalize();
}
// 'new' .dmg mode
else {
const appdmg = require('appdmg');
var target = path.join(appsDir, get_release_filename('macOS', 'dmg'));
console.log('Creating DMG file: ' + target);
var basepath = path.join(appsDir, pkg.name, 'osx64');
console.log('Base path: ' + basepath);
if (fs.existsSync(target)) {
fs.unlinkSync(target);
}
var specs = {};
specs["title"] = "INAV Configurator";
specs["contents"] = [
{ "x": 448, "y": 342, "type": "link", "path": "/Applications" },
{ "x": 192, "y": 344, "type": "file", "path": pkg.name + ".app", "name": "INAV Configurator.app" },
];
specs["background"] = path.join(__dirname, 'assets/osx/dmg-background.png');
specs["format"] = "UDZO";
specs["window"] = {
"size": {
"width": 638,
"height": 479,
}
};
const codesignIdentity = getArguments()['codesign-identity'];
if (getArguments().codesign) {
specs['code-sign'] = {
'signing-identity': codesignIdentity,
}
}
const ee = appdmg({
target: target,
basepath: basepath,
specification: specs,
});
ee.on('progress', function(info) {
//console.log(info);
});
ee.on('error', function(err) {
console.log(err);
});
ee.on('finish', function() {
if (getArguments().codesign) {
// Check if the bundle is signed
const codesignCheckArgs = [ 'codesign', '-vvv', '--deep', '--strict', target ];
execSync.apply(this, codesignCheckArgs);
}
if (getArguments().notarize) {
console.log('Notarizing DMG file: ' + target);
const notarizeArgs = ['xcrun', 'notarytool', 'submit'];
notarizeArgs.push(target);
const notarizationUsername = getArguments()['notarization-username'];
if (notarizationUsername) {
notarizeArgs.push('--apple-id', notarizationUsername)
} else {
throw new Error('Missing notarization username');
}
const notarizationPassword = getArguments()['notarization-password'];
if (notarizationPassword) {
notarizeArgs.push('--password', notarizationPassword)
} else {
throw new Error('Missing notarization password');
}
const notarizationTeamId = getArguments()['notarization-team-id'];
if (notarizationTeamId) {
notarizeArgs.push('--team-id', notarizationTeamId)
} else {
throw new Error('Missing notarization Team ID');
}
notarizeArgs.push('--wait');
const notarizationWebhook = getArguments()['notarization-webhook'];
if (notarizationWebhook) {
notarizeArgs.push('--webhook', notarizationWebhook);
}
execSync.apply(this, notarizeArgs);
console.log('Stapling DMG file: ' + target);
const stapleArgs = ['xcrun', 'stapler', 'staple'];
stapleArgs.push(target);
execSync.apply(this, stapleArgs);
console.log('Checking DMG file: ' + target);
const checkArgs = ['spctl', '-vvv', '--assess', '--type', 'install', target];
execSync.apply(this, checkArgs);
}
done();
});
}
});
function post_build(arch, folder) {
return function post_build_linux(done) {
if ((arch === 'linux32') || (arch === 'linux64')) {
const metadata = require('./package.json');
// Copy Ubuntu launcher scripts to destination dir
const launcherDir = path.join(folder, metadata.name, arch);
console.log(`Copy Ubuntu launcher scripts to ${launcherDir}`);
return gulp.src('assets/linux/**')
.pipe(gulp.dest(launcherDir));
}
return done();
}
}
// Create the dir directory, with write permissions
function createDirIfNotExists(dir) {
fs.mkdir(dir, '0775', function(err) {
if (err && err.code !== 'EEXIST') {
throw err;
}
});
}
function release_deb(arch) {
return function release_deb_proc(done) {
if (!getArguments().installer) {
done();
return null;
}
// Check if dpkg-deb exists
if (!commandExistsSync('dpkg-deb')) {
console.warn(`dpkg-deb command not found, not generating deb package for ${arch}`);
done();
return null;
}
const deb = require('gulp-debian');
const LINUX_INSTALL_DIR = '/opt/inav';
const metadata = require('./package.json');
console.log(`Generating deb package for ${arch}`);
return gulp.src([path.join(appsDir, metadata.name, arch, '*')])
.pipe(deb({
package: metadata.name,
version: metadata.version,
section: 'base',
priority: 'optional',
architecture: getLinuxPackageArch('deb', arch),
maintainer: metadata.author,
description: metadata.description,
preinst: [`rm -rf ${LINUX_INSTALL_DIR}/${metadata.name}`],
postinst: [
`chown root:root ${LINUX_INSTALL_DIR}`,
`chown -R root:root ${LINUX_INSTALL_DIR}/${metadata.name}`,
`xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`,
],
prerm: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`],
depends: ['libgconf-2-4', 'libatomic1'],
changelog: [],
_target: `${LINUX_INSTALL_DIR}/${metadata.name}`,
_out: appsDir,
_copyright: 'assets/linux/copyright',
_clean: true,
}));
}
}
function post_release_deb(arch) {
return function post_release_linux_deb(done) {
if (!getArguments().installer) {
done();
return null;
}
if ((arch === 'linux32') || (arch === 'linux64')) {
var rename = require("gulp-rename");
const metadata = require('./package.json');
const renameFrom = path.join(appsDir, metadata.name + '_' + metadata.version + '_' + getLinuxPackageArch('.deb', arch) + '.deb');
const renameTo = path.join(appsDir, get_release_filename_base(arch) + '_' + metadata.version + '.deb');
// Rename .deb build to common naming
console.log(`Renaming .deb installer ${renameFrom} to ${renameTo}`);
return gulp.src(renameFrom)
.pipe(rename(renameTo))
.pipe(gulp.dest("."));
}
return done();
}
}
function release_rpm(arch) {
return function release_rpm_proc(done) {
if (!getArguments().installer) {
done();
return null;
}
// Check if rpmbuild exists
if (!commandExistsSync('rpmbuild')) {
console.warn(`rpmbuild command not found, not generating rpm package for ${arch}`);
done();
return;
}
const buildRpm = require('rpm-builder');
const NAME_REGEX = /-/g;
const LINUX_INSTALL_DIR = '/opt/inav';
const metadata = require('./package.json');
console.log(`Generating rpm package for ${arch}`);
// The buildRpm does not generate the folder correctly, manually
createDirIfNotExists(appsDir);
const options = {
name: get_release_filename_base(arch), // metadata.name,
version: metadata.version.replace(NAME_REGEX, '_'), // RPM does not like release candidate versions
buildArch: getLinuxPackageArch('rpm', arch),
vendor: metadata.author,
summary: metadata.description,
license: 'GNU General Public License v3.0',
requires: ['libatomic1'],
prefix: '/opt',
files: [{
cwd: path.join(appsDir, metadata.name, arch),
src: '*',
dest: `${LINUX_INSTALL_DIR}/${metadata.name}`,
}],
postInstallScript: [`xdg-desktop-menu install ${LINUX_INSTALL_DIR}/${metadata.name}/${metadata.name}.desktop`],
preUninstallScript: [`xdg-desktop-menu uninstall ${metadata.name}.desktop`],
tempDir: path.join(appsDir, `tmp-rpm-build-${arch}`),
keepTemp: false,
verbose: false,
rpmDest: appsDir,
execOpts: { maxBuffer: 1024 * 1024 * 16 },
};
buildRpm(options, function(err) {
if (err) {
console.error(`Error generating rpm package: ${err}`);
}
done();
});
}
}
function getLinuxPackageArch(type, arch) {
let packArch;
switch (arch) {
case 'linux32':
packArch = 'i386';
break;
case 'linux64':
if (type === 'rpm') {
packArch = 'x86_64';
} else {
packArch = 'amd64';
}
break;
default:
console.error(`Package error, arch: ${arch}`);
process.exit(1);
break;
}
return packArch;
}
function releaseLinux(bits) {
return function() {
console.log(`Generating zip package for linux${bits}`);
var dirname = 'linux' + bits;
var pkg = require('./package.json');
var src = path.join(appsDir, pkg.name, dirname);
var output = fs.createWriteStream(path.join(appsDir, get_release_filename(dirname, 'tar.gz')));
var archive = archiver('tar', {
zlib: { level: 9 },
gzip: true
});
archive.on('warning', function(err) { throw err; });
archive.on('error', function(err) { throw err; });
archive.pipe(output);
archive.directory(src, 'INAV Configurator');
return archive.finalize();
}
}
gulp.task('release-linux32', gulp.series(releaseLinux(32), post_build('linux32', appsDir), release_deb('linux32'), post_release_deb('linux32')));
gulp.task('release-linux64', gulp.series(releaseLinux(64), post_build('linux64', appsDir), release_deb('linux64'), post_release_deb('linux64'), release_rpm('linux64')));
// Create distributable .zip files in ./apps
gulp.task('release', gulp.series('apps', getPlatforms().map(function(v) { return 'release-' + v; })));
gulp.task('watch', function () {
for(var k in output) {
gulp.watch(sources[k], gulp.series(get_task_name(k)));
}
});
gulp.task('default', gulp.series('build'));

@ -20,8 +20,12 @@
<div class="modal__text" data-i18n="appUpdateNotificationDescription"></div>
</div>
<div class="modal__buttons modal__buttons--upbottom">
<a href="https://github.com/iNavFlight/inav-configurator/releases" target="_blank" id="update-notification-download" class="modal__button modal__button--main modal__button--main--inline" data-i18n="downloadUpdatesBtn"></a>
<a id="update-notification-close" class="modal__button modal__button--main modal__button--main--inline" data-i18n="closeUpdateBtn"></a>
<a href="https://github.com/iNavFlight/inav-configurator/releases" target="_blank"
id="update-notification-download"
class="modal__button modal__button--main modal__button--main--inline"
data-i18n="downloadUpdatesBtn"></a>
<a id="update-notification-close" class="modal__button modal__button--main modal__button--main--inline"
data-i18n="closeUpdateBtn"></a>
</div>
</div>
<div class="headerbar">
@ -53,31 +57,31 @@
<div class="dropdown dropdown-dark portsinput__top-element">
<!--suppress HtmlFormInputWithoutLabel -->
<select class="dropdown-select" id="port" title="Port">
<option value="manual" i18n="mainManual"></option>
<!-- port list gets generated here -->
</select>
<option value="manual" i18n="mainManual"></option>
<!-- port list gets generated here -->
</select>
</div>
<div class="dropdown dropdown-dark portsinput__top-element">
<!--suppress HtmlFormInputWithoutLabel -->
<select class="dropdown-select" id="baud" title="Baud Rate">
<option value="115200" selected="selected">115200</option>
<option value="57600">57600</option>
<option value="38400">38400</option>
<option value="28800">28800</option>
<option value="19200">19200</option>
<option value="14400">14400</option>
<option value="9600">9600</option>
<option value="4800">4800</option>
<option value="2400">2400</option>
<option value="1200">1200</option>
</select>
<option value="115200" selected="selected">115200</option>
<option value="57600">57600</option>
<option value="38400">38400</option>
<option value="28800">28800</option>
<option value="19200">19200</option>
<option value="14400">14400</option>
<option value="9600">9600</option>
<option value="4800">4800</option>
<option value="2400">2400</option>
<option value="1200">1200</option>
</select>
</div>
</div>
<div class="portsinput__row">
<div class="portsinput__top-element portsinput__top-element--inline">
<label for="wireless-mode">
<span class="" data-i18n="wirelessModeSwitch"></span>
</label>
<span class="" data-i18n="wirelessModeSwitch"></span>
</label>
<input id="wireless-mode" class=" togglesmall" type="checkbox" />
</div>
</div>
@ -106,10 +110,10 @@
<form name="profile-change" id="profile-change">
<!--suppress HtmlFormInputWithoutLabel -->
<select class="dropdown-select" id="profilechange">
<option value="0" i18n="sensorProfile1"></option>
<option value="1" i18n="sensorProfile2"></option>
<option value="2" i18n="sensorProfile3"></option>
</select>
<option value="0" i18n="sensorProfile1"></option>
<option value="1" i18n="sensorProfile2"></option>
<option value="2" i18n="sensorProfile3"></option>
</select>
</form>
</div>
</div>
@ -118,10 +122,10 @@
<form name="battery-profile-change" id="battery-profile-change">
<!--suppress HtmlFormInputWithoutLabel -->
<select class="dropdown-select" id="batteryprofilechange">
<option value="0" i18n="sensorBatteryProfile1"></option>
<option value="1" i18n="sensorBatteryProfile2"></option>
<option value="2" i18n="sensorBatteryProfile3"></option>
</select>
<option value="0" i18n="sensorBatteryProfile1"></option>
<option value="1" i18n="sensorBatteryProfile2"></option>
<option value="2" i18n="sensorBatteryProfile3"></option>
</select>
</form>
</div>
</div>
@ -228,13 +232,16 @@
<a href="#" data-i18n="tabLanding" class="tabicon ic_welcome" title="Welcome"></a>
</li>
<li class="tab_help">
<a href="https://github.com/iNavFlight/inav/wiki" target="_blank" data-i18n="tabHelp" class="tabicon ic_help" title="Documentation &amp; Support"></a>
<a href="https://github.com/iNavFlight/inav/wiki" target="_blank" data-i18n="tabHelp"
class="tabicon ic_help" title="Documentation &amp; Support"></a>
</li>
<li class="tab_mission_control">
<a href="#" data-i18n="tabMissionControl" class="tabicon ic_mission" title="Mission Control"></a>
<a href="#" data-i18n="tabMissionControl" class="tabicon ic_mission"
title="Mission Control"></a>
</li>
<li class="tab_firmware_flasher">
<a href="#" data-i18n="tabFirmwareFlasher" class="tabicon ic_flasher" title="Firmware Flasher"></a>
<a href="#" data-i18n="tabFirmwareFlasher" class="tabicon ic_flasher"
title="Firmware Flasher"></a>
</li>
<li class="tab_sitl">
<a href="#" data-i18n="tabSitl" class="tabicon ic_sitl" title="SITL"></a>
@ -266,7 +273,8 @@
<a href="#" data-i18n="tabPidTuning" class="tabicon ic_pid" data-i18n_title="tabPidTuning"></a>
</li>
<li class="tab_advanced_tuning">
<a href="#" data-i18n="tabAdvancedTuning" class="tabicon ic_advanced" title="Advanced Tuning"></a>
<a href="#" data-i18n="tabAdvancedTuning" class="tabicon ic_advanced"
title="Advanced Tuning"></a>
</li>
<li class="tab_programming">
<a href="#" data-i18n="tabProgramming" class="tabicon ic_config" title="Programming"></a>
@ -287,7 +295,8 @@
<a href="#" data-i18n="tabMAGNETOMETER" class="tabicon ic_mag" title="MAGNETOMETER"></a>
</li>
<li class="tab_mission_control">
<a href="#" data-i18n="tabMissionControl" class="tabicon ic_mission" title="Mission Control"></a>
<a href="#" data-i18n="tabMissionControl" class="tabicon ic_mission"
title="Mission Control"></a>
</li>
<li class="tab_osd">
<a href="#" data-i18n="tabOSD" class="tabicon ic_osd" title="OSD"></a>
@ -359,7 +368,9 @@
<div data-i18n="savingDefaults"></div>
</div>
<div id="defaults-wrapper" style="display: none">
<div class="defaults-dialog__background"></div>
<div class="defaults-dialog__wizard"></div>
<div class="defaults-dialog__content">
<div class="tab_title" data-i18n="defaultsDialogTitle"></div>
<div class="defaults-dialog__content--wrapper">

@ -24,6 +24,7 @@ const update = require('./globalUpdates');
const appUpdater = require('./appUpdater');
const CliAutoComplete = require('./CliAutoComplete');
const { SITLProcess } = require('./sitl');
const settingsCache = require('./settingsCache');
process.on('uncaughtException', function (error) {
if (process.env.NODE_ENV !== 'development') {
@ -385,6 +386,9 @@ $(function() {
$('#demoModeReset').on('click', function () {
SITLProcess.deleteEepromFile('demo.bin');
});
$('#maintenanceFlushSettingsCache').on('click', function () {
settingsCache.flush();
});
function close_and_cleanup(e) {
if (e.type == 'click' && !$.contains($('div#options-window')[0], e.target) || e.type == 'keyup' && e.keyCode == 27) {
$(document).unbind('click keyup', close_and_cleanup);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,881 @@
'use strict';
var defaultsDialogData = [
{
"title": 'Mini Quad with 3" propellers',
"id": 6,
"notRecommended": false,
"reboot": true,
"mixerToApply": 3,
"wizardPages": ['receiver'],
"settings": [
{
key: "model_preview_type",
value: 3
},
/*
System
*/
{
key: "gyro_hardware_lpf",
value: "256HZ"
},
{
key: "looptime",
value: 500
},
{
key: "motor_pwm_protocol",
value: "DSHOT300"
},
/*
Ez Tune setup
*/
{
key: "ez_enabled",
value: "ON"
},
{
key: "ez_filter_hz",
value: 90
},
{
key: "ez_axis_ratio",
value: 116
},
{
key: "ez_response",
value: 71
},
{
key: "ez_damping",
value: 103
},
{
key: "ez_stability",
value: 105
},
{
key: "ez_aggressiveness",
value: 100
},
{
key: "ez_rate",
value: 134
},
{
key: "ez_expo",
value: 118
},
/*
Mechanics
*/
{
key: "airmode_type",
value: "THROTTLE_THRESHOLD"
},
{
key: "airmode_throttle_threshold",
value: 1150
},
{
key: "mc_iterm_relax",
value: "RP"
},
{
key: "d_boost_min",
value: 1.0
},
{
key: "d_boost_max",
value: 1.0
},
{
key: "antigravity_gain",
value: 2
},
{
key: "antigravity_accelerator",
value: 5
},
/*
* TPA
*/
{
key: "tpa_rate",
value: 20
},
{
key: "tpa_breakpoint",
value: 1200
},
{
key: "platform_type",
value: "MULTIROTOR"
},
{
key: "applied_defaults",
value: 6
},
{
key: "failsafe_procedure",
value: "DROP"
}
]
},
{
"title": 'Mini Quad with 5" propellers',
"id": 2,
"notRecommended": false,
"reboot": true,
"mixerToApply": 3,
"wizardPages": ['receiver'],
"settings": [
{
key: "model_preview_type",
value: 3
},
/*
System
*/
{
key: "gyro_hardware_lpf",
value: "256HZ"
},
{
key: "looptime",
value: 500
},
{
key: "motor_pwm_protocol",
value: "DSHOT300"
},
/*
Ez Tune setup
*/
{
key: "ez_enabled",
value: "ON"
},
{
key: "ez_filter_hz",
value: 110
},
{
key: "ez_axis_ratio",
value: 110
},
{
key: "ez_response",
value: 92
},
{
key: "ez_damping",
value: 108
},
{
key: "ez_stability",
value: 110
},
{
key: "ez_aggressiveness",
value: 80
},
{
key: "ez_rate",
value: 134
},
{
key: "ez_expo",
value: 118
},
/*
Dynamic gyro LPF
*/
{
key: "gyro_use_dyn_lpf",
value: "ON"
},
{
key: "gyro_dyn_lpf_min_hz",
value: 85
},
{
key: "gyro_dyn_lpf_max_hz",
value: 300
},
{
key: "gyro_dyn_lpf_curve_expo",
value: 3
},
/*
Mechanics
*/
{
key: "airmode_type",
value: "THROTTLE_THRESHOLD"
},
{
key: "airmode_throttle_threshold",
value: 1150
},
{
key: "mc_iterm_relax",
value: "RP"
},
{
key: "d_boost_min",
value: 1.0
},
{
key: "d_boost_max",
value: 1.0
},
{
key: "antigravity_gain",
value: 2
},
{
key: "antigravity_accelerator",
value: 5
},
/*
* TPA
*/
{
key: "tpa_rate",
value: 20
},
{
key: "tpa_breakpoint",
value: 1200
},
{
key: "platform_type",
value: "MULTIROTOR"
},
{
key: "applied_defaults",
value: 2
},
{
key: "failsafe_procedure",
value: "DROP"
}
]
},
{
"title": 'Mini Quad with 7" propellers',
"id": 5,
"notRecommended": false,
"reboot": true,
"mixerToApply": 3,
"wizardPages": ['receiver'],
"settings": [
{
key: "model_preview_type",
value: 3
},
/*
System
*/
{
key: "gyro_hardware_lpf",
value: "256HZ"
},
{
key: "looptime",
value: 500
},
{
key: "motor_pwm_protocol",
value: "DSHOT300"
},
/*
Ez Tune setup
*/
{
key: "ez_enabled",
value: "ON"
},
{
key: "ez_filter_hz",
value: 90
},
{
key: "ez_axis_ratio",
value: 110
},
{
key: "ez_response",
value: 101
},
{
key: "ez_damping",
value: 115
},
{
key: "ez_stability",
value: 100
},
{
key: "ez_aggressiveness",
value: 100
},
{
key: "ez_rate",
value: 134
},
{
key: "ez_expo",
value: 118
},
/*
Mechanics
*/
{
key: "airmode_type",
value: "THROTTLE_THRESHOLD"
},
{
key: "airmode_throttle_threshold",
value: 1150
},
{
key: "mc_iterm_relax",
value: "RPY"
},
{
key: "d_boost_min",
value: 0.8
},
{
key: "d_boost_max",
value: 1.2
},
{
key: "antigravity_gain",
value: 2
},
{
key: "antigravity_accelerator",
value: 5
},
/*
* TPA
*/
{
key: "tpa_rate",
value: 20
},
{
key: "tpa_breakpoint",
value: 1200
},
{
key: "platform_type",
value: "MULTIROTOR"
},
{
key: "applied_defaults",
value: 5
},
{
key: "failsafe_procedure",
value: "DROP"
}
]
},
{
"title": 'Airplane with a Tail',
"notRecommended": false,
"id": 3,
"reboot": true,
"mixerToApply": 14,
"wizardPages": ['receiver'],
"settings": [
{
key: "model_preview_type",
value: 14
},
{
key: "platform_type",
value: "AIRPLANE"
},
{
key: "applied_defaults",
value: 3
},
{
key: "gyro_hardware_lpf",
value: "256HZ"
},
{
key: "gyro_main_lpf_hz",
value: 25
},
{
key: "dterm_lpf_hz",
value: 10
},
{
key: "d_boost_min",
value: 1
},
{
key: "d_boost_max",
value: 1
},
{
key: "dynamic_gyro_notch_enabled",
value: "ON"
},
{
key: "dynamic_gyro_notch_q",
value: 250
},
{
key: "dynamic_gyro_notch_min_hz",
value: 30
},
{
key: "motor_pwm_protocol",
value: "STANDARD"
},
{
key: "ahrs_inertia_comp_method",
value: "ADAPTIVE"
},
{
key: "throttle_idle",
value: 5.0
},
{
key: "rc_yaw_expo",
value: 30
},
{
key: "rc_expo",
value: 30
},
{
key: "roll_rate",
value: 18
},
{
key: "pitch_rate",
value: 9
},
{
key: "yaw_rate",
value: 3
},
{
key: "nav_fw_pos_z_p",
value: 25
},
{
key: "nav_fw_pos_z_i",
value: 5
},
{
key: "nav_fw_pos_z_d",
value: 8
},
{
key: "nav_fw_pos_xy_p",
value: 55
},
{
key: "fw_turn_assist_pitch_gain",
value: 0.4
},
{
key: "max_angle_inclination_rll",
value: 450
},
{
key: "nav_fw_bank_angle",
value: 35
},
{
key: "fw_p_pitch",
value: 15
},
{
key: "fw_i_pitch",
value: 5
},
{
key: "fw_d_pitch",
value: 5
},
{
key: "fw_ff_pitch",
value: 80
},
{
key: "fw_p_roll",
value: 15
},
{
key: "fw_i_roll",
value: 3
},
{
key: "fw_d_roll",
value: 7
},
{
key: "fw_ff_roll",
value: 50
},
{
key: "fw_p_yaw",
value: 50
},
{
key: "fw_i_yaw",
value: 0
},
{
key: "fw_d_yaw",
value: 20
},
{
key: "fw_ff_yaw",
value: 255
},
{
key: "airmode_type",
value: "STICK_CENTER_ONCE"
},
{
key: "small_angle",
value: 180
},
{
key: "nav_fw_control_smoothness",
value: 2
},
{
key: "nav_rth_allow_landing",
value: "FS_ONLY"
},
{
key: "nav_rth_altitude",
value: 5000
},
{
key: "nav_wp_radius",
value: 800
},
{
key: "nav_wp_max_safe_distance",
value: 500
},
{
key: "nav_fw_launch_max_angle",
value: 45
},
{
key: "nav_fw_launch_motor_delay",
value: 100
},
{
key: "nav_fw_launch_max_altitude",
value: 5000
},
{
key: "nav_fw_launch_climb_angle",
value: 25
},
],
},
{
"title": 'Airplane without a Tail (Wing, Delta, etc)',
"notRecommended": false,
"id": 4,
"reboot": true,
"mixerToApply": 8,
"wizardPages": ['receiver'],
"settings": [
{
key: "model_preview_type",
value: 8
},
{
key: "platform_type",
value: "AIRPLANE"
},
{
key: "applied_defaults",
value: 4
},
{
key: "gyro_hardware_lpf",
value: "256HZ"
},
{
key: "gyro_main_lpf_hz",
value: 25
},
{
key: "dterm_lpf_hz",
value: 10
},
{
key: "d_boost_min",
value: 1
},
{
key: "d_boost_max",
value: 1
},
{
key: "dynamic_gyro_notch_enabled",
value: "ON"
},
{
key: "dynamic_gyro_notch_q",
value: 250
},
{
key: "dynamic_gyro_notch_min_hz",
value: 30
},
{
key: "motor_pwm_protocol",
value: "STANDARD"
},
{
key: "ahrs_inertia_comp_method",
value: "ADAPTIVE"
},
{
key: "throttle_idle",
value: 5.0
},
{
key: "rc_yaw_expo",
value: 30
},
{
key: "rc_expo",
value: 30
},
{
key: "roll_rate",
value: 18
},
{
key: "pitch_rate",
value: 9
},
{
key: "yaw_rate",
value: 3
},
{
key: "nav_fw_pos_z_p",
value: 35
},
{
key: "nav_fw_pos_z_i",
value: 5
},
{
key: "nav_fw_pos_z_d",
value: 10
},
{
key: "nav_fw_pos_xy_p",
value: 70
},
{
key: "fw_turn_assist_pitch_gain",
value: 0.3
},
{
key: "max_angle_inclination_rll",
value: 550
},
{
key: "nav_fw_bank_angle",
value: 45
},
{
key: "fw_p_pitch",
value: 15
},
{
key: "fw_i_pitch",
value: 5
},
{
key: "fw_d_pitch",
value: 5
},
{
key: "fw_ff_pitch",
value: 70
},
{
key: "fw_p_roll",
value: 15
},
{
key: "fw_i_roll",
value: 3
},
{
key: "fw_d_roll",
value: 7
},
{
key: "fw_ff_roll",
value: 50
},
{
key: "fw_p_yaw",
value: 20
},
{
key: "fw_i_yaw",
value: 0
},
{
key: "fw_d_yaw",
value: 0
},
{
key: "fw_ff_yaw",
value: 100
},
{
key: "airmode_type",
value: "STICK_CENTER_ONCE"
},
{
key: "small_angle",
value: 180
},
{
key: "nav_fw_control_smoothness",
value: 2
},
{
key: "nav_rth_allow_landing",
value: "FS_ONLY"
},
{
key: "nav_rth_altitude",
value: 5000
},
{
key: "nav_wp_radius",
value: 1000
},
{
key: "nav_wp_max_safe_distance",
value: 500
},
{
key: "nav_fw_launch_max_angle",
value: 75
},
{
key: "nav_fw_launch_motor_delay",
value: 100
},
{
key: "nav_fw_launch_max_altitude",
value: 5000
},
{
key: "nav_fw_launch_climb_angle",
value: 25
},
],
},
{
"title": 'Rovers & Boats',
"id": 1,
"notRecommended": false,
"reboot": true,
"mixerToApply": 31,
"wizardPages": ['receiver'],
"settings": [
{
key: "model_preview_type",
value: 31
},
{
key: "gyro_hardware_lpf",
value: "256HZ"
},
{
key: "gyro_main_lpf_hz",
value: 10
},
{
key: "motor_pwm_protocol",
value: "STANDARD"
},
{
key: "applied_defaults",
value: 1
},
{
key: "failsafe_procedure",
value: "DROP"
},
{
key: "platform_type",
value: "ROVER"
},
{
key: "nav_wp_max_safe_distance",
value: 500
},
{
key: "nav_fw_loiter_radius",
value: 100
},
{
key: "nav_fw_yaw_deadband",
value: 5
},
{
key: "pidsum_limit_yaw",
value: 500
},
{
key: "nav_fw_pos_hdg_p",
value: 60
},
{
key: "nav_fw_pos_hdg_i",
value: 2
},
{
key: "nav_fw_pos_hdg_d",
value: 0
}
]
},
{
"title": 'Keep current settings (Not recommended)',
"id": 0,
"notRecommended": true,
"reboot": false,
"settings": [
{
key: "applied_defaults",
value: 1
}
]
}
];
module.exports = defaultsDialogData;

@ -14,6 +14,7 @@ const FwApproachCollection = require('./fwApproachCollection')
const { PLATFORM } = require('./model')
const VTX = require('./vtx');
const BitHelper = require('./bitHelper');
const { FLIGHT_MODES } = require('./flightModes');
var FC = {
@ -28,8 +29,8 @@ var FC = {
RC_MAP: null,
RC: null,
RC_tuning: null,
AUX_CONFIG: null,
AUX_CONFIG_IDS: null,
AUX_CONFIG: [],
AUX_CONFIG_IDS: [],
MODE_RANGES: null,
ADJUSTMENT_RANGES: null,
SERVO_CONFIG: null,
@ -77,7 +78,6 @@ var FC = {
MIXER_CONFIG: null,
BATTERY_CONFIG: null,
OUTPUT_MAPPING: null,
SETTINGS: null,
BRAKING_CONFIG: null,
SAFEHOMES: null,
BOARD_ALIGNMENT: null,
@ -85,6 +85,7 @@ var FC = {
FEATURES: null,
RATE_DYNAMICS: null,
EZ_TUNE: null,
FLIGHT_MODES: null,
restartRequired: false,
MAX_SERVO_RATE: 125,
@ -198,8 +199,16 @@ var FC = {
manual_yaw_rate: 0,
};
this.AUX_CONFIG = [];
this.AUX_CONFIG_IDS = [];
this.generateAuxConfig = function () {
console.log('Generating AUX_CONFIG');
this.AUX_CONFIG = [];
for ( let i = 0; i < this.AUX_CONFIG_IDS.length; i++ ) {
let found = FLIGHT_MODES.find( mode => mode.permanentId === this.AUX_CONFIG_IDS[i] );
if (found) {
this.AUX_CONFIG.push(found.boxName);
}
}
};
this.MODE_RANGES = [];
this.ADJUSTMENT_RANGES = [];
@ -560,8 +569,6 @@ var FC = {
this.OUTPUT_MAPPING = new OutputMappingCollection();
this.SETTINGS = {};
this.SAFEHOMES = new SafehomeCollection();
this.RATE_DYNAMICS = {
@ -940,11 +947,13 @@ var FC = {
return this.getServoMixInputNames()[input];
},
getModeId: function (name) {
for (var i = 0; i < this.AUX_CONFIG.length; i++) {
if (this.AUX_CONFIG[i] == name)
return i;
let mode = FLIGHT_MODES.find( mode => mode.boxName === name );
if (mode) {
return mode.permanentId;
} else {
return -1;
}
return -1;
},
isModeBitSet: function (i) {
return BitHelper.bit_check(this.CONFIG.mode[Math.trunc(i / 32)], i % 32);

@ -0,0 +1,286 @@
'use strict';
var FLIGHT_MODES = [
{
boxId: 0,
boxName: "ARM",
permanentId: 0
},
{
boxId: 1,
boxName: "ANGLE",
permanentId: 1
},
{
boxId: 2,
boxName: "HORIZON",
permanentId: 2
},
{
boxId: 3,
boxName: "NAV ALTHOLD",
permanentId: 3
},
{
boxId: 4,
boxName: "HEADING HOLD",
permanentId: 5
},
{
boxId: 5,
boxName: "HEADFREE",
permanentId: 6
},
{
boxId: 6,
boxName: "HEADADJ",
permanentId: 7
},
{
boxId: 7,
boxName: "CAMSTAB",
permanentId: 8
},
{
boxId: 8,
boxName: "NAV RTH",
permanentId: 10
},
{
boxId: 9,
boxName: "NAV POSHOLD",
permanentId: 11
},
{
boxId: 10,
boxName: "MANUAL",
permanentId: 12
},
{
boxId: 11,
boxName: "BEEPER",
permanentId: 13
},
{
boxId: 12,
boxName: "LEDS OFF",
permanentId: 15
},
{
boxId: 13,
boxName: "LIGHTS",
permanentId: 16
},
{
boxId: 15,
boxName: "OSD OFF",
permanentId: 19
},
{
boxId: 16,
boxName: "TELEMETRY",
permanentId: 20
},
{
boxId: 28,
boxName: "AUTO TUNE",
permanentId: 21
},
{
boxId: 17,
boxName: "BLACKBOX",
permanentId: 26
},
{
boxId: 18,
boxName: "FAILSAFE",
permanentId: 27
},
{
boxId: 19,
boxName: "NAV WP",
permanentId: 28
},
{
boxId: 20,
boxName: "AIR MODE",
permanentId: 29
},
{
boxId: 21,
boxName: "HOME RESET",
permanentId: 30
},
{
boxId: 22,
boxName: "GCS NAV",
permanentId: 31
},
{
boxId: 39,
boxName: "FPV ANGLE MIX",
permanentId: 32
},
{
boxId: 24,
boxName: "SURFACE",
permanentId: 33
},
{
boxId: 25,
boxName: "FLAPERON",
permanentId: 34
},
{
boxId: 26,
boxName: "TURN ASSIST",
permanentId: 35
},
{
boxId: 14,
boxName: "NAV LAUNCH",
permanentId: 36
},
{
boxId: 27,
boxName: "SERVO AUTOTRIM",
permanentId: 37
},
{
boxId: 23,
boxName: "KILLSWITCH",
permanentId: 38
},
{
boxId: 29,
boxName: "CAMERA CONTROL 1",
permanentId: 39
},
{
boxId: 30,
boxName: "CAMERA CONTROL 2",
permanentId: 40
},
{
boxId: 31,
boxName: "CAMERA CONTROL 3",
permanentId: 41
},
{
boxId: 32,
boxName: "OSD ALT 1",
permanentId: 42
},
{
boxId: 33,
boxName: "OSD ALT 2",
permanentId: 43
},
{
boxId: 34,
boxName: "OSD ALT 3",
permanentId: 44
},
{
boxId: 35,
boxName: "NAV COURSE HOLD",
permanentId: 45
},
{
boxId: 36,
boxName: "MC BRAKING",
permanentId: 46
},
{
boxId: 37,
boxName: "USER1",
permanentId: 47
},
{
boxId: 38,
boxName: "USER2",
permanentId: 48
},
{
boxId: 48,
boxName: "USER3",
permanentId: 57
},
{
boxId: 49,
boxName: "USER4",
permanentId: 58
},
{
boxId: 40,
boxName: "LOITER CHANGE",
permanentId: 49
},
{
boxId: 41,
boxName: "MSP RC OVERRIDE",
permanentId: 50
},
{
boxId: 42,
boxName: "PREARM",
permanentId: 51
},
{
boxId: 43,
boxName: "TURTLE",
permanentId: 52
},
{
boxId: 44,
boxName: "NAV CRUISE",
permanentId: 53
},
{
boxId: 45,
boxName: "AUTO LEVEL TRIM",
permanentId: 54
},
{
boxId: 46,
boxName: "WP PLANNER",
permanentId: 55
},
{
boxId: 47,
boxName: "SOARING",
permanentId: 56
},
{
boxId: 50,
boxName: "MISSION CHANGE",
permanentId: 59
},
{
boxId: 51,
boxName: "BEEPER MUTE",
permanentId: 60
},
{
boxId: 52,
boxName: "MULTI FUNCTION",
permanentId: 61
},
{
boxId: 53,
boxName: "MIXER PROFILE 2",
permanentId: 62
},
{
boxId: 54,
boxName: "MIXER TRANSITION",
permanentId: 63
},
{
boxId: 55,
boxName: "ANGLE HOLD",
permanentId: 64
}
];
module.exports = {FLIGHT_MODES};

@ -9,6 +9,7 @@ const FC = require('./fc');
const interval = require('./intervals');
const { scaleRangeInt } = require('./helpers');
const i18n = require('./localization');
const mspDeduplicationQueue = require("./msp/mspDeduplicationQueue");
var TABS = {}; // filled by individual tab js file
@ -90,6 +91,7 @@ GUI_control.prototype.log = function (message) {
// default switch doesn't require callback to be set
GUI_control.prototype.tab_switch_cleanup = function (callback) {
MSP.callbacks_cleanup(); // we don't care about any old data that might or might not arrive
mspDeduplicationQueue.flush();
interval.killAll(['global_data_refresh', 'msp-load-update', 'ltm-connection-check']);

@ -84,7 +84,6 @@ var MSPCodes = {
MSP_PID: 112,
MSP_ACTIVEBOXES: 113,
MSP_MOTOR_PINS: 115,
MSP_BOXNAMES: 116,
MSP_PIDNAMES: 117,
MSP_WP: 118,
MSP_BOXIDS: 119,

@ -20,6 +20,7 @@ const { FwApproach } = require('./../fwApproach');
const Waypoint = require('./../waypoint');
const mspDeduplicationQueue = require('./mspDeduplicationQueue');
const mspStatistics = require('./mspStatistics');
const settingsCache = require('./../settingsCache');
var mspHelper = (function () {
var self = {};
@ -386,21 +387,6 @@ var mspHelper = (function () {
case MSPCodes.MSP_MOTOR_PINS:
console.log(data);
break;
case MSPCodes.MSP_BOXNAMES:
//noinspection JSUndeclaredVariable
FC.AUX_CONFIG = []; // empty the array as new data is coming in
buff = [];
for (let i = 0; i < data.byteLength; i++) {
if (data.getUint8(i) == 0x3B) { // ; (delimeter char)
FC.AUX_CONFIG.push(String.fromCharCode.apply(null, buff)); // convert bytes into ASCII and save as strings
// empty buffer
buff = [];
} else {
buff.push(data.getUint8(i));
}
}
break;
case MSPCodes.MSP_PIDNAMES:
//noinspection JSUndeclaredVariable
FC.PID_names = []; // empty the array as new data is coming in
@ -1863,6 +1849,7 @@ var mspHelper = (function () {
break;
case MSPCodes.MSP2_SET_CF_SERIAL_CONFIG:
console.log('will crunch', FC.SERIAL_CONFIG);
for (let i = 0; i < FC.SERIAL_CONFIG.ports.length; i++) {
var serialPort = FC.SERIAL_CONFIG.ports[i];
@ -3078,9 +3065,12 @@ var mspHelper = (function () {
};
self._getSetting = function (name) {
if (FC.SETTINGS[name]) {
return Promise.resolve(FC.SETTINGS[name]);
const storedSetting = settingsCache.get(name);
if (typeof storedSetting !== 'undefined') {
return Promise.resolve(storedSetting);
}
var data = [];
self._encodeSettingReference(name, null, data);
return MSP.promise(MSPCodes.MSP2_COMMON_SETTING_INFO, data).then(function (result) {
@ -3127,7 +3117,7 @@ var mspHelper = (function () {
}
setting.table = { values: values };
}
FC.SETTINGS[name] = setting;
settingsCache.set(name, setting);
return setting;
});
}
@ -3244,7 +3234,7 @@ var mspHelper = (function () {
this.encodeSetting(name, value).then(function (data) {
return MSP.promise(MSPCodes.MSPV2_SET_SETTING, data).then(callback);
}).catch(error => {
console.log("Invalid setting: " + name);
console.log("Invalid setting: " + name, error);
return Promise.resolve().then(callback);
});
};

@ -8,7 +8,8 @@ const ConnectionSerial = require('./connection/connectionSerial');
var usbDevices = [
{ 'vendorId': 1155, 'productId': 57105},
{ 'vendorId': 11836, 'productId': 57105}
{ 'vendorId': 11836, 'productId': 57105},
{ 'vendorId': 12619, 'productId': 262}, // APM32 DFU Bootloader
];
@ -297,4 +298,4 @@ PortHandler.flush_callbacks = function () {
return killed;
};
module.exports = { usbDevices, PortHandler };
module.exports = { usbDevices, PortHandler };

@ -335,6 +335,7 @@ const serialPortHelper = (function () {
publicScope.clearByFunction(functionName);
let config = publicScope.getPortByIdentifier(port);
if (config) {
config.functions = [functionName];

@ -460,27 +460,22 @@ var SerialBackend = (function () {
$('#dataflash_wrapper_global').show();
/*
* Get BOXNAMES since it is used for some reason....
* Init PIDs bank with a length that depends on the version
*/
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, function () {
/*
* Init PIDs bank with a length that depends on the version
*/
let pidCount = 11;
let pidCount = 11;
for (let i = 0; i < pidCount; i++) {
FC.PIDs.push(new Array(4));
}
for (let i = 0; i < pidCount; i++) {
FC.PIDs.push(new Array(4));
}
interval.add('msp-load-update', function () {
$('#msp-version').text("MSP version: " + MSP.protocolVersion.toFixed(0));
$('#msp-load').text("MSP load: " + mspQueue.getLoad().toFixed(1));
$('#msp-roundtrip').text("MSP round trip: " + mspQueue.getRoundtrip().toFixed(0));
$('#hardware-roundtrip').text("HW round trip: " + mspQueue.getHardwareRoundtrip().toFixed(0));
}, 100);
interval.add('msp-load-update', function () {
$('#msp-version').text("MSP version: " + MSP.protocolVersion.toFixed(0));
$('#msp-load').text("MSP load: " + mspQueue.getLoad().toFixed(1));
$('#msp-roundtrip').text("MSP round trip: " + mspQueue.getRoundtrip().toFixed(0));
$('#hardware-roundtrip').text("HW round trip: " + mspQueue.getHardwareRoundtrip().toFixed(0));
}, 100);
interval.add('global_data_refresh', periodicStatusUpdater.run, periodicStatusUpdater.getUpdateInterval(CONFIGURATOR.connection.bitrate), false);
});
interval.add('global_data_refresh', periodicStatusUpdater.run, periodicStatusUpdater.getUpdateInterval(CONFIGURATOR.connection.bitrate), false);
});
}

@ -0,0 +1,47 @@
'use strict';
const Store = require('electron-store');
const store = new Store();
const FC = require('./fc');
var settingsCache = (function() {
let publicScope = {};
let privateScope = {};
const SETTINGS_KEY = 'settings';
privateScope.getSetingKey = function(settingName) {
return FC.CONFIG.target + '_' + FC.CONFIG.flightControllerVersion + '_' + settingName;
}
publicScope.flush = function() {
store.delete(SETTINGS_KEY);
console.log('Settings cache flushed');
};
publicScope.get = function(settingName) {
let settings = store.get(SETTINGS_KEY, null);
if (settings === null) {
return undefined;
}
let setting = settings[privateScope.getSetingKey(settingName)];
return setting;
};
publicScope.set = function(settingName, value) {
let settings = store.get(SETTINGS_KEY, null);
if (settings === null) {
settings = {};
}
settings[privateScope.getSetingKey(settingName)] = value;
store.set(SETTINGS_KEY, settings);
};
return publicScope;
}());
module.exports = settingsCache;

@ -0,0 +1,58 @@
'use strict';
const mspHelper = require('./msp/MSPHelper');
const serialPortHelper = require('./serialPortHelper');
const FC = require('./fc');
var wizardSaveFramework = (function () {
let self = {};
self.saveSetting = function (config, callback) {
/*
serialrx_provider to 2
serialrx_provider to 6
*/
switch (config.name) {
case 'receiverPort':
serialPortHelper.set(config.value, 'RX_SERIAL', null);
mspHelper.saveSerialPorts(callback);
break;
case 'receiverProtocol':
mspHelper.setSetting('serialrx_provider', config.value, callback);
break;
default:
callback();
break;
}
};
self.handleSetting = function (configs, finalCallback) {
if (configs.length > 0) {
let setting = configs.shift();
self.saveSetting(setting, function () {
self.handleSetting(configs, finalCallback);
});
} else {
console.log('Nothing to save');
finalCallback();
}
};
self.persist = function (config, finalCallback) {
if (config === null || config === undefined || config.length === 0) {
finalCallback();
return;
}
let configCopy = Array.from(config);
self.handleSetting(configCopy, finalCallback);
}
return self;
})();
module.exports = wizardSaveFramework;

@ -0,0 +1,41 @@
'use strict';
const mspHelper = require('./msp/MSPHelper');
const serialPortHelper = require('./serialPortHelper');
const wizardUiBindings = (function () {
let self = {};
self.receiver = function ($content) {
mspHelper.loadSerialPorts(function () {
let $receiverPort = $content.find('#wizard-receiver-port');
let ports = serialPortHelper.getPortIdentifiersForFunction('RX_SERIAL');
let currentPort = null;
if (ports.length > 0) {
currentPort = ports[0];
}
let availablePorts = serialPortHelper.getPortList();
$receiverPort.append('<option value="-1">NONE</option>');
for (let i = 0; i < availablePorts.length; i++) {
let port = availablePorts[i];
$receiverPort.append('<option value="' + port.identifier + '">' + port.displayName + '</option>');
}
if (currentPort !== null) {
$receiverPort.val(currentPort);
} else {
$receiverPort.val(-1);
}
});
}
return self;
})();
module.exports = wizardUiBindings;

@ -1325,6 +1325,24 @@
"portsFunction_SBUS_OUTPUT": {
"message": "SBus Output"
},
"pidTuning_Other": {
"message": "Other"
},
"pidTuning_Limits": {
"message": "Limits"
},
"pidTuning_HeadingHold_Rate": {
"message": "Heading Hold Rate (°/s)"
},
"pidTuning_Max_Inclination_Angle": {
"message": "Max Inclination Angle"
},
"pidTuning_Max_Roll": {
"message": "Roll (°/10)"
},
"pidTuning_Max_Pitch": {
"message": "Pitch (°/10)"
},
"pidTuning_ShowAllPIDs": {
"message": "Show all PIDs"
},
@ -1391,6 +1409,30 @@
"pidTuning_RatesAndExpo": {
"message": "Rates & Expo"
},
"pidTuning_Rates_Stabilized": {
"message": "Stabilized Rates"
},
"pidTuning_Rates_Roll": {
"message": "Roll (°/s)"
},
"pidTuning_Rates_Pitch": {
"message": "Pitch (°/s)"
},
"pidTuning_Rates_Yaw": {
"message": "Yaw (°/s)"
},
"pidTuning_Expo_Stabilized": {
"message": "Stabilized Expo"
},
"pidTuning_Expo_Manual": {
"message": "Manual Expo"
},
"pidTuning_Expo_RollPitch": {
"message": "Roll & Pitch (%)"
},
"pidTuning_Expo_Yaw": {
"message": "Yaw (%)"
},
"pidTuning_RateDynamics": {
"message": "Rate Dynamics"
},
@ -1592,6 +1634,18 @@
"pidTuning_LoadedProfile": {
"message": "Loaded Profile: <strong style=\"color: #37a8db\">$1</strong>"
},
"pidTuning_Manual_Rates": {
"message": "Manual Rates"
},
"pidTuning_Manual_Roll": {
"message": "Roll (%)"
},
"pidTuning_Manual_Pitch": {
"message": "Pitch (%)"
},
"pidTuning_Manual_Yaw": {
"message": "Yaw (%)"
},
"pidTuning_gyro_use_dyn_lpf": {
"message": "Dynamic gyro LPF"
},
@ -5812,5 +5866,11 @@
},
"gsTelemetrySpeed": {
"message": "Speed"
},
"maintenance": {
"message": "Maintenance"
},
"maintenanceFlushSettingsCache": {
"message": "Flush settings cache"
}
}

@ -48,7 +48,8 @@
"alwaysOnTopWindows",
{"usbDevices": [
{"vendorId": 1155, "productId": 57105},
{"vendorId": 11836, "productId": 57105}
{"vendorId": 11836, "productId": 57105},
{'vendorId': 12619, 'productId': 262} // APM32 DFU Bootloader
]}
],

@ -9,7 +9,22 @@
z-index: 2001;
}
.defaults-dialog__content {
.defaults-dialog__bottom_buttons {
position: absolute;
bottom: 1em;
width: 500px;
}
.defaults-dialog__bottom_buttons .btn {
float: right;
margin-left: 2em;
}
.defaults-dialog__bottom_buttons {
font-size: 14pt !important;
}
.defaults-dialog__content, .defaults-dialog__wizard {
background-color: #efefef;
border-radius: 2px;
z-index: 2002;
@ -25,6 +40,25 @@
padding: 1em;
}
.defaults-dialog__wizard {
font-size: 1.2em;
}
.defaults-dialog__wizard > div {
margin-top: 1em;
}
.defaults-dialog__wizard label {
min-width: 30%;
display: inline-block;
}
.defaults-dialog__wizard p {
margin-top: 1em;
font-size: 0.9em;
color:#444;
}
.defaults-dialog__content--wrapper {
overflow-y: auto;
}

@ -410,7 +410,7 @@
.pid-slider-row span,
.pid-switch-row .label {
margin-right: 2em;
width: 120px;
width: 150px;
line-height: 22px;;
}
@ -465,4 +465,15 @@
#pid-sliders {
margin-bottom: 1em;
}
.tab-pid_tuning .expo-chart {
margin: 0 0px 0px 0;
width: 200px;
height: 117px;
border: 1px solid silver;
border-radius: 3px;
background-image: url(./../../../images/paper.jpg);
background-size: 200%;
background-position: center;
}

@ -17,13 +17,7 @@ TABS.adjustments.initialize = function (callback) {
GUI.active_tab_ref = this;
GUI.active_tab = 'adjustments';
function get_adjustment_ranges() {
MSP.send_message(MSPCodes.MSP_ADJUSTMENT_RANGES, false, false, get_box_ids);
}
function get_box_ids() {
MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, get_rc_data);
}
MSP.send_message(MSPCodes.MSP_ADJUSTMENT_RANGES, false, false, get_rc_data);
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, load_html);
@ -33,8 +27,6 @@ TABS.adjustments.initialize = function (callback) {
GUI.load(path.join(__dirname, "adjustments.html"), process_html);
}
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, get_adjustment_ranges);
function addAdjustment(adjustmentIndex, adjustmentRange, auxChannelCount) {
var template = $('#tab-adjustments-templates').find('.adjustments .adjustment');

@ -22,24 +22,26 @@ TABS.auxiliary = {};
TABS.auxiliary.initialize = function (callback) {
GUI.active_tab_ref = this;
GUI.active_tab = 'auxiliary';
function get_mode_ranges() {
MSP.send_message(MSPCodes.MSP_MODE_RANGES, false, false, get_box_ids);
}
let LOCAL_AUX_CONFIG = [];
let LOCAL_AUX_CONFIG_IDS = [];
MSP.send_message(MSPCodes.MSP_MODE_RANGES, false, false, get_box_ids);
function get_box_ids() {
MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, get_rc_data);
}
MSP.send_message(MSPCodes.MSP_BOXIDS, false, false, function () {
FC.generateAuxConfig();
function get_rc_data() {
if (FC.SERIAL_CONFIG.ports.length == 0) {
MSP.send_message(MSPCodes.MSP_RC, false, false, get_ports_data);
} else {
MSP.send_message(MSPCodes.MSP_RC, false, false, load_html);
}
//Copy global settings into local ones
LOCAL_AUX_CONFIG = Array.from(FC.AUX_CONFIG);
LOCAL_AUX_CONFIG_IDS = Array.from(FC.AUX_CONFIG_IDS);
get_rc_data();
});
}
function get_ports_data() {
MSP.send_message(MSPCodes.MSP2_CF_SERIAL_CONFIG, false, false, load_html);
function get_rc_data() {
MSP.send_message(MSPCodes.MSP_RC, false, false, load_html);
}
function load_html() {
@ -47,12 +49,10 @@ TABS.auxiliary.initialize = function (callback) {
GUI.load(path.join(__dirname, "auxiliary.html"), process_html);
}
MSP.send_message(MSPCodes.MSP_BOXNAMES, false, false, get_mode_ranges);
// This object separates out the dividers. This is also used to order the modes
const modeSections = {};
modeSections["Arming"] = ["ARM", "PREARM"];
modeSections["Flight Modes"] = ["ANGLE", "HORIZON", "MANUAL"];
modeSections["Flight Modes"] = ["ANGLE", "HORIZON", "MANUAL", "ANGLE HOLD"];
modeSections["Navigation Modes"] = ["NAV COURSE HOLD", "NAV CRUISE", "NAV POSHOLD", "NAV RTH", "NAV WP", "GCS NAV"];
modeSections["Flight Mode Modifiers"] = ["NAV ALTHOLD", "HEADING HOLD", "AIR MODE", "SOARING", "SURFACE", "TURN ASSIST"];
modeSections["Fixed Wing"] = ["AUTO TUNE", "SERVO AUTOTRIM", "AUTO LEVEL TRIM", "NAV LAUNCH", "LOITER CHANGE", "FLAPERON"];
@ -68,21 +68,21 @@ TABS.auxiliary.initialize = function (callback) {
var found = false;
var sortedID = 0;
for (let i=0; i<FC.AUX_CONFIG.length; i++) {
tmpAUX_CONFIG[i] = FC.AUX_CONFIG[i];
tmpAUX_CONFIG_IDS[i] = FC.AUX_CONFIG_IDS[i];
for (let i=0; i<LOCAL_AUX_CONFIG.length; i++) {
tmpAUX_CONFIG[i] = LOCAL_AUX_CONFIG[i];
tmpAUX_CONFIG_IDS[i] = LOCAL_AUX_CONFIG_IDS[i];
}
FC.AUX_CONFIG = [];
FC.AUX_CONFIG_IDS = [];
LOCAL_AUX_CONFIG = [];
LOCAL_AUX_CONFIG_IDS = [];
for (let categoryModesIndex in modeSections) {
let categoryModes = modeSections[categoryModesIndex];
for (let cM=0; cM<categoryModes.length; cM++){
for(let j=0; j<tmpAUX_CONFIG.length; j++) {
if (categoryModes[cM] === tmpAUX_CONFIG[j]) {
FC.AUX_CONFIG[sortedID] = tmpAUX_CONFIG[j];
FC.AUX_CONFIG_IDS[sortedID] = tmpAUX_CONFIG_IDS[j];
LOCAL_AUX_CONFIG[sortedID] = tmpAUX_CONFIG[j];
LOCAL_AUX_CONFIG_IDS[sortedID] = tmpAUX_CONFIG_IDS[j];
ORIG_AUX_CONFIG_IDS[sortedID++] = j;
break;
@ -92,19 +92,19 @@ TABS.auxiliary.initialize = function (callback) {
}
// There are modes that are missing from the modeSections object. Add them to the end until they are ordered correctly.
if (tmpAUX_CONFIG.length > FC.AUX_CONFIG.length) {
if (tmpAUX_CONFIG.length > LOCAL_AUX_CONFIG.length) {
for (let i=0; i<tmpAUX_CONFIG.length; i++) {
found = false;
for (let j=0; j<FC.AUX_CONFIG.length; j++) {
if (tmpAUX_CONFIG[i] === FC.AUX_CONFIG[j]) {
for (let j=0; j<LOCAL_AUX_CONFIG.length; j++) {
if (tmpAUX_CONFIG[i] === LOCAL_AUX_CONFIG[j]) {
found = true;
break;
}
}
if (!found) {
FC.AUX_CONFIG[sortedID] = tmpAUX_CONFIG[i];
FC.AUX_CONFIG_IDS[sortedID] = tmpAUX_CONFIG_IDS[i];
LOCAL_AUX_CONFIG[sortedID] = tmpAUX_CONFIG[i];
LOCAL_AUX_CONFIG_IDS[sortedID] = tmpAUX_CONFIG_IDS[i];
ORIG_AUX_CONFIG_IDS[sortedID++] = i;
}
}
@ -123,7 +123,7 @@ TABS.auxiliary.initialize = function (callback) {
function createMode(modeIndex, modeId) {
var modeTemplate = $('#tab-auxiliary-templates .mode');
var newMode = modeTemplate.clone();
var modeName = FC.AUX_CONFIG[modeIndex];
var modeName = LOCAL_AUX_CONFIG[modeIndex];
// If the runcam split peripheral is used, then adjust the boxname(BOXCAMERA1, BOXCAMERA2, BOXCAMERA3)
// If platform is fixed wing, rename POS HOLD to LOITER
@ -135,7 +135,7 @@ TABS.auxiliary.initialize = function (callback) {
$(newMode).data('index', modeIndex);
$(newMode).data('id', modeId);
$(newMode).data('origId', ORIG_AUX_CONFIG_IDS[modeIndex]);
$(newMode).data('modeName', FC.AUX_CONFIG[modeIndex]);
$(newMode).data('modeName', LOCAL_AUX_CONFIG[modeIndex]);
$(newMode).find('.name').data('modeElement', newMode);
$(newMode).find('a.addRange').data('modeElement', newMode);
@ -230,10 +230,10 @@ TABS.auxiliary.initialize = function (callback) {
let modeSelectionID = "";
let modeSelectionRange = "";
for (var modeIndex = 0; modeIndex < FC.AUX_CONFIG.length; modeIndex++) {
for (var modeIndex = 0; modeIndex < LOCAL_AUX_CONFIG.length; modeIndex++) {
// Get current mode category
for (modeSelectionRange in modeSections) {
if (modeSections[modeSelectionRange].indexOf(FC.AUX_CONFIG[modeIndex]) != -1) {
if (modeSections[modeSelectionRange].indexOf(LOCAL_AUX_CONFIG[modeIndex]) != -1) {
break;
}
}
@ -245,7 +245,7 @@ TABS.auxiliary.initialize = function (callback) {
modeTableBodyElement.append(newSection);
}
var modeId = FC.AUX_CONFIG_IDS[modeIndex];
var modeId = LOCAL_AUX_CONFIG_IDS[modeIndex];
var newMode = createMode(modeIndex, modeId);
modeTableBodyElement.append(newMode);
@ -387,7 +387,7 @@ TABS.auxiliary.initialize = function (callback) {
update_marker(i, FC.RC.channels[i + 4]);
}
for (var i = 0; i < FC.AUX_CONFIG.length; i++) {
for (var i = 0; i < LOCAL_AUX_CONFIG.length; i++) {
var modeElement = $('#mode-' + i);
let inRange = false;
@ -442,7 +442,7 @@ TABS.auxiliary.initialize = function (callback) {
}
let hideUnused = hideUnusedModes && hasUsedMode;
for (let i = 0; i < FC.AUX_CONFIG.length; i++) {
for (let i = 0; i < LOCAL_AUX_CONFIG.length; i++) {
let modeElement = $('#mode-' + i);
if (modeElement.find(' .range').length == 0) {
modeElement.toggle(!hideUnused);
@ -504,7 +504,7 @@ TABS.auxiliary.initialize = function (callback) {
})
.prop("checked", !!hideUnusedModesStore)
.trigger('change');
// update ui instantly on first load
update_ui();

@ -229,8 +229,8 @@ TABS.gps.initialize = function (callback) {
var feature = mapHandler.forEachFeatureAtPixel(mapHandler.getEventPixel(evt.originalEvent), function(feature, layer) {
return feature;
});
if (feature) {
if (feature && feature.get('data') && feature.get('name')) {
TABS.gps.toolboxAdsbVehicle.setContent(
`icao: <strong>` + feature.get('name') + `</strong><br />`
+ `lat: <strong>`+ (feature.get('data').lat / 10000000) + `</strong><br />`

@ -87,5 +87,15 @@
</div>
</div>
</div>
<div class="options-section gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="maintenance"></div>
</div>
<div class="spacer_box settings">
<div class="default_btn" style="float: none; width: 200px;">
<a id="maintenanceFlushSettingsCache" href="#" i18n="maintenanceFlushSettingsCache"></a>
</div>
</div>
</div>
</div>
</div>

@ -149,11 +149,6 @@ SYM.AH_AIRCRAFT4 = 0x1A6;
SYM.AH_CROSSHAIRS = new Array(0x166, 0x1A4, new Array(0x190, 0x191, 0x192), new Array(0x193, 0x194, 0x195), new Array(0x196, 0x197, 0x198), new Array(0x199, 0x19A, 0x19B), new Array (0x19C, 0x19D, 0x19E), new Array (0x19F, 0x1A0, 0x1A1));
var useESCTelemetry = false;
var useBaro = false;
var useCRSFRx = false;
var usePitot = false;
var video_type = null;
var isGuidesChecked = false;
var FONT = FONT || {};
@ -522,9 +517,7 @@ OSD.initData = function () {
item_count: 0,
items: [],
groups: {},
preview: [],
isDjiHdFpv: false,
isMspDisplay: false
preview: []
};
};
@ -954,7 +947,7 @@ OSD.constants = {
name: 'AIR_SPEED',
id: 27,
enabled: function() {
return usePitot;
return HARDWARE.capabilities.usePitot;
},
preview: function(osd_data) {
var speed;
@ -979,7 +972,7 @@ OSD.constants = {
name: 'AIR_MAX_SPEED',
id: 127,
enabled: function() {
return usePitot;
return HARDWARE.capabilities.usePitot;
},
preview: function(osd_data) {
// 3 chars
@ -1010,7 +1003,7 @@ OSD.constants = {
id: 106,
min_version: '2.3.0',
enabled: function() {
return useESCTelemetry;
return HARDWARE.capabilities.useESCTelemetry;
},
preview: function(){
let rpmPreview = '112974'.substr((6 - parseInt(Settings.getInputValue('osd_esc_rpm_precision'))));
@ -1090,7 +1083,7 @@ OSD.constants = {
name: 'BARO_TEMPERATURE',
id: 87,
enabled: function() {
return useBaro;
return HARDWARE.capabilities.useBaro;
},
preview: function(osd_data) {
switch (OSD.data.preferences.units) {
@ -1106,7 +1099,7 @@ OSD.constants = {
id: 107,
min_version: '2.5.0',
enabled: function() {
return useESCTelemetry;
return HARDWARE.capabilities.useESCTelemetry;
},
preview: function(osd_data) {
switch (OSD.data.preferences.units) {
@ -1804,7 +1797,7 @@ OSD.constants = {
{
name: 'osdGroupCRSF',
enabled: function() {
return useCRSFRx;
return HARDWARE.capabilities.useCRSFRx;
},
items: [
{
@ -2175,17 +2168,6 @@ OSD.reload = function(callback) {
}
};
MSP.promise(MSPCodes.MSP2_CF_SERIAL_CONFIG).then(function (resp) {
$.each(FC.SERIAL_CONFIG.ports, function(index, port){
if(port.functions.includes('DJI_FPV')) {
OSD.data.isDjiHdFpv = true;
}
if(port.functions.includes('MSP_DISPLAYPORT')) {
OSD.data.isMspDisplay = true;
}
});
});
MSP.promise(MSPCodes.MSP2_INAV_OSD_LAYOUTS).then(function (resp) {
OSD.msp.decodeLayoutCounts(resp);
@ -2576,11 +2558,11 @@ OSD.GUI.updateVideoMode = function() {
// video mode
var $videoTypes = $('.video-types').empty();
if (!OSD.data.isDjiHdFpv) {
if (!HARDWARE.capabilities.isDjiHdFpv) {
$('#dji_settings').hide();
}
if (OSD.data.isMspDisplay) {
if (HARDWARE.capabilities.isMspDisplay) {
if (mspVideoSystem.includes(OSD.data.preferences.video_system) == false) {
OSD.data.preferences.video_system = OSD.constants.VIDEO_TYPES.indexOf('HDZERO');
OSD.updateDisplaySize();
@ -2594,7 +2576,7 @@ OSD.GUI.updateVideoMode = function() {
}
}
if (OSD.data.isMspDisplay) {
if (HARDWARE.capabilities.isMspDisplay) {
for (var i = 0; i < OSD.constants.VIDEO_TYPES.length; i++) {
if (mspVideoSystem.includes(i))
{
@ -2805,7 +2787,7 @@ OSD.GUI.updateFields = function() {
if ($('#djiUnsupportedElementsToggle').length == false) {
$('#djiUnsupportedElements').prepend(
$('<input id="djiUnsupportedElementsToggle" type="checkbox" class="toggle" />')
.attr('checked', OSD.data.isDjiHdFpv && !OSD.data.isMspDisplay)
.attr('checked', HARDWARE.capabilities.isDjiHdFpv && !HARDWARE.capabilities.isMspDisplay)
.on('change', function () {
OSD.GUI.updateDjiView(this.checked);
OSD.GUI.updatePreviews();
@ -2942,10 +2924,10 @@ OSD.GUI.updateDjiView = function(on) {
};
OSD.GUI.updateAlarms = function() {
$(".osd_use_airspeed_alarm").toggle(usePitot);
$(".osd_use_baro_temp_alarm").toggle(useBaro);
$(".osd_use_esc_telemetry").toggle(useESCTelemetry);
$(".osd_use_crsf").toggle(useCRSFRx);
$(".osd_use_airspeed_alarm").toggle(HARDWARE.capabilities.usePitot);
$(".osd_use_baro_temp_alarm").toggle(HARDWARE.capabilities.useBaro);
$(".osd_use_esc_telemetry").toggle(HARDWARE.capabilities.useESCTelemetry);
$(".osd_use_crsf").toggle(HARDWARE.capabilities.useCRSFRx);
};
OSD.GUI.updateMapPreview = function(mapCenter, name, directionSymbol, centerSymbol) {
@ -3046,8 +3028,8 @@ OSD.GUI.updatePreviews = function() {
// crosshairs
if ($('input[name="CROSSHAIRS"]').prop('checked')) {
crsHNumber = Settings.getInputValue('osd_crosshairs_style');
if (crsHNumber == 1) {
let crsHNumber = Settings.getInputValue('osd_crosshairs_style');
if (crsHNumber == 1) {
// AIRCRAFT style
OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition - 2, SYM.AH_AIRCRAFT0);
OSD.GUI.checkAndProcessSymbolPosition(hudCenterPosition - 1, SYM.AH_AIRCRAFT1);
@ -3225,7 +3207,7 @@ OSD.GUI.updateAll = function() {
OSD.GUI.updateFields();
OSD.GUI.updatePreviews();
OSD.GUI.updateGuidesView($('#videoGuides').find('input').is(':checked'));
OSD.GUI.updateDjiView(OSD.data.isDjiHdFpv && !OSD.data.isMspDisplay);
OSD.GUI.updateDjiView(HARDWARE.capabilities.isDjiHdFpv && !HARDWARE.capabilities.isMspDisplay);
OSD.GUI.updateAlarms();
};
@ -3247,6 +3229,51 @@ OSD.GUI.saveConfig = function() {
});
};
let HARDWARE = {};
HARDWARE.init = function() {
HARDWARE.capabilities = {
isDjiHdFpv: false,
isMspDisplay: false,
useESCTelemetry: false,
useCRSFRx: false,
useBaro: false,
usePitot: false
};
};
HARDWARE.update = function(callback) {
HARDWARE.init();
MSP.send_message(MSPCodes.MSP2_CF_SERIAL_CONFIG, false, false, function() {
$.each(FC.SERIAL_CONFIG.ports, function(index, port){
if(port.functions.includes('DJI_FPV')) {
HARDWARE.capabilities.isDjiHdFpv = true;
}
if(port.functions.includes('MSP_DISPLAYPORT')) {
HARDWARE.capabilities.isMspDisplay = true;
}
if (port.functions.includes('ESC')) {
HARDWARE.capabilities.useESCTelemetry = true;
}
});
// Update RX data for Crossfire detection
mspHelper.loadRxConfig(function() {
HARDWARE.capabilities.useCRSFRx = (FC.RX_CONFIG.serialrx_provider == 6);
mspHelper.loadSensorConfig(function () {
HARDWARE.capabilities.useBaro = (FC.SENSOR_CONFIG.barometer != 0);
HARDWARE.capabilities.usePitot = (FC.SENSOR_CONFIG.pitot != 0);
if (callback) {
callback();
}
});
});
});
};
TABS.osd = {};
TABS.osd.initialize = function (callback) {
@ -3263,204 +3290,183 @@ TABS.osd.initialize = function (callback) {
});
}
GUI.load(path.join(__dirname, "osd.html"), Settings.processHtml(function () {
// translate to user-selected language
i18n.localize();
// Open modal window
OSD.GUI.jbox = new jBox('Modal', {
width: 750,
height: 300,
position: {y:'bottom'},
offset: {y:-50},
closeButton: 'title',
animation: false,
attach: $('#fontmanager'),
title: 'OSD Font Manager',
content: $('#fontmanagercontent')
});
$('a.save').on('click', function () {
Settings.saveInputs(save_to_eeprom);
});
// Initialise guides checkbox
isGuidesChecked = store.get('showOSDGuides', false);
// Setup switch indicators
$(".osdSwitchInd_channel option").each(function() {
$(this).text("Ch " + $(this).text());
});
// Function when text for switch indicators change
$('.osdSwitchIndName').on('keyup', function() {
// Make sure that the switch hint only contains A to Z
let testExp = new RegExp('^[A-Za-z0-9]');
let testText = $(this).val();
if (testExp.test(testText.slice(-1))) {
$(this).val(testText.toUpperCase());
} else {
$(this).val(testText.slice(0, -1));
}
// Update the OSD preview
refreshOSDSwitchIndicators();
});
// Function to update the OSD layout when the switch text alignment changes
$("#switchIndicators_alignLeft").on('change', function() {
refreshOSDSwitchIndicators();
});
// Functions for when pan servo settings change
$('#osdPanServoIndicatorShowDegrees').on('change', function() {
// Update the OSD preview
updatePanServoPreview();
});
$('#panServoOutput').on('change', function() {
// Update the OSD preview
updatePanServoPreview();
});
// Function for when text for craft name changes
$('#craft_name').on('keyup', function() {
// Make sure that the craft name only contains A to Z, 0-9, spaces, and basic ASCII symbols
let testExp = new RegExp('^[A-Za-z0-9 !_,:;=@#\\%\\&\\-\\*\\^\\(\\)\\.\\+\\<\\>\\[\\]]');
let testText = $(this).val();
if (testExp.test(testText.slice(-1))) {
$(this).val(testText.toUpperCase());
} else {
$(this).val(testText.slice(0, -1));
HARDWARE.update(function () {
GUI.load(path.join(__dirname, "osd.html"), Settings.processHtml(function () {
// translate to user-selected language
i18n.localize();
// Open modal window
OSD.GUI.jbox = new jBox('Modal', {
width: 750,
height: 300,
position: {y:'bottom'},
offset: {y:-50},
closeButton: 'title',
animation: false,
attach: $('#fontmanager'),
title: 'OSD Font Manager',
content: $('#fontmanagercontent')
});
$('a.save').on('click', function () {
Settings.saveInputs(save_to_eeprom);
});
// Initialise guides checkbox
isGuidesChecked = store.get('showOSDGuides', false);
// Setup switch indicators
$(".osdSwitchInd_channel option").each(function() {
$(this).text("Ch " + $(this).text());
});
// Function when text for switch indicators change
$('.osdSwitchIndName').on('keyup', function() {
// Make sure that the switch hint only contains A to Z
let testExp = new RegExp('^[A-Za-z0-9]');
let testText = $(this).val();
if (testExp.test(testText.slice(-1))) {
$(this).val(testText.toUpperCase());
} else {
$(this).val(testText.slice(0, -1));
}
// Update the OSD preview
refreshOSDSwitchIndicators();
});
// Function to update the OSD layout when the switch text alignment changes
$("#switchIndicators_alignLeft").on('change', function() {
refreshOSDSwitchIndicators();
});
// Functions for when pan servo settings change
$('#osdPanServoIndicatorShowDegrees').on('change', function() {
// Update the OSD preview
updatePanServoPreview();
});
$('#panServoOutput').on('change', function() {
// Update the OSD preview
updatePanServoPreview();
});
// Function for when text for craft name changes
$('#craft_name').on('keyup', function() {
// Make sure that the craft name only contains A to Z, 0-9, spaces, and basic ASCII symbols
let testExp = new RegExp('^[A-Za-z0-9 !_,:;=@#\\%\\&\\-\\*\\^\\(\\)\\.\\+\\<\\>\\[\\]]');
let testText = $(this).val();
if (testExp.test(testText.slice(-1))) {
$(this).val(testText.toUpperCase());
} else {
$(this).val(testText.slice(0, -1));
}
// Update the OSD preview
updatePilotAndCraftNames();
});
$('#pilot_name').on('keyup', function() {
// Make sure that the pilot name only contains A to Z, 0-9, spaces, and basic ASCII symbols
let testExp = new RegExp('^[A-Za-z0-9 !_,:;=@#\\%\\&\\-\\*\\^\\(\\)\\.\\+\\<\\>\\[\\]]');
let testText = $(this).val();
if (testExp.test(testText.slice(-1))) {
$(this).val(testText.toUpperCase());
} else {
$(this).val(testText.slice(0, -1));
}
// Update the OSD preview
updatePilotAndCraftNames();
});
// font preview window
var $preview = $('.font-preview');
// init structs once, also clears current font
FONT.initData();
var $fontPicker = $('.fontbuttons button');
$fontPicker.on('click', function () {
if (!$(this).data('font-file')) {
return;
}
$fontPicker.removeClass('active');
$(this).addClass('active');
$.get('./resources/osd/analogue/' + $(this).data('font-file') + '.mcm', function (data) {
FONT.parseMCMFontFile(data);
FONT.preview($preview);
OSD.GUI.update();
});
store.set('osd_font', $(this).data('font-file'));
});
// load the last selected font when we change tabs
var osd_font = store.get('osd_font', false);
var previous_font_button;
if (osd_font) {
previous_font_button = $('.fontbuttons button[data-font-file="' + osd_font + '"]');
if (previous_font_button.attr('data-font-file') == undefined) previous_font_button = undefined;
}
// Update the OSD preview
updatePilotAndCraftNames();
});
$('#pilot_name').on('keyup', function() {
// Make sure that the pilot name only contains A to Z, 0-9, spaces, and basic ASCII symbols
let testExp = new RegExp('^[A-Za-z0-9 !_,:;=@#\\%\\&\\-\\*\\^\\(\\)\\.\\+\\<\\>\\[\\]]');
let testText = $(this).val();
if (testExp.test(testText.slice(-1))) {
$(this).val(testText.toUpperCase());
if (typeof previous_font_button == "undefined") {
$fontPicker.first().trigger( "click" );
} else {
$(this).val(testText.slice(0, -1));
previous_font_button.trigger( "click" );
}
// Update the OSD preview
updatePilotAndCraftNames();
});
// font preview window
var $preview = $('.font-preview');
// init structs once, also clears current font
FONT.initData();
var $fontPicker = $('.fontbuttons button');
$fontPicker.on('click', function () {
if (!$(this).data('font-file')) {
return;
}
$fontPicker.removeClass('active');
$(this).addClass('active');
$.get('./resources/osd/analogue/' + $(this).data('font-file') + '.mcm', function (data) {
FONT.parseMCMFontFile(data);
FONT.preview($preview);
OSD.GUI.update();
});
store.set('osd_font', $(this).data('font-file'));
});
// load the last selected font when we change tabs
var osd_font = store.get('osd_font', false);
var previous_font_button;
if (osd_font) {
previous_font_button = $('.fontbuttons button[data-font-file="' + osd_font + '"]');
if (previous_font_button.attr('data-font-file') == undefined) previous_font_button = undefined;
}
if (typeof previous_font_button == "undefined") {
$fontPicker.first().trigger( "click" );
} else {
previous_font_button.trigger( "click" );
}
$('button.load_font_file').on('click', function () {
$fontPicker.removeClass('active');
FONT.openFontFile().then(function () {
FONT.preview($preview);
OSD.GUI.update();
$('button.load_font_file').on('click', function () {
$fontPicker.removeClass('active');
FONT.openFontFile().then(function () {
FONT.preview($preview);
OSD.GUI.update();
});
});
});
// font upload
$('a.flash_font').on('click', function () {
if (!GUI.connect_lock) { // button disabled while flashing is in progress
var progressLabel = $('.progressLabel');
var progressBar = $('.progress');
var uploading = i18n.getMessage('uploadingCharacters');
progressLabel.text(uploading);
var progressCallback = function(done, total, percentage) {
progressBar.val(percentage);
if (done == total) {
progressLabel.text(i18n.getMessage('uploadedCharacters'), [total]);
} else {
progressLabel.text(uploading + ' (' + done + '/' + total + ')');
// font upload
$('a.flash_font').on('click', function () {
if (!GUI.connect_lock) { // button disabled while flashing is in progress
var progressLabel = $('.progressLabel');
var progressBar = $('.progress');
var uploading = i18n.getMessage('uploadingCharacters');
progressLabel.text(uploading);
var progressCallback = function(done, total, percentage) {
progressBar.val(percentage);
if (done == total) {
progressLabel.text(i18n.getMessage('uploadedCharacters'), [total]);
} else {
progressLabel.text(uploading + ' (' + done + '/' + total + ')');
}
}
FONT.upload(progressCallback);
}
FONT.upload(progressCallback);
}
});
$('.update_preview').on('change', function () {
if (OSD.data) {
// Force an OSD redraw by saving any element
// with a small delay, to make sure the setting
// change is performance before the OSD starts
// the full redraw.
// This will also update all previews
setTimeout(function() {
OSD.GUI.saveItem({id: 0});
}, 100);
}
});
$('#useCraftnameForMessages').on('change', function() {
OSD.GUI.updateDjiMessageElements(this.checked);
});
// Update RX data for Crossfire detection
mspHelper.loadRxConfig(function() {
useCRSFRx = (FC.RX_CONFIG.serialrx_provider == 6);
});
// Get status of ESC Telemetry
useESCTelemetry = false;
MSP.send_message(MSPCodes.MSP2_CF_SERIAL_CONFIG, false, false, function() {
for (var portIndex = 0; portIndex < FC.SERIAL_CONFIG.ports.length; portIndex++) {
var serialPort = FC.SERIAL_CONFIG.ports[portIndex];
if (serialPort.functions.indexOf("ESC") >= 0) {
useESCTelemetry = true;
break;
});
$('.update_preview').on('change', function () {
if (OSD.data) {
// Force an OSD redraw by saving any element
// with a small delay, to make sure the setting
// change is performance before the OSD starts
// the full redraw.
// This will also update all previews
setTimeout(function() {
OSD.GUI.saveItem({id: 0});
}, 100);
}
});
$('#useCraftnameForMessages').on('change', function() {
OSD.GUI.updateDjiMessageElements(this.checked);
});
if(semver.gte(FC.CONFIG.flightControllerVersion, '7.1.0')) {
mspHelper.loadOsdCustomElements(createCustomElements);
}
});
// Update SENSOR_CONFIG, used to detect
// OSD_AIR_SPEED
mspHelper.loadSensorConfig(function () {
useBaro = (FC.SENSOR_CONFIG.barometer != 0);
usePitot = (FC.SENSOR_CONFIG.pitot != 0);
GUI.content_ready(callback);
});
if(semver.gte(FC.CONFIG.flightControllerVersion, '7.1.0')) {
mspHelper.loadOsdCustomElements(createCustomElements);
}
}));
}));
});
};
function createCustomElements(){

@ -185,6 +185,11 @@
<td id="preview-yaw-expo"></td>
</tr>
</table>
<div style="margin-top: 1em;">
<div id="ez_tune_expo_curve" class="expo-chart" style="width: 250px; height: 200px; margin: auto;">
<canvas width="250" height="200"></canvas>
</div>
</div>
</div>
</div>
@ -436,82 +441,137 @@
</div>
<div id="subtab-rates" class="subtab__content">
<div class="tab_subtitle" style="margin-top: 1em;" data-i18n="pidTuning_RatesAndExpo"></div>
<div class="clear-both"></div>
<div class="cf_column">
<table class="settings-table settings-table--inav">
<tbody>
<tr class="not-for-ez-tune">
<th class="roll" data-i18n="pidTuning_RollRate"></th>
<td class="roll">
<div class="pidTuning_number"><input id="rate-roll" type="number" class="rate-tpa_input" data-setting="roll_rate" data-unit="decadegps" /></div>
</td>
</tr>
<tr class="not-for-ez-tune">
<th class="pitch" data-i18n="pidTuning_PitchRate"></th>
<td class="pitch">
<div class="pidTuning_number"><input id="rate-pitch" type="number" class="rate-tpa_input" data-setting="pitch_rate" data-unit="decadegps" /></div>
</td>
</tr>
<tr class="not-for-ez-tune">
<th class="yaw" data-i18n="pidTuning_YawRate"></th>
<td class="yaw">
<div class="pidTuning_number"><input id="rate-yaw" type="number" class="rate-tpa_input" data-setting="yaw_rate" data-unit="decadegps" /></div>
</td>
</tr>
<tr class="not-for-ez-tune">
<th data-i18n="pidTuning_RollAndPitchExpo"></th>
<td>
<div class="pidTuning_number"><input type="number" class="rate-tpa_input" data-setting="rc_expo" data-unit="percent" /></div>
</td>
</tr>
<tr class="not-for-ez-tune">
<th data-i18n="pidTuning_YawExpo"></th>
<td>
<div class="pidTuning_number"><input type="number" class="rate-tpa_input" data-setting="rc_yaw_expo" data-unit="percent" /></div>
</td>
</tr>
<tr>
<th data-i18n="pidTuning_MaxRollAngle"></th>
<td>
<div class="pidTuning_number"><input id="maxRollInclination" type="number" class="rate-tpa_input" data-setting="max_angle_inclination_rll" data-unit="decideg-lrg" /></div>
<div for="maxRollInclination" class="helpicon cf_tip" data-i18n_title="pidTuning_MaxRollAngleHelp"></div>
</td>
</tr>
<tr>
<th data-i18n="pidTuning_MaxPitchAngle"></th>
<td>
<div class="pidTuning_number"><input id="maxPitchInclination" type="number" class="rate-tpa_input" data-setting="max_angle_inclination_pit" data-unit="decideg-lrg" /></div>
<div for="maxPitchInclination" class="helpicon cf_tip" data-i18n_title="pidTuning_MaxPitchAngleHelp"></div>
</td>
</tr>
<tr>
<th data-i18n="pidTuning_magHoldYawRate"></th>
<td>
<div class="pidTuning_number"><input id="headingHoldRate" type="number" class="rate-tpa_input" data-setting="heading_hold_rate_limit" data-unit="degps" /></div>
<div for="headingHoldRate" class="helpicon cf_tip" data-i18n_title="pidTuning_MagHoldYawRateHelp"></div>
</td>
</tr>
<tr>
<th class="roll" data-i18n="pidTuning_ManualRollRate"></th>
<td class="roll">
<div class="pidTuning_number"><input id="rate-manual-roll" type="number" class="rate-tpa_input" data-setting="manual_roll_rate" data-unit="percent" /></div>
</td>
</tr>
<tr>
<th class="pitch" data-i18n="pidTuning_ManualPitchRate"></th>
<td class="pitch">
<div class="pidTuning_number"><input id="rate-manual-pitch" type="number" class="rate-tpa_input" data-setting="manual_pitch_rate" data-unit="percent" /></div>
</td>
</tr>
<tr>
<th class="yaw" data-i18n="pidTuning_ManualYawRate"></th>
<td class="yaw">
<div class="pidTuning_number"><input id="rate-manual-yaw" type="number" class="rate-tpa_input" data-setting="manual_yaw_rate" data-unit="percent" /></div>
</td>
</tr>
</tbody>
</table>
<div class="tab_subtitle" style="margin-top: 1em;" data-i18n="pidTuning_RatesAndExpo"></div>
<div class="pid-sliders-axis not-for-ez-tune" data-axis="roll">
<h3 data-i18n="pidTuning_Rates_Stabilized"></h3>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Rates_Roll"></span>
<div class="number no-border">
<input id="rate_roll_rate" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Rates_Pitch"></span>
<div class="number no-border">
<input id="rate_pitch_rate" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Rates_Yaw"></span>
<div class="number no-border">
<input id="rate_yaw_rate" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
</div>
<div class="pid-sliders-axis not-for-ez-tune" data-axis="pitch">
<div style="float: right">
<div class="pitch_roll_curve expo-chart">
<canvas width="200" height="117"></canvas>
</div>
</div>
<h3 data-i18n="pidTuning_Expo_Stabilized"></h3>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Expo_RollPitch"></span>
<div class="number no-border">
<input id="rate_rollpitch_expo" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Expo_Yaw"></span>
<div class="number no-border">
<input id="rate_yaw_expo" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
</div>
<div class="pid-sliders-axis not-for-multirotor" data-axis="yaw">
<h3 data-i18n="pidTuning_Manual_Rates"></h3>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Manual_Roll"></span>
<div class="number no-border">
<input id="rate_manual_roll" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Manual_Pitch"></span>
<div class="number no-border">
<input id="rate_manual_pitch" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Manual_Yaw"></span>
<div class="number no-border">
<input id="rate_manual_yaw" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
</div>
<div class="pid-sliders-axis not-for-multirotor" data-axis="3">
<div style="float: right">
<div class="manual_expo_curve expo-chart">
<canvas width="200" height="117"></canvas>
</div>
</div>
<h3 data-i18n="pidTuning_Expo_Manual"></h3>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Expo_RollPitch"></span>
<div class="number no-border">
<input id="manual_rollpitch_expo" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Expo_Yaw"></span>
<div class="number no-border">
<input id="manual_yaw_expo" class="controlProfileHighlightActive" type="number"/>
</div>
<div class="clear-both"></div>
</div>
</div>
<div class="pid-sliders-axis only-for-multirotor" data-axis="3">
<h3 data-i18n="pidTuning_Other"></h3>
<div class="pid-slider-row">
<span data-i18n="pidTuning_HeadingHold_Rate"></span>
<div class="number no-border">
<input id="heading_hold_rate_limit" class="controlProfileHighlightActive rate-tpa_input" data-setting="heading_hold_rate_limit" type="number" data-presentation="range" />
</div>
<div class="clear-both"></div>
</div>
</div>
<div class="tab_subtitle" style="margin-top: 1em;" data-i18n="pidTuning_Limits"></div>
<div class="pid-sliders-axis" data-axis="roll">
<h3 data-i18n="pidTuning_Max_Inclination_Angle"></h3>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Max_Roll"></span>
<div class="number no-border">
<input id="max_angle_inclination_rll" class="controlProfileHighlightActive rate-tpa_input" data-setting="max_angle_inclination_rll" type="number" data-presentation="range" />
</div>
<div class="clear-both"></div>
</div>
<div class="pid-slider-row">
<span data-i18n="pidTuning_Max_Pitch"></span>
<div class="number no-border">
<input id="max_angle_inclination_pit" class="controlProfileHighlightActive rate-tpa_input" data-setting="max_angle_inclination_pit" type="number" data-presentation="range" />
</div>
<div class="clear-both"></div>
</div>
</div>
<div class="clear-both"></div>

@ -9,17 +9,15 @@ const mspHelper = require('./../js/msp/MSPHelper');
const MSPCodes = require('./../js/msp/MSPCodes');
const MSP = require('./../js/msp');
const { GUI, TABS } = require('./../js/gui');
const features = require('./../js/feature_framework');
const tabs = require('./../js/tabs');
const FC = require('./../js/fc');
const Settings = require('./../js/settings');
const i18n = require('./../js/localization');
const { scaleRangeInt } = require('./../js/helpers');
const SerialBackend = require('./../js/serial_backend');
const BitHelper = require('./../js/bitHelper');
const interval = require('./../js/intervals');
TABS.pid_tuning = {
rateChartHeight: 117
};
TABS.pid_tuning.initialize = function (callback) {
@ -32,10 +30,10 @@ TABS.pid_tuning.initialize = function (callback) {
var loadChain = [
mspHelper.loadPidData,
mspHelper.loadRateDynamics,
mspHelper.loadRateProfileData,
mspHelper.loadEzTune,
mspHelper.loadMixerConfig,
];
loadChain.push(mspHelper.loadRateProfileData);
loadChainer.setChain(loadChain);
loadChainer.setExitPoint(load_html);
@ -49,6 +47,75 @@ TABS.pid_tuning.initialize = function (callback) {
GUI.load(path.join(__dirname, "pid_tuning.html"), Settings.processHtml(process_html));
}
function drawExpoCanvas(value, $element, color, width, height, clear) {
let context = $element.getContext("2d");
if (value < 0 || value > 1) {
return;
}
if (clear === true) {
context.clearRect(0, 0, width, height);
}
context.beginPath();
context.moveTo(0, height);
context.quadraticCurveTo(width / 2, height - ((height / 2) * (1 - value)), width, 0);
context.lineWidth = 2;
context.strokeStyle = color;
context.stroke();
};
function drawRollPitchYawExpo() {
let pitch_roll_curve = $('.pitch_roll_curve canvas').get(0);
let manual_expo_curve = $('.manual_expo_curve canvas').get(0);
drawExpoCanvas(
parseFloat($('#rate_rollpitch_expo').val()) / 100,
pitch_roll_curve,
'#a00000',
200,
TABS.pid_tuning.rateChartHeight,
true
);
drawExpoCanvas(
parseFloat($('#rate_yaw_expo').val()) / 100,
pitch_roll_curve,
'#00a000',
200,
TABS.pid_tuning.rateChartHeight,
false
);
drawExpoCanvas(
parseFloat($('#manual_rollpitch_expo').val()) / 100,
manual_expo_curve,
'#a00000',
200,
TABS.pid_tuning.rateChartHeight,
true
);
drawExpoCanvas(
parseFloat($('#manual_yaw_expo').val()) / 100,
manual_expo_curve,
'#00a000',
200,
TABS.pid_tuning.rateChartHeight,
false
);
drawExpoCanvas(
Math.floor(scaleRange($('#ez_tune_expo').val(), 0, 200, 40, 100)) / 100,
$('#ez_tune_expo_curve canvas').get(0),
'#a00000',
250,
200,
true
);
}
function pid_and_rc_to_form() {
// Fill in the data from FC.PIDs array
@ -67,15 +134,6 @@ TABS.pid_tuning.initialize = function (callback) {
}
});
// Fill in data from FC.RC_tuning object
$('#rate-roll').val(FC.RC_tuning.roll_rate);
$('#rate-pitch').val(FC.RC_tuning.pitch_rate);
$('#rate-yaw').val(FC.RC_tuning.yaw_rate);
$('#rate-manual-roll').val(FC.RC_tuning.manual_roll_rate);
$('#rate-manual-pitch').val(FC.RC_tuning.manual_pitch_rate);
$('#rate-manual-yaw').val(FC.RC_tuning.manual_yaw_rate);
$('#tpa').val(FC.RC_tuning.dynamic_THR_PID);
$('#tpa-breakpoint').val(FC.RC_tuning.dynamic_THR_breakpoint);
}
@ -98,17 +156,23 @@ TABS.pid_tuning.initialize = function (callback) {
}
});
// catch FC.RC_tuning changes
FC.RC_tuning.roll_rate = parseFloat($('#rate-roll').val());
FC.RC_tuning.pitch_rate = parseFloat($('#rate-pitch').val());
FC.RC_tuning.yaw_rate = parseFloat($('#rate-yaw').val());
// catch RC_tuning changes
FC.RC_tuning.roll_rate = parseFloat($('#rate_roll_rate').val());
FC.RC_tuning.pitch_rate = parseFloat($('#rate_pitch_rate').val());
FC.RC_tuning.yaw_rate = parseFloat($('#rate_yaw_rate').val());
FC.RC_tuning.RC_EXPO = parseFloat($('#rate_rollpitch_expo').val()) / 100;
FC.RC_tuning.RC_YAW_EXPO = parseFloat($('#rate_yaw_expo').val()) / 100;
FC.RC_tuning.dynamic_THR_PID = parseInt($('#tpaRate').val());
FC.RC_tuning.dynamic_THR_breakpoint = parseInt($('#tpaBreakpoint').val());
FC.RC_tuning.manual_roll_rate = $('#rate-manual-roll').val();
FC.RC_tuning.manual_pitch_rate = $('#rate-manual-pitch').val();
FC.RC_tuning.manual_yaw_rate = $('#rate-manual-yaw').val();
FC.RC_tuning.manual_roll_rate = $('#rate_manual_roll').val();
FC.RC_tuning.manual_pitch_rate = $('#rate_manual_pitch').val();
FC.RC_tuning.manual_yaw_rate = $('#rate_manual_yaw').val();
FC.RC_tuning.manual_RC_EXPO = $('#manual_rollpitch_expo').val() / 100;
FC.RC_tuning.manual_RC_YAW_EXPO = $('#manual_yaw_expo').val() / 100;
// Rate Dynamics
FC.RATE_DYNAMICS.sensitivityCenter = parseInt($('#rate_dynamics_center_sensitivity').val());
@ -189,6 +253,11 @@ TABS.pid_tuning.initialize = function (callback) {
if (!FC.isMultirotor()) {
$('#ez-tune-switch').hide();
$('.only-for-multirotor').hide();
}
if (FC.isMultirotor()) {
$('.not-for-multirotor').hide();
}
$("#ez_tune_enabled").prop('checked', FC.EZ_TUNE.enabled).trigger('change');
@ -209,6 +278,21 @@ TABS.pid_tuning.initialize = function (callback) {
updatePreview();
});
//Slider rates
GUI.sliderize($('#rate_roll_rate'), FC.RC_tuning.roll_rate, 40, 1000);
GUI.sliderize($('#rate_pitch_rate'), FC.RC_tuning.pitch_rate, 40, 1000);
GUI.sliderize($('#rate_yaw_rate'), FC.RC_tuning.yaw_rate, 40, 1000);
GUI.sliderize($('#rate_rollpitch_expo'), FC.RC_tuning.RC_EXPO * 100, 0, 100);
GUI.sliderize($('#rate_yaw_expo'), FC.RC_tuning.RC_YAW_EXPO * 100, 0, 100);
GUI.sliderize($('#rate_manual_roll'), FC.RC_tuning.manual_roll_rate, 0, 100);
GUI.sliderize($('#rate_manual_pitch'), FC.RC_tuning.manual_pitch_rate, 0, 100);
GUI.sliderize($('#rate_manual_yaw'), FC.RC_tuning.manual_yaw_rate, 0, 100);
GUI.sliderize($('#manual_rollpitch_expo'), FC.RC_tuning.manual_RC_EXPO * 100, 0, 100);
GUI.sliderize($('#manual_yaw_expo'), FC.RC_tuning.manual_RC_YAW_EXPO * 100, 0, 100);
updatePreview();
tabs.init($('.tab-pid_tuning'));
@ -303,6 +387,10 @@ TABS.pid_tuning.initialize = function (callback) {
$('.rpy_d').prop('disabled', 'disabled');
}
interval.add("drawRollPitchYawExpo", function () {
drawRollPitchYawExpo();
}, 100);
GUI.simpleBind();
// UI Hooks

@ -70,28 +70,13 @@
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="rcSmoothing"></div>
</div>
<div class="spacer_box">
<div class="select">
<select id="rc_filter_auto" data-setting="rc_filter_auto"></select>
<span data-i18n="rc_filter_auto"></span>
</label>
</div>
<div class="number">
<input id="rc_filter_lpf_hz" data-setting="rc_filter_lpf_hz" type="number" data-presentation="range" data-normal-max="125" />
<label for="rc_filter_lpf_hz">
<span data-i18n="rc_filter_lpf_hz"></span>
</label>
</div>
<div class="spacer_box">
<div class="number">
<input id="rc_filter_smoothing_factor" data-setting="rc_filter_smoothing_factor" type="number" data-presentation="range" />
<label for="rc_filter_smoothing_factor">
<span data-i18n="rc_filter_smoothing_factor"></span>
</label>
</div>
</div>
</div>
@ -134,43 +119,7 @@
</div>
</div>
</div>
<div class="gui_box grey">
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="fc_column curves" style="width: calc(50% - 10px);">
<table>
<tr>
<td>
<div class="pitch_roll_curve">
<canvas width="200" height="117"></canvas>
</div>
</td>
</tr>
</table>
</div>
<div class="fc_column half tunings">
<table class="rate">
<tr>
<th data-i18n="receiverRcExpo"></th>
<th data-i18n="receiverManualRcExpo"></th>
</tr>
<tr>
<td><input class="controlProfileHighlight" type="number" name="expo" step="0.01" min="0" max="2" /></td>
<td><input class="controlProfileHighlight" type="number" name="manual_expo" step="0.01" min="0" max="1" /></td>
</tr>
</table>
<table class="yaw_rate" style="margin-bottom: 0;">
<tr>
<th data-i18n="receiverRcYawExpo"></th>
<th data-i18n="receiverManualRcYawExpo"></th>
</tr>
<tr>
<td><input class="controlProfileHighlight" type="number" name="yaw_expo" step="0.01" min="0" max="2" /></td>
<td><input class="controlProfileHighlight" type="number" name="manual_yaw_expo" step="0.01" min="0" max="1" /></td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="content_toolbar">

@ -47,41 +47,6 @@ TABS.receiver.initialize = function (callback) {
Settings.saveInputs(onComplete);
}
function drawRollPitchExpo() {
var pitch_roll_curve = $('.pitch_roll_curve canvas').get(0);
var context = pitch_roll_curve.getContext("2d");
var expoAVal = $('.tunings .rate input[name="expo"]');
var expoA = parseFloat(expoAVal.val());
var expoMVal = $('.tunings .rate input[name="manual_expo"]');
var expoM = parseFloat(expoMVal.val());
if (expoA <= parseFloat(expoAVal.prop('min')) || expoA >= parseFloat(expoAVal.prop('max')) ||
expoM <= parseFloat(expoMVal.prop('min')) || expoM >= parseFloat(expoMVal.prop('max'))) {
return;
}
var rateHeight = TABS.receiver.rateChartHeight;
// draw
context.clearRect(0, 0, 200, rateHeight);
context.beginPath();
context.moveTo(0, rateHeight);
context.quadraticCurveTo(110, rateHeight - ((rateHeight / 2) * (1 - expoA)), 200, 0);
context.lineWidth = 2;
context.strokeStyle = '#37a8db';
context.stroke();
context.beginPath();
context.moveTo(0, rateHeight);
context.quadraticCurveTo(110, rateHeight - ((rateHeight / 2) * (1 - expoM)), 200, 0);
context.lineWidth = 2;
context.strokeStyle = '#a837db';
context.stroke();
}
function process_html() {
// translate to user-selected language
i18n.localize();;
@ -120,12 +85,6 @@ TABS.receiver.initialize = function (callback) {
$('.tunings .throttle input[name="mid"]').val(FC.RC_tuning.throttle_MID.toFixed(2));
$('.tunings .throttle input[name="expo"]').val(FC.RC_tuning.throttle_EXPO.toFixed(2));
$('.tunings .rate input[name="expo"]').val(FC.RC_tuning.RC_EXPO.toFixed(2));
$('.tunings .yaw_rate input[name="yaw_expo"]').val(FC.RC_tuning.RC_YAW_EXPO.toFixed(2));
$('.tunings .rate input[name="manual_expo"]').val(FC.RC_tuning.manual_RC_EXPO.toFixed(2));
$('.tunings .yaw_rate input[name="manual_yaw_expo"]').val(FC.RC_tuning.manual_RC_YAW_EXPO.toFixed(2));
$('.deadband input[name="yaw_deadband"]').val(FC.RC_deadband.yaw_deadband);
$('.deadband input[name="deadband"]').val(FC.RC_deadband.deadband);
@ -284,23 +243,11 @@ TABS.receiver.initialize = function (callback) {
}, 0);
}).trigger('input');
$('.tunings .rate input').on('input change', function () {
setTimeout(function () { // let global validation trigger and adjust the values first
drawRollPitchExpo();
}, 0);
}).trigger('input');
$('a.update').on('click', function () {
// catch RC_tuning changes
FC.RC_tuning.throttle_MID = parseFloat($('.tunings .throttle input[name="mid"]').val());
FC.RC_tuning.throttle_EXPO = parseFloat($('.tunings .throttle input[name="expo"]').val());
FC.RC_tuning.RC_EXPO = parseFloat($('.tunings .rate input[name="expo"]').val());
FC.RC_tuning.RC_YAW_EXPO = parseFloat($('.tunings .yaw_rate input[name="yaw_expo"]').val());
FC.RC_tuning.manual_RC_EXPO = parseFloat($('.tunings .rate input[name="manual_expo"]').val());
FC.RC_tuning.manual_RC_YAW_EXPO = parseFloat($('.tunings .yaw_rate input[name="manual_yaw_expo"]').val());
FC.RC_deadband.yaw_deadband = parseInt($('.deadband input[name="yaw_deadband"]').val());
FC.RC_deadband.deadband = parseInt($('.deadband input[name="deadband"]').val());

@ -31,7 +31,8 @@ TABS.setup.initialize = function (callback) {
mspHelper.loadFeatures,
mspHelper.queryFcStatus,
mspHelper.loadMixerConfig,
mspHelper.loadMiscV2
mspHelper.loadMiscV2,
mspHelper.loadSerialPorts
];
loadChainer.setChain(loadChain);

@ -0,0 +1,8 @@
<div class="defaults-dialog__bottom_buttons">
<div class="btn default_btn narrow green">
<a id="wizard-next" href="#">Next</a>
</div>
<div class="btn default_btn narrow">
<a id="wizard-skip" href="#">Skip</a>
</div>
</div>

@ -0,0 +1 @@
<h2>Filters wizard</h2>

@ -0,0 +1 @@
<h2>GPS wizard</h2>

@ -0,0 +1 @@
<h2>Outputs wizard</h2>

@ -0,0 +1 @@
<h2>PIDs wizard</h2>

@ -0,0 +1,11 @@
<h2>Receiver wizard</h2>
<p>
Configure receiver serial port and protocol. If you unsure about serial port or protocol, click `Skip` to go to the next page.
You can change those settings later with the configurator UI.
</p>
<div>
<label for="wizard-receiver-port">Receiver Serial Port</label><select id="wizard-receiver-port"></select>
</div>
<div>
<label for="wizard-receiver-protocol">Receiver Protocol</label><select id="wizard-receiver-protocol" data-setting="serialrx_provider"></select>
</div>
Loading…
Cancel
Save