diff --git a/MAPPROXY.md b/MAPPROXY.md index d6d99a2d6..7da63d648 100644 --- a/MAPPROXY.md +++ b/MAPPROXY.md @@ -175,13 +175,13 @@ https://hub.docker.com/r/yagajs/mapproxy/ Docker image (untested) base: GLOBAL_MERCATOR origin: ul ``` -1. You can use any map provider that is compatible with MapProxy, and once you zoom in on the region you will be flying in, the map tiles will be cached for offline use. You can test this by disabling your internet connection and browsing the demo url in a browser +1. You can use any map provider that is compatible with MapProxy, and once you zoom in on the region you will be flying in, the map tiles will be cached for offline use. You can test this by disabling your internet connection and browsing the demo URL in a browser https://wiki.openstreetmap.org/wiki/WMS#OSM_WMS_Servers OpenStreetMap WSM servers https://lpdaac.usgs.gov/data_access/web_map_services_wms # USGS currently has 400+ WMS layers - * You can use QGIS to browse different provieders and pick the maps you like for your iNav layers + * You can use QGIS to browse different providers and pick the maps you like for your iNav layers https://qgis.org/en/site/ - * There are many government and public wms providers available in different regions worldwide + * There are many government and public WMS providers available in different regions worldwide diff --git a/README.md b/README.md index 9bcbb5711..d84f356d8 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,42 @@ # INAV Configurator -INAV Configurator is a crossplatform configuration tool for the [INAV](https://github.com/iNavFlight/inav) flight control system. +INAV Configurator is a cross-platform configuration tool for the [INAV](https://github.com/iNavFlight/inav) flight control system. It runs as an app within Google Chrome and allows you to configure the INAV software running on any supported INAV target. -Various types of aircraft are supported by the tool and by INAV, e.g. quadcopters, hexacopters, octocopters and fixed-wing aircraft. +Various types of aircraft are supported by the tool and by INAV, e.g. quadcopters, hexacopters, octocopters, and fixed-wing aircraft. # Support -INAV Configurator comes `as is`, without any warranty and support from authors. If you found a bug, please create an issue on [GitHub](https://github.com/iNavFlight/inav-configurator/issues). +INAV Configurator comes `as is`, without any warranty and support from the authors. If you find a bug, please create an issue on [GitHub](https://github.com/iNavFlight/inav-configurator/issues). -GitHub issue tracker is reserved for bugs and other technical problems. If you do not know how to setup -everything, hardware is not working or have any other _support_ problem, please consult: +The GitHub issue tracker is reserved for bugs and other technical problems. If you do not know how to set up +everything, the hardware is not working, or you have any other _support_ problem, please consult: * [INAV Discord Server](https://discord.gg/peg2hhbYwN) * [INAV Official on Facebook](https://www.facebook.com/groups/INAVOfficial) * [RC Groups Support](https://www.rcgroups.com/forums/showthread.php?2495732-Cleanflight-iNav-(navigation-rewrite)-project) * [INAV Official on Telegram](https://t.me/INAVFlight) -## INAV Configurator start minimized, what should I do? +## INAV Configurator starts minimized, what should I do? -You have to remove `C:\Users%Your_UserNname%\AppData\Local\inav-configurator` folder and all its content. +You have to remove the `C:\Users%Your_UserName%\AppData\Local\inav-configurator` folder and all its content. [https://www.youtube.com/watch?v=XMoULyiFDp4](https://www.youtube.com/watch?v=XMoULyiFDp4) -Alternatively, on Windows with PowerShell you can use `post_install_cleanup.ps1` script that will do the cleaning. (thank you James Cherrill) +Alternatively, on Windows with PowerShell, you can use the `post_install_cleanup.ps1` script that will do the cleaning. (thank you, James Cherrill) ## Installation -Depending on target operating system, _INAV Configurator_ is distributed as _standalone_ application or Chrome App. +Depending on the target operating system, _INAV Configurator_ is distributed as a _standalone_ application or Chrome App. ### Windows 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) 1. Download Configurator for Windows platform (win32 or win64 is present) 1. Extract ZIP archive -1. Run INAV Configurator app from unpacked folder -1. Configurator is not signed, so you have to allow Windows to run untrusted application. There might be a monit for it during first run +1. Run the INAV Configurator app from the unpacked folder +1. Configurator is not signed, so you have to allow Windows to run untrusted applications. There might be a monit for it during the first run ### Linux @@ -47,7 +47,7 @@ Depending on target operating system, _INAV Configurator_ is distributed as _sta * **.tar.gz** is a universal archive. Download and continue with these instructions to install 3. Change to the directory containing the downloaded **tar.gz** file 4. download [this](https://raw.githubusercontent.com/iNavFlight/inav-configurator/master/assets/linux/inav-configurator.desktop) file to the same directory. Its filename should be `inav-configurator.desktop`. -5. Extract **tar.gz** archive +5. Extract **tar.gz** archive ``` tar -C /tmp/ -xf INAV-Configurator_linuxNN_x.y.z.tar.gz ``` @@ -58,7 +58,7 @@ tar -C /tmp/ -xf INAV-Configurator_linuxNN_x.y.z.tar.gz sudo mkdir /opt/inav sudo chown $USER /opt/inav ``` -7. Move the temporary files in to their home +7. Move the temporary files into their home ``` mv /tmp/INAV\ Configurator /opt/inav/inav-configurator ``` @@ -67,55 +67,55 @@ mv /tmp/INAV\ Configurator /opt/inav/inav-configurator sudo mkdir /opt/inav/inav-configurator/icon sudo cp /opt/inav/inav-configurator/images/inav_icon_128.png /opt/inav/inav-configurator/icon ``` -9. As a one off, move the desktop file into the applications directory +9. As a one-off, move the desktop file into the applications directory ``` sudo mv inav-configurator.desktop /usr/share/applications/ ``` 10. Make the following files executable: * inav-configurator `chmod +x /opt/inav/inav-configurator/inav-configurator` * (5.0.0+) chrome_crashpad_handler `chmod +x /opt/inav/inav-configurator/chrome_crashpad_handler` -11. Run INAV Configurator app from unpacked folder `/opt/inav/inav-configurator/inav-configurator` +11. Run the INAV Configurator app from the unpacked folder `/opt/inav/inav-configurator/inav-configurator` #### Notes -On some Linux distros, you may be missing `libatomic`, a `NW.JS` (specially `libnode.so`) dependency. If so, please install `libatomic` using your distro's package manager, e.g: +On some Linux distros, you may be missing `libatomic` and/or `NW.JS` (especially `libnode.so`) dependencies. If so, please install `libatomic` using your distro's package manager, e.g: * Arch Linux: `sudo pacman -S --needed libatomic_ops` * Debian / Ubuntu: `sudo apt install libatomic1` * Fedora: `sudo dnf install libatomic` -1. Dont forget to add your user into dialout group "sudo usermod -aG dialout YOUR_USERNAME" for serial access +1. Don't forget to add your user to the dialout group "sudo usermod -aG dialout YOUR_USERNAME" for serial access 2. If you have 3D model animation problems, enable "Override software rendering list" in Chrome flags chrome://flags/#ignore-gpu-blacklist ### Mac 1. Visit [release page](https://github.com/iNavFlight/inav-configurator/releases) -1. Download Configurator for Mac platform +1. Download Configurator for the Mac platform 1. Extract ZIP archive 1. Run INAV Configurator ## Building and running INAV Configurator locally (for development) -For local development, **node.js** build system is used. +For local development, the **node.js** build system is used. 1. Install node.js -1. From project folder run `npm install` +1. From the project folder run `npm install` 1. To build the JS and CSS files and start the configurator: - With NW.js: Run `npm start`. - With Chrome: Run `npm run gulp`. Then open `chrome://extensions`, enable - the `Developer mode`, click on the `Load unpacked extension...` button and select the `inav-configurator` directory. + the `Developer mode`, click on the `Load unpacked extension...` button, and select the `inav-configurator` directory. Other tasks are also defined in `gulpfile.js`. To run a task, use `node ./node_modules/gulp/bin/gulp.js task-name`. Available ones are: - **build**: Generate JS and CSS output files used by the configurator from their sources. It must be run whenever changes are made to any `.js` or `.css` files in order to have those changes appear in the configurator. If new files are added, they must be included in `gulpfile.js`. See the comments at the top of `gulpfile.js` to learn how to do so. See also the `watch` task. - **watch**: Watch JS and CSS sources for changes and run the `build` task whenever they're edited. -- **dist**: Create a distribution of the app (valid for packaging both as a Chrome app or a NW.js app) +- **dist**: Create a distribution of the app (valid for packaging both as a Chrome app or NW.js app) in the `./dist/` directory. - **release**: Create NW.js apps for each supported platform (win32, osx64 and linux64) in the `./apps` -directory. Running this task on macOS or Linux requires Wine, since it's needed to set the icon -for the Windows app. If you don't have Wine installed you can create a release by running the **release-only-linux** task. -
`--installer` argument can be added to build installers for particular OS. NOTE: MacOS Installer can be built with MacOS only. +directory. Running this task on macOS or Linux requires Wine since it's needed to set the icon +for the Windows app. If you don't have Wine installed, you can create a release by running the **release-only-Linux** task. +
`--installer` argument can be added to build installers for a particular OS. NOTE: MacOS Installer can be built with MacOS only. To build a specific release, use the command `release --platform="win64"` for example. @@ -127,17 +127,17 @@ To be able to open Inspector, you will need SDK flavours of NW.js ## Different map providers -INAV Configurator 2.1 allows to choose between OpenStreetMap, Bing Maps (Aerial View), and MapProxy map providers. -INAV Configurator is shipped **WITHOUT** API key for Bing Maps. That means: every user who wants to use Bing Maps has to create own account, agree to all _Terms and Conditions_ required by Bing Maps and configure INAV Configuerator by himself. +INAV Configurator 2.1 allows you to choose between OpenStreetMap, Bing Maps (Aerial View), and MapProxy map providers. +INAV Configurator is shipped **WITHOUT** API key for Bing Maps. That means: every user who wants to use Bing Maps has to create their own account, agree to all _Terms and Conditions_ required by Bing Maps, and configure INAV Configurator by himself. -### How to choose Map provider +### How to choose a Map provider 1. Click **Settings** icon in the top-right corner of INAV Configurator -1. Choose provider: OpenStreetMap, Bing, or MapProxy +1. Choose a provider: OpenStreetMap, Bing, or MapProxy 1. In the case of Bing Maps, you have to provide your own, personal, generated by you, Bing Maps API key 1. For MapProxy, you need to provide a server URL and layer name to be used -### How to get Bing Maps API key +### How to get the Bing Maps API key 1. Go to the Bing Maps Dev Center at [https://www.bingmapsportal.com/](https://www.bingmapsportal.com/). * If you have a Bing Maps account, sign in with the Microsoft account that you used to create the account or create a new one. For new accounts, follow the instructions in [Creating a Bing Maps Account](https://msdn.microsoft.com/library/gg650598.aspx). @@ -145,22 +145,22 @@ INAV Configurator is shipped **WITHOUT** API key for Bing Maps. That means: ever 1. Select the option to create a new key. 1. Provide the following information to create a key: 1. Application name: Required. The name of the application. - 1. Application URL: The URL of the application. This is an optional field which is useful in helping you remember the purpose of that key in the future. + 1. Application URL: The URL of the application. This is an optional field that is useful in helping you remember the purpose of that key in the future. 1. Key type: Required. Select the key type that you want to create. You can find descriptions of key and application types here. 1. Application type: Required. Select the application type that best represents the application that will use this key. You can find descriptions of key and application types [here](https://www.microsoft.com/maps/create-a-bing-maps-key.aspx). -1. Click the **Create** button. The new key displays in the list of available keys. Use this key to authenticate your Bing Maps application as described in the documentation for the Bing Maps API you are using. +1. Click the **Create** button. The new key is displayed in the list of available keys. Use this key to authenticate your Bing Maps application as described in the documentation for the Bing Maps API you are using. -### How to setup a MapProxy server for offline caching and mission planning -1. Follow process described in [MAPPROXY.md](MAPPROXY.md) -1. Test your MapProxy server in web browser, eg: http://192.168.145.20/inavmapproxy/ +### How to set up a MapProxy server for offline caching and mission planning +1. Follow the process described in [MAPPROXY.md](MAPPROXY.md) +1. Test your MapProxy server in a web browser, eg: http://192.168.145.20/inavmapproxy/ 1. Once you have a working MapProxy server choose MapProxy as your map provider 1. Enter MapProxy service URL, eg: http://192.168.145.20/inavmapproxy/service? 1. Enter MapProxy service layer (inav_layer if configured from MAPPROXY.md) -1. Once completed, you can zoom in on area you will be flying in while connected to the internet in either GPS or Mission Control tab to save the cache for offline use +1. Once completed, you can zoom in on the area you will be flying in while connected to the internet in either the GPS or Mission Control tab to save the cache for offline use ## Font Customisation -INAV provides the font images so that custom fonts can be created for your personal preference. This is the case for both analogue and digital fonts. The resources can be found in the [osd](/resources/osd) folder. Within the **analogue** and **digital** subfolders, you will find information on compiling your own fonts. There is also an [INAV Character Map](/resources/osd/INAV%20Character%20Map.md) document. This contains previews of all the character images in the fonts, and the appropriate variable names within the firmware and Configurator. There are tools for compiling the [analogue](https://github.com/fiam/max7456tool) and [digital](https://github.com/MrD-RC/hdosd-font-tool) fonts. New font submissions via pull requests are welcome. +INAV provides the font images so that custom fonts can be created for your personal preference. This is the case for both analogue and digital fonts. The resources can be found in the [osd](/resources/osd) folder. Within the **analogue** and **digital** subfolders, you will find information on compiling your own fonts. There is also an [INAV Character Map](/resources/osd/INAV%20Character%20Map.md) document. This contains previews of all the character images in the fonts and the appropriate variable names within the firmware and Configurator. There are tools for compiling the [analogue](https://github.com/fiam/max7456tool) and [digital](https://github.com/MrD-RC/hdosd-font-tool) fonts. New font submissions via pull requests are welcome. ## Notes @@ -174,7 +174,7 @@ For INAV configurator issues raise them here https://github.com/iNavFlight/inav-configurator/issues -For INAV firmware issues raise them here +For INAV firmware issues, raise them here https://github.com/iNavFlight/inav/issues diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 48581c1d6..93924d4d9 100755 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -110,6 +110,9 @@ "tabFailsafe": { "message": "Failsafe" }, + "tabEzTune": { + "message": "Ez Tune" + }, "tabGPS": { "message": "GPS" }, @@ -1581,6 +1584,9 @@ "pidTuning_gyro_dyn_lpf_max_hz_help": { "message": "Defines the gyro LPF cutoff frequency at maximum throttle. When throttle is decreased, LPF cutoff frequency is decreased as well, down to the minimum cutoff frequency." }, + "loadedMixerProfile": { + "message": "Loaded Mixer Profile: $1, Check modes tab: MIXER PROFILE 2 if you don't see the changes" + }, "loadedBatteryProfile": { "message": "Loaded Battery Profile: $1" }, @@ -3398,8 +3404,8 @@ "osd_main_voltage_decimals": { "message": "Voltage Decimals" }, - "osd_mah_used_precision": { - "message": "mAh Used Precision" + "osd_mah_precision": { + "message": "mAh Precision" }, "osd_coordinate_digits": { "message": "Coordinate Digits" @@ -4406,6 +4412,9 @@ "no_waypoints_to_save": { "message": "No waypoints to save !" }, + "mixerThrottleWarning": { + "message": "Warning:value beyond normal operation range." + }, "servoMixer": { "message": "Servo mixer" }, @@ -4889,6 +4898,12 @@ "motor_direction_inverted_hint": { "message": "Enable if the motor direction is reversed and the props are mounted in the opposite direction." }, + "mixer_pid_profile_linking": { + "message": "PID Profile will use same index as Mixer Profile index" + }, + "mixer_pid_profile_linking_hint": { + "message": "mixer_pid_profile_linking: Enable on both Mixer Profile if you want PID Profile switching handled by Mixer Profile switching(Recommend in vtol/mixed plaform type setup)" + }, "blackboxFields": { "message": "Blackbox fields" }, @@ -5204,14 +5219,20 @@ "sensorDataFlashFreeSpace": { "message": "Dataflash: free space" }, + "mixerProfile1": { + "message": "Mixer profile 1" + }, + "mixerProfile2": { + "message": "Mixer profile 2" + }, "sensorProfile1": { - "message": "Profile 1" + "message": "PID profile 1" }, "sensorProfile2": { - "message": "Profile 2" + "message": "PID profile 2" }, "sensorProfile3": { - "message": "Profile 3" + "message": "PID profile 3" }, "sensorBatteryProfile1": { "message": "Battery profile 1" @@ -5492,5 +5513,77 @@ }, "timerOutputs": { "message": "Timer outputs" + }, + "ezTuneFilterHz": { + "message": "Filter Hz" + }, + "ezTuneAxisRatio": { + "message": "Axis ratio" + }, + "ezTuneResponse": { + "message": "Response" + }, + "ezTuneDamping": { + "message": "Damping" + }, + "ezTuneStability": { + "message": "Stability" + }, + "ezTuneAggressiveness": { + "message": "Aggressiveness" + }, + "ezTuneRate": { + "message": "Rate" + }, + "ezTuneExpo": { + "message": "Expo" + }, + "ezTuneFilterHzTips": { + "message": "This sets the base cutoff frequency for all INAV gyro and D-term filters. Higher values will result in lower filter delay and better stabilization, but more noise will go through the filters and motors will get hot, UAV might oscillate and be unfyable. Your goal is to increase this value as high as possible before any negative effects appear. Negative effects include: hot motors, audible osciallations, UAV rapidly shaking, UAV gaining altitude by itself. Usual starting points for 'Filter Hz' are: 3-inch props: 90, 5-inch props: 110, 7-inch props: 90, 10-inch props: 75, 12-inch props: 60. Use Blackbox and common sense to find a value that is most suited for your UAV." + }, + "ezTuneAxisRatioTips": { + "message": "Describes the weight/moment of inertia distribution of your UAV. The longer the frame (more mass on the front-back axis) more Axis Ratio is requires. Perfect X frame is ratio 100. Most modern frames should fit somewhere between 110 and 130. Default 110 is a good starting point." + }, + "ezTuneResponseTips": { + "message": "This setting defines how fast the UAV will react to stick movements and gyro signal. Higher values will result in faster reaction, but also in more overshoots and oscillations. If UAV feels sluggish or has a slow wobble, increase the Response. If it has hot motors, audibly osciallates, overshoots or feels too nervous, decrease the Response. Most modern quads should beefy motors will fly best with Response below 80. Should be tuned together with Damping. It is a P-term equivalent." + }, + "ezTuneDampingTips": { + "message": "Describes the stengths of a force that opposes any rotation speed change. It dampens roll and pitch acceleration and causes smoother and more stable flight. Your task during tuning is to find out how much you can increase it before any negative symptoms appear: hot motors, audible osciallations, overshoot. Most modern quads should accept 'Damping' up to 150-180. It is a D-term equivalent." + }, + "ezTuneStabilityTips": { + "message": "Defines long-term stabilization strength. Most modern quads should tolerate 'Stability' even up to 120-130. Usually does not have to be tuned at all. If UAV suffers from heavy propwash during vertical descent, lowering 'Stability' might help. It is a I-term equivalent" + }, + "ezTuneAggressivenessTips": { + "message": "Defines how fast your UAV will react to fast stick movements. Higher 'Aggressiveness' results in snappier fast manouvers. It does not affect stabilization, only stick feeling. It is a FF-term equivalent." + }, + "ezTuneRateTips": { + "message": "Defines how fast your UAV will rotate around roll, pitch and yaw axis. Higher 'Rate' results in faster rotation. Value of 0 is the equivalent of 300dps, 100 is the equivalent of 600dps, 200 is the equivalent of 900dps." + }, + "ezTuneExpoTips": { + "message": "Defines expo of the RC input. Lower values result in more sensitive stick in the center. Higher values result in less sensitive center and more rapid response at the end of the stick. Value of 0 is the equivalent of 0 expo, 100 is the equivalent of 0.7 expo, 200 is the equivalent of 1.0 expo." + }, + "ezTunePidPreview": { + "message": "PID preview" + }, + "ezTuneRatePreview": { + "message": "Rate preview" + }, + "ezTuneRatePreviewAxis" : { + "message": "Axis" + }, + "ezTuneRatePreviewRate" : { + "message": "Rate" + }, + "ezTuneRatePreviewExpo" : { + "message": "Expo" + }, + "ezTuneEnabledTips": { + "message": "When enabled, Ez Tune will override multiple INAV setting to simplify the tuning process. Instead of setting each PID and filtering setting independently, you only have to work with 7 sliders. Ez Tune will automatically adjust all other settings to match your needs. Ez Tune is a great starting point for new users and a great way to quickly tune a new UAV. It is not recommended to use Ez Tune on advanced builds, as it will override all your settings and you will not be able to fine tune your UAV. When Ez Tune is enabled, settings from the PID tuning tab will be overriden by EzTune." + }, + "ezTuneDisclaimer": { + "message": "Disclaimer: Ez Tune is an experimental function. It is not guaranteed to work on all UAVs. It is not guaranteed to work with all frame types. It is not guaranteed to work with all propellers. All computations and tuning result can change in future versions of INAV. We still encourage you to check it out and share your experience on INAV Discord in the #ez-tune channel" + }, + "ezTuneNote": { + "message": "Important Ez Tune is enabled. All settings on this tab are set and controlled by the Ez Tune. To use PID Tuning tab you have to disable Ez Tune. To do it, uncheck the Enabled checkbox on the Ez Tune tab." } -} +} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 48e9400dd..5d4d6fcd4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -126,6 +126,7 @@ sources.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', diff --git a/js/defaults_dialog.js b/js/defaults_dialog.js index 0a57d701b..9f9641b11 100644 --- a/js/defaults_dialog.js +++ b/js/defaults_dialog.js @@ -822,10 +822,6 @@ helper.defaultsDialog = (function () { key: "nav_fw_launch_climb_angle", value: 25 }, - { - key: "motorstop_on_low", - value: "ON" - }, ], }, { @@ -1039,10 +1035,6 @@ helper.defaultsDialog = (function () { key: "nav_fw_launch_climb_angle", value: 25 }, - { - key: "motorstop_on_low", - value: "ON" - }, ], }, { @@ -1201,6 +1193,7 @@ helper.defaultsDialog = (function () { MIXER_CONFIG.platformType = currentMixerPreset.platform; MIXER_CONFIG.appliedMixerPreset = selectedDefaultPreset.mixerToApply; + MIXER_CONFIG.motorStopOnLow = (currentMixerPreset.motorStopOnLow === true) ? true : false; MIXER_CONFIG.hasFlaps = (currentMixerPreset.hasFlaps === true) ? true : false; SERVO_RULES.cleanup(); diff --git a/js/fc.js b/js/fc.js index bd86911ed..42eabd1de 100644 --- a/js/fc.js +++ b/js/fc.js @@ -118,6 +118,7 @@ var FC = { i2cError: 0, activeSensors: 0, mode: [], + mixer_profile: 0, profile: 0, battery_profile: 0, uid: [0, 0, 0], @@ -551,6 +552,18 @@ var FC = { weightCenter: null, weightEnd: null }; + + EZ_TUNE = { + enabled: null, + filterHz: null, + axisRatio: null, + response: null, + damping: null, + stability: null, + aggressiveness: null, + rate: null, + expo: null + }; }, getOutputUsages: function() { return { @@ -1218,6 +1231,12 @@ var FC = { hasOperand: [true, true], output: "boolean" }, + 52: { + name: "LED Pin PWM", + operandType: "Set Flight Parameter", + hasOperand: [true, false], + output: "raw" + }, } }, getOperandTypes: function () { diff --git a/js/gui.js b/js/gui.js index 0c506e691..db2f9bd02 100644 --- a/js/gui.js +++ b/js/gui.js @@ -41,7 +41,8 @@ var GUI_control = function () { 'advanced_tuning', 'mission_control', 'mixer', - 'programming' + 'programming', + 'ez_tune' ]; this.allowedTabs = this.defaultAllowedTabsWhenDisconnected; @@ -260,6 +261,7 @@ GUI_control.prototype.updateStatusBar = function() { }; GUI_control.prototype.updateProfileChange = function() { + $('#mixerprofilechange').val(CONFIG.mixer_profile); $('#profilechange').val(CONFIG.profile); $('#batteryprofilechange').val(CONFIG.battery_profile); }; @@ -455,6 +457,7 @@ GUI_control.prototype.sliderize = function ($input, value, min, max) { } $input.val(val); + $input.trigger('updated'); }); $input.on('change', function() { @@ -473,6 +476,7 @@ GUI_control.prototype.sliderize = function ($input, value, min, max) { } $range.val(newVal); + $input.trigger('updated'); }); $input.trigger('change'); diff --git a/js/model.js b/js/model.js index 806140b14..aaeafd7a7 100644 --- a/js/model.js +++ b/js/model.js @@ -350,6 +350,7 @@ const mixerList = [ enabled: true, legacy: true, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.0), ], @@ -373,6 +374,7 @@ const mixerList = [ enabled: true, legacy: false, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.1), new MotorMixRule(1.0, 0.0, 0.0, -0.1) @@ -399,6 +401,7 @@ const mixerList = [ enabled: true, legacy: true, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, hasFlaps: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.0), @@ -427,6 +430,7 @@ const mixerList = [ enabled: true, legacy: false, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, hasFlaps: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.3), @@ -456,6 +460,7 @@ const mixerList = [ enabled: true, legacy: false, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, hasFlaps: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.0), @@ -486,6 +491,7 @@ const mixerList = [ enabled: true, legacy: false, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, hasFlaps: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.3), @@ -516,6 +522,7 @@ const mixerList = [ enabled: true, legacy: false, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.0), ], @@ -542,6 +549,7 @@ const mixerList = [ legacy: false, platform: PLATFORM_AIRPLANE, hasFlaps: true, + motorStopOnLow: true, motorMixer: [ new MotorMixRule(1.0, 0.0, 0.0, 0.0), ], @@ -568,6 +576,7 @@ const mixerList = [ enabled: false, legacy: true, platform: PLATFORM_AIRPLANE, + motorStopOnLow: true, motorMixer: [], servoMixer: [] }, // 24 diff --git a/js/motorMixRule.js b/js/motorMixRule.js index 16b241e18..069578214 100644 --- a/js/motorMixRule.js +++ b/js/motorMixRule.js @@ -6,7 +6,7 @@ var MotorMixRule = function (throttle, roll, pitch, yaw) { var self = {}; self.fromMsp = function (mspThrottle, mspRoll, mspPitch, mspYaw) { - throttle = mspThrottle / 1000; + throttle = Math.round(((mspThrottle / 1000) - 2) * 1000) / 1000; roll = Math.round(((mspRoll / 1000) - 2) * 1000) / 1000; pitch = Math.round(((mspPitch / 1000) - 2) * 1000) / 1000; yaw = Math.round(((mspYaw / 1000) - 2) * 1000) / 1000; @@ -17,11 +17,11 @@ var MotorMixRule = function (throttle, roll, pitch, yaw) { }; self.getThrottle = function () { - return constrain(parseFloat(throttle, 10), 0, 1); + return constrain(parseFloat(throttle, 10), -2, 2); }; self.getThrottleForMsp = function () { - return self.getThrottle() * 1000; + return (self.getThrottle()+2) * 1000; }; self.setThrottle = function (data) { diff --git a/js/motorMixerRuleCollection.js b/js/motorMixerRuleCollection.js index 6a5a6c409..a995f867d 100644 --- a/js/motorMixerRuleCollection.js +++ b/js/motorMixerRuleCollection.js @@ -5,6 +5,7 @@ var MotorMixerRuleCollection = function () { let self = {}, data = [], + inactiveData = [], maxMotorCount = 8; self.setMotorCount = function (value) { @@ -16,7 +17,11 @@ var MotorMixerRuleCollection = function () { }; self.put = function (element) { - data.push(element); + if (data.length < self.getMotorCount()){ + data.push(element); + }else{ + inactiveData.push(element); //store the data for mixer_profile 2 + } }; self.get = function () { @@ -30,18 +35,25 @@ var MotorMixerRuleCollection = function () { self.flush = function () { data = []; + inactiveData = []; }; self.cleanup = function () { var tmpData = []; + var tmpInactiveData = []; data.forEach(function (element) { if (element.isUsed()) { tmpData.push(element); } }); - + inactiveData.forEach(function (element) { + if (element.isUsed()) { + tmpInactiveData.push(element); + } + }); data = tmpData; + inactiveData = tmpInactiveData; }; self.inflate = function () { @@ -55,7 +67,7 @@ var MotorMixerRuleCollection = function () { }; self.getNumberOfConfiguredMotors = function () { - return data.length; + return data.length > inactiveData.length ? data.length : inactiveData.length; }; return self; diff --git a/js/msp/MSPCodes.js b/js/msp/MSPCodes.js index 1d68e548a..fe28c2a3c 100644 --- a/js/msp/MSPCodes.js +++ b/js/msp/MSPCodes.js @@ -240,5 +240,10 @@ var MSPCodes = { MSP2_INAV_SET_LED_STRIP_CONFIG_EX: 0x2049, MSP2_INAV_RATE_DYNAMICS: 0x2060, - MSP2_INAV_SET_RATE_DYNAMICS: 0x2061 + MSP2_INAV_SET_RATE_DYNAMICS: 0x2061, + + MSP2_INAV_EZ_TUNE: 0x2070, + MSP2_INAV_EZ_TUNE_SET: 0x2071, + + MSP2_INAV_SELECT_MIXER_PROFILE: 0x2080 }; diff --git a/js/msp/MSPHelper.js b/js/msp/MSPHelper.js index a4254e97e..d440b29ff 100644 --- a/js/msp/MSPHelper.js +++ b/js/msp/MSPHelper.js @@ -79,6 +79,8 @@ var mspHelper = (function (gui) { profile_byte = data.getUint8(offset++) CONFIG.profile = profile_byte & 0x0F; CONFIG.battery_profile = (profile_byte & 0xF0) >> 4; + profile_byte = data.getUint8(offset++) + CONFIG.mixer_profile = profile_byte & 0x0F; CONFIG.armingFlags = data.getUint32(offset, true); offset += 4; gui.updateStatusBar(); @@ -1571,6 +1573,22 @@ var mspHelper = (function (gui) { console.log('Rate dynamics saved'); break; + case MSPCodes.MSP2_INAV_EZ_TUNE: + EZ_TUNE.enabled = data.getUint8(0); + EZ_TUNE.filterHz = data.getUint16(1, true); + EZ_TUNE.axisRatio = data.getUint8(3); + EZ_TUNE.response = data.getUint8(4); + EZ_TUNE.damping = data.getUint8(5); + EZ_TUNE.stability = data.getUint8(6); + EZ_TUNE.aggressiveness = data.getUint8(7); + EZ_TUNE.rate = data.getUint8(8); + EZ_TUNE.expo = data.getUint8(9); + break; + + case MSPCodes.MSP2_INAV_EZ_TUNE_SET: + console.log('EzTune settings saved'); + break; + default: console.log('Unknown code detected: ' + dataHandler.code); } else { @@ -2207,6 +2225,22 @@ var mspHelper = (function (gui) { buffer.push(RATE_DYNAMICS.weightEnd); break; + case MSPCodes.MSP2_INAV_EZ_TUNE_SET: + + buffer.push(EZ_TUNE.enabled); + buffer.push(lowByte(EZ_TUNE.filterHz)); + buffer.push(highByte(EZ_TUNE.filterHz)); + buffer.push(EZ_TUNE.axisRatio); + buffer.push(EZ_TUNE.response); + buffer.push(EZ_TUNE.damping); + buffer.push(EZ_TUNE.stability); + buffer.push(EZ_TUNE.aggressiveness); + buffer.push(EZ_TUNE.rate); + buffer.push(EZ_TUNE.expo); + console.log(buffer); + break; + + default: return false; } @@ -3409,6 +3443,14 @@ var mspHelper = (function (gui) { MSP.send_message(MSPCodes.MSP2_INAV_SET_RATE_DYNAMICS, mspHelper.crunch(MSPCodes.MSP2_INAV_SET_RATE_DYNAMICS), false, callback); } + self.loadEzTune = function (callback) { + MSP.send_message(MSPCodes.MSP2_INAV_EZ_TUNE, false, false, callback); + } + + self.saveEzTune = function (callback) { + MSP.send_message(MSPCodes.MSP2_INAV_EZ_TUNE_SET, mspHelper.crunch(MSPCodes.MSP2_INAV_EZ_TUNE_SET), false, callback); + } + self.loadParameterGroups = function (callback) { MSP.send_message(MSPCodes.MSP2_COMMON_PG_LIST, false, false, function (resp) { var groups = []; diff --git a/js/servoMixerRuleCollection.js b/js/servoMixerRuleCollection.js index 8c3399a2d..6f2b701c8 100644 --- a/js/servoMixerRuleCollection.js +++ b/js/servoMixerRuleCollection.js @@ -5,6 +5,7 @@ let ServoMixerRuleCollection = function () { let self = {}, data = [], + inactiveData = [], maxServoCount = 16; self.setServoCount = function (value) { @@ -20,7 +21,11 @@ let ServoMixerRuleCollection = function () { } self.put = function (element) { - data.push(element); + if (data.length < self.getServoRulesCount()) { + data.push(element); + }else{ + inactiveData.push(element); //store the data for mixer_profile 2 + } }; self.get = function () { @@ -34,18 +39,24 @@ let ServoMixerRuleCollection = function () { self.flush = function () { data = []; + inactiveData = []; }; self.cleanup = function () { var tmpData = []; - + var tmpInactiveData = []; data.forEach(function (element) { if (element.isUsed()) { tmpData.push(element); } }); - + inactiveData.forEach(function (element) { + if (element.isUsed()) { + tmpInactiveData.push(element); + } + }); data = tmpData; + inactiveData = tmpInactiveData; }; self.inflate = function () { @@ -69,6 +80,15 @@ let ServoMixerRuleCollection = function () { } } } + for (let ruleIndex in inactiveData) { + if (inactiveData.hasOwnProperty(ruleIndex)) { + let rule = inactiveData[ruleIndex]; + + if (rule.getTarget() == servoId && rule.isUsed()) { + return true; + } + } + } return false; }; @@ -106,12 +126,17 @@ let ServoMixerRuleCollection = function () { out.push(rule.getTarget()); } } + for (let ruleIndex in inactiveData) { + if (inactiveData.hasOwnProperty(ruleIndex)) { + let rule = inactiveData[ruleIndex]; + out.push(rule.getTarget()); + } + } - let unique = [...new Set(out)]; - return unique.sort(function(a, b) { - return a-b; - }); + let minIndex = Math.min(...out); + let maxIndex = Math.max(...out); + return Array.from({ length: maxIndex - minIndex + 1 }, (_, index) => minIndex + index); } self.getNextUnusedIndex = function() { diff --git a/main.css b/main.css index e919e1292..010657661 100644 --- a/main.css +++ b/main.css @@ -1643,7 +1643,7 @@ dialog { /* fixing padding for all Tabs*/ .tab-setup, .tab-landing, .tab-adjustments, .tab-auxiliary, .tab-cli, .tab-configuration, .tab-failsafe, .tab-onboard_logging, .tab-firmware_flasher, .tab-gps, .tab-magnetometer, .tab-help, .tab-led-strip, .tab-logging, .tab-modes, .tab-motors, .tab-pid_tuning, -.tab-ports, .tab-receiver, .tab-sensors, .tab-servos, .tab-osd, .tab-calibration { +.tab-ports, .tab-receiver, .tab-sensors, .tab-servos, .tab-osd, .tab-calibration, .tab-ez_tune { height: 100%; position: relative; } @@ -1685,7 +1685,7 @@ dialog { color: white; font-size: 10px; margin-top: 20px; - width: 269px; + width: 410px; float: right; margin-right: 10px; line-height: 12px; @@ -1700,6 +1700,15 @@ dialog { } +#mixer_profile_change { + color: white; + margin-top: 16px; + width: 130px; + float: left; + margin-right: 10px; + line-height: 12px; +} + #profile_change { color: white; margin-top: 16px; @@ -2278,4 +2287,8 @@ ol li { .no-border { border: none !important; +} + +.bold { + font-weight: bold; } \ No newline at end of file diff --git a/main.html b/main.html index 3deabe822..92c89a58d 100755 --- a/main.html +++ b/main.html @@ -87,6 +87,16 @@

+
+ +
diff --git a/main.js b/main.js index 7086b3cc9..f31976395 100644 --- a/main.js +++ b/main.js @@ -301,6 +301,9 @@ $(document).ready(function () { case 'cli': TABS.cli.initialize(content_ready); break; + case 'ez_tune': + TABS.ez_tune.initialize(content_ready); + break; default: console.log('Tab not found:' + tab); @@ -545,6 +548,19 @@ $(document).ready(function () { }); + var mixerprofile_e = $('#mixerprofilechange'); + + mixerprofile_e.change(function () { + var mixerprofile = parseInt($(this).val()); + MSP.send_message(MSPCodes.MSP2_INAV_SELECT_MIXER_PROFILE, [mixerprofile], false, function () { + GUI.log(chrome.i18n.getMessage('loadedMixerProfile', [mixerprofile + 1])); + MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, function () { + GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.handleReconnect(); + }); + }); + }); + var profile_e = $('#profilechange'); profile_e.change(function () { diff --git a/resources/sitl/linux/inav_SITL b/resources/sitl/linux/inav_SITL index 8c5e5c3b2..64f98d3c1 100755 Binary files a/resources/sitl/linux/inav_SITL and b/resources/sitl/linux/inav_SITL differ diff --git a/resources/sitl/windows/inav_SITL.exe b/resources/sitl/windows/inav_SITL.exe index e1c8e1c13..6f170a69d 100755 Binary files a/resources/sitl/windows/inav_SITL.exe and b/resources/sitl/windows/inav_SITL.exe differ diff --git a/src/css/tabs/ez_tune.css b/src/css/tabs/ez_tune.css new file mode 100644 index 000000000..de947d63f --- /dev/null +++ b/src/css/tabs/ez_tune.css @@ -0,0 +1,32 @@ +.ez-tune-preview { + background-color: #8ecae6; + margin-left: 1em; + margin-bottom: 1em; + min-width: 25%; + padding: 0.5em; +} + +.ez-tune-preview h2 { + font-size: 1.3em; + margin-bottom: 1em; + margin-top: 0.5em; + color: #303030 +} + +.ez-tune-preview table { + width: 100%; +} + +.ez-tune-preview table td, +.ez-tune-preview table th { + padding: 0.5em; + text-align: center; +} + +.ez-tune-preview table th { + background-color: #3EA5D4; +} + +.ez-tune-preview table td { + background-color: #A8D6EC; +} diff --git a/src/css/tabs/pid_tuning.css b/src/css/tabs/pid_tuning.css index c8b0f9545..37a9aa512 100644 --- a/src/css/tabs/pid_tuning.css +++ b/src/css/tabs/pid_tuning.css @@ -401,18 +401,21 @@ /* background: #D6D6D6 linear-gradient(-45deg, rgba(255, 255, 255, .2) 10%, transparent 10%, transparent 20%, rgba(255, 255, 255, .2) 20%, rgba(255, 255, 255, .2) 30%, transparent 30%, transparent 40%, rgba(255, 255, 255, .2) 40%, rgba(255, 255, 255, .2) 50%, transparent 50%, transparent 60%, rgba(255, 255, 255, .2) 60%, rgba(255, 255, 255, .2) 70%, transparent 70%, transparent 80%, rgba(255, 255, 255, .2) 80%, rgba(255, 255, 255, .2) 90%, transparent 90%, transparent 100%, rgba(255, 255, 255, .2) 100%, transparent); */ } -.pid-slider-row { +.pid-slider-row, +.pid-switch-row { display: flex; padding: 4px; } -.pid-slider-row span { +.pid-slider-row span, +.pid-switch-row .label { margin-right: 2em; width: 120px; line-height: 22px;; } -.pid-slider-row input[type="number"] { +.pid-slider-row input[type="number"], +.pid-switch-row input[type="number"] { font-family: 'open_sansregular', 'Segoe UI', Tahoma, sans-serif; background-color: #fff; border: 1px solid #ccc; @@ -422,7 +425,9 @@ margin-right: 2em; } -.pid-slider-row input[type="range"] { +.pid-slider-row input[type="range"], +.pid-switch-row input[type="range"] + { display: block; flex-grow: 100; } @@ -436,18 +441,28 @@ padding: 4px; } -.pid-sliders-axis[data-axis="roll"] { +.pid-sliders-axis[data-axis="roll"], +.pid-sliders-axis[data-axis="0"] { background-color: #8ecae6; } -.pid-sliders-axis[data-axis="pitch"] { +.pid-sliders-axis[data-axis="pitch"], +.pid-sliders-axis[data-axis="1"] + { background-color: #00b4d8; } -.pid-sliders-axis[data-axis="yaw"] { +.pid-sliders-axis[data-axis="yaw"], +.pid-sliders-axis[data-axis="2"] +{ background-color: #e9c46a; } +.pid-sliders-axis[data-axis="3"] +{ + background-color: #f4a261; +} + #pid-sliders { margin-bottom: 1em; } \ No newline at end of file diff --git a/tabs/ez_tune.html b/tabs/ez_tune.html new file mode 100644 index 000000000..6545bbfb7 --- /dev/null +++ b/tabs/ez_tune.html @@ -0,0 +1,175 @@ + +
+
+
+
+
+
+

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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 PIDFF
+

+ + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/tabs/ez_tune.js b/tabs/ez_tune.js new file mode 100644 index 000000000..8128deb5e --- /dev/null +++ b/tabs/ez_tune.js @@ -0,0 +1,161 @@ +/*global chrome,helper,mspHelper*/ +'use strict'; + +TABS.ez_tune = { + +}; + +TABS.ez_tune.initialize = function (callback) { + + let loadChainer = new MSPChainerClass(); + + let loadChain = [ + mspHelper.loadEzTune, + ]; + + let EZ_TUNE_PID_RP_DEFAULT = [40, 75, 23, 100]; + let EZ_TUNE_PID_YAW_DEFAULT = [45, 80, 0, 100]; + + loadChain.push(mspHelper.loadRateProfileData); + + loadChainer.setChain(loadChain); + loadChainer.setExitPoint(load_html); + loadChainer.execute(); + + var saveChainer = new MSPChainerClass(); + + var saveChain = [ + mspHelper.saveEzTune, + mspHelper.saveToEeprom + ]; + + saveChainer.setChain(saveChain); + saveChainer.setExitPoint(reboot); + + function reboot() { + //noinspection JSUnresolvedVariable + GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + GUI.tab_switch_cleanup(function () { + MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); + }); + } + + function reinitialize() { + GUI.log(chrome.i18n.getMessage('deviceRebooting')); + GUI.handleReconnect($('.tab_ez_tune a')); + } + + if (GUI.active_tab != 'ez_tune') { + GUI.active_tab = 'ez_tune'; + googleAnalytics.sendAppView('Ez Tune'); + } + + function load_html() { + GUI.load("./tabs/ez_tune.html", Settings.processHtml(process_html)); + } + + function getYawPidScale(input) { + const normalized = (input - 100) * 0.01; + + return 1.0 + (normalized * 0.5); + } + + function scaleRange(x, srcMin, srcMax, destMin, destMax) { + let a = (destMax - destMin) * (x - srcMin); + let b = srcMax - srcMin; + return ((a / b) + destMin); + } + + function updatePreview() { + + let axisRatio = $('#ez_tune_axis_ratio').val() / 100; + let response = $('#ez_tune_response').val(); + let damping = $('#ez_tune_damping').val(); + let stability = $('#ez_tune_stability').val(); + let aggressiveness = $('#ez_tune_aggressiveness').val(); + let rate = $('#ez_tune_rate').val(); + let expo = $('#ez_tune_expo').val(); + + $('#preview-roll-p').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[0] * response / 100)); + $('#preview-roll-i').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[1] * stability / 100)); + $('#preview-roll-d').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[2] * damping / 100)); + $('#preview-roll-ff').html(Math.floor(EZ_TUNE_PID_RP_DEFAULT[3] * aggressiveness / 100)); + + $('#preview-pitch-p').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[0] * response / 100)); + $('#preview-pitch-i').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[1] * stability / 100)); + $('#preview-pitch-d').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[2] * damping / 100)); + $('#preview-pitch-ff').html(Math.floor(axisRatio * EZ_TUNE_PID_RP_DEFAULT[3] * aggressiveness / 100)); + + $('#preview-yaw-p').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[0] * getYawPidScale(response))); + $('#preview-yaw-i').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[1] * getYawPidScale(stability))); + $('#preview-yaw-d').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[2] * getYawPidScale(damping))); + $('#preview-yaw-ff').html(Math.floor(EZ_TUNE_PID_YAW_DEFAULT[3] * getYawPidScale(aggressiveness))); + + $('#preview-roll-rate').html(Math.floor(scaleRange(rate, 0, 200, 30, 90)) * 10 + " dps"); + $('#preview-pitch-rate').html(Math.floor(scaleRange(rate, 0, 200, 30, 90)) * 10 + " dps"); + $('#preview-yaw-rate').html((Math.floor(scaleRange(rate, 0, 200, 30, 90)) - 10) * 10 + " dps"); + + $('#preview-roll-expo').html(Math.floor(scaleRange(expo, 0, 200, 40, 100)) + "%"); + $('#preview-pitch-expo').html(Math.floor(scaleRange(expo, 0, 200, 40, 100)) + "%"); + $('#preview-yaw-expo').html(Math.floor(scaleRange(expo, 0, 200, 40, 100)) + "%"); + + } + + function process_html() { + localize(); + + helper.tabs.init($('.tab-ez_tune')); + helper.features.updateUI($('.tab-ez_tune'), FEATURES); + + $("#ez_tune_enabled").prop('checked', EZ_TUNE.enabled); + + GUI.sliderize($('#ez_tune_filter_hz'), EZ_TUNE.filterHz, 10, 300); + GUI.sliderize($('#ez_tune_axis_ratio'), EZ_TUNE.axisRatio, 25, 175); + GUI.sliderize($('#ez_tune_response'), EZ_TUNE.response, 0, 200); + GUI.sliderize($('#ez_tune_damping'), EZ_TUNE.damping, 0, 200); + GUI.sliderize($('#ez_tune_stability'), EZ_TUNE.stability, 0, 200); + GUI.sliderize($('#ez_tune_aggressiveness'), EZ_TUNE.aggressiveness, 0, 200); + + GUI.sliderize($('#ez_tune_rate'), EZ_TUNE.rate, 0, 200); + GUI.sliderize($('#ez_tune_expo'), EZ_TUNE.expo, 0, 200); + + + $('.ez-element').on('updated', function () { + updatePreview(); + }); + + updatePreview(); + + GUI.simpleBind(); + + GUI.content_ready(callback); + + $('a.update').on('click', function () { + + if ($("#ez_tune_enabled").is(":checked")) { + EZ_TUNE.enabled = 1; + } else { + EZ_TUNE.enabled = 0; + } + + EZ_TUNE.filterHz = $('#ez_tune_filter_hz').val(); + EZ_TUNE.axisRatio = $('#ez_tune_axis_ratio').val(); + EZ_TUNE.response = $('#ez_tune_response').val(); + EZ_TUNE.damping = $('#ez_tune_damping').val(); + EZ_TUNE.stability = $('#ez_tune_stability').val(); + EZ_TUNE.aggressiveness = $('#ez_tune_aggressiveness').val(); + EZ_TUNE.rate = $('#ez_tune_rate').val(); + EZ_TUNE.expo = $('#ez_tune_expo').val(); + + saveChainer.execute(); + }); + + } + +}; + +TABS.ez_tune.cleanup = function (callback) { + if (callback) { + callback(); + } +}; \ No newline at end of file diff --git a/tabs/mixer.html b/tabs/mixer.html index ddfad9a76..85debdd6a 100644 --- a/tabs/mixer.html +++ b/tabs/mixer.html @@ -20,6 +20,11 @@
+
+ + +
+
diff --git a/tabs/mixer.js b/tabs/mixer.js index 331d14754..8a997b593 100644 --- a/tabs/mixer.js +++ b/tabs/mixer.js @@ -170,8 +170,9 @@ TABS.mixer.initialize = function (callback, scrollPosition) { motors.push(outputPad); } else { let servo = servoRules.getServoMixRuleFromTarget(omIndex[1]); + if (servo == null) {continue;} let divID = "servoPreview" + omIndex[1]; - + switch (parseInt(servo.getInput())) { case INPUT_STABILIZED_PITCH: case STABILIZED_PITCH_POSITIVE: @@ -435,7 +436,10 @@ TABS.mixer.initialize = function (callback, scrollPosition) { $motorMixTableBody.append('\ \ \ - \ + \ + \ +
\ + \ \ \ \ @@ -446,9 +450,26 @@ TABS.mixer.initialize = function (callback, scrollPosition) { const $row = $motorMixTableBody.find('tr:last'); $row.find('.mix-rule-motor').html(index); - $row.find('.mix-rule-throttle').val(rule.getThrottle()).change(function () { - rule.setThrottle($(this).val()); - }); + const $throttleInput = $row.find('.mix-rule-throttle').val(rule.getThrottle()); + const $warningBox = $row.find('.throttle-warning-text'); + + // Function to update throttle and show/hide warning box + function updateThrottle() { + rule.setThrottle($throttleInput.val()); + // Change color if value exceeds 1 + if (parseFloat($throttleInput.val()) > 1 || parseFloat($throttleInput.val()) < 0) { + $throttleInput.css('background-color', 'orange'); + // Show warning box + $warningBox.show(); + } else { + $throttleInput.css('background-color', ''); // Reset to default + // Hide warning box + $warningBox.hide(); + } + } + updateThrottle(); + $throttleInput.change(updateThrottle); + $row.find('.mix-rule-roll').val(rule.getRoll()).change(function () { rule.setRoll($(this).val()); }); diff --git a/tabs/osd.html b/tabs/osd.html index bf29aa48d..99d215b5e 100644 --- a/tabs/osd.html +++ b/tabs/osd.html @@ -93,8 +93,8 @@