diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 000000000..91a168095 --- /dev/null +++ b/gulpfile.js @@ -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')); diff --git a/index.html b/index.html index 4b0d8aec5..e41c0f288 100644 --- a/index.html +++ b/index.html @@ -20,8 +20,12 @@

@@ -53,31 +57,31 @@

+ + +

+ +
@@ -106,10 +110,10 @@

+ + + + @@ -118,10 +122,10 @@

+ + + + @@ -228,13 +232,16 @@

  • - +
  • - +
  • - +
  • @@ -266,7 +273,8 @@

  • - +
  • @@ -287,7 +295,8 @@

  • - +
  • @@ -359,7 +368,9 @@

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    \ No newline at end of file diff --git a/tabs/osd.js b/tabs/osd.js index 5ffca961e..4b1c989dd 100644 --- a/tabs/osd.js +++ b/tabs/osd.js @@ -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( $('') - .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(){ diff --git a/tabs/pid_tuning.html b/tabs/pid_tuning.html index f982e2f8e..9da4d855a 100644 --- a/tabs/pid_tuning.html +++ b/tabs/pid_tuning.html @@ -185,6 +185,11 @@

    +
    +
    + +
    +
    @@ -436,82 +441,137 @@

    -
    +
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +
    + +
    +

    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +

    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    +

    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    +

    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    +

    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +

    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    diff --git a/tabs/pid_tuning.js b/tabs/pid_tuning.js index bae27211b..78efb95eb 100644 --- a/tabs/pid_tuning.js +++ b/tabs/pid_tuning.js @@ -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 diff --git a/tabs/receiver.html b/tabs/receiver.html index fbea3946f..5609b6369 100644 --- a/tabs/receiver.html +++ b/tabs/receiver.html @@ -70,28 +70,13 @@
    -
    -
    - - - -
    - -
    - - -
    - +
    - -
    @@ -134,43 +119,7 @@
    -
    -
    -
    - - - - -
    -
    - -
    -
    -
    -
    - - - - - - - - - -
    - - - - - - - - - -
    -
    -
    -
    +
    diff --git a/tabs/receiver.js b/tabs/receiver.js index 9b20874f0..9046d5d00 100644 --- a/tabs/receiver.js +++ b/tabs/receiver.js @@ -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()); diff --git a/tabs/setup.js b/tabs/setup.js index 7579998fb..e2e1f642a 100755 --- a/tabs/setup.js +++ b/tabs/setup.js @@ -31,7 +31,8 @@ TABS.setup.initialize = function (callback) { mspHelper.loadFeatures, mspHelper.queryFcStatus, mspHelper.loadMixerConfig, - mspHelper.loadMiscV2 + mspHelper.loadMiscV2, + mspHelper.loadSerialPorts ]; loadChainer.setChain(loadChain); diff --git a/wizard/buttons.html b/wizard/buttons.html new file mode 100644 index 000000000..71258e645 --- /dev/null +++ b/wizard/buttons.html @@ -0,0 +1,8 @@ +
    +
    + Next +
    +
    + Skip +
    +
    \ No newline at end of file diff --git a/wizard/filters.html b/wizard/filters.html new file mode 100644 index 000000000..320c51e70 --- /dev/null +++ b/wizard/filters.html @@ -0,0 +1 @@ +

    Filters wizard

    \ No newline at end of file diff --git a/wizard/gps.html b/wizard/gps.html new file mode 100644 index 000000000..1177218c0 --- /dev/null +++ b/wizard/gps.html @@ -0,0 +1 @@ +

    GPS wizard

    \ No newline at end of file diff --git a/wizard/outputs.html b/wizard/outputs.html new file mode 100644 index 000000000..85f57a3b2 --- /dev/null +++ b/wizard/outputs.html @@ -0,0 +1 @@ +

    Outputs wizard

    \ No newline at end of file diff --git a/wizard/pids.html b/wizard/pids.html new file mode 100644 index 000000000..01ddf707b --- /dev/null +++ b/wizard/pids.html @@ -0,0 +1 @@ +

    PIDs wizard

    \ No newline at end of file diff --git a/wizard/receiver.html b/wizard/receiver.html new file mode 100644 index 000000000..3c2b2ee6e --- /dev/null +++ b/wizard/receiver.html @@ -0,0 +1,11 @@ +

    Receiver wizard

    +

    + 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. +

    +
    + +
    +
    + +
    \ No newline at end of file