Merge remote-tracking branch 'origin/master' into sh_vtol_nav
7
.vscode/settings.json
vendored
|
@ -2,7 +2,12 @@
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"chrono": "c",
|
"chrono": "c",
|
||||||
"cmath": "c",
|
"cmath": "c",
|
||||||
"ranges": "c"
|
"ranges": "c",
|
||||||
|
"navigation.h": "c",
|
||||||
|
"rth_trackback.h": "c"
|
||||||
|
"platform.h": "c",
|
||||||
|
"timer.h": "c",
|
||||||
|
"bus.h": "c"
|
||||||
},
|
},
|
||||||
"editor.tabSize": 4,
|
"editor.tabSize": 4,
|
||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
|
|
|
@ -51,7 +51,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(INAV VERSION 7.0.0)
|
project(INAV VERSION 8.0.0)
|
||||||
|
|
||||||
enable_language(ASM)
|
enable_language(ASM)
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,18 @@ include(gcc)
|
||||||
set(arm_none_eabi_triplet "arm-none-eabi")
|
set(arm_none_eabi_triplet "arm-none-eabi")
|
||||||
|
|
||||||
# Keep version in sync with the distribution files below
|
# Keep version in sync with the distribution files below
|
||||||
set(arm_none_eabi_gcc_version "10.3.1")
|
set(arm_none_eabi_gcc_version "13.2.1")
|
||||||
set(arm_none_eabi_base_url "https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.10/gcc-arm-none-eabi-10.3-2021.10")
|
# This is the output directory "pretty" name and URI name prefix
|
||||||
|
set(base_dir_name "arm-gnu-toolchain-13.2.rel1")
|
||||||
|
# This is the name inside the archive, which is no longer evincible from URI, alas
|
||||||
|
set(archive_base_dir_name "arm-gnu-toolchain-13.2.Rel1")
|
||||||
|
set(arm_none_eabi_base_url "https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/${base_dir_name}")
|
||||||
# suffix and checksum
|
# suffix and checksum
|
||||||
set(arm_none_eabi_win32 "win32.zip" 2bc8f0c4c4659f8259c8176223eeafc1)
|
set(arm_none_eabi_win32 "mingw-w64-i686-arm-none-eabi.zip" 7fd677088038cdf82f33f149e2e943ee)
|
||||||
set(arm_none_eabi_linux_amd64 "x86_64-linux.tar.bz2" 2383e4eb4ea23f248d33adc70dc3227e)
|
set(arm_none_eabi_linux_amd64 "x86_64-arm-none-eabi.tar.xz" 791754852f8c18ea04da7139f153a5b7)
|
||||||
set(arm_none_eabi_linux_aarch64 "aarch64-linux.tar.bz2" 3fe3d8bb693bd0a6e4615b6569443d0d)
|
set(arm_none_eabi_linux_aarch64 "aarch64-arm-none-eabi.tar.xz" 5a08122e6d4caf97c6ccd1d29e62599c)
|
||||||
set(arm_none_eabi_gcc_macos "mac.tar.bz2" 7f2a7b7b23797302a9d6182c6e482449)
|
set(arm_none_eabi_darwin_amd64 "darwin-x86_64-arm-none-eabi.tar.xz" 41d49840b0fc676d2ae35aab21a58693)
|
||||||
|
set(arm_none_eabi_darwin_aarch64 "darwin-arm64-arm-none-eabi.tar.xz" 2c43e9d72206c1f81227b0a685df5ea6)
|
||||||
function(arm_none_eabi_gcc_distname var)
|
|
||||||
string(REPLACE "/" ";" url_parts ${arm_none_eabi_base_url})
|
|
||||||
list(LENGTH url_parts n)
|
|
||||||
math(EXPR last "${n} - 1")
|
|
||||||
list(GET url_parts ${last} basename)
|
|
||||||
set(${var} ${basename} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(host_uname_machine var)
|
function(host_uname_machine var)
|
||||||
# We need to call uname -m manually, since at the point
|
# We need to call uname -m manually, since at the point
|
||||||
|
@ -47,7 +44,14 @@ function(arm_none_eabi_gcc_install)
|
||||||
message("-- no precompiled ${arm_none_eabi_triplet} toolchain for machine ${machine}")
|
message("-- no precompiled ${arm_none_eabi_triplet} toolchain for machine ${machine}")
|
||||||
endif()
|
endif()
|
||||||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
set(dist ${arm_none_eabi_gcc_macos})
|
host_uname_machine(machine)
|
||||||
|
if(machine STREQUAL "x86_64" OR machine STREQUAL "amd64")
|
||||||
|
set(dist ${arm_none_eabi_darwin_amd64})
|
||||||
|
elseif(machine STREQUAL "aarch64")
|
||||||
|
set(dist ${arm_none_eabi_darwin_aarch64})
|
||||||
|
else()
|
||||||
|
message("-- no precompiled ${arm_none_eabi_triplet} toolchain for machine ${machine}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(dist STREQUAL "")
|
if(dist STREQUAL "")
|
||||||
|
@ -83,11 +87,27 @@ function(arm_none_eabi_gcc_install)
|
||||||
if(NOT status EQUAL 0)
|
if(NOT status EQUAL 0)
|
||||||
message(FATAL_ERROR "error extracting ${basename}: ${status}")
|
message(FATAL_ERROR "error extracting ${basename}: ${status}")
|
||||||
endif()
|
endif()
|
||||||
|
string(REPLACE "." ";" url_parts ${dist_suffix})
|
||||||
|
list(GET url_parts 0 host_dir_name)
|
||||||
|
set(dir_name "${archive_base_dir_name}-${host_dir_name}")
|
||||||
|
file(REMOVE_RECURSE "${TOOLS_DIR}/${base_dir_name}")
|
||||||
|
file(RENAME "${TOOLS_DIR}/${dir_name}" "${TOOLS_DIR}/${base_dir_name}")
|
||||||
|
# This is **somewhat ugly**
|
||||||
|
# the newlib distributed by ARM generates suprious warnings from re-entrant POSIX functions
|
||||||
|
# that INAV doesn't use. These "harmless" warnings can be surpressed by removing the
|
||||||
|
# errant section from the only libnosys used by INAV ...
|
||||||
|
# So look the other way ... while this is "fixed"
|
||||||
|
execute_process(COMMAND arm-none-eabi-objcopy -w -R .gnu.warning.* "${TOOLS_DIR}/${base_dir_name}/arm-none-eabi/lib/thumb/v7e-m+fp/hard/libnosys.a"
|
||||||
|
RESULT_VARIABLE status
|
||||||
|
WORKING_DIRECTORY ${TOOLS_DIR}
|
||||||
|
)
|
||||||
|
if(NOT status EQUAL 0)
|
||||||
|
message(FATAL_ERROR "error fixing libnosys.a: ${status}")
|
||||||
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(arm_none_eabi_gcc_add_path)
|
function(arm_none_eabi_gcc_add_path)
|
||||||
arm_none_eabi_gcc_distname(dist_name)
|
set(gcc_path "${TOOLS_DIR}/${base_dir_name}/bin")
|
||||||
set(gcc_path "${TOOLS_DIR}/${dist_name}/bin")
|
|
||||||
if(CMAKE_HOST_SYSTEM MATCHES ".*Windows.*")
|
if(CMAKE_HOST_SYSTEM MATCHES ".*Windows.*")
|
||||||
set(sep "\\;")
|
set(sep "\\;")
|
||||||
else()
|
else()
|
||||||
|
@ -110,7 +130,7 @@ function(arm_none_eabi_gcc_check)
|
||||||
message("-- found ${prog} ${version} at ${prog_path}")
|
message("-- found ${prog} ${version} at ${prog_path}")
|
||||||
if(COMPILER_VERSION_CHECK AND NOT arm_none_eabi_gcc_version STREQUAL version)
|
if(COMPILER_VERSION_CHECK AND NOT arm_none_eabi_gcc_version STREQUAL version)
|
||||||
message("-- expecting ${prog} version ${arm_none_eabi_gcc_version}, but got version ${version} instead")
|
message("-- expecting ${prog} version ${arm_none_eabi_gcc_version}, but got version ${version} instead")
|
||||||
arm_none_eabi_gcc_install()
|
arm_none_eabi_gcc_install()
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
|
@ -9,7 +9,7 @@ option(SEMIHOSTING "Enable semihosting")
|
||||||
message("-- DEBUG_HARDFAULTS: ${DEBUG_HARDFAULTS}, SEMIHOSTING: ${SEMIHOSTING}")
|
message("-- DEBUG_HARDFAULTS: ${DEBUG_HARDFAULTS}, SEMIHOSTING: ${SEMIHOSTING}")
|
||||||
|
|
||||||
set(CMSIS_DIR "${MAIN_LIB_DIR}/lib/main/AT32F43x/Drivers/CMSIS")
|
set(CMSIS_DIR "${MAIN_LIB_DIR}/lib/main/AT32F43x/Drivers/CMSIS")
|
||||||
set(CMSIS_INCLUDE_DIR "${CMSIS_DIR}/cm4/core_support")
|
set(CMSIS_INCLUDE_DIR "${CMSIS_DIR}/cm4/core_support")
|
||||||
# DSP use common
|
# DSP use common
|
||||||
set(CMSIS_DSP_DIR "${MAIN_LIB_DIR}/main/CMSIS/DSP")
|
set(CMSIS_DSP_DIR "${MAIN_LIB_DIR}/main/CMSIS/DSP")
|
||||||
set(CMSIS_DSP_INCLUDE_DIR "${CMSIS_DSP_DIR}/Include")
|
set(CMSIS_DSP_INCLUDE_DIR "${CMSIS_DSP_DIR}/Include")
|
||||||
|
@ -50,8 +50,8 @@ main_sources(AT32_ASYNCFATFS_SRC
|
||||||
)
|
)
|
||||||
|
|
||||||
main_sources(AT32_MSC_SRC
|
main_sources(AT32_MSC_SRC
|
||||||
msc/at32_msc_diskio.c
|
msc/at32_msc_diskio.c
|
||||||
msc/emfat.c
|
msc/emfat.c
|
||||||
msc/emfat_file.c
|
msc/emfat_file.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -92,6 +92,7 @@ set(AT32_LINK_OPTIONS
|
||||||
-Wl,--cref
|
-Wl,--cref
|
||||||
-Wl,--no-wchar-size-warning
|
-Wl,--no-wchar-size-warning
|
||||||
-Wl,--print-memory-usage
|
-Wl,--print-memory-usage
|
||||||
|
-Wl,--no-warn-rwx-segments
|
||||||
)
|
)
|
||||||
# Get target features
|
# Get target features
|
||||||
macro(get_at32_target_features output_var dir target_name)
|
macro(get_at32_target_features output_var dir target_name)
|
||||||
|
@ -264,7 +265,7 @@ function(add_at32_executable)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Main function of AT32
|
# Main function of AT32
|
||||||
function(target_at32)
|
function(target_at32)
|
||||||
if(NOT arm-none-eabi STREQUAL TOOLCHAIN)
|
if(NOT arm-none-eabi STREQUAL TOOLCHAIN)
|
||||||
return()
|
return()
|
||||||
|
|
|
@ -12,6 +12,9 @@ These boards are well tested with INAV and are known to be of good quality and r
|
||||||
| [Holybro Kakute H7](https://inavflight.com/shop/s/bg/1914066) | H7 | KAKUTEH7 | All | All | All | All | All | SERIAL, SD |
|
| [Holybro Kakute H7](https://inavflight.com/shop/s/bg/1914066) | H7 | KAKUTEH7 | All | All | All | All | All | SERIAL, SD |
|
||||||
|
|
||||||
It's possible to find more supported and tested boards [here](https://github.com/iNavFlight/inav/wiki/Welcome-to-INAV,-useful-links-and-products)
|
It's possible to find more supported and tested boards [here](https://github.com/iNavFlight/inav/wiki/Welcome-to-INAV,-useful-links-and-products)
|
||||||
|
|
||||||
|
There is also a [full list of all supported boards](https://github.com/iNavFlight/inav/wiki/Boards,-Targets-and-PWM-allocations).
|
||||||
|
|
||||||
### Boards documentation
|
### Boards documentation
|
||||||
|
|
||||||
See the [docs/boards](https://github.com/iNavFlight/inav/tree/master/docs/boards) folder for additional information regards to many targets in INAV, to example help in finding pinout and features. _Feel free to help improve the docs._
|
See the [docs/boards](https://github.com/iNavFlight/inav/tree/master/docs/boards) folder for additional information regards to many targets in INAV, to example help in finding pinout and features. _Feel free to help improve the docs._
|
||||||
|
|
132
docs/GPS_fix_estimation.md
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
# GPS Fix estimation (dead reconing, RTH without GPS) for fixed wing
|
||||||
|
|
||||||
|
Video demonstration
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=wzvgRpXCS4U)
|
||||||
|
|
||||||
|
There is possibility to allow plane to estimate it's position when GPS fix is lost.
|
||||||
|
The main purpose is RTH without GPS.
|
||||||
|
It works for fixed wing only.
|
||||||
|
|
||||||
|
Plane should have the following sensors:
|
||||||
|
- acceleromenter, gyroscope
|
||||||
|
- barometer
|
||||||
|
- GPS
|
||||||
|
- magnethometer (optional, highly recommended)
|
||||||
|
- pitot (optional)
|
||||||
|
|
||||||
|
By befault, all navigation modes are disabled when GPS fix is lost. If RC signal is lost also, plane will not be able to enable RTH. Plane will switch to LANDING instead. When flying above unreachable spaces, plane will be lost.
|
||||||
|
|
||||||
|
GPS fix estimation allows to recover plane using magnetometer and baromener only.
|
||||||
|
|
||||||
|
GPS Fix is also estimated on GPS Sensor timeouts (hardware failures).
|
||||||
|
|
||||||
|
Note, that GPS fix estimation is not a solution for navigation without GPS. Without GPS fix, position error accumulates quickly. But it is acceptable for RTH. This is not a solution for flying under spoofing also. GPS is the most trusted sensor in Inav. It's output is not validated.
|
||||||
|
|
||||||
|
# How it works ?
|
||||||
|
|
||||||
|
In normal situation, plane is receiving it's position from GPS sensor. This way it is able to hold course, RTH or navigate by waypoints.
|
||||||
|
|
||||||
|
Without GPS fix, plane has nose heading from magnetometer and height from barometer only.
|
||||||
|
|
||||||
|
To navigate without GPS fix, we make the following assumptions:
|
||||||
|
- plane is flying in the direction where nose is pointing
|
||||||
|
- (if pitot tube is not installed) plane is flying with constant airspeed, specified in settings
|
||||||
|
|
||||||
|
It is possible to roughly estimate position using theese assumptions. To increase accuracy, plane will use information about wind direction and speed, estimated before GPS fix was lost. To increase groundspeed estimation accuracy, plane will use pitot tube data(if available).
|
||||||
|
|
||||||
|
From estimated heading direction and speed, plane is able to **roughty** estimate it's position.
|
||||||
|
|
||||||
|
It is assumed, that plane will fly in roughly estimated direction to home position untill either GPS fix or RC signal is recovered.
|
||||||
|
|
||||||
|
*Plane has to aquire GPS fix and store home position before takeoff. Estimation completely without GPS fix will not work*.
|
||||||
|
|
||||||
|
# Estimation without magnethometer
|
||||||
|
|
||||||
|
Without magnethometer, navigation accuracy is very poor. The problem is heading drift.
|
||||||
|
|
||||||
|
The longer plane flies without magnethometer or GPS, the bigger is course estimation error.
|
||||||
|
|
||||||
|
After few minutes and few turns, "North" direction estimation can be completely broken.
|
||||||
|
In general, accuracy is enough to perform RTH U-turn when both RC controls and GPS are lost, and roughtly keep RTH direction in areas with occasional GPS outages.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
(purple line - estimated position, black line - real position).
|
||||||
|
|
||||||
|
It is recommened to use GPS fix estimation without magnethometer as last resort only. For example, if plane is flying above lake, landing means loss of plane. With GPS Fix estimation, plane will try to do RTH in very rought direction, instead of landing.
|
||||||
|
|
||||||
|
It is up to user to estimate the risk of fly-away.
|
||||||
|
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
|
||||||
|
GPS Fix estimation is enabled with CLI command:
|
||||||
|
|
||||||
|
```set inav_allow_gps_fix_estimation=ON```
|
||||||
|
|
||||||
|
Also you have to specify cruise airspeed of the plane.
|
||||||
|
|
||||||
|
To find out cruise airspeed, make a test flight. Enable ground speed display on OSD. Flight in CRUISE mode in two opposite directions. Take average speed.
|
||||||
|
|
||||||
|
Cruise airspeed is specified in cm/s.
|
||||||
|
|
||||||
|
To convert km/h to m/s, multiply by 27.77.
|
||||||
|
|
||||||
|
|
||||||
|
Example: 100 km/h = 100 * 27.77 = 2777 cm/s
|
||||||
|
|
||||||
|
```set fw_reference_airspeed=2777```
|
||||||
|
|
||||||
|
*It is important, that plane fly with specified speed in CRUISE mode. If you have set option "Increase cruise speed with throttle" - do not use it without GPS Fix.*
|
||||||
|
|
||||||
|
*If pitot is available, pitot sensor data will be used instead of constant. It is not necessary to specify fw_reference_airspeed. However, it is still adviced to specify for the case of pitot failure.*
|
||||||
|
|
||||||
|
*Note related command: to continue mission without RC signal, see command ```set failsafe_mission_delay=-1```.*
|
||||||
|
|
||||||
|
**After entering CLI command, make sure that settings are saved:**
|
||||||
|
|
||||||
|
```save```
|
||||||
|
|
||||||
|
# Disabling GPS sensor from RC controller
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
For testing purposes, it is possible to disable GPS sensor fix from RC controller in programming tab:
|
||||||
|
|
||||||
|
*GPS can be disabled only after: 1) initial GPS fix is acquired 2) in ARMED mode.*
|
||||||
|
|
||||||
|
# Allowing wp missions with GPS Fix estimation
|
||||||
|
|
||||||
|
```failsafe_gps_fix_estimation_delay```
|
||||||
|
|
||||||
|
Controls whether waypoint mission is allowed to proceed with gps fix estimation. Sets the time delay in seconds between gps fix lost event and RTH activation. Minimum delay is 7 seconds. If set to -1 the mission will continue until the end. With default setting(7), waypoint mission is aborted and switched to RTH with 7 seconds delay. RTH is done with GPS Fix estimation. RTH is trigerred regradless of failsafe procedure selected in configurator.
|
||||||
|
|
||||||
|
# Expected error (mag + baro)
|
||||||
|
|
||||||
|
Realistic expected error is up to 200m per 1km of flight path. In tests, 500m drift per 5km path was seen.
|
||||||
|
|
||||||
|
To dicrease drift:
|
||||||
|
- fly one large circle with GPS available to get good wind estimation
|
||||||
|
- use airspeed sensor. If airspeed sensor is not installed, fly in cruise mode without throttle override.
|
||||||
|
- do smooth, large turns
|
||||||
|
- make sure compass is pointing in nose direction precicely
|
||||||
|
- calibrate compass correctly
|
||||||
|
|
||||||
|
This video shows real world test where GPS was disabled occasionally. Wind is 10km/h south-west:
|
||||||
|
|
||||||
|
|
||||||
|
https://github.com/RomanLut/inav/assets/11955117/0599a3c3-df06-4d40-a32a-4d8f96140592
|
||||||
|
|
||||||
|
|
||||||
|
Purple line shows estimated position. Black line shows real position. "EST ERR" sensor shows estimation error in metters. Estimation is running when satellite icon displays "ES". Estimated position snaps to real position when GPS fix is reaquired.
|
||||||
|
|
||||||
|
|
||||||
|
# Is it possible to implement this for multirotor ?
|
||||||
|
|
||||||
|
There are some ideas, but there is no solution now. We can not make assumptions with multirotor which we can make with a fixed wing.
|
||||||
|
|
||||||
|
|
||||||
|
# Links
|
||||||
|
|
||||||
|
INAV HITL https://github.com/RomanLut/INAV-X-Plane-HITL
|
90
docs/LED pin PWM.md
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
# LED pin PWM
|
||||||
|
|
||||||
|
Normally LED pin is used to drive WS2812 led strip. LED pin is held low, and every 10ms or 20ms a set of pulses is sent to change color of the 32 LEDs:
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
As alternative function, it is possible to generate PWM signal with specified duty ratio on the LED pin.
|
||||||
|
|
||||||
|
Feature can be used to drive external devices. It is also used to simulate [OSD joystick](OSD%20Joystick.md) to control cameras.
|
||||||
|
|
||||||
|
PWM frequency is fixed to 24kHz with duty ratio between 0 and 100%:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
There are four modes of operation:
|
||||||
|
- low
|
||||||
|
- high
|
||||||
|
- shared_low
|
||||||
|
- shared_high
|
||||||
|
|
||||||
|
Mode is configured using ```led_pin_pwm_mode``` setting: ```LOW```, ```HIGH```, ```SHARED_LOW```, ```SHARED_HIGH```
|
||||||
|
|
||||||
|
*Note that in any mode, there will be ~2 seconds LOW pulse on boot.*
|
||||||
|
|
||||||
|
## LOW
|
||||||
|
LED Pin is initialized to output low level by default and can be used to generate PWM signal.
|
||||||
|
|
||||||
|
ws2812 strip can not be controlled.
|
||||||
|
|
||||||
|
## HIGH
|
||||||
|
LED Pin is initialized to output high level by default and can be used to generate PWM signal.
|
||||||
|
|
||||||
|
ws2812 strip can not be controlled.
|
||||||
|
|
||||||
|
## SHARED_LOW (default)
|
||||||
|
LED Pin is used to drive WS2812 strip. Pauses between pulses are low:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It is possible to generate PWM signal with duty ratio >0...100%.
|
||||||
|
|
||||||
|
While PWM signal is generated, ws2811 strip is not updated.
|
||||||
|
|
||||||
|
When PWM generation is disabled, LED pin is used to drive ws2812 strip.
|
||||||
|
|
||||||
|
Total ws2812 pulses duration is ~1ms with ~9ms pauses. Thus connected device should ignore PWM signal with duty ratio < ~10%.
|
||||||
|
|
||||||
|
## SHARED_HIGH
|
||||||
|
LED Pin is used to drive WS2812 strip. Pauses between pulses are high. ws2812 pulses are prefixed with 50us low 'reset' pulse:
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
It is possible to generate PWM signal with duty ratio 0...<100%.
|
||||||
|
|
||||||
|
While PWM signal is generated, ws2811 strip is not updated.
|
||||||
|
|
||||||
|
When PWM generation is disabled, LED pin is used to drive ws2812 strip. Total ws2812 pulses duration is ~1ms with ~9ms pauses. Thus connected device should ignore PWM signal with duty ratio > ~90%.
|
||||||
|
|
||||||
|
After sending ws2812 protocol pulses for 32 LEDS, we held line high for 9ms, then send 50us low 'reset' pulse. Datasheet for ws2812 protocol does not describe behavior for long high pulse, but in practice it works the same as 'reset' pulse. To be safe, we also send correct low 'reset' pulse before starting next LEDs update sequence.
|
||||||
|
|
||||||
|
This mode is used to simulate OSD joystick. It is Ok that effectively voltage level is held >90% while driving LEDs, because OSD joystick keypress voltages are below 90%.
|
||||||
|
|
||||||
|
See [OSD Joystick](OSD%20Joystick.md) for more information.
|
||||||
|
|
||||||
|
# Generating PWM signal with programming framework
|
||||||
|
|
||||||
|
See "LED Pin PWM" operation in [Programming Framework](Programming%20Framework.md)
|
||||||
|
|
||||||
|
|
||||||
|
# Generating PWM signal from CLI
|
||||||
|
|
||||||
|
```ledpinpwm <value>``` - value = 0...100 - enable PWM generation with specified duty cycle
|
||||||
|
|
||||||
|
```ledpinpwm``` - disable PWM generation ( disable to allow ws2812 LEDs updates in shared modes )
|
||||||
|
|
||||||
|
|
||||||
|
# Example of driving LED
|
||||||
|
|
||||||
|
It is possible to drive single color LED with brightness control. Current consumption should not be greater then 1-2ma, thus LED can be used for indication only.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Example of driving powerfull white LED
|
||||||
|
|
||||||
|
To drive power LED with brightness control, Mosfet should be used:
|
||||||
|
|
||||||
|

|
||||||
|
|
|
@ -1,80 +1,30 @@
|
||||||
# MixerProfile
|
# MixerProfile
|
||||||
|
|
||||||
A MixerProfile is a set of motor mixer, servo-mixer and platform type configuration settings to enable VTOL transitions.
|
A MixerProfile is a set of motor mixer, servo-mixer and platform type configuration settings. It is designed for experienced inav users.
|
||||||
|
|
||||||
Currently two profiles are supported on targets other than F411 and F722 (due to resource constraints on these FC). i.e VTOL transition is not available on F411 and F722 targets.
|
### For a tutorial of vtol setup, Read https://github.com/iNavFlight/inav/blob/master/docs/VTOL.md
|
||||||
|
|
||||||
By default, switching between profiles requires reboot to take affect. However, when all conditions are met, and a suitable [configuration](#configuration) has been applied, `mixer_profile` also allows in-flight profile [switching](#rc-mode-settings) to allow things like VTOL operation. This is the recommended operating mode.
|
Not limited to VTOL. air/land/sea mixed vehicle is also achievable with this feature. Model behaves according to current mixer_profile's platform_type and configured custom motor/servo mixer
|
||||||
|
|
||||||
|
Currently two profiles are supported on targets other than F411(due to resource constraints on F411). i.e VTOL transition is not available on F411.
|
||||||
|
|
||||||
|
For VTOL setup. one mixer_profile is used for multi-rotor(MR) and the other is used for fixed-wing(FW)
|
||||||
|
By default, switching between profiles requires reboot to take affect. However, using the RC mode: `MIXER PROFILE 2` will allow in flight switching for things like VTOL operation
|
||||||
|
. And will re-initialize pid and navigation controllers for current MC or FW flying mode.
|
||||||
|
|
||||||
Please note that this is an emerging / experimental capability that will require some effort by the pilot to implement.
|
Please note that this is an emerging / experimental capability that will require some effort by the pilot to implement.
|
||||||
|
|
||||||
## Setup for VTOL
|
## Mixer Transition input
|
||||||
- A VTOL specific FC target or `timer_output_mode` overrides was required in the early stage of the development, But since unified mapping introduced in INAV 7.0 It is not needed anymore.
|
|
||||||
- ~~For mixer profile switching it is necessary to keep motor and servo PWM mapping consistent between Fixed-Wing (FW) and Multi-rotor (MR) profiles~~
|
|
||||||
- ~~Traditionally, FW and MR have had different enumerations to define the PWM mappings. For VTOL operation it is necessary to set the `timer_output_mode` overrides to allow a consistent enumeration and thus mapping between MR and FW modes.~~
|
|
||||||
- ~~In operation, it is necessary to set the `mixer_profile` and the `pid_profile` separately and to set a [RC mode](#rc-mode-settings) to switch between them.~~
|
|
||||||
## Configuration
|
|
||||||
### Timer overrides
|
|
||||||
Set the timer overrides for the outputs that you are intended to use.
|
|
||||||
For SITL builds, is not necessary to set timer overrides.
|
|
||||||
Please note that there are some display issues on the configurator that will show wrong mapping on the mixer_profile which has less motor/servo compared with the another
|
|
||||||
### Profile Switch
|
|
||||||
|
|
||||||
Setup the FW mode and MR mode separately in two different mixer profiles:
|
|
||||||
|
|
||||||
In this example, FW mode is `mixer_profile` 1 and MR mode is `mixer_profile` 2.
|
|
||||||
|
|
||||||
Currently, the INAV Configurator does not fully support `mixer_profile`, so some of the settings have to be done in CLI.
|
|
||||||
|
|
||||||
Add `set mixer_pid_profile_linking = ON` in order to enable `pid_profile` auto handling. It will change the `pid profile` index according to the `mixer_profile` index on FC boot and allow `mixer_profile` hot switching (this is recommended usage).
|
|
||||||
|
|
||||||
The following 2 `mixer_profile` sections are added in the CLI:
|
|
||||||
|
|
||||||
```
|
|
||||||
#switch to mixer_profile by cli
|
|
||||||
mixer_profile 1
|
|
||||||
|
|
||||||
set platform_type = AIRPLANE
|
|
||||||
set model_preview_type = 26
|
|
||||||
# motor stop feature have been moved to here
|
|
||||||
set motorstop_on_low = ON
|
|
||||||
# enable pid_profile auto handling (recommended).
|
|
||||||
set mixer_pid_profile_linking = ON
|
|
||||||
save
|
|
||||||
```
|
|
||||||
Then finish the aeroplane setting on mixer_profile 1
|
|
||||||
|
|
||||||
```
|
|
||||||
mixer_profile 2
|
|
||||||
|
|
||||||
set platform_type = TRICOPTER
|
|
||||||
set model_preview_type = 1
|
|
||||||
# also enable pid_profile auto handling
|
|
||||||
set mixer_pid_profile_linking = ON
|
|
||||||
save
|
|
||||||
```
|
|
||||||
Then finish the multi-rotor setting on `mixer_profile` 2.
|
|
||||||
|
|
||||||
Note that default profile is profile `1`.
|
|
||||||
|
|
||||||
You can use `MAX` servo input to set a fixed input for the tilting servo. Speed setting for `MAX` input is available in the CLI.
|
|
||||||
|
|
||||||
It is recommended to have some amount of control surface (elevon / elevator) mapped for stabilization even in MR mode to get improved authority when airspeed is high.
|
|
||||||
|
|
||||||
**Double check all settings in CLI with the `diff all` command**; make sure you have set the correct settings. Also check what will change with `mixer_profile`. For example servo output min / max range will not change. But `smix` and `mmix` will change.
|
|
||||||
|
|
||||||
### Mixer Transition input
|
|
||||||
|
|
||||||
Typically, 'transition input' will be useful in MR mode to gain airspeed.
|
Typically, 'transition input' will be useful in MR mode to gain airspeed.
|
||||||
Both the servo mixer and motor mixer can accept transition mode as an input.
|
|
||||||
The associated motor or servo will then move accordingly when transition mode is activated.
|
The associated motor or servo will then move accordingly when transition mode is activated.
|
||||||
Transition input is disabled when navigation mode is activate
|
Transition input is disabled when navigation mode is activate
|
||||||
|
|
||||||
The use of Transition Mode is recommended to enable further features and future developments like fail-safe support. Mapping motor to servo output, or servo with logic conditions is **not** recommended
|
The use of Transition Mode is recommended to enable further features and future developments like fail-safe support. Mapping motor to servo output, or servo with logic conditions is **not** recommended
|
||||||
|
|
||||||
#### Servo
|
## Servo
|
||||||
|
|
||||||
38 is the input source for transition input; use this to tilt motor to gain airspeed.
|
`Mixer Transition` is the input source for transition input; use this to tilt motor to gain airspeed.
|
||||||
|
|
||||||
Example: Increase servo 1 output by +45 with speed of 150 when transition mode is activated for tilted motor setup:
|
Example: Increase servo 1 output by +45 with speed of 150 when transition mode is activated for tilted motor setup:
|
||||||
|
|
||||||
|
@ -82,15 +32,14 @@ Example: Increase servo 1 output by +45 with speed of 150 when transition mode i
|
||||||
# rule no; servo index; input source; rate; speed; activate logic function number
|
# rule no; servo index; input source; rate; speed; activate logic function number
|
||||||
smix 6 1 38 45 150 -1
|
smix 6 1 38 45 150 -1
|
||||||
```
|
```
|
||||||
Please note there will be a time window that tilting motors is providing up lift but rear motor isn't. Result in a sudden pitch raise on the entering of the mode. More forward tilting servo position on transition input(you can use 'speed' in servo rules to slowly move to this position), A faster tilting servo speed on `MAX` servo input will reduce the time window. OR lower the throttle on the entering of the FW mode to mitigate the effect.
|
|
||||||
|
|
||||||
#### Motor
|
## Motor
|
||||||
|
|
||||||
The default `mmix` throttle value is 0.0, It will not show in `diff` command when throttle value is 0.0 (unused); this causes the motor to stop.
|
The default `mmix` throttle value is 0.0, It will not show in `diff` command when throttle value is 0.0 (unused);
|
||||||
|
|
||||||
- 0.0<throttle<=1.0 : normal mapping
|
- 0.0<throttle<=1.0 : normal mapping
|
||||||
- -1.0<throttle<=0.0 : motor stop, default value 0
|
- -1.0<throttle<=0.0 : motor stop, default value 0, set to -1 to use a place holder for subsequent motor rules
|
||||||
- -2.0<throttle<-1.0 : spin regardless of the radio's throttle position at speed `abs(throttle)-1` when Mixer Transition is activated.
|
- -2.0<throttle<-1.0 : spin regardless of throttle position at speed `abs(throttle)-1` when Mixer Transition is activated.
|
||||||
|
|
||||||
Example: This will spin motor number 5 (counting from 1) at 20%, in transition mode only, to gain speed for a "4 rotor 1 pusher" setup:
|
Example: This will spin motor number 5 (counting from 1) at 20%, in transition mode only, to gain speed for a "4 rotor 1 pusher" setup:
|
||||||
|
|
||||||
|
@ -99,14 +48,13 @@ Example: This will spin motor number 5 (counting from 1) at 20%, in transition m
|
||||||
mmix 4 -1.200 0.000 0.000 0.000
|
mmix 4 -1.200 0.000 0.000 0.000
|
||||||
```
|
```
|
||||||
|
|
||||||
### RC mode settings
|
## RC mode settings
|
||||||
|
|
||||||
It is recommend that the pilot uses a RC mode switch to activate modes or switch profiles.
|
It is recommend that the pilot uses a RC mode switch to activate modes or switch profiles.
|
||||||
Profile files Switching is not available until the runtime sensor calibration is done. Switching is NOT available when navigation mode is activate or position controller is activate, including altitude hold.
|
Profile files Switching is not available until the runtime sensor calibration is done. Switching is NOT available when navigation mode is activate.
|
||||||
|
|
||||||
`mixer_profile` 1 will be used as default, `mixer_profile` 2 will be used when the `MIXER PROFILE 2` mode box is activated. Once successfully set, you can see the profiles / model preview etc. will switch accordingly when you view the relevant INAV Configurator tabs. Checking these tabs in the INAV Configurator will help make the setup easier.
|
`mixer_profile` 1 will be used as default, `mixer_profile` 2 will be used when the `MIXER PROFILE 2` mode box is activated.
|
||||||
|
Set `MIXER TRANSITION` accordingly when you want to use `MIXER TRANSITION` input for motors and servos. Here is sample of using these RC modes:
|
||||||
Set `MIXER TRANSITION` accordingly when you want to use `MIXER TRANSITION` input for motors and servos. Here is sample of using the `MIXER TRANSITION` mode:
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -116,29 +64,50 @@ Set `MIXER TRANSITION` accordingly when you want to use `MIXER TRANSITION` input
|
||||||
|
|
||||||
It is also possible to set it as 4 state switch by adding FW(profile1) with transition on.
|
It is also possible to set it as 4 state switch by adding FW(profile1) with transition on.
|
||||||
|
|
||||||
### Automated Transition
|
## Automated Transition
|
||||||
This feature is mainly for RTH in a failsafe event. When set properly, model will use the FW mode to fly home efficiently, And land in the MC mode for easier landing.
|
This feature is mainly for RTH in a failsafe event. When set properly, model will use the FW mode to fly home efficiently, And land in the MC mode for easier landing.
|
||||||
Set `mixer_automated_switch` to `ON` in mixer_profile for MC mode. Set `mixer_switch_trans_timer` in mixer_profile for MC mode for the time required to gain airspeed for your model before entering to FW mode, for example, 50 ds. Finally set `mixer_automated_switch` to `ON` in mixer_profile for FW mode to let the model land in MC mode.
|
|
||||||
```
|
|
||||||
mixer_profile 2
|
|
||||||
set mixer_automated_switch = ON
|
|
||||||
set mixer_switch_trans_timer = 50
|
|
||||||
mixer_profile 1
|
|
||||||
set mixer_automated_switch = ON
|
|
||||||
save
|
|
||||||
```
|
|
||||||
|
|
||||||
`ON` for a mixer_profile\`s `mixer_automated_switch` means to schedule a Automated Transition when RTH head home(applies for MC mixer_profile) or RTH Land(applies for FW mixer_profile) is requested by navigation controller.
|
`ON` for a mixer_profile\`s `mixer_automated_switch` means to schedule a Automated Transition when RTH head home(applies for MC mixer_profile) or RTH Land(applies for FW mixer_profile) is requested by navigation controller.
|
||||||
|
Set `mixer_automated_switch` to `ON` in mixer_profile for MC mode. Set `mixer_switch_trans_timer` in mixer_profile for MC mode for the time required to gain airspeed for your model before entering to FW mode.
|
||||||
When `mixer_automated_switch`:`OFF` is set for all mixer_profiles(defaults). Model will not perform automated transition at all.
|
When `mixer_automated_switch`:`OFF` is set for all mixer_profiles(defaults). Model will not perform automated transition at all.
|
||||||
|
|
||||||
|
## TailSitter (planned for INAV 7.1)
|
||||||
|
TailSitter is supported by add a 90deg offset to the board alignment. Set the board aliment normally in the mixer_profile for FW mode(`set platform_type = AIRPLANE`), The motor trust axis should be same direction as the airplane nose. Then, in the mixer_profile for takeoff and landing set `tailsitter_orientation_offset = ON ` to apply orientation offset. orientation offset will also add a 45deg orientation offset.
|
||||||
|
|
||||||
|
## Parameter list (Partial List)
|
||||||
|
#### Please be aware of what parameter is shared among FW/MC modes and what isn't.
|
||||||
|
### Shared Parameters
|
||||||
|
|
||||||
|
- **Timer Overrides**
|
||||||
|
- **Outputs [Servo]:**
|
||||||
|
- Servo min-point, mid-point, max-point settings
|
||||||
|
- **Motor Configuration:**
|
||||||
|
- motor_pwm_protocol
|
||||||
|
- motor_poles
|
||||||
|
- **Servo Configuration:**
|
||||||
|
- servo_protocol
|
||||||
|
- servo_pwm_rate
|
||||||
|
- **Board Alignment**
|
||||||
|
- ·······
|
||||||
|
### Profile-Specific Parameters in VTOL
|
||||||
|
- **Mixer Profile**
|
||||||
|
- **Mixer Configuration:**
|
||||||
|
- platform_type
|
||||||
|
- motor_stop_on_low
|
||||||
|
- tailsitter_orientation_offset
|
||||||
|
- motor_direction_inverted, and more·······
|
||||||
|
- **Motor Mixing (mmix)**
|
||||||
|
- **Servo Mixing (smix)**
|
||||||
|
- **PID Profile**
|
||||||
|
- PIDs for Roll, Pitch, Yaw
|
||||||
|
- PIDs for Navigation Modes
|
||||||
|
- TPA (Throttle PID Attenuation) Settings
|
||||||
|
- Rate Settings
|
||||||
|
- ·······
|
||||||
|
|
||||||
## Happy flying
|
## Happy flying
|
||||||
|
|
||||||
Remember that this is currently an emerging capability:
|
Remember that this is currently an emerging capability:
|
||||||
|
|
||||||
* Test every thing on bench first.
|
* Test every thing on bench first.
|
||||||
* Remove the props and try `MIXER PROFILE 2`, `MIXER TRANSITION` RC modes while arming.
|
* Try MR or FW mode separately see if there are any problems.
|
||||||
* Then try MR or FW mode separately see if there are any problems.
|
|
||||||
* Try it somewhere you can recover your model in case of fail-safe. Fail-safe behavior is unknown at the current stage of development.
|
|
||||||
* Use the INAV Discord for help and setup questions; use the Github Issues for reporting bugs and unexpected behaviors. For reporting on Github, a CLI `diff all`, a DVR and a Blackbox log of the incident will assist investigation.
|
* Use the INAV Discord for help and setup questions; use the Github Issues for reporting bugs and unexpected behaviors. For reporting on Github, a CLI `diff all`, a DVR and a Blackbox log of the incident will assist investigation.
|
||||||
|
|
94
docs/OSD Joystick.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
# OSD joystick
|
||||||
|
|
||||||
|
LED pin can be used to emulate 5key OSD joystick for OSD camera pin, while still driving ws2812 LEDs (shared functionality).
|
||||||
|
|
||||||
|
See [LED pin PWM](LED%20pin%20PWM.md) for more details.
|
||||||
|
|
||||||
|
Note that for cameras which support RuncamDevice protocol, there is alternative functionality using serial communication: [Runcam device](Runcam%20device.md)
|
||||||
|
|
||||||
|
Also special adapters exist to convert RuncamDevice protocol to OSD Joystick: [Runcam control adapter](https://www.runcam.com/download/runcam_control_adapter_manual.pdf)
|
||||||
|
|
||||||
|
# OSD Joystick schematics
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Camera internal resistance seems to be 47kOhm or 9kOhm depending on camera model.
|
||||||
|
|
||||||
|
Each key effectively turns on voltage divider. Voltage is sensed by the camera and is compared to the list of keys voltages with some threshold.
|
||||||
|
|
||||||
|
Key voltage has to be held for at least 200ms.
|
||||||
|
|
||||||
|
To simulate 5key joystick, it is sufficient to generate correct voltage on camera OSD pin.
|
||||||
|
|
||||||
|
# Enabling OSD Joystick emulation
|
||||||
|
|
||||||
|
```set led_pin_pwm_mode=shared_high```
|
||||||
|
|
||||||
|
```set osd_joystick_enabled=on```
|
||||||
|
|
||||||
|
Also enable "Multi-color RGB LED Strip support" in Configuration tab.
|
||||||
|
|
||||||
|
# Connection diagram
|
||||||
|
|
||||||
|
We use LED pin PWM functionality with RC filter to generate voltage:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Example PCB layout (SMD components)
|
||||||
|
|
||||||
|
RC Filter can be soldered on a small piece of PCB:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Configuring keys voltages
|
||||||
|
|
||||||
|
If default voltages does not work with your camera model, then you have to measure voltages and find out corresponding PWM duty ratios.
|
||||||
|
|
||||||
|
1. Connect 5keys joystick to camera.
|
||||||
|
2. Measure voltages on OSD pin while each key is pressed.
|
||||||
|
3. Connect camera to FC throught RC filter as shown on schematix above.
|
||||||
|
4. Enable OSD Joystick emulation (see "Enabling OSD Joystick emulation" above)
|
||||||
|
4. Use cli command ```led_pin_pwm <value>```, value = 0...100 to find out PWM values for each voltage.
|
||||||
|
5. Specify PWM values in configuration and save:
|
||||||
|
|
||||||
|
```set osd_joystick_down=0```
|
||||||
|
|
||||||
|
```set osd_joystick_up=48```
|
||||||
|
|
||||||
|
```set osd_joystick_left=63```
|
||||||
|
|
||||||
|
```set osd_joystick_right=28```
|
||||||
|
|
||||||
|
```set osd_joystick_enter=75```
|
||||||
|
|
||||||
|
```save```
|
||||||
|
|
||||||
|
# Entering OSD Joystick emulation mode
|
||||||
|
|
||||||
|
Emulation can be enabled in unarmed state only.
|
||||||
|
|
||||||
|
OSD Joystick emulation mode is enabled using the following stick combination:
|
||||||
|
|
||||||
|
```Throttle:CENTER Yaw:RIGHT```
|
||||||
|
|
||||||
|
|
||||||
|
Than camera OSD can be navigated using right stick. See [Controls](Controls.md) for all stick combinations.
|
||||||
|
|
||||||
|
*Note that the same stick combination is used to enable 5keys joystick emulation with RuncamDevice protocol.*
|
||||||
|
|
||||||
|
Mode is exited using stick combination:
|
||||||
|
|
||||||
|
```Throttle:CENTER Yaw:LEFT```
|
||||||
|
|
||||||
|
# RC Box
|
||||||
|
|
||||||
|
There are 3 RC Boxes which can be used in armed and unarmed state:
|
||||||
|
- Camera 1 - Enter
|
||||||
|
- Camera 2 - Up
|
||||||
|
- Camera 3 - Down
|
||||||
|
|
||||||
|
Other keys can be emulated using Programming framework ( see [LED pin PWM](LED%20pin%20PWM.md) for more details ).
|
||||||
|
|
||||||
|
# Behavior on boot
|
||||||
|
|
||||||
|
There is ~2 seconds LOW pulse during boot sequence, which corresponds to DOWN key. Fortunately, cameras seem to ignore any key events few seconds after statup.
|
339
docs/OSD.md
|
@ -5,163 +5,190 @@ The On Screen Display, or OSD, is a feature that overlays flight data over the v
|
||||||
## Features and Limitations
|
## Features and Limitations
|
||||||
Not all OSDs are created equally. This table shows the differences between the different systems available.
|
Not all OSDs are created equally. This table shows the differences between the different systems available.
|
||||||
|
|
||||||
| OSD System | Character grid | Character | Canvas | MSP DisplayPort | All elements supported |
|
| OSD System | Character grid | Character | Canvas | MSP DisplayPort | All elements supported |
|
||||||
|---------------|-------------------|-----------|-----------|-------------------|---------------------------|
|
|---------------|----------------|-----------|--------|-----------------|-------------------------|
|
||||||
| Analogue PAL | 30 x 16 | X | | | YES |
|
| Analogue PAL | 30 x 16 | X | | | YES |
|
||||||
| Analogue NTSC | 30 x 13 | X | | | YES |
|
| Analogue NTSC | 30 x 13 | X | | | YES |
|
||||||
| PixelOSD | As PAL or NTSC | | X | | YES |
|
| PixelOSD | As PAL or NTSC | | X | | YES |
|
||||||
| DJI OSD | 30 x 16 | X | | | NO - BF Characters only |
|
| DJI OSD | 30 x 16 | X | | | NO - BF Characters only |
|
||||||
| DJI WTFOS | 60 x 22 | X | | X | YES |
|
| DJI WTFOS | 60 x 22 | X | | X | YES |
|
||||||
| HDZero | 50 x 18 | X | | X | YES |
|
| HDZero | 50 x 18 | X | | X | YES |
|
||||||
| Avatar | 53 x 20 | X | | X | YES |
|
| Avatar | 53 x 20 | X | | X | YES |
|
||||||
| DJI O3 | 53 x 20 (HD) | X | | X (partial) | NO - BF Characters only |
|
| DJI O3 | 53 x 20 (HD) | X | | X (partial) | NO - BF Characters only |
|
||||||
|
|
||||||
## OSD Elements
|
## OSD Elements
|
||||||
Here are the OSD Elements provided by INAV.
|
Here are the OSD Elements provided by INAV.
|
||||||
|
|
||||||
| ID | Element | Added |
|
| ID | Element | Added |
|
||||||
|-------|-----------------------------------------------|-------|
|
|-----|--------------------------------------------------|--------|
|
||||||
| 0 | OSD_RSSI_VALUE | 1.0.0 |
|
| 0 | OSD_RSSI_VALUE | 1.0.0 |
|
||||||
| 1 | OSD_MAIN_BATT_VOLTAGE | 1.0.0 |
|
| 1 | OSD_MAIN_BATT_VOLTAGE | 1.0.0 |
|
||||||
| 2 | OSD_CROSSHAIRS | 1.0.0 |
|
| 2 | OSD_CROSSHAIRS | 1.0.0 |
|
||||||
| 3 | OSD_ARTIFICIAL_HORIZON | 1.0.0 |
|
| 3 | OSD_ARTIFICIAL_HORIZON | 1.0.0 |
|
||||||
| 4 | OSD_HORIZON_SIDEBARS | 1.0.0 |
|
| 4 | OSD_HORIZON_SIDEBARS | 1.0.0 |
|
||||||
| 5 | OSD_ONTIME | 1.0.0 |
|
| 5 | OSD_ONTIME | 1.0.0 |
|
||||||
| 6 | OSD_FLYTIME | 1.0.0 |
|
| 6 | OSD_FLYTIME | 1.0.0 |
|
||||||
| 7 | OSD_FLYMODE | 1.0.0 |
|
| 7 | OSD_FLYMODE | 1.0.0 |
|
||||||
| 8 | OSD_CRAFT_NAME | 1.0.0 |
|
| 8 | OSD_CRAFT_NAME | 1.0.0 |
|
||||||
| 9 | OSD_THROTTLE_POS | 1.0.0 |
|
| 9 | OSD_THROTTLE_POS | 1.0.0 |
|
||||||
| 10 | OSD_VTX_CHANNEL | 1.0.0 |
|
| 10 | OSD_VTX_CHANNEL | 1.0.0 |
|
||||||
| 11 | OSD_CURRENT_DRAW | 1.0.0 |
|
| 11 | OSD_CURRENT_DRAW | 1.0.0 |
|
||||||
| 12 | OSD_MAH_DRAWN | 1.0.0 |
|
| 12 | OSD_MAH_DRAWN | 1.0.0 |
|
||||||
| 13 | OSD_GPS_SPEED | 1.0.0 |
|
| 13 | OSD_GPS_SPEED | 1.0.0 |
|
||||||
| 14 | OSD_GPS_SATS | 1.0.0 |
|
| 14 | OSD_GPS_SATS | 1.0.0 |
|
||||||
| 15 | OSD_ALTITUDE | 1.0.0 |
|
| 15 | OSD_ALTITUDE | 1.0.0 |
|
||||||
| 16 | OSD_ROLL_PIDS | 1.6.0 |
|
| 16 | OSD_ROLL_PIDS | 1.6.0 |
|
||||||
| 17 | OSD_PITCH_PIDS | 1.6.0 |
|
| 17 | OSD_PITCH_PIDS | 1.6.0 |
|
||||||
| 18 | OSD_YAW_PIDS | 1.6.0 |
|
| 18 | OSD_YAW_PIDS | 1.6.0 |
|
||||||
| 19 | OSD_POWER | 1.6.0 |
|
| 19 | OSD_POWER | 1.6.0 |
|
||||||
| 20 | OSD_GPS_LON | 1.6.0 |
|
| 20 | OSD_GPS_LON | 1.6.0 |
|
||||||
| 21 | OSD_GPS_LAT | 1.6.0 |
|
| 21 | OSD_GPS_LAT | 1.6.0 |
|
||||||
| 22 | OSD_HOME_DIR | 1.6.0 |
|
| 22 | OSD_HOME_DIR | 1.6.0 |
|
||||||
| 23 | OSD_HOME_DIST | 1.6.0 |
|
| 23 | OSD_HOME_DIST | 1.6.0 |
|
||||||
| 24 | OSD_HEADING | 1.6.0 |
|
| 24 | OSD_HEADING | 1.6.0 |
|
||||||
| 25 | OSD_VARIO | 1.6.0 |
|
| 25 | OSD_VARIO | 1.6.0 |
|
||||||
| 26 | OSD_VARIO_NUM | 1.6.0 |
|
| 26 | OSD_VARIO_NUM | 1.6.0 |
|
||||||
| 27 | OSD_AIR_SPEED | 1.7.3 |
|
| 27 | OSD_AIR_SPEED | 1.7.3 |
|
||||||
| 28 | OSD_ONTIME_FLYTIME | 1.8.0 |
|
| 28 | OSD_ONTIME_FLYTIME | 1.8.0 |
|
||||||
| 29 | OSD_RTC_TIME | 1.8.0 |
|
| 29 | OSD_RTC_TIME | 1.8.0 |
|
||||||
| 30 | OSD_MESSAGES | 1.8.0 |
|
| 30 | OSD_MESSAGES | 1.8.0 |
|
||||||
| 31 | OSD_GPS_HDOP | 1.8.0 |
|
| 31 | OSD_GPS_HDOP | 1.8.0 |
|
||||||
| 32 | OSD_MAIN_BATT_CELL_VOLTAGE | 1.8.0 |
|
| 32 | OSD_MAIN_BATT_CELL_VOLTAGE | 1.8.0 |
|
||||||
| 33 | OSD_SCALED_THROTTLE_POS | 1.8.0 |
|
| 33 | OSD_SCALED_THROTTLE_POS | 1.8.0 |
|
||||||
| 34 | OSD_HEADING_GRAPH | 1.8.0 |
|
| 34 | OSD_HEADING_GRAPH | 1.8.0 |
|
||||||
| 35 | OSD_EFFICIENCY_MAH_PER_KM | 1.9.0 |
|
| 35 | OSD_EFFICIENCY_MAH_PER_KM | 1.9.0 |
|
||||||
| 36 | OSD_WH_DRAWN | 1.9.0 |
|
| 36 | OSD_WH_DRAWN | 1.9.0 |
|
||||||
| 37 | OSD_BATTERY_REMAINING_CAPACITY | 1.9.0 |
|
| 37 | OSD_BATTERY_REMAINING_CAPACITY | 1.9.0 |
|
||||||
| 38 | OSD_BATTERY_REMAINING_PERCENT | 1.9.0 |
|
| 38 | OSD_BATTERY_REMAINING_PERCENT | 1.9.0 |
|
||||||
| 39 | OSD_EFFICIENCY_WH_PER_KM | 1.9.0 |
|
| 39 | OSD_EFFICIENCY_WH_PER_KM | 1.9.0 |
|
||||||
| 40 | OSD_TRIP_DIST | 1.9.1 |
|
| 40 | OSD_TRIP_DIST | 1.9.1 |
|
||||||
| 41 | OSD_ATTITUDE_PITCH | 2.0.0 |
|
| 41 | OSD_ATTITUDE_PITCH | 2.0.0 |
|
||||||
| 42 | OSD_ATTITUDE_ROLL | 2.0.0 |
|
| 42 | OSD_ATTITUDE_ROLL | 2.0.0 |
|
||||||
| 43 | OSD_MAP_NORTH | 2.0.0 |
|
| 43 | OSD_MAP_NORTH | 2.0.0 |
|
||||||
| 44 | OSD_MAP_TAKEOFF | 2.0.0 |
|
| 44 | OSD_MAP_TAKEOFF | 2.0.0 |
|
||||||
| 45 | OSD_RADAR | 2.0.0 |
|
| 45 | OSD_RADAR | 2.0.0 |
|
||||||
| 46 | OSD_WIND_SPEED_HORIZONTAL | 2.0.0 |
|
| 46 | OSD_WIND_SPEED_HORIZONTAL | 2.0.0 |
|
||||||
| 47 | OSD_WIND_SPEED_VERTICAL | 2.0.0 |
|
| 47 | OSD_WIND_SPEED_VERTICAL | 2.0.0 |
|
||||||
| 48 | OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH | 2.0.0 |
|
| 48 | OSD_REMAINING_FLIGHT_TIME_BEFORE_RTH | 2.0.0 |
|
||||||
| 49 | OSD_REMAINING_DISTANCE_BEFORE_RTH | 2.0.0 |
|
| 49 | OSD_REMAINING_DISTANCE_BEFORE_RTH | 2.0.0 |
|
||||||
| 50 | OSD_HOME_HEADING_ERROR | 2.0.0 |
|
| 50 | OSD_HOME_HEADING_ERROR | 2.0.0 |
|
||||||
| 51 | OSD_COURSE_HOLD_ERROR | 2.0.0 |
|
| 51 | OSD_COURSE_HOLD_ERROR | 2.0.0 |
|
||||||
| 52 | OSD_COURSE_HOLD_ADJUSTMENT | 2.0.0 |
|
| 52 | OSD_COURSE_HOLD_ADJUSTMENT | 2.0.0 |
|
||||||
| 53 | OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE | 2.0.0 |
|
| 53 | OSD_SAG_COMPENSATED_MAIN_BATT_VOLTAGE | 2.0.0 |
|
||||||
| 54 | OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE | 2.0.0 |
|
| 54 | OSD_MAIN_BATT_SAG_COMPENSATED_CELL_VOLTAGE | 2.0.0 |
|
||||||
| 55 | OSD_POWER_SUPPLY_IMPEDANCE | 2.0.0 |
|
| 55 | OSD_POWER_SUPPLY_IMPEDANCE | 2.0.0 |
|
||||||
| 56 | OSD_LEVEL_PIDS | 2.0.0 |
|
| 56 | OSD_LEVEL_PIDS | 2.0.0 |
|
||||||
| 57 | OSD_POS_XY_PIDS | 2.0.0 |
|
| 57 | OSD_POS_XY_PIDS | 2.0.0 |
|
||||||
| 58 | OSD_POS_Z_PIDS | 2.0.0 |
|
| 58 | OSD_POS_Z_PIDS | 2.0.0 |
|
||||||
| 59 | OSD_VEL_XY_PIDS | 2.0.0 |
|
| 59 | OSD_VEL_XY_PIDS | 2.0.0 |
|
||||||
| 60 | OSD_VEL_Z_PIDS | 2.0.0 |
|
| 60 | OSD_VEL_Z_PIDS | 2.0.0 |
|
||||||
| 61 | OSD_HEADING_P | 2.0.0 |
|
| 61 | OSD_HEADING_P | 2.0.0 |
|
||||||
| 62 | OSD_BOARD_ALIGN_ROLL | 2.0.0 |
|
| 62 | OSD_BOARD_ALIGN_ROLL | 2.0.0 |
|
||||||
| 63 | OSD_BOARD_ALIGN_PITCH | 2.0.0 |
|
| 63 | OSD_BOARD_ALIGN_PITCH | 2.0.0 |
|
||||||
| 64 | OSD_RC_EXPO | 2.0.0 |
|
| 64 | OSD_RC_EXPO | 2.0.0 |
|
||||||
| 65 | OSD_RC_YAW_EXPO | 2.0.0 |
|
| 65 | OSD_RC_YAW_EXPO | 2.0.0 |
|
||||||
| 66 | OSD_THROTTLE_EXPO | 2.0.0 |
|
| 66 | OSD_THROTTLE_EXPO | 2.0.0 |
|
||||||
| 67 | OSD_PITCH_RATE | 2.0.0 |
|
| 67 | OSD_PITCH_RATE | 2.0.0 |
|
||||||
| 68 | OSD_ROLL_RATE | 2.0.0 |
|
| 68 | OSD_ROLL_RATE | 2.0.0 |
|
||||||
| 69 | OSD_YAW_RATE | 2.0.0 |
|
| 69 | OSD_YAW_RATE | 2.0.0 |
|
||||||
| 70 | OSD_MANUAL_RC_EXPO | 2.0.0 |
|
| 70 | OSD_MANUAL_RC_EXPO | 2.0.0 |
|
||||||
| 71 | OSD_MANUAL_RC_YAW_EXPO | 2.0.0 |
|
| 71 | OSD_MANUAL_RC_YAW_EXPO | 2.0.0 |
|
||||||
| 72 | OSD_MANUAL_PITCH_RATE | 2.0.0 |
|
| 72 | OSD_MANUAL_PITCH_RATE | 2.0.0 |
|
||||||
| 73 | OSD_MANUAL_ROLL_RATE | 2.0.0 |
|
| 73 | OSD_MANUAL_ROLL_RATE | 2.0.0 |
|
||||||
| 74 | OSD_MANUAL_YAW_RATE | 2.0.0 |
|
| 74 | OSD_MANUAL_YAW_RATE | 2.0.0 |
|
||||||
| 75 | OSD_NAV_FW_CRUISE_THR | 2.0.0 |
|
| 75 | OSD_NAV_FW_CRUISE_THR | 2.0.0 |
|
||||||
| 76 | OSD_NAV_FW_PITCH2THR | 2.0.0 |
|
| 76 | OSD_NAV_FW_PITCH2THR | 2.0.0 |
|
||||||
| 77 | OSD_FW_MIN_THROTTLE_DOWN_PITCH_ANGLE | 2.0.0 |
|
| 77 | OSD_FW_MIN_THROTTLE_DOWN_PITCH_ANGLE | 2.0.0 |
|
||||||
| 78 | OSD_DEBUG | 2.0.0 |
|
| 78 | OSD_DEBUG | 2.0.0 |
|
||||||
| 79 | OSD_FW_ALT_PID_OUTPUTS | 2.0.0 |
|
| 79 | OSD_FW_ALT_PID_OUTPUTS | 2.0.0 |
|
||||||
| 80 | OSD_FW_POS_PID_OUTPUTS | 2.0.0 |
|
| 80 | OSD_FW_POS_PID_OUTPUTS | 2.0.0 |
|
||||||
| 81 | OSD_MC_VEL_X_PID_OUTPUTS | 2.0.0 |
|
| 81 | OSD_MC_VEL_X_PID_OUTPUTS | 2.0.0 |
|
||||||
| 82 | OSD_MC_VEL_Y_PID_OUTPUTS | 2.0.0 |
|
| 82 | OSD_MC_VEL_Y_PID_OUTPUTS | 2.0.0 |
|
||||||
| 83 | OSD_MC_VEL_Z_PID_OUTPUTS | 2.0.0 |
|
| 83 | OSD_MC_VEL_Z_PID_OUTPUTS | 2.0.0 |
|
||||||
| 84 | OSD_MC_POS_XYZ_P_OUTPUTS | 2.0.0 |
|
| 84 | OSD_MC_POS_XYZ_P_OUTPUTS | 2.0.0 |
|
||||||
| 85 | OSD_3D_SPEED | 2.1.0 |
|
| 85 | OSD_3D_SPEED | 2.1.0 |
|
||||||
| 86 | OSD_IMU_TEMPERATURE | 2.1.0 |
|
| 86 | OSD_IMU_TEMPERATURE | 2.1.0 |
|
||||||
| 87 | OSD_BARO_TEMPERATURE | 2.1.0 |
|
| 87 | OSD_BARO_TEMPERATURE | 2.1.0 |
|
||||||
| 88 | OSD_TEMP_SENSOR_0_TEMPERATURE | 2.1.0 |
|
| 88 | OSD_TEMP_SENSOR_0_TEMPERATURE | 2.1.0 |
|
||||||
| 89 | OSD_TEMP_SENSOR_1_TEMPERATURE | 2.1.0 |
|
| 89 | OSD_TEMP_SENSOR_1_TEMPERATURE | 2.1.0 |
|
||||||
| 90 | OSD_TEMP_SENSOR_2_TEMPERATURE | 2.1.0 |
|
| 90 | OSD_TEMP_SENSOR_2_TEMPERATURE | 2.1.0 |
|
||||||
| 91 | OSD_TEMP_SENSOR_3_TEMPERATURE | 2.1.0 |
|
| 91 | OSD_TEMP_SENSOR_3_TEMPERATURE | 2.1.0 |
|
||||||
| 92 | OSD_TEMP_SENSOR_4_TEMPERATURE | 2.1.0 |
|
| 92 | OSD_TEMP_SENSOR_4_TEMPERATURE | 2.1.0 |
|
||||||
| 93 | OSD_TEMP_SENSOR_5_TEMPERATURE | 2.1.0 |
|
| 93 | OSD_TEMP_SENSOR_5_TEMPERATURE | 2.1.0 |
|
||||||
| 94 | OSD_TEMP_SENSOR_6_TEMPERATURE | 2.1.0 |
|
| 94 | OSD_TEMP_SENSOR_6_TEMPERATURE | 2.1.0 |
|
||||||
| 95 | OSD_TEMP_SENSOR_7_TEMPERATURE | 2.1.0 |
|
| 95 | OSD_TEMP_SENSOR_7_TEMPERATURE | 2.1.0 |
|
||||||
| 96 | OSD_ALTITUDE_MSL | 2.1.0 |
|
| 96 | OSD_ALTITUDE_MSL | 2.1.0 |
|
||||||
| 97 | OSD_PLUS_CODE | 2.1.0 |
|
| 97 | OSD_PLUS_CODE | 2.1.0 |
|
||||||
| 98 | OSD_MAP_SCALE | 2.2.0 |
|
| 98 | OSD_MAP_SCALE | 2.2.0 |
|
||||||
| 99 | OSD_MAP_REFERENCE | 2.2.0 |
|
| 99 | OSD_MAP_REFERENCE | 2.2.0 |
|
||||||
| 100 | OSD_GFORCE | 2.2.0 |
|
| 100 | OSD_GFORCE | 2.2.0 |
|
||||||
| 101 | OSD_GFORCE_X | 2.2.0 |
|
| 101 | OSD_GFORCE_X | 2.2.0 |
|
||||||
| 102 | OSD_GFORCE_Y | 2.2.0 |
|
| 102 | OSD_GFORCE_Y | 2.2.0 |
|
||||||
| 103 | OSD_GFORCE_Z | 2.2.0 |
|
| 103 | OSD_GFORCE_Z | 2.2.0 |
|
||||||
| 104 | OSD_RC_SOURCE | 2.2.0 |
|
| 104 | OSD_RC_SOURCE | 2.2.0 |
|
||||||
| 105 | OSD_VTX_POWER | 2.2.0 |
|
| 105 | OSD_VTX_POWER | 2.2.0 |
|
||||||
| 106 | OSD_ESC_RPM | 2.3.0 |
|
| 106 | OSD_ESC_RPM | 2.3.0 |
|
||||||
| 107 | OSD_ESC_TEMPERATURE | 2.5.0 |
|
| 107 | OSD_ESC_TEMPERATURE | 2.5.0 |
|
||||||
| 108 | OSD_AZIMUTH | 2.6.0 |
|
| 108 | OSD_AZIMUTH | 2.6.0 |
|
||||||
| 109 | OSD_CRSF_RSSI_DBM | 2.6.0 |
|
| 109 | OSD_CRSF_RSSI_DBM | 2.6.0 |
|
||||||
| 110 | OSD_CRSF_LQ | 2.6.0 |
|
| 110 | OSD_CRSF_LQ | 2.6.0 |
|
||||||
| 111 | OSD_CRSF_SNR_DB | 2.6.0 |
|
| 111 | OSD_CRSF_SNR_DB | 2.6.0 |
|
||||||
| 112 | OSD_CRSF_TX_POWER | 2.6.0 |
|
| 112 | OSD_CRSF_TX_POWER | 2.6.0 |
|
||||||
| 113 | OSD_GVAR_0 | 2.6.0 |
|
| 113 | OSD_GVAR_0 | 2.6.0 |
|
||||||
| 114 | OSD_GVAR_1 | 2.6.0 |
|
| 114 | OSD_GVAR_1 | 2.6.0 |
|
||||||
| 115 | OSD_GVAR_2 | 2.6.0 |
|
| 115 | OSD_GVAR_2 | 2.6.0 |
|
||||||
| 116 | OSD_GVAR_3 | 2.6.0 |
|
| 116 | OSD_GVAR_3 | 2.6.0 |
|
||||||
| 117 | OSD_TPA | 2.6.0 |
|
| 117 | OSD_TPA | 2.6.0 |
|
||||||
| 118 | OSD_NAV_FW_CONTROL_SMOOTHNESS | 2.6.0 |
|
| 118 | OSD_NAV_FW_CONTROL_SMOOTHNESS | 2.6.0 |
|
||||||
| 119 | OSD_VERSION | 3.0.0 |
|
| 119 | OSD_VERSION | 3.0.0 |
|
||||||
| 120 | OSD_RANGEFINDER | 3.0.0 |
|
| 120 | OSD_RANGEFINDER | 3.0.0 |
|
||||||
| 121 | OSD_PLIMIT_REMAINING_BURST_TIME | 3.0.0 |
|
| 121 | OSD_PLIMIT_REMAINING_BURST_TIME | 3.0.0 |
|
||||||
| 122 | OSD_PLIMIT_ACTIVE_CURRENT_LIMIT | 3.0.0 |
|
| 122 | OSD_PLIMIT_ACTIVE_CURRENT_LIMIT | 3.0.0 |
|
||||||
| 123 | OSD_PLIMIT_ACTIVE_POWER_LIMIT | 3.0.0 |
|
| 123 | OSD_PLIMIT_ACTIVE_POWER_LIMIT | 3.0.0 |
|
||||||
| 124 | OSD_GLIDESLOPE | 3.0.1 |
|
| 124 | OSD_GLIDESLOPE | 3.0.1 |
|
||||||
| 125 | OSD_GPS_MAX_SPEED | 4.0.0 |
|
| 125 | OSD_GPS_MAX_SPEED | 4.0.0 |
|
||||||
| 126 | OSD_3D_MAX_SPEED | 4.0.0 |
|
| 126 | OSD_3D_MAX_SPEED | 4.0.0 |
|
||||||
| 127 | OSD_AIR_MAX_SPEED | 4.0.0 |
|
| 127 | OSD_AIR_MAX_SPEED | 4.0.0 |
|
||||||
| 128 | OSD_ACTIVE_PROFILE | 4.0.0 |
|
| 128 | OSD_ACTIVE_PROFILE | 4.0.0 |
|
||||||
| 129 | OSD_MISSION | 4.0.0 |
|
| 129 | OSD_MISSION | 4.0.0 |
|
||||||
| 130 | OSD_SWITCH_INDICATOR_0 | 5.0.0 |
|
| 130 | OSD_SWITCH_INDICATOR_0 | 5.0.0 |
|
||||||
| 131 | OSD_SWITCH_INDICATOR_1 | 5.0.0 |
|
| 131 | OSD_SWITCH_INDICATOR_1 | 5.0.0 |
|
||||||
| 132 | OSD_SWITCH_INDICATOR_2 | 5.0.0 |
|
| 132 | OSD_SWITCH_INDICATOR_2 | 5.0.0 |
|
||||||
| 133 | OSD_SWITCH_INDICATOR_3 | 5.0.0 |
|
| 133 | OSD_SWITCH_INDICATOR_3 | 5.0.0 |
|
||||||
| 134 | OSD_TPA_TIME_CONSTANT | 5.0.0 |
|
| 134 | OSD_TPA_TIME_CONSTANT | 5.0.0 |
|
||||||
| 135 | OSD_FW_LEVEL_TRIM | 5.0.0 |
|
| 135 | OSD_FW_LEVEL_TRIM | 5.0.0 |
|
||||||
| 136 | OSD_GLIDE_TIME_REMAINING | 6.0.0 |
|
| 136 | OSD_GLIDE_TIME_REMAINING | 6.0.0 |
|
||||||
| 137 | OSD_GLIDE_RANGE | 6.0.0 |
|
| 137 | OSD_GLIDE_RANGE | 6.0.0 |
|
||||||
| 138 | OSD_CLIMB_EFFICIENCY | 6.0.0 |
|
| 138 | OSD_CLIMB_EFFICIENCY | 6.0.0 |
|
||||||
| 139 | OSD_NAV_WP_MULTI_MISSION_INDEX | 6.0.0 |
|
| 139 | OSD_NAV_WP_MULTI_MISSION_INDEX | 6.0.0 |
|
||||||
| 140 | OSD_GROUND_COURSE | 6.0.0 |
|
| 140 | OSD_GROUND_COURSE | 6.0.0 |
|
||||||
| 141 | OSD_CROSS_TRACK_ERROR | 6.0.0 |
|
| 141 | OSD_CROSS_TRACK_ERROR | 6.0.0 |
|
||||||
| 142 | OSD_PILOT_NAME | 6.0.0 |
|
| 142 | OSD_PILOT_NAME | 6.0.0 |
|
||||||
| 143 | OSD_PAN_SERVO_CENTRED | 6.0.0 |
|
| 143 | OSD_PAN_SERVO_CENTRED | 6.0.0 |
|
||||||
|
| 144 | OSD_MULTI_FUNCTION | 7.0.0 |
|
||||||
|
| 145 | OSD_ODOMETER | 7.0.0 |
|
||||||
|
| 146 | OSD_PILOT_LOGO | 7.0.0 |
|
||||||
|
|
||||||
|
# Pilot Logos
|
||||||
|
|
||||||
|
From INAV 7.0.0, pilots can add their own logos to the OSD. These can appear in 2 places: the power on/arming screens or as an element on the standard OSD. Please note that the power on/arming screen large pilot logos are only available on HD systems.
|
||||||
|
|
||||||
|
To use the pilot logos, you will need to make a custom font for your OSD system. Base fonts and information can be found in the [OSD folder](https://github.com/iNavFlight/inav-configurator/tree/master/resources/osd) in the Configurator resources. Each system will need a specific method to create the font image files. So they will not be covered here. There are two pilot logos.
|
||||||
|
|
||||||
|
<img alt="Default small INAV Pilot logo" src="https://github.com/iNavFlight/inav-configurator/raw/master/resources/osd/digital/default/24x36/469_471.png" align="right" />The small pilot logo appears on standard OSD layouts, when you add the elemement to the OSD screen. This is a 3 character wide symbol (characters 469-471).
|
||||||
|
|
||||||
|
<img alt="Default large INAV Pilot logo" src="https://github.com/iNavFlight/inav-configurator/raw/master/resources/osd/digital/default/24x36/472_511.png" align="right" />The large pilot logo appears on the power on and arming screens, when you enable the feature in the CLI. To do this, set the `osd_use_pilot_logo` parameter to `on`. This is a 10 character wide, 4 character high symbol (characters 472-511).
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
* `osd_arm_screen_display_time` The amount of time the arming screen is displayed.
|
||||||
|
* `osd_inav_to_pilot_logo_spacing` The spacing between two logos. This can be set to `0`, so the original INAV logo and Pilot Logo can be combined in to a larger logo. Any non-0 value will be adjusted to best align the logos. For example, the Avatar system has an odd number of columns. If you set the spacing to 8, the logos would look misaligned. So the even number will be changed to an odd number for better alignment.
|
||||||
|
* `osd_use_pilot_logo` Enable to use the large pilot logo.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
This is an example of the arming screen with the pilot logo enabled. This is using the default settings.
|
||||||
|

|
||||||
|
|
||||||
|
This is an example of setting the `osd_inav_to_pilot_logo_spacing` to 0. This will allow a larger, single logo.
|
||||||
|

|
||||||
|
|
|
@ -83,7 +83,7 @@ IPF can be edited using INAV Configurator user interface, or via CLI. To use COn
|
||||||
| 35 | Trigonometry: Tangent | Computes TAN of `Operand A` value in degrees. Output is multiplied by `Operand B` value. If `Operand B` is `0`, result is multiplied by `500` |
|
| 35 | Trigonometry: Tangent | Computes TAN of `Operand A` value in degrees. Output is multiplied by `Operand B` value. If `Operand B` is `0`, result is multiplied by `500` |
|
||||||
| 36 | MAP_INPUT | Scales `Operand A` from [`0` : `Operand B`] to [`0` : `1000`]. Note: input will be constrained and then scaled |
|
| 36 | MAP_INPUT | Scales `Operand A` from [`0` : `Operand B`] to [`0` : `1000`]. Note: input will be constrained and then scaled |
|
||||||
| 37 | MAP_OUTPUT | Scales `Operand A` from [`0` : `1000`] to [`0` : `Operand B`]. Note: input will be constrained and then scaled |
|
| 37 | MAP_OUTPUT | Scales `Operand A` from [`0` : `1000`] to [`0` : `Operand B`]. Note: input will be constrained and then scaled |
|
||||||
| 38 | RC_CHANNEL_OVERRIDE | Overrides channel set by `Operand A` to value of `Operand B` |
|
| 38 | RC_CHANNEL_OVERRIDE | Overrides channel set by `Operand A` to value of `Operand B`. Note operand A should normally be set as a "Value", NOT as "Get RC Channel"|
|
||||||
| 39 | SET_HEADING_TARGET | Sets heading-hold target to `Operand A`, in degrees. Value wraps-around. |
|
| 39 | SET_HEADING_TARGET | Sets heading-hold target to `Operand A`, in degrees. Value wraps-around. |
|
||||||
| 40 | Modulo | Modulo. Divide `Operand A` by `Operand B` and returns the remainder |
|
| 40 | Modulo | Modulo. Divide `Operand A` by `Operand B` and returns the remainder |
|
||||||
| 41 | LOITER_RADIUS_OVERRIDE | Sets the loiter radius to `Operand A` [`0` : `100000`] in cm. If the value is lower than the loiter radius set in the **Advanced Tuning**, that will be used. |
|
| 41 | LOITER_RADIUS_OVERRIDE | Sets the loiter radius to `Operand A` [`0` : `100000`] in cm. If the value is lower than the loiter radius set in the **Advanced Tuning**, that will be used. |
|
||||||
|
@ -97,6 +97,7 @@ IPF can be edited using INAV Configurator user interface, or via CLI. To use COn
|
||||||
| 49 | TIMER | A simple on - off timer. `true` for the duration of `Operand A` [ms]. Then `false` for the duration of `Operand B` [ms]. |
|
| 49 | TIMER | A simple on - off timer. `true` for the duration of `Operand A` [ms]. Then `false` for the duration of `Operand B` [ms]. |
|
||||||
| 50 | DELTA | This returns `true` when the value of `Operand A` has changed by the value of `Operand B` or greater within 100ms. |
|
| 50 | DELTA | This returns `true` when the value of `Operand A` has changed by the value of `Operand B` or greater within 100ms. |
|
||||||
| 51 | APPROX_EQUAL | `true` if `Operand B` is within 1% of `Operand A`. |
|
| 51 | APPROX_EQUAL | `true` if `Operand B` is within 1% of `Operand A`. |
|
||||||
|
| 52 | LED_PIN_PWM | Value `Operand A` from [`0` : `100`] starts PWM generation on LED Pin. See [LED pin PWM](LED%20pin%20PWM.md). Any other value stops PWM generation (stop to allow ws2812 LEDs updates in shared modes)|
|
||||||
|
|
||||||
### Operands
|
### Operands
|
||||||
|
|
||||||
|
|
32
docs/Runcam device.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Runcam device
|
||||||
|
|
||||||
|
Cameras which support [Runcam device protocol](https://support.runcam.com/hc/en-us/articles/360014537794-RunCam-Device-Protocol), can be configured using sticks.
|
||||||
|
|
||||||
|
Note that for cameras which has OSD pin, there is alternative functionality: [OSD Joystick](OSD%20Joystick.md).
|
||||||
|
|
||||||
|
Camera's RX/TX should be connected to FC's UART, which has "Runcam device" option selected.
|
||||||
|
|
||||||
|
# Entering Joystick emulation mode
|
||||||
|
|
||||||
|
Emulation can be enabled in unarmed state only.
|
||||||
|
|
||||||
|
Joystick emulation mode is enabled using the following stick combination:
|
||||||
|
|
||||||
|
```RIGHT CENTER```
|
||||||
|
|
||||||
|
|
||||||
|
Than camera OSD can be navigated using right stick. See [Controls](Controls.md) for all stick combinations.
|
||||||
|
|
||||||
|
*Note that the same stick combination is used to enable [OSD Joystick](OSD%20Joystick.md).*
|
||||||
|
|
||||||
|
Mode is exited using stick combination:
|
||||||
|
|
||||||
|
```LEFT CENTER```
|
||||||
|
|
||||||
|
# RC Box
|
||||||
|
|
||||||
|
There are 3 RC Boxes which can be used in armed and unarmed state:
|
||||||
|
- Camera 1 - Simulate Wifi button
|
||||||
|
- Camera 2 - Simulate POWER button
|
||||||
|
- Camera 3 - Simulate Change Mode button.
|
||||||
|
|
|
@ -79,7 +79,7 @@ For this you need a FT232 module. With FT-Prog (https://ftdichip.com/utilities/)
|
||||||
For SBUS, the command line arguments of the python script are:
|
For SBUS, the command line arguments of the python script are:
|
||||||
```python tcp_serial_redirect.py --parity E --stopbits 2 -c 127.0.0.1:[INAV-UART-PORT] COMXX 100000```
|
```python tcp_serial_redirect.py --parity E --stopbits 2 -c 127.0.0.1:[INAV-UART-PORT] COMXX 100000```
|
||||||
|
|
||||||
### Telemtry
|
### Telemetry
|
||||||
|
|
||||||
LTM and MAVLink telemetry are supported, either as a discrete function or shared with MSP.
|
LTM and MAVLink telemetry are supported, either as a discrete function or shared with MSP.
|
||||||
|
|
||||||
|
|
BIN
docs/Screenshots/mixerprofile_4puls1_mix.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/Screenshots/mixerprofile_flow.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
docs/Screenshots/mixerprofile_fw_mixer.png
Normal file
After Width: | Height: | Size: 314 KiB |
BIN
docs/Screenshots/mixerprofile_mc_mixer.png
Normal file
After Width: | Height: | Size: 394 KiB |
BIN
docs/Screenshots/mixerprofile_servo_transition_mix.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/Screenshots/programming_disable_gps_sensor_fix.png
Normal file
After Width: | Height: | Size: 15 KiB |
160
docs/Settings.md
|
@ -242,6 +242,16 @@ Inertial Measurement Unit KP Gain for compass measurements
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### ahrs_gps_yaw_weight
|
||||||
|
|
||||||
|
Arhs gps yaw weight when mag is avaliable, 0 means no gps yaw, 100 means equal weight as compass
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 100 | 0 | 500 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### ahrs_gps_yaw_windcomp
|
### ahrs_gps_yaw_windcomp
|
||||||
|
|
||||||
Wind compensation in heading estimation from gps groundcourse(fixed wing only)
|
Wind compensation in heading estimation from gps groundcourse(fixed wing only)
|
||||||
|
@ -1002,6 +1012,16 @@ Requested yaw rate to execute when `LAND` (or old `SET-THR`) failsafe is active
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### failsafe_gps_fix_estimation_delay
|
||||||
|
|
||||||
|
Controls whether waypoint mission is allowed to proceed with gps fix estimation. Sets the time delay in seconds between gps fix lost event and RTH activation. Minimum delay is 7 seconds. If set to -1 the mission will continue until the end. With default setting(7), waypoint mission is aborted and switched to RTH with 7 seconds delay. RTH is done with GPS Fix estimation.
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 7 | -1 | 600 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### failsafe_lights
|
### failsafe_lights
|
||||||
|
|
||||||
Enable or disable the lights when the `FAILSAFE` flight mode is enabled. The target needs to be compiled with `USE_LIGHTS` [ON/OFF].
|
Enable or disable the lights when the `FAILSAFE` flight mode is enabled. The target needs to be compiled with `USE_LIGHTS` [ON/OFF].
|
||||||
|
@ -1474,11 +1494,11 @@ Enable automatic configuration of UBlox GPS receivers.
|
||||||
|
|
||||||
### gps_dyn_model
|
### gps_dyn_model
|
||||||
|
|
||||||
GPS navigation model: Pedestrian, Air_1g, Air_4g. Default is AIR_1G. Use pedestrian with caution, can cause flyaways with fast flying.
|
GPS navigation model: Pedestrian, Automotive, Air<1g, Air<2g, Air<4g. Default is AIR_2G. Use pedestrian/Automotive with caution, can cause flyaways with fast flying.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| AIR_1G | | |
|
| AIR_2G | | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1762,6 +1782,16 @@ Defines if INAV will dead-reckon over short GPS outages. May also be useful for
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### inav_allow_gps_fix_estimation
|
||||||
|
|
||||||
|
Defines if inav will estimate GPS fix with magnetometer and barometer on GPS outages. Enables navigation and RTH without GPS fix on fixed wing. Also see failsafe_gps_fix_estimation_delay.
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| OFF | OFF | ON |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### inav_auto_mag_decl
|
### inav_auto_mag_decl
|
||||||
|
|
||||||
Automatic setting of magnetic declination based on GPS position. When used manual magnetic declination is ignored.
|
Automatic setting of magnetic declination based on GPS position. When used manual magnetic declination is ignored.
|
||||||
|
@ -1912,33 +1942,23 @@ Decay coefficient for estimated velocity when GPS reference for position is lost
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### inav_w_xyz_acc_p
|
|
||||||
|
|
||||||
_// TODO_
|
|
||||||
|
|
||||||
| Default | Min | Max |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 1.0 | 0 | 1 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### inav_w_z_baro_p
|
### inav_w_z_baro_p
|
||||||
|
|
||||||
Weight of barometer measurements in estimated altitude and climb rate
|
Weight of barometer measurements in estimated altitude and climb rate. Setting is used on both airplanes and multirotors.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 0.35 | 0 | 10 |
|
| 0.4 | 0 | 10 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### inav_w_z_gps_p
|
### inav_w_z_gps_p
|
||||||
|
|
||||||
Weight of GPS altitude measurements in estimated altitude. Setting is used only of airplanes
|
Weight of GPS altitude measurements in estimated altitude. Setting is used on both airplanes and multirotors.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 0.2 | 0 | 10 |
|
| 0.4 | 0 | 10 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1948,7 +1968,7 @@ Weight of GPS climb rate measurements in estimated climb rate. Setting is used o
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 0.1 | 0 | 10 |
|
| 0.8 | 0 | 10 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -2012,6 +2032,16 @@ Used to prevent Iterm accumulation on during maneuvers. Iterm will be dampened w
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### led_pin_pwm_mode
|
||||||
|
|
||||||
|
PWM mode of LED pin.
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| SHARED_LOW | | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### ledstrip_visual_beeper
|
### ledstrip_visual_beeper
|
||||||
|
|
||||||
_// TODO_
|
_// TODO_
|
||||||
|
@ -2434,7 +2464,7 @@ This is the maximum value (in us) sent to esc when armed. Default of 1850 are OK
|
||||||
|
|
||||||
### mc_cd_lpf_hz
|
### mc_cd_lpf_hz
|
||||||
|
|
||||||
Cutoff frequency for Control Derivative. Lower value smoother reaction on fast stick movements. With higher values, response will be more aggressive, jerky
|
Cutoff frequency for Control Derivative. This controls the cutoff for the LPF that is applied to the CD (Feed Forward) signal to the PID controller. Lower value will produce a smoother CD gain to the controller, but it will be more delayed. Higher values will produce CD gain that may have more noise in the signal depending on your RC link but wil be less delayed.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
@ -2444,7 +2474,7 @@ Cutoff frequency for Control Derivative. Lower value smoother reaction on fast s
|
||||||
|
|
||||||
### mc_cd_pitch
|
### mc_cd_pitch
|
||||||
|
|
||||||
Multicopter Control Derivative gain for PITCH
|
Multicopter Control Derivative gain for PITCH (known as 'Feed Forward' in Betaflight). The CD intoduces a term to the PID controller that is the magnitude of the Setpoint change. Fast inputs produce a high CD gain to help push the MC into a move; in advance of the P-gain if set high enough.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
@ -2454,7 +2484,7 @@ Multicopter Control Derivative gain for PITCH
|
||||||
|
|
||||||
### mc_cd_roll
|
### mc_cd_roll
|
||||||
|
|
||||||
Multicopter Control Derivative gain for ROLL
|
Multicopter Control Derivative gain for ROLL (known as 'Feed Forward' in Betaflight). The CD intoduces a term to the PID controller that is the magnitude of the Setpoint change. Fast inputs produce a high CD gain to help push the MC into a move; in advance of the P-gain if set high enough.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
@ -2464,7 +2494,7 @@ Multicopter Control Derivative gain for ROLL
|
||||||
|
|
||||||
### mc_cd_yaw
|
### mc_cd_yaw
|
||||||
|
|
||||||
Multicopter Control Derivative gain for YAW
|
Multicopter Control Derivative gain for YAW (known as 'Feed Forward' in Betaflight). The CD intoduces a term to the PID controller that is the magnitude of the Setpoint change. Fast inputs produce a high CD gain to help push the MC into a move; in advance of the P-gain if set high enough.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
@ -2778,7 +2808,7 @@ Max YAW rate when NAV COURSE HOLD/CRUISE mode is enabled. Set to 0 to disable on
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 20 | 0 | 120 |
|
| 60 | 0 | 120 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -3692,6 +3722,16 @@ When ON, NAV engine will slow down when switching to the next waypoint. This pri
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### nav_min_ground_speed
|
||||||
|
|
||||||
|
Minimum ground speed for navigation flight modes [m/s]. Default 7 m/s.
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 7 | 6 | 50 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### nav_min_rth_distance
|
### nav_min_rth_distance
|
||||||
|
|
||||||
Minimum distance from homepoint when RTH full procedure will be activated [cm]. Below this distance, the mode will activate at the current location and the final phase is executed (loiter / land). Above this distance, the full procedure is activated, which may include initial climb and flying directly to the homepoint before entering the loiter / land phase.
|
Minimum distance from homepoint when RTH full procedure will be activated [cm]. Below this distance, the mode will activate at the current location and the final phase is executed (loiter / land). Above this distance, the full procedure is activated, which may include initial climb and flying directly to the homepoint before entering the loiter / land phase.
|
||||||
|
@ -3822,6 +3862,16 @@ If set to ON, aircraft will execute initial climb regardless of position sensor
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### nav_rth_fs_landing_delay
|
||||||
|
|
||||||
|
If landing is active on Failsafe and this is above 0. The aircraft will hover or loiter for X seconds before performing the landing. If the battery enters the warning or critical levels, the land will proceed. Default = 0 [seconds]
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 0 | 0 | 1800 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### nav_rth_home_altitude
|
### nav_rth_home_altitude
|
||||||
|
|
||||||
Aircraft will climb/descend to this altitude after reaching home if landing is not enabled. Set to 0 to stay at `nav_rth_altitude` (default) [cm]
|
Aircraft will climb/descend to this altitude after reaching home if landing is not enabled. Set to 0 to stay at `nav_rth_altitude` (default) [cm]
|
||||||
|
@ -4452,6 +4502,66 @@ The space between the INAV and pilot logos, if `osd_use_pilot_logo` is `ON`. Thi
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### osd_joystick_down
|
||||||
|
|
||||||
|
PWM value for DOWN key
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 0 | 0 | 100 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### osd_joystick_enabled
|
||||||
|
|
||||||
|
Enable OSD Joystick emulation
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| OFF | OFF | ON |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### osd_joystick_enter
|
||||||
|
|
||||||
|
PWM value for ENTER key
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 75 | 0 | 100 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### osd_joystick_left
|
||||||
|
|
||||||
|
PWM value for LEFT key
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 63 | 0 | 100 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### osd_joystick_right
|
||||||
|
|
||||||
|
PWM value for RIGHT key
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 28 | 0 | 100 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### osd_joystick_up
|
||||||
|
|
||||||
|
PWM value for UP key
|
||||||
|
|
||||||
|
| Default | Min | Max |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 48 | 0 | 100 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### osd_left_sidebar_scroll
|
### osd_left_sidebar_scroll
|
||||||
|
|
||||||
_// TODO_
|
_// TODO_
|
||||||
|
@ -4998,7 +5108,7 @@ Selection of pitot hardware.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| VIRTUAL | | |
|
| NONE | | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -5934,11 +6044,11 @@ Warning voltage per cell, this triggers battery-warning alarms, in 0.01V units,
|
||||||
|
|
||||||
### vtx_band
|
### vtx_band
|
||||||
|
|
||||||
Configure the VTX band. Set to zero to use `vtx_freq`. Bands: 1: A, 2: B, 3: E, 4: F, 5: Race.
|
Configure the VTX band. Bands: 1: A, 2: B, 3: E, 4: F, 5: Race.
|
||||||
|
|
||||||
| Default | Min | Max |
|
| Default | Min | Max |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 1 | VTX_SETTINGS_NO_BAND | VTX_SETTINGS_MAX_BAND |
|
| 1 | VTX_SETTINGS_MIN_BAND | VTX_SETTINGS_MAX_BAND |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
236
docs/VTOL.md
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
# Welcome to INAV VTOL
|
||||||
|
|
||||||
|
Thank you for trying the INAV VTOL. Read every line in this tutorial. Your patience can save both time and potential repair costs for the model.
|
||||||
|
|
||||||
|
## Who Should Use This Tutorial?
|
||||||
|
|
||||||
|
This tutorial is designed for individuals who
|
||||||
|
- have prior experience with **both INAV multi-rotor and INAV fixed-wing configurations/operations.**
|
||||||
|
- know how to create a custom mixer for their model.
|
||||||
|
- know basic physics of vtol operation
|
||||||
|
|
||||||
|
## Firmware Status
|
||||||
|
|
||||||
|
The firmware is in a flyable state, but it hasn't undergone extensive testing yet. This means there may be potential issues that have not yet been discovered.
|
||||||
|
|
||||||
|
## Future Changes
|
||||||
|
|
||||||
|
Please be aware that both the setup procedure and firmware may change in response to user feedback and testing results.
|
||||||
|
## Your Feedback Matters
|
||||||
|
|
||||||
|
We highly value your feedback as it plays a crucial role in the development and refinement of INAV VTOL capabilities. Please share your experiences, suggestions, and any issues you encounter during testing. Your insights are invaluable in making INAV VTOL better for everyone.
|
||||||
|
|
||||||
|
# VTOL Configuration Steps
|
||||||
|
|
||||||
|
### The VTOL functionality is achieved by switching/transitioning between two configurations stored in the FC. VTOL specific configurations are Mixer Profiles with associated PID profiles. One profile set is for fixed-wing(FW) mode, One is for multi-copter(MC) mode. Configuration/Settings other than Mixer/PID profiles are shared among two modes
|
||||||
|

|
||||||
|
|
||||||
|
0. **Find a DIFF ALL file for your model and start from there if possible**
|
||||||
|
- Be aware that `MIXER PROFILE 2` RC mode setting introduced by diff file can get your stuck in a mixer_profile. remove or change channel to proceed
|
||||||
|
1. **Setup Profile 1:**
|
||||||
|
- Configure it as a normal fixed-wing/multi-copter.
|
||||||
|
|
||||||
|
2. **Setup Profile 2:**
|
||||||
|
- Configure it as a normal multi-copter/fixed-wing.
|
||||||
|
|
||||||
|
3. **Mode Tab Settings:**
|
||||||
|
- Set up switching in the mode tab.
|
||||||
|
|
||||||
|
4. *(Recommended)* **Transition Mixing (Multi-Rotor Profile):**
|
||||||
|
- Configure transition mixing to gain airspeed in the multi-rotor profile.
|
||||||
|
|
||||||
|
5. *(Optional)* **Automated Switching (RTH):**
|
||||||
|
- Optionally, set up automated switching in case of failsafe.
|
||||||
|
|
||||||
|
# STEP0: Load parameter preset/templates
|
||||||
|
Find a working diff file if you can and start from there. If not, select keep current settings and apply following parameter in cli but read description about which one to apply.
|
||||||
|
|
||||||
|
```
|
||||||
|
set small_angle = 180
|
||||||
|
set gyro_main_lpf_hz = 80
|
||||||
|
set dynamic_gyro_notch_min_hz = 50
|
||||||
|
set dynamic_gyro_notch_mode = 3D
|
||||||
|
set motor_pwm_protocol = DSHOT300 #Try dshot first and see if it works
|
||||||
|
set airmode_type = STICK_CENTER_ONCE
|
||||||
|
|
||||||
|
|
||||||
|
set nav_disarm_on_landing = OFF #band-aid for false landing detection in NAV landing of multi-copter
|
||||||
|
set nav_rth_allow_landing = FS_ONLY
|
||||||
|
set nav_wp_max_safe_distance = 500
|
||||||
|
set nav_fw_control_smoothness = 2
|
||||||
|
set nav_fw_launch_max_altitude = 5000
|
||||||
|
|
||||||
|
set servo_pwm_rate = 160 #If model using servo for stabilization in MC mode and servo can tolerate it
|
||||||
|
set servo_lpf_hz = 30 #If model using servo for stabilization in MC mode
|
||||||
|
|
||||||
|
|
||||||
|
## profile 1 as airplane and profile 2 as multi rotor
|
||||||
|
mixer_profile 1
|
||||||
|
|
||||||
|
set platform_type = AIRPLANE
|
||||||
|
set model_preview_type = 26
|
||||||
|
set motorstop_on_low = ON
|
||||||
|
set mixer_pid_profile_linking = ON
|
||||||
|
|
||||||
|
mixer_profile 2
|
||||||
|
|
||||||
|
set platform_type = TRICOPTER
|
||||||
|
set model_preview_type = 1
|
||||||
|
set mixer_pid_profile_linking = ON
|
||||||
|
|
||||||
|
profile 1 #pid profile
|
||||||
|
set dterm_lpf_hz = 10
|
||||||
|
set d_boost_min = 1.000
|
||||||
|
set d_boost_max = 1.000
|
||||||
|
set fw_level_pitch_trim = 5.000
|
||||||
|
set roll_rate = 18
|
||||||
|
set pitch_rate = 9
|
||||||
|
set yaw_rate = 3
|
||||||
|
set fw_turn_assist_pitch_gain = 0.4
|
||||||
|
set max_angle_inclination_rll = 450
|
||||||
|
set fw_ff_pitch = 80
|
||||||
|
set fw_ff_roll = 50
|
||||||
|
set fw_p_pitch = 15
|
||||||
|
set fw_p_roll = 15
|
||||||
|
|
||||||
|
profile 2
|
||||||
|
set dterm_lpf_hz = 60
|
||||||
|
set dterm_lpf_type = PT3
|
||||||
|
set d_boost_min = 0.800
|
||||||
|
set d_boost_max = 1.200
|
||||||
|
set d_boost_gyro_delta_lpf_hz = 60
|
||||||
|
set antigravity_gain = 2.000
|
||||||
|
set antigravity_accelerator = 5.000
|
||||||
|
set smith_predictor_delay = 1.500
|
||||||
|
set tpa_rate = 20
|
||||||
|
set tpa_breakpoint = 1200
|
||||||
|
set tpa_on_yaw = ON #If model using control surface/tilt mechanism for stabilization in MC mode
|
||||||
|
set roll_rate = 18
|
||||||
|
set pitch_rate = 18
|
||||||
|
set yaw_rate = 9
|
||||||
|
set mc_iterm_relax = RPY
|
||||||
|
|
||||||
|
save
|
||||||
|
```
|
||||||
|
|
||||||
|
# STEP1&2: Configuring as a Normal fixed-wing/Multi-Copter in two profiles separately
|
||||||
|
|
||||||
|
1. **Select the fisrt Mixer Profile and PID Profile:**
|
||||||
|
- In the CLI, switch to the mixer_profile and pid_profile you wish to set first. You can also switch mixer_profile/pid_profile through gui if with aforementioned presets loaded.
|
||||||
|
```
|
||||||
|
mixer_profile 1 #in this example, we set profile 1 first
|
||||||
|
set mixer_pid_profile_linking = ON # Let the mixer_profile handle the pid_profile switch on this mixer_profile
|
||||||
|
set platform_type = AIRPLANE
|
||||||
|
save
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure the fixed-wing/Multi-Copter:**
|
||||||
|
- Configure your fixed-wing/Multi-Copter as you normally would, or you can copy and paste default settings to expedite the process.
|
||||||
|
- Dshot esc protocol availability might be limited depends on outputs and fc board you are using. change the motor wiring or use oneshot/multishot esc protocol and calibrate throttle range.
|
||||||
|
- You can use throttle = -1 as a placeholder for the motor you wish to stop if the motor isn't the last motor
|
||||||
|
- Consider conducting a test flight to ensure that everything operates as expected. And tune the settings, trim the servos.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. **Switch to Another Mixer Profile with PID Profile:**
|
||||||
|
- In the CLI, switch to another mixer_profile along with the appropriate pid_profile. You can also switch mixer_profile/pid_profile through gui if with aforementioned presets loaded.
|
||||||
|
```
|
||||||
|
mixer_profile 2
|
||||||
|
set mixer_pid_profile_linking = ON
|
||||||
|
set platform_type = MULTIROTOR/TRICOPTER
|
||||||
|
save
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Configure the Multi-Copter/fixed-wing:**
|
||||||
|
- Set up your multi-copter/fixed-wing as usual, this time for mixer_profile 2 and pid_profile 2.
|
||||||
|
- Utilize the 'MAX' input in the servo mixer to tilt the motors without altering the servo midpoint.
|
||||||
|
- At this stage, focus on configuring profile-specific settings. You can streamline this process by copying and pasting the default PID settings.
|
||||||
|
- you can set -1 in motor mixer throttle as a place holder: disable that motor but will load following motor rules
|
||||||
|
- compass is required to enable navigation modes for multi-rotor profile.
|
||||||
|
- Consider conducting a test flight to ensure that everything operates as expected. And tune the settings.
|
||||||
|
- It is advisable to have a certain degree of control surface (elevon / elevator) mapping for stabilization even in multi-copter mode. This helps improve control authority when airspeed is high. It might be unable to recover from a dive without them.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5. **Tailsitters:planned for INAV 7.1**
|
||||||
|
- Configure the fixed-wing mode/profile sets normally. Use MultiCopter platform type for tail_sitting flying mode/profile sets.
|
||||||
|
- The baseline board aliment is FW mode(ROLL axis is the trust axis). So set `tailsitter_orientation_offset = ON ` in the tail_sitting MC mode.
|
||||||
|
- Configure mixer ROLL/YAW mixing according to tail_sitting orientation in the tail_sitting MC mode. YAW axis is the trust axis.
|
||||||
|
- Conduct a bench test and see the orientation of the model changes in inav-configurator setup tab
|
||||||
|
|
||||||
|
# STEP3: Mode Tab Settings:
|
||||||
|
### We recommend using an 3-pos switch on you radio to activate these modes, So pilot can jump in or bell out at any moment.
|
||||||
|
### Here is a example, in the bottom of inav-configurator Modes tab:
|
||||||
|

|
||||||
|
| 1000~1300 | 1300~1700 | 1700~2000 |
|
||||||
|
| :-- | :-- | :-- |
|
||||||
|
| Profile1(FW) with transition off | Profile2(MC) with transition on | Profile2(MC) with transition off |
|
||||||
|
|
||||||
|
- Profile file switching becomes available after completing the runtime sensor calibration(15-30s after booting). And It is **not available** when a navigation mode or position hold is active.
|
||||||
|
|
||||||
|
- By default, `mixer_profile 1` is used. `mixer_profile 2` is used when the `MIXER PROFILE 2` mode is activate. Once configured successfully, you will notice that the profiles and model preview changes accordingly when you refresh the relevant INAV Configurator tabs.
|
||||||
|
|
||||||
|
- Use the `MIXER TRANSITION` mode to gain airspeed in MC profile, set `MIXER TRANSITION` accordingly.
|
||||||
|
|
||||||
|
Conduct a bench test on the model (without props attached). The model can now switch between fixed-wing and multi-copter modes while armed. Furthermore, it is capable of mid-air switching, resulting in an immediate stall upon entering fixed-wing profile
|
||||||
|
|
||||||
|
# STEP4: Transition Mixing (Multi-Rotor Profile)(Recommended)
|
||||||
|
### Transition Mixing is typically useful in multi-copter profile to gain airspeed in prior to entering the fixed-wing profile. When the `MIXER TRANSITION` mode is activated, the associated motor or servo will move according to your configured Transition Mixing.
|
||||||
|
|
||||||
|
Please note that transition input is disabled when a navigation mode is activated. The use of Transition Mixing is necessary to enable additional features such as VTOL RTH with out stalling.
|
||||||
|
## Servo 'Transition Mixing': Tilting rotor configuration.
|
||||||
|
Add new servo mixer rules, and select 'Mixer Transition' in input. Set the weight/rate according to your desired angle. This will allow tilting the motor for tilting rotor model.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Motor 'Transition Mixing': Dedicated forward motor configuration
|
||||||
|
In motor mixer set:
|
||||||
|
- -2.0 < throttle < -1.0: The motor will spin regardless of the radio's throttle position at a speed of `abs(throttle) - 1` multiplied by throttle range only when Mixer Transition is activated.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## TailSitter 'Transition Mixing':
|
||||||
|
No additional settings needed, 45deg off set will be added to target pitch angle for angle mode in the firmware.
|
||||||
|
|
||||||
|
### With aforementioned settings, your model should be able to enter fixed-wing profile without stalling.
|
||||||
|
|
||||||
|
# Automated Switching (RTH) (Optional):
|
||||||
|
### This is one of the least tested features. This feature is primarily designed for Return to Home (RTH) in the event of a failsafe.
|
||||||
|
When configured correctly, the model will use the Fixed-Wing (FW) mode to efficiently return home and then transition to Multi-Copter (MC) mode for easier landing.
|
||||||
|
|
||||||
|
To enable this feature, type following command in cli
|
||||||
|
|
||||||
|
1. In your MC mode mixer profile (e.g., mixer_profile 2), set `mixer_automated_switch` to `ON`. leave it to `OFF` if burning remaining battery capacity on the way home is acceptable.
|
||||||
|
```
|
||||||
|
mixer_profile 2or1
|
||||||
|
set mixer_automated_switch= ON
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Set `mixer_switch_trans_timer` ds in cli in the MC mode mixer profile to specify the time required for your model to gain sufficient airspeed before transitioning to FW mode.
|
||||||
|
```
|
||||||
|
mixer_profile 2or1
|
||||||
|
set mixer_switch_trans_timer = 30 # 3s, 3000ms
|
||||||
|
```
|
||||||
|
3. In your FW mode mixer profile (e.g., mixer_profile 1), also set `mixer_automated_switch` to `ON`. leave it to `OFF` if automated landing in fixed-wing is acceptable.
|
||||||
|
```
|
||||||
|
mixer_profile 1or2
|
||||||
|
set mixer_automated_switch = ON
|
||||||
|
```
|
||||||
|
4. Save your settings. type `save` in cli.
|
||||||
|
|
||||||
|
If you set `mixer_automated_switch` to `OFF` for all mixer profiles (the default setting), the model will not perform automated transitions. You can always enable navigation modes after performing a manual transition.
|
||||||
|
|
||||||
|
|
||||||
|
# Notes and Experiences
|
||||||
|
## General
|
||||||
|
- VTOL model operating in multi-copter (MC) mode may encounter challenges in windy conditions. Please exercise caution when testing in such conditions.
|
||||||
|
- Make sure you can recover from a complete stall before trying the mid air transition
|
||||||
|
- It will be much safer if you can understand every line in diff all, read your diff all before maiden
|
||||||
|
|
||||||
|
## Tilting-rotor
|
||||||
|
- In some tilting motor models, you may experience roll/yaw coupled oscillations when `MIXER TRANSITION` is activated. To address this issue, you can try the following:
|
||||||
|
1. Use prop blade meets at top/rear prop direction for tilting motors to balance the effects of torque and P-factor.
|
||||||
|
2. In addition to 1. Add a little yaw mixing(about 0.2) in tilt motors.
|
||||||
|
- There will be a time window that tilting motors is providing up lift but rear motor isn't. Result in a sudden pitch raise on the entering of the mode. Use the max speed or faster speed in tiling servo to reduce the time window. OR lower the throttle on the entering of the FW mode to mitigate the effect.
|
||||||
|
## Dedicated forward motor
|
||||||
|
- Easiest way to setup a vtol. and efficiency can be improved by using different motor/prop for hover and forward flight
|
BIN
docs/assets/images/led_pin_pwm.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
docs/assets/images/ledpinpwmfilter.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
docs/assets/images/ledpinpwmled.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
docs/assets/images/ledpinpwmpowerled.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
docs/assets/images/osd_joystick.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
docs/assets/images/osd_joystick_keys.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
docs/assets/images/ws2811_data.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
docs/assets/images/ws2811_data_high.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
docs/assets/images/ws2811_packets.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
docs/assets/images/ws2811_packets_high.png
Normal file
After Width: | Height: | Size: 5 KiB |
5
docs/boards/MAMBAH743_2022B.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# VTX Power SWITCH
|
||||||
|
|
||||||
|
Contrary to what the documentation suggests, VTX power is actually on USER2.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# SpeedyBee F405 V3
|
# SpeedyBee F405 V3
|
||||||
|
|
||||||
> Notice: At the moment, DSHOT is not supported on SpeedyBe F405 V3. Use Multishot instead
|
> Notice: DSHOT on SpeedyBe F405 V3 requires INAV 7.0.0 or later. If you are using an older version, use MULTISHOT instead.
|
||||||
|
|
||||||
> Notice: The analog OSD and the SD card (blackbox) share the same SPI bus, which can cause problems when trying to use analog OSD and blackbox at the same time.
|
> Notice: The analog OSD and the SD card (blackbox) share the same SPI bus, which can cause problems when trying to use analog OSD and blackbox at the same time.
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ To run `cmake` in the latest version you will need to update from Ubuntu `18_04
|
||||||
|
|
||||||
Mount MS windows C drive and clone INAV
|
Mount MS windows C drive and clone INAV
|
||||||
1. `cd /mnt/c`
|
1. `cd /mnt/c`
|
||||||
1. `git clone https://github.com/iNavFlight/inav.git`
|
2. `git clone https://github.com/iNavFlight/inav.git`
|
||||||
|
3. `git checkout 6.1.1` (to switch to a specific release tag, for this example INAV version 6.1.1)
|
||||||
|
4. `git checkout -b my-branch` (to create own branch)
|
||||||
|
|
||||||
You are ready!
|
You are ready!
|
||||||
You now have a folder called inav in the root of C drive that you can edit in windows
|
You now have a folder called inav in the root of C drive that you can edit in windows
|
||||||
|
|
|
@ -48,7 +48,7 @@ typedef enum FIRMWARE_VERSION_TYPE
|
||||||
} FIRMWARE_VERSION_TYPE;
|
} FIRMWARE_VERSION_TYPE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** @brief Flags to report failure cases over the high latency telemtry. */
|
/** @brief Flags to report failure cases over the high latency telemetry. */
|
||||||
#ifndef HAVE_ENUM_HL_FAILURE_FLAG
|
#ifndef HAVE_ENUM_HL_FAILURE_FLAG
|
||||||
#define HAVE_ENUM_HL_FAILURE_FLAG
|
#define HAVE_ENUM_HL_FAILURE_FLAG
|
||||||
typedef enum HL_FAILURE_FLAG
|
typedef enum HL_FAILURE_FLAG
|
||||||
|
|
|
@ -184,8 +184,8 @@ __ALIGN_BEGIN uint8_t APP_Rx_Buffer [APP_RX_DATA_SIZE] __ALIGN_END ;
|
||||||
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
|
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
|
||||||
__ALIGN_BEGIN uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END ;
|
__ALIGN_BEGIN uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END ;
|
||||||
|
|
||||||
uint32_t APP_Rx_ptr_in = 0;
|
volatile uint32_t APP_Rx_ptr_in = 0;
|
||||||
uint32_t APP_Rx_ptr_out = 0;
|
volatile uint32_t APP_Rx_ptr_out = 0;
|
||||||
uint32_t APP_Rx_length = 0;
|
uint32_t APP_Rx_length = 0;
|
||||||
|
|
||||||
uint8_t USB_Tx_State = USB_CDC_IDLE;
|
uint8_t USB_Tx_State = USB_CDC_IDLE;
|
||||||
|
@ -482,8 +482,8 @@ uint8_t usbd_cdc_Init (void *pdev,
|
||||||
uint8_t usbd_cdc_DeInit (void *pdev,
|
uint8_t usbd_cdc_DeInit (void *pdev,
|
||||||
uint8_t cfgidx)
|
uint8_t cfgidx)
|
||||||
{
|
{
|
||||||
(void)pdev;
|
(void)pdev;
|
||||||
(void)cfgidx;
|
(void)cfgidx;
|
||||||
/* Open EP IN */
|
/* Open EP IN */
|
||||||
DCD_EP_Close(pdev,
|
DCD_EP_Close(pdev,
|
||||||
CDC_IN_EP);
|
CDC_IN_EP);
|
||||||
|
@ -594,7 +594,7 @@ uint8_t usbd_cdc_Setup (void *pdev,
|
||||||
*/
|
*/
|
||||||
uint8_t usbd_cdc_EP0_RxReady (void *pdev)
|
uint8_t usbd_cdc_EP0_RxReady (void *pdev)
|
||||||
{
|
{
|
||||||
(void)pdev;
|
(void)pdev;
|
||||||
if (cdcCmd != NO_CMD)
|
if (cdcCmd != NO_CMD)
|
||||||
{
|
{
|
||||||
/* Process the data */
|
/* Process the data */
|
||||||
|
@ -617,60 +617,45 @@ uint8_t usbd_cdc_EP0_RxReady (void *pdev)
|
||||||
*/
|
*/
|
||||||
uint8_t usbd_cdc_DataIn (void *pdev, uint8_t epnum)
|
uint8_t usbd_cdc_DataIn (void *pdev, uint8_t epnum)
|
||||||
{
|
{
|
||||||
(void)pdev;
|
(void)pdev;
|
||||||
(void)epnum;
|
(void)epnum;
|
||||||
uint16_t USB_Tx_ptr;
|
uint16_t USB_Tx_length;
|
||||||
uint16_t USB_Tx_length;
|
|
||||||
|
if (USB_Tx_State == USB_CDC_BUSY)
|
||||||
if (USB_Tx_State == USB_CDC_BUSY)
|
|
||||||
{
|
|
||||||
if (APP_Rx_length == 0)
|
|
||||||
{
|
{
|
||||||
USB_Tx_State = USB_CDC_IDLE;
|
if (APP_Rx_length == 0)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE){
|
|
||||||
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
||||||
USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
|
|
||||||
|
|
||||||
APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
|
|
||||||
APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
||||||
USB_Tx_length = APP_Rx_length;
|
|
||||||
|
|
||||||
APP_Rx_ptr_out += APP_Rx_length;
|
|
||||||
APP_Rx_length = 0;
|
|
||||||
if(USB_Tx_length == CDC_DATA_IN_PACKET_SIZE)
|
|
||||||
{
|
{
|
||||||
USB_Tx_State = USB_CDC_ZLP;
|
USB_Tx_State = USB_CDC_IDLE;
|
||||||
|
} else {
|
||||||
|
if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE) {
|
||||||
|
USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
|
||||||
|
} else {
|
||||||
|
USB_Tx_length = APP_Rx_length;
|
||||||
|
|
||||||
|
if (USB_Tx_length == CDC_DATA_IN_PACKET_SIZE) {
|
||||||
|
USB_Tx_State = USB_CDC_ZLP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare the available data buffer to be sent on IN endpoint */
|
||||||
|
DCD_EP_Tx(pdev, CDC_IN_EP, (uint8_t*)&APP_Rx_Buffer[APP_Rx_ptr_out], USB_Tx_length);
|
||||||
|
|
||||||
|
// Advance the out pointer
|
||||||
|
APP_Rx_ptr_out = (APP_Rx_ptr_out + USB_Tx_length) % APP_RX_DATA_SIZE;
|
||||||
|
APP_Rx_length -= USB_Tx_length;
|
||||||
|
|
||||||
|
return USBD_OK;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare the available data buffer to be sent on IN endpoint */
|
|
||||||
DCD_EP_Tx (pdev,
|
|
||||||
CDC_IN_EP,
|
|
||||||
(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
|
|
||||||
USB_Tx_length);
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/* Avoid any asynchronous transfer during ZLP */
|
||||||
/* Avoid any asynchronous transfer during ZLP */
|
if (USB_Tx_State == USB_CDC_ZLP) {
|
||||||
if (USB_Tx_State == USB_CDC_ZLP)
|
/*Send ZLP to indicate the end of the current transfer */
|
||||||
{
|
DCD_EP_Tx(pdev, CDC_IN_EP, NULL, 0);
|
||||||
/*Send ZLP to indicate the end of the current transfer */
|
|
||||||
DCD_EP_Tx (pdev,
|
USB_Tx_State = USB_CDC_IDLE;
|
||||||
CDC_IN_EP,
|
}
|
||||||
NULL,
|
return USBD_OK;
|
||||||
0);
|
|
||||||
|
|
||||||
USB_Tx_State = USB_CDC_IDLE;
|
|
||||||
}
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -731,67 +716,49 @@ uint8_t usbd_cdc_SOF (void *pdev)
|
||||||
*/
|
*/
|
||||||
static void Handle_USBAsynchXfer (void *pdev)
|
static void Handle_USBAsynchXfer (void *pdev)
|
||||||
{
|
{
|
||||||
uint16_t USB_Tx_ptr;
|
uint16_t USB_Tx_length;
|
||||||
uint16_t USB_Tx_length;
|
|
||||||
|
if (USB_Tx_State == USB_CDC_IDLE) {
|
||||||
if(USB_Tx_State == USB_CDC_IDLE)
|
if (APP_Rx_ptr_out == APP_Rx_ptr_in) {
|
||||||
{
|
// Ring buffer is empty
|
||||||
if (APP_Rx_ptr_out == APP_RX_DATA_SIZE)
|
return;
|
||||||
{
|
}
|
||||||
APP_Rx_ptr_out = 0;
|
|
||||||
}
|
if (APP_Rx_ptr_out > APP_Rx_ptr_in) {
|
||||||
|
// Transfer bytes up to the end of the ring buffer
|
||||||
if(APP_Rx_ptr_out == APP_Rx_ptr_in)
|
APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out;
|
||||||
{
|
} else {
|
||||||
USB_Tx_State = USB_CDC_IDLE;
|
// Transfer all bytes in ring buffer
|
||||||
return;
|
APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(APP_Rx_ptr_out > APP_Rx_ptr_in) /* rollback */
|
|
||||||
{
|
|
||||||
APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out;
|
|
||||||
|
|
||||||
}
|
|
||||||
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
|
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
|
||||||
APP_Rx_length &= ~0x03;
|
// Only transfer whole 32 bit words of data
|
||||||
|
APP_Rx_length &= ~0x03;
|
||||||
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
|
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
|
||||||
|
|
||||||
|
if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE) {
|
||||||
|
USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
|
||||||
|
|
||||||
|
USB_Tx_State = USB_CDC_BUSY;
|
||||||
|
} else {
|
||||||
|
USB_Tx_length = APP_Rx_length;
|
||||||
|
|
||||||
|
if (USB_Tx_length == CDC_DATA_IN_PACKET_SIZE) {
|
||||||
|
USB_Tx_State = USB_CDC_ZLP;
|
||||||
|
} else {
|
||||||
|
USB_Tx_State = USB_CDC_BUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE)
|
DCD_EP_Tx (pdev,
|
||||||
{
|
CDC_IN_EP,
|
||||||
USB_Tx_ptr = APP_Rx_ptr_out;
|
(uint8_t*)&APP_Rx_Buffer[APP_Rx_ptr_out],
|
||||||
USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
|
USB_Tx_length);
|
||||||
|
|
||||||
APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
|
APP_Rx_ptr_out = (APP_Rx_ptr_out + USB_Tx_length) % APP_RX_DATA_SIZE;
|
||||||
APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
|
APP_Rx_length -= USB_Tx_length;
|
||||||
USB_Tx_State = USB_CDC_BUSY;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
USB_Tx_ptr = APP_Rx_ptr_out;
|
|
||||||
USB_Tx_length = APP_Rx_length;
|
|
||||||
|
|
||||||
APP_Rx_ptr_out += APP_Rx_length;
|
|
||||||
APP_Rx_length = 0;
|
|
||||||
if(USB_Tx_length == CDC_DATA_IN_PACKET_SIZE)
|
|
||||||
{
|
|
||||||
USB_Tx_State = USB_CDC_ZLP;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
USB_Tx_State = USB_CDC_BUSY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DCD_EP_Tx (pdev,
|
|
||||||
CDC_IN_EP,
|
|
||||||
(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
|
|
||||||
USB_Tx_length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -803,7 +770,7 @@ static void Handle_USBAsynchXfer (void *pdev)
|
||||||
*/
|
*/
|
||||||
static uint8_t *USBD_cdc_GetCfgDesc (uint8_t speed, uint16_t *length)
|
static uint8_t *USBD_cdc_GetCfgDesc (uint8_t speed, uint16_t *length)
|
||||||
{
|
{
|
||||||
(void)speed;
|
(void)speed;
|
||||||
*length = sizeof (usbd_cdc_CfgDesc);
|
*length = sizeof (usbd_cdc_CfgDesc);
|
||||||
return usbd_cdc_CfgDesc;
|
return usbd_cdc_CfgDesc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# INAV - navigation capable flight controller
|
# INAV - navigation capable flight controller
|
||||||
|
|
||||||
|
# PSA
|
||||||
|
|
||||||
|
> INAV no longer accepts targets based on STM32 F411 MCU.
|
||||||
|
|
||||||
|
> INAV 7 is the last INAV official release available for F411 based flight controllers. The next milestone, INAV 8 will not be available for F411 boards.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# INAV Community
|
# INAV Community
|
||||||
|
|
|
@ -525,6 +525,8 @@ main_sources(COMMON_SRC
|
||||||
io/osd_grid.h
|
io/osd_grid.h
|
||||||
io/osd_hud.c
|
io/osd_hud.c
|
||||||
io/osd_hud.h
|
io/osd_hud.h
|
||||||
|
io/osd_joystick.c
|
||||||
|
io/osd_joystick.h
|
||||||
io/smartport_master.c
|
io/smartport_master.c
|
||||||
io/smartport_master.h
|
io/smartport_master.h
|
||||||
io/vtx.c
|
io/vtx.c
|
||||||
|
@ -556,6 +558,8 @@ main_sources(COMMON_SRC
|
||||||
navigation/navigation_rover_boat.c
|
navigation/navigation_rover_boat.c
|
||||||
navigation/sqrt_controller.c
|
navigation/sqrt_controller.c
|
||||||
navigation/sqrt_controller.h
|
navigation/sqrt_controller.h
|
||||||
|
navigation/rth_trackback.c
|
||||||
|
navigation/rth_trackback.h
|
||||||
|
|
||||||
sensors/barometer.c
|
sensors/barometer.c
|
||||||
sensors/barometer.h
|
sensors/barometer.h
|
||||||
|
|
|
@ -310,6 +310,7 @@ static const blackboxDeltaFieldDefinition_t blackboxMainFields[] = {
|
||||||
{"accSmooth", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
{"accSmooth", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
||||||
{"accSmooth", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
{"accSmooth", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
||||||
{"accSmooth", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
{"accSmooth", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
||||||
|
{"accVib", -1, UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ACC},
|
||||||
{"attitude", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ATTITUDE},
|
{"attitude", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ATTITUDE},
|
||||||
{"attitude", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ATTITUDE},
|
{"attitude", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ATTITUDE},
|
||||||
{"attitude", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ATTITUDE},
|
{"attitude", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_ATTITUDE},
|
||||||
|
@ -436,6 +437,7 @@ static const blackboxSimpleFieldDefinition_t blackboxSlowFields[] = {
|
||||||
{"escTemperature", -1, SIGNED, PREDICT(PREVIOUS), ENCODING(SIGNED_VB)},
|
{"escTemperature", -1, SIGNED, PREDICT(PREVIOUS), ENCODING(SIGNED_VB)},
|
||||||
#endif
|
#endif
|
||||||
{"rxUpdateRate", -1, UNSIGNED, PREDICT(PREVIOUS), ENCODING(UNSIGNED_VB)},
|
{"rxUpdateRate", -1, UNSIGNED, PREDICT(PREVIOUS), ENCODING(UNSIGNED_VB)},
|
||||||
|
{"activeWpNumber", -1, UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB)},
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum BlackboxState {
|
typedef enum BlackboxState {
|
||||||
|
@ -487,6 +489,7 @@ typedef struct blackboxMainState_s {
|
||||||
int16_t gyroPeaksYaw[DYN_NOTCH_PEAK_COUNT];
|
int16_t gyroPeaksYaw[DYN_NOTCH_PEAK_COUNT];
|
||||||
|
|
||||||
int16_t accADC[XYZ_AXIS_COUNT];
|
int16_t accADC[XYZ_AXIS_COUNT];
|
||||||
|
int16_t accVib;
|
||||||
int16_t attitude[XYZ_AXIS_COUNT];
|
int16_t attitude[XYZ_AXIS_COUNT];
|
||||||
int32_t debug[DEBUG32_VALUE_COUNT];
|
int32_t debug[DEBUG32_VALUE_COUNT];
|
||||||
int16_t motor[MAX_SUPPORTED_MOTORS];
|
int16_t motor[MAX_SUPPORTED_MOTORS];
|
||||||
|
@ -554,6 +557,7 @@ typedef struct blackboxSlowState_s {
|
||||||
int8_t escTemperature;
|
int8_t escTemperature;
|
||||||
#endif
|
#endif
|
||||||
uint16_t rxUpdateRate;
|
uint16_t rxUpdateRate;
|
||||||
|
uint8_t activeWpNumber;
|
||||||
} __attribute__((__packed__)) blackboxSlowState_t; // We pack this struct so that padding doesn't interfere with memcmp()
|
} __attribute__((__packed__)) blackboxSlowState_t; // We pack this struct so that padding doesn't interfere with memcmp()
|
||||||
|
|
||||||
//From rc_controls.c
|
//From rc_controls.c
|
||||||
|
@ -917,6 +921,7 @@ static void writeIntraframe(void)
|
||||||
|
|
||||||
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ACC)) {
|
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ACC)) {
|
||||||
blackboxWriteSigned16VBArray(blackboxCurrent->accADC, XYZ_AXIS_COUNT);
|
blackboxWriteSigned16VBArray(blackboxCurrent->accADC, XYZ_AXIS_COUNT);
|
||||||
|
blackboxWriteUnsignedVB(blackboxCurrent->accVib);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ATTITUDE)) {
|
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ATTITUDE)) {
|
||||||
|
@ -1182,6 +1187,7 @@ static void writeInterframe(void)
|
||||||
|
|
||||||
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ACC)) {
|
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ACC)) {
|
||||||
blackboxWriteArrayUsingAveragePredictor16(offsetof(blackboxMainState_t, accADC), XYZ_AXIS_COUNT);
|
blackboxWriteArrayUsingAveragePredictor16(offsetof(blackboxMainState_t, accADC), XYZ_AXIS_COUNT);
|
||||||
|
blackboxWriteSignedVB(blackboxCurrent->accVib - blackboxLast->accVib);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ATTITUDE)) {
|
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_ATTITUDE)) {
|
||||||
|
@ -1291,6 +1297,7 @@ static void writeSlowFrame(void)
|
||||||
blackboxWriteSignedVB(slowHistory.escTemperature);
|
blackboxWriteSignedVB(slowHistory.escTemperature);
|
||||||
#endif
|
#endif
|
||||||
blackboxWriteUnsignedVB(slowHistory.rxUpdateRate);
|
blackboxWriteUnsignedVB(slowHistory.rxUpdateRate);
|
||||||
|
blackboxWriteUnsignedVB(slowHistory.activeWpNumber);
|
||||||
|
|
||||||
blackboxSlowFrameIterationTimer = 0;
|
blackboxSlowFrameIterationTimer = 0;
|
||||||
}
|
}
|
||||||
|
@ -1368,6 +1375,7 @@ static void loadSlowState(blackboxSlowState_t *slow)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
slow->rxUpdateRate = getRcUpdateFrequency();
|
slow->rxUpdateRate = getRcUpdateFrequency();
|
||||||
|
slow->activeWpNumber = getActiveWpNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1601,6 +1609,7 @@ static void loadMainState(timeUs_t currentTimeUs)
|
||||||
blackboxCurrent->mcVelAxisOutput[i] = lrintf(nav_pids->vel[i].output_constrained);
|
blackboxCurrent->mcVelAxisOutput[i] = lrintf(nav_pids->vel[i].output_constrained);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
blackboxCurrent->accVib = lrintf(accGetVibrationLevel() * acc.dev.acc_1G);
|
||||||
|
|
||||||
if (STATE(FIXED_WING_LEGACY)) {
|
if (STATE(FIXED_WING_LEGACY)) {
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,11 @@ float navPidApply3(
|
||||||
/* Pre-calculate output and limit it if actuator is saturating */
|
/* Pre-calculate output and limit it if actuator is saturating */
|
||||||
const float outVal = newProportional + (pid->integrator * gainScaler) + newDerivative + newFeedForward;
|
const float outVal = newProportional + (pid->integrator * gainScaler) + newDerivative + newFeedForward;
|
||||||
const float outValConstrained = constrainf(outVal, outMin, outMax);
|
const float outValConstrained = constrainf(outVal, outMin, outMax);
|
||||||
|
float backCalc = outValConstrained - outVal;//back-calculation anti-windup
|
||||||
|
if (SIGN(backCalc) == SIGN(pid->integrator)) {
|
||||||
|
//back calculation anti-windup can only shrink integrator, will not push it to the opposite direction
|
||||||
|
backCalc = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
pid->proportional = newProportional;
|
pid->proportional = newProportional;
|
||||||
pid->integral = pid->integrator;
|
pid->integral = pid->integrator;
|
||||||
|
@ -104,7 +109,7 @@ float navPidApply3(
|
||||||
!(pidFlags & PID_ZERO_INTEGRATOR) &&
|
!(pidFlags & PID_ZERO_INTEGRATOR) &&
|
||||||
!(pidFlags & PID_FREEZE_INTEGRATOR)
|
!(pidFlags & PID_FREEZE_INTEGRATOR)
|
||||||
) {
|
) {
|
||||||
const float newIntegrator = pid->integrator + (error * pid->param.kI * gainScaler * dt) + ((outValConstrained - outVal) * pid->param.kT * dt);
|
const float newIntegrator = pid->integrator + (error * pid->param.kI * gainScaler * dt) + (backCalc * pid->param.kT * dt);
|
||||||
|
|
||||||
if (pidFlags & PID_SHRINK_INTEGRATOR) {
|
if (pidFlags & PID_SHRINK_INTEGRATOR) {
|
||||||
// Only allow integrator to shrink
|
// Only allow integrator to shrink
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
#define RAD (M_PIf / 180.0f)
|
#define RAD (M_PIf / 180.0f)
|
||||||
|
|
||||||
#define DEGREES_TO_CENTIDEGREES(angle) ((angle) * 100)
|
#define DEGREES_TO_CENTIDEGREES(angle) ((angle) * 100)
|
||||||
#define CENTIDEGREES_TO_DEGREES(angle) ((angle) / 100.0f)
|
#define CENTIDEGREES_TO_DEGREES(angle) ((angle) * 0.01f)
|
||||||
|
|
||||||
#define CENTIDEGREES_TO_DECIDEGREES(angle) ((angle) / 10.0f)
|
#define CENTIDEGREES_TO_DECIDEGREES(angle) ((angle) / 10.0f)
|
||||||
#define DECIDEGREES_TO_CENTIDEGREES(angle) ((angle) * 10)
|
#define DECIDEGREES_TO_CENTIDEGREES(angle) ((angle) * 10)
|
||||||
|
@ -54,11 +54,11 @@
|
||||||
#define RADIANS_TO_DECIDEGREES(angle) (((angle) * 10.0f) / RAD)
|
#define RADIANS_TO_DECIDEGREES(angle) (((angle) * 10.0f) / RAD)
|
||||||
|
|
||||||
#define RADIANS_TO_CENTIDEGREES(angle) (((angle) * 100.0f) / RAD)
|
#define RADIANS_TO_CENTIDEGREES(angle) (((angle) * 100.0f) / RAD)
|
||||||
#define CENTIDEGREES_TO_RADIANS(angle) (((angle) / 100.0f) * RAD)
|
#define CENTIDEGREES_TO_RADIANS(angle) (((angle) * 0.01f) * RAD)
|
||||||
|
|
||||||
#define CENTIMETERS_TO_CENTIFEET(cm) (cm / 0.3048f)
|
#define CENTIMETERS_TO_CENTIFEET(cm) (cm / 0.3048f)
|
||||||
#define CENTIMETERS_TO_FEET(cm) (cm / 30.48f)
|
#define CENTIMETERS_TO_FEET(cm) (cm / 30.48f)
|
||||||
#define CENTIMETERS_TO_METERS(cm) (cm / 100.0f)
|
#define CENTIMETERS_TO_METERS(cm) (cm * 0.01f)
|
||||||
|
|
||||||
#define METERS_TO_CENTIMETERS(m) (m * 100)
|
#define METERS_TO_CENTIMETERS(m) (m * 100)
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@
|
||||||
)
|
)
|
||||||
#define MIN(a, b) _CHOOSE(<, a, b)
|
#define MIN(a, b) _CHOOSE(<, a, b)
|
||||||
#define MAX(a, b) _CHOOSE(>, a, b)
|
#define MAX(a, b) _CHOOSE(>, a, b)
|
||||||
|
#define SIGN(a) ((a >= 0) ? 1 : -1)
|
||||||
|
|
||||||
#define _ABS_II(x, var) \
|
#define _ABS_II(x, var) \
|
||||||
( __extension__ ({ \
|
( __extension__ ({ \
|
||||||
|
|
|
@ -121,7 +121,10 @@
|
||||||
#define PG_OSD_COMMON_CONFIG 1031
|
#define PG_OSD_COMMON_CONFIG 1031
|
||||||
#define PG_TIMER_OVERRIDE_CONFIG 1032
|
#define PG_TIMER_OVERRIDE_CONFIG 1032
|
||||||
#define PG_EZ_TUNE 1033
|
#define PG_EZ_TUNE 1033
|
||||||
#define PG_INAV_END PG_EZ_TUNE
|
#define PG_LEDPIN_CONFIG 1034
|
||||||
|
#define PG_OSD_JOYSTICK_CONFIG 1035
|
||||||
|
#define PG_INAV_END PG_OSD_JOYSTICK_CONFIG
|
||||||
|
|
||||||
|
|
||||||
// OSD configuration (subject to change)
|
// OSD configuration (subject to change)
|
||||||
//#define PG_OSD_FONT_CONFIG 2047
|
//#define PG_OSD_FONT_CONFIG 2047
|
||||||
|
|
|
@ -179,11 +179,11 @@ void adcHardwareInit(drv_adc_config_t *init)
|
||||||
adcDevice_t * adc = &adcHardware[adcConfig[i].adcDevice];
|
adcDevice_t * adc = &adcHardware[adcConfig[i].adcDevice];
|
||||||
// init adc gpio port
|
// init adc gpio port
|
||||||
IOInit(IOGetByTag(adcConfig[i].tag), OWNER_ADC, RESOURCE_ADC_CH1 + (i - ADC_CHN_1), 0);
|
IOInit(IOGetByTag(adcConfig[i].tag), OWNER_ADC, RESOURCE_ADC_CH1 + (i - ADC_CHN_1), 0);
|
||||||
IOConfigGPIO(IOGetByTag(adcConfig[i].tag), IO_CONFIG(GPIO_MODE_ANALOG, 0, GPIO_OUTPUT_OPEN_DRAIN, GPIO_PULL_NONE));
|
IOConfigGPIO(IOGetByTag(adcConfig[i].tag), IO_CONFIG(GPIO_MODE_ANALOG, 0, 0, 0));
|
||||||
|
|
||||||
adcConfig[i].adcChannel = adcChannelByTag(adcConfig[i].tag);
|
adcConfig[i].adcChannel = adcChannelByTag(adcConfig[i].tag);
|
||||||
adcConfig[i].dmaIndex = adc->usedChannelCount++;
|
adcConfig[i].dmaIndex = adc->usedChannelCount++;
|
||||||
adcConfig[i].sampleTime = ADC_SAMPLETIME_6_5;
|
adcConfig[i].sampleTime = ADC_SAMPLETIME_640_5;
|
||||||
adcConfig[i].enabled = true;
|
adcConfig[i].enabled = true;
|
||||||
|
|
||||||
adc->enabled = true;
|
adc->enabled = true;
|
||||||
|
|
|
@ -37,7 +37,7 @@ static bool fakeMagInit(magDev_t *magDev)
|
||||||
{
|
{
|
||||||
UNUSED(magDev);
|
UNUSED(magDev);
|
||||||
// initially point north
|
// initially point north
|
||||||
fakeMagData[X] = 4096;
|
fakeMagData[X] = 1024;
|
||||||
fakeMagData[Y] = 0;
|
fakeMagData[Y] = 0;
|
||||||
fakeMagData[Z] = 0;
|
fakeMagData[Z] = 0;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -180,13 +180,13 @@ int displayWriteChar(displayPort_t *instance, uint8_t x, uint8_t y, uint16_t c)
|
||||||
|
|
||||||
#ifdef USE_SIMULATOR
|
#ifdef USE_SIMULATOR
|
||||||
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
|
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
|
||||||
//some FCs do not power max7456 from USB power
|
//some FCs do not power max7456 from USB power
|
||||||
//driver can not read font metadata
|
//driver can not read font metadata
|
||||||
//chip assumed to not support second bank of font
|
//chip assumed to not support second bank of font
|
||||||
//artifical horizon, variometer and home direction are not drawn ( display.c: displayUpdateMaxChar())
|
//artifical horizon, variometer and home direction are not drawn ( display.c: displayUpdateMaxChar())
|
||||||
//return dummy metadata to let all OSD elements to work in simulator mode
|
//return dummy metadata to let all OSD elements to work in simulator mode
|
||||||
instance->maxChar = 512;
|
instance->maxChar = 512;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (c > instance->maxChar) {
|
if (c > instance->maxChar) {
|
||||||
|
|
|
@ -43,15 +43,26 @@
|
||||||
#include "drivers/timer.h"
|
#include "drivers/timer.h"
|
||||||
#include "drivers/light_ws2811strip.h"
|
#include "drivers/light_ws2811strip.h"
|
||||||
|
|
||||||
|
#include "config/parameter_group_ids.h"
|
||||||
|
#include "fc/settings.h"
|
||||||
|
#include "fc/runtime_config.h"
|
||||||
|
|
||||||
#define WS2811_PERIOD (WS2811_TIMER_HZ / WS2811_CARRIER_HZ)
|
#define WS2811_PERIOD (WS2811_TIMER_HZ / WS2811_CARRIER_HZ)
|
||||||
#define WS2811_BIT_COMPARE_1 ((WS2811_PERIOD * 2) / 3)
|
#define WS2811_BIT_COMPARE_1 ((WS2811_PERIOD * 2) / 3)
|
||||||
#define WS2811_BIT_COMPARE_0 (WS2811_PERIOD / 3)
|
#define WS2811_BIT_COMPARE_0 (WS2811_PERIOD / 3)
|
||||||
|
|
||||||
|
PG_REGISTER_WITH_RESET_TEMPLATE(ledPinConfig_t, ledPinConfig, PG_LEDPIN_CONFIG, 0);
|
||||||
|
|
||||||
|
PG_RESET_TEMPLATE(ledPinConfig_t, ledPinConfig,
|
||||||
|
.led_pin_pwm_mode = SETTING_LED_PIN_PWM_MODE_DEFAULT
|
||||||
|
);
|
||||||
|
|
||||||
static DMA_RAM timerDMASafeType_t ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE];
|
static DMA_RAM timerDMASafeType_t ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE];
|
||||||
|
|
||||||
static IO_t ws2811IO = IO_NONE;
|
static IO_t ws2811IO = IO_NONE;
|
||||||
static TCH_t * ws2811TCH = NULL;
|
static TCH_t * ws2811TCH = NULL;
|
||||||
static bool ws2811Initialised = false;
|
static bool ws2811Initialised = false;
|
||||||
|
static bool pwmMode = false;
|
||||||
|
|
||||||
static hsvColor_t ledColorBuffer[WS2811_LED_STRIP_LENGTH];
|
static hsvColor_t ledColorBuffer[WS2811_LED_STRIP_LENGTH];
|
||||||
|
|
||||||
|
@ -91,6 +102,24 @@ void setStripColors(const hsvColor_t *colors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ledConfigureDMA(void) {
|
||||||
|
/* Compute the prescaler value */
|
||||||
|
uint8_t period = WS2811_TIMER_HZ / WS2811_CARRIER_HZ;
|
||||||
|
|
||||||
|
timerConfigBase(ws2811TCH, period, WS2811_TIMER_HZ);
|
||||||
|
timerPWMConfigChannel(ws2811TCH, 0);
|
||||||
|
|
||||||
|
return timerPWMConfigChannelDMA(ws2811TCH, ledStripDMABuffer, sizeof(ledStripDMABuffer[0]), WS2811_DMA_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledConfigurePWM(void) {
|
||||||
|
timerConfigBase(ws2811TCH, 100, WS2811_TIMER_HZ );
|
||||||
|
timerPWMConfigChannel(ws2811TCH, 0);
|
||||||
|
timerPWMStart(ws2811TCH);
|
||||||
|
timerEnable(ws2811TCH);
|
||||||
|
pwmMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
void ws2811LedStripInit(void)
|
void ws2811LedStripInit(void)
|
||||||
{
|
{
|
||||||
const timerHardware_t * timHw = timerGetByTag(IO_TAG(WS2811_PIN), TIM_USE_ANY);
|
const timerHardware_t * timHw = timerGetByTag(IO_TAG(WS2811_PIN), TIM_USE_ANY);
|
||||||
|
@ -104,27 +133,32 @@ void ws2811LedStripInit(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compute the prescaler value */
|
|
||||||
uint8_t period = WS2811_TIMER_HZ / WS2811_CARRIER_HZ;
|
|
||||||
|
|
||||||
ws2811IO = IOGetByTag(IO_TAG(WS2811_PIN));
|
ws2811IO = IOGetByTag(IO_TAG(WS2811_PIN));
|
||||||
IOInit(ws2811IO, OWNER_LED_STRIP, RESOURCE_OUTPUT, 0);
|
IOInit(ws2811IO, OWNER_LED_STRIP, RESOURCE_OUTPUT, 0);
|
||||||
IOConfigGPIOAF(ws2811IO, IOCFG_AF_PP_FAST, timHw->alternateFunction);
|
IOConfigGPIOAF(ws2811IO, IOCFG_AF_PP_FAST, timHw->alternateFunction);
|
||||||
|
|
||||||
timerConfigBase(ws2811TCH, period, WS2811_TIMER_HZ);
|
if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_LOW ) {
|
||||||
timerPWMConfigChannel(ws2811TCH, 0);
|
ledConfigurePWM();
|
||||||
|
*timerCCR(ws2811TCH) = 0;
|
||||||
|
} else if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_HIGH ) {
|
||||||
|
ledConfigurePWM();
|
||||||
|
*timerCCR(ws2811TCH) = 100;
|
||||||
|
} else {
|
||||||
|
if (!ledConfigureDMA()) {
|
||||||
|
// If DMA failed - abort
|
||||||
|
ws2811Initialised = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If DMA failed - abort
|
// Zero out DMA buffer
|
||||||
if (!timerPWMConfigChannelDMA(ws2811TCH, ledStripDMABuffer, sizeof(ledStripDMABuffer[0]), WS2811_DMA_BUFFER_SIZE)) {
|
memset(&ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
|
||||||
ws2811Initialised = false;
|
if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_SHARED_HIGH ) {
|
||||||
return;
|
ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE-1] = 255;
|
||||||
|
}
|
||||||
|
ws2811Initialised = true;
|
||||||
|
|
||||||
|
ws2811UpdateStrip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero out DMA buffer
|
|
||||||
memset(&ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
|
|
||||||
ws2811Initialised = true;
|
|
||||||
|
|
||||||
ws2811UpdateStrip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWS2811LedStripReady(void)
|
bool isWS2811LedStripReady(void)
|
||||||
|
@ -140,7 +174,7 @@ STATIC_UNIT_TESTED void fastUpdateLEDDMABuffer(rgbColor24bpp_t *color)
|
||||||
uint32_t grb = (color->rgb.g << 16) | (color->rgb.r << 8) | (color->rgb.b);
|
uint32_t grb = (color->rgb.g << 16) | (color->rgb.r << 8) | (color->rgb.b);
|
||||||
|
|
||||||
for (int8_t index = 23; index >= 0; index--) {
|
for (int8_t index = 23; index >= 0; index--) {
|
||||||
ledStripDMABuffer[dmaBufferOffset++] = (grb & (1 << index)) ? WS2811_BIT_COMPARE_1 : WS2811_BIT_COMPARE_0;
|
ledStripDMABuffer[WS2811_DELAY_BUFFER_LENGTH + dmaBufferOffset++] = (grb & (1 << index)) ? WS2811_BIT_COMPARE_1 : WS2811_BIT_COMPARE_0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +187,7 @@ void ws2811UpdateStrip(void)
|
||||||
static rgbColor24bpp_t *rgb24;
|
static rgbColor24bpp_t *rgb24;
|
||||||
|
|
||||||
// don't wait - risk of infinite block, just get an update next time round
|
// don't wait - risk of infinite block, just get an update next time round
|
||||||
if (timerPWMDMAInProgress(ws2811TCH)) {
|
if (pwmMode || timerPWMDMAInProgress(ws2811TCH)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,4 +212,40 @@ void ws2811UpdateStrip(void)
|
||||||
timerPWMStartDMA(ws2811TCH);
|
timerPWMStartDMA(ws2811TCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//value
|
||||||
|
void ledPinStartPWM(uint16_t value) {
|
||||||
|
if (ws2811TCH == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !pwmMode ) {
|
||||||
|
timerPWMStopDMA(ws2811TCH);
|
||||||
|
//FIXME: implement method to release DMA
|
||||||
|
ws2811TCH->dma->owner = OWNER_FREE;
|
||||||
|
|
||||||
|
ledConfigurePWM();
|
||||||
|
}
|
||||||
|
*timerCCR(ws2811TCH) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledPinStopPWM(void) {
|
||||||
|
if (ws2811TCH == NULL || !pwmMode ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_HIGH ) {
|
||||||
|
*timerCCR(ws2811TCH) = 100;
|
||||||
|
return;
|
||||||
|
} else if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_LOW ) {
|
||||||
|
*timerCCR(ws2811TCH) = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pwmMode = false;
|
||||||
|
|
||||||
|
if (!ledConfigureDMA()) {
|
||||||
|
ws2811Initialised = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,23 +17,41 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "common/color.h"
|
#include "common/color.h"
|
||||||
|
#include "config/parameter_group.h"
|
||||||
|
|
||||||
#define WS2811_LED_STRIP_LENGTH 32
|
#define WS2811_LED_STRIP_LENGTH 32
|
||||||
#define WS2811_BITS_PER_LED 24
|
#define WS2811_BITS_PER_LED 24
|
||||||
#define WS2811_DELAY_BUFFER_LENGTH 42 // for 50us delay
|
#define WS2811_DELAY_BUFFER_LENGTH 42 // for 50us delay
|
||||||
|
|
||||||
#define WS2811_DATA_BUFFER_SIZE (WS2811_BITS_PER_LED * WS2811_LED_STRIP_LENGTH)
|
#define WS2811_DATA_BUFFER_SIZE (WS2811_BITS_PER_LED * WS2811_LED_STRIP_LENGTH)
|
||||||
|
|
||||||
#define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE + WS2811_DELAY_BUFFER_LENGTH) // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes)
|
#define WS2811_DMA_BUFFER_SIZE (WS2811_DELAY_BUFFER_LENGTH + WS2811_DATA_BUFFER_SIZE + 1) // leading bytes (reset low 302us) + data bytes LEDS*3 + 1 byte(keep line high optionally)
|
||||||
|
|
||||||
#define WS2811_TIMER_HZ 2400000
|
#define WS2811_TIMER_HZ 2400000
|
||||||
#define WS2811_CARRIER_HZ 800000
|
#define WS2811_CARRIER_HZ 800000
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LED_PIN_PWM_MODE_SHARED_LOW = 0,
|
||||||
|
LED_PIN_PWM_MODE_SHARED_HIGH = 1,
|
||||||
|
LED_PIN_PWM_MODE_LOW = 2,
|
||||||
|
LED_PIN_PWM_MODE_HIGH = 3
|
||||||
|
} led_pin_pwm_mode_e;
|
||||||
|
|
||||||
|
typedef struct ledPinConfig_s {
|
||||||
|
uint8_t led_pin_pwm_mode; //led_pin_pwm_mode_e
|
||||||
|
} ledPinConfig_t;
|
||||||
|
|
||||||
|
PG_DECLARE(ledPinConfig_t, ledPinConfig);
|
||||||
|
|
||||||
void ws2811LedStripInit(void);
|
void ws2811LedStripInit(void);
|
||||||
void ws2811LedStripHardwareInit(void);
|
void ws2811LedStripHardwareInit(void);
|
||||||
void ws2811LedStripDMAEnable(void);
|
void ws2811LedStripDMAEnable(void);
|
||||||
bool ws2811LedStripDMAInProgress(void);
|
bool ws2811LedStripDMAInProgress(void);
|
||||||
|
|
||||||
|
//value 0...100
|
||||||
|
void ledPinStartPWM(uint16_t value);
|
||||||
|
void ledPinStopPWM(void);
|
||||||
|
|
||||||
void ws2811UpdateStrip(void);
|
void ws2811UpdateStrip(void);
|
||||||
|
|
||||||
void setLedHsv(uint16_t index, const hsvColor_t *color);
|
void setLedHsv(uint16_t index, const hsvColor_t *color);
|
||||||
|
|
|
@ -265,13 +265,8 @@ uint32_t tcpTotalRxBytesWaiting(const serialPort_t *instance)
|
||||||
|
|
||||||
uint32_t tcpTotalTxBytesFree(const serialPort_t *instance)
|
uint32_t tcpTotalTxBytesFree(const serialPort_t *instance)
|
||||||
{
|
{
|
||||||
tcpPort_t *port = (tcpPort_t*)instance;
|
UNUSED(instance);
|
||||||
|
return TCP_MAX_PACKET_SIZE;
|
||||||
if (port->isClientConnected) {
|
|
||||||
return TCP_MAX_PACKET_SIZE;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTcpTransmitBufferEmpty(const serialPort_t *instance)
|
bool isTcpTransmitBufferEmpty(const serialPort_t *instance)
|
||||||
|
|
|
@ -48,10 +48,10 @@ void systemBeep(bool onoff)
|
||||||
|
|
||||||
#ifdef USE_SIMULATOR
|
#ifdef USE_SIMULATOR
|
||||||
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
|
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
|
||||||
if (SIMULATOR_HAS_OPTION(HITL_MUTE_BEEPER)) {
|
if (SIMULATOR_HAS_OPTION(HITL_MUTE_BEEPER)) {
|
||||||
onoff = false;
|
onoff = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (beeperConfig()->pwmMode) {
|
if (beeperConfig()->pwmMode) {
|
||||||
|
|
|
@ -81,13 +81,16 @@ void impl_timerConfigBase(TCH_t * tch, uint16_t period, uint32_t hz)
|
||||||
TIM_HandleTypeDef * timHandle = tch->timCtx->timHandle;
|
TIM_HandleTypeDef * timHandle = tch->timCtx->timHandle;
|
||||||
TIM_TypeDef * timer = tch->timCtx->timDef->tim;
|
TIM_TypeDef * timer = tch->timCtx->timDef->tim;
|
||||||
|
|
||||||
if (timHandle->Instance == timer) {
|
uint16_t period1 = (period - 1) & 0xffff;
|
||||||
|
uint16_t prescaler1 = lrintf((float)timerGetBaseClock(tch) / hz + 0.01f) - 1;
|
||||||
|
|
||||||
|
if (timHandle->Instance == timer && timHandle->Init.Prescaler == prescaler1 && timHandle->Init.Period == period1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
timHandle->Instance = timer;
|
timHandle->Instance = timer;
|
||||||
timHandle->Init.Prescaler = lrintf((float)timerGetBaseClock(tch) / hz + 0.01f) - 1;
|
timHandle->Init.Prescaler = prescaler1;
|
||||||
timHandle->Init.Period = (period - 1) & 0xffff; // AKA TIMx_ARR
|
timHandle->Init.Period = period1; // AKA TIMx_ARR
|
||||||
timHandle->Init.RepetitionCounter = 0;
|
timHandle->Init.RepetitionCounter = 0;
|
||||||
timHandle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
timHandle->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||||
timHandle->Init.CounterMode = TIM_COUNTERMODE_UP;
|
timHandle->Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||||
|
@ -565,6 +568,15 @@ void impl_timerPWMStartDMA(TCH_t * tch)
|
||||||
|
|
||||||
void impl_timerPWMStopDMA(TCH_t * tch)
|
void impl_timerPWMStopDMA(TCH_t * tch)
|
||||||
{
|
{
|
||||||
(void)tch;
|
const uint32_t streamLL = lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)];
|
||||||
// FIXME
|
DMA_TypeDef *dmaBase = tch->dma->dma;
|
||||||
|
|
||||||
|
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
|
||||||
|
LL_TIM_DisableDMAReq_CCx(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex]);
|
||||||
|
LL_DMA_DisableStream(dmaBase, streamLL);
|
||||||
|
DMA_CLEAR_FLAG(tch->dma, DMA_IT_TCIF);
|
||||||
|
}
|
||||||
|
tch->dmaState = TCH_DMA_IDLE;
|
||||||
|
|
||||||
|
HAL_TIM_Base_Start(tch->timCtx->timHandle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -516,5 +516,6 @@ void impl_timerPWMStopDMA(TCH_t * tch)
|
||||||
{
|
{
|
||||||
DMA_Cmd(tch->dma->ref, DISABLE);
|
DMA_Cmd(tch->dma->ref, DISABLE);
|
||||||
TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE);
|
TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE);
|
||||||
|
tch->dmaState = TCH_DMA_IDLE;
|
||||||
TIM_Cmd(tch->timHw->tim, ENABLE);
|
TIM_Cmd(tch->timHw->tim, ENABLE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,6 +404,6 @@ void impl_timerPWMStopDMA(TCH_t * tch)
|
||||||
{
|
{
|
||||||
dma_channel_enable(tch->dma->ref,FALSE);
|
dma_channel_enable(tch->dma->ref,FALSE);
|
||||||
tmr_dma_request_enable(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], FALSE);
|
tmr_dma_request_enable(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], FALSE);
|
||||||
|
tch->dmaState = TCH_DMA_IDLE;
|
||||||
tmr_counter_enable(tch->timHw->tim, TRUE);
|
tmr_counter_enable(tch->timHw->tim, TRUE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
#include "common/time.h"
|
#include "common/time.h"
|
||||||
|
|
||||||
#define VTX_SETTINGS_NO_BAND 0 // used for custom frequency selection mode
|
|
||||||
#define VTX_SETTINGS_MIN_BAND 1
|
#define VTX_SETTINGS_MIN_BAND 1
|
||||||
#define VTX_SETTINGS_MAX_BAND 5
|
#define VTX_SETTINGS_MAX_BAND 5
|
||||||
#define VTX_SETTINGS_MIN_CHANNEL 1
|
#define VTX_SETTINGS_MIN_CHANNEL 1
|
||||||
|
@ -33,9 +32,6 @@
|
||||||
#define VTX_SETTINGS_DEFAULT_PITMODE_CHANNEL 1
|
#define VTX_SETTINGS_DEFAULT_PITMODE_CHANNEL 1
|
||||||
#define VTX_SETTINGS_DEFAULT_LOW_POWER_DISARM 0
|
#define VTX_SETTINGS_DEFAULT_LOW_POWER_DISARM 0
|
||||||
|
|
||||||
#define VTX_SETTINGS_MIN_FREQUENCY_MHZ 0 //min freq (in MHz) for 'vtx_freq' setting
|
|
||||||
#define VTX_SETTINGS_MAX_FREQUENCY_MHZ 5999 //max freq (in MHz) for 'vtx_freq' setting
|
|
||||||
|
|
||||||
#if defined(USE_VTX_SMARTAUDIO) || defined(USE_VTX_TRAMP) || defined(USE_VTX_MSP)
|
#if defined(USE_VTX_SMARTAUDIO) || defined(USE_VTX_TRAMP) || defined(USE_VTX_MSP)
|
||||||
|
|
||||||
#define VTX_SETTINGS_POWER_COUNT 5
|
#define VTX_SETTINGS_POWER_COUNT 5
|
||||||
|
|
|
@ -68,6 +68,7 @@ bool cliMode = false;
|
||||||
#include "drivers/time.h"
|
#include "drivers/time.h"
|
||||||
#include "drivers/usb_msc.h"
|
#include "drivers/usb_msc.h"
|
||||||
#include "drivers/vtx_common.h"
|
#include "drivers/vtx_common.h"
|
||||||
|
#include "drivers/light_ws2811strip.h"
|
||||||
|
|
||||||
#include "fc/fc_core.h"
|
#include "fc/fc_core.h"
|
||||||
#include "fc/cli.h"
|
#include "fc/cli.h"
|
||||||
|
@ -1688,6 +1689,20 @@ static void cliModeColor(char *cmdline)
|
||||||
cliPrintLinef("mode_color %u %u %u", modeIdx, funIdx, color);
|
cliPrintLinef("mode_color %u %u %u", modeIdx, funIdx, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cliLedPinPWM(char *cmdline)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (isEmpty(cmdline)) {
|
||||||
|
ledPinStopPWM();
|
||||||
|
cliPrintLine("PWM stopped");
|
||||||
|
} else {
|
||||||
|
i = fastA2I(cmdline);
|
||||||
|
ledPinStartPWM(i);
|
||||||
|
cliPrintLinef("PWM started: %d%%",i);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void cliDelay(char* cmdLine) {
|
static void cliDelay(char* cmdLine) {
|
||||||
|
@ -4035,6 +4050,7 @@ const clicmd_t cmdTable[] = {
|
||||||
CLI_COMMAND_DEF("help", NULL, NULL, cliHelp),
|
CLI_COMMAND_DEF("help", NULL, NULL, cliHelp),
|
||||||
#ifdef USE_LED_STRIP
|
#ifdef USE_LED_STRIP
|
||||||
CLI_COMMAND_DEF("led", "configure leds", NULL, cliLed),
|
CLI_COMMAND_DEF("led", "configure leds", NULL, cliLed),
|
||||||
|
CLI_COMMAND_DEF("ledpinpwm", "start/stop PWM on LED pin, 0..100 duty ratio", "[<value>]\r\n", cliLedPinPWM),
|
||||||
#endif
|
#endif
|
||||||
CLI_COMMAND_DEF("map", "configure rc channel order", "[<map>]", cliMap),
|
CLI_COMMAND_DEF("map", "configure rc channel order", "[<map>]", cliMap),
|
||||||
CLI_COMMAND_DEF("memory", "view memory usage", NULL, cliMemory),
|
CLI_COMMAND_DEF("memory", "view memory usage", NULL, cliMemory),
|
||||||
|
|
127
src/main/fc/fc_core.c
Executable file → Normal file
|
@ -548,7 +548,7 @@ void tryArm(void)
|
||||||
if (STATE(MULTIROTOR) && turtleIsActive && !FLIGHT_MODE(TURTLE_MODE) && emergencyArmingCanOverrideArmingDisabled() && isMotorProtocolDshot()) {
|
if (STATE(MULTIROTOR) && turtleIsActive && !FLIGHT_MODE(TURTLE_MODE) && emergencyArmingCanOverrideArmingDisabled() && isMotorProtocolDshot()) {
|
||||||
sendDShotCommand(DSHOT_CMD_SPIN_DIRECTION_REVERSED);
|
sendDShotCommand(DSHOT_CMD_SPIN_DIRECTION_REVERSED);
|
||||||
ENABLE_ARMING_FLAG(ARMED);
|
ENABLE_ARMING_FLAG(ARMED);
|
||||||
enableFlightMode(TURTLE_MODE);
|
ENABLE_FLIGHT_MODE(TURTLE_MODE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -678,28 +678,21 @@ void processRx(timeUs_t currentTimeUs)
|
||||||
// Angle mode forced on briefly after emergency inflight rearm to help stabilise attitude (currently limited to MR)
|
// Angle mode forced on briefly after emergency inflight rearm to help stabilise attitude (currently limited to MR)
|
||||||
bool emergRearmAngleEnforce = STATE(MULTIROTOR) && emergRearmStabiliseTimeout > US2MS(currentTimeUs);
|
bool emergRearmAngleEnforce = STATE(MULTIROTOR) && emergRearmStabiliseTimeout > US2MS(currentTimeUs);
|
||||||
bool autoEnableAngle = failsafeRequiresAngleMode() || navigationRequiresAngleMode() || emergRearmAngleEnforce;
|
bool autoEnableAngle = failsafeRequiresAngleMode() || navigationRequiresAngleMode() || emergRearmAngleEnforce;
|
||||||
bool canUseHorizonMode = true;
|
|
||||||
|
|
||||||
if (sensors(SENSOR_ACC) && (IS_RC_MODE_ACTIVE(BOXANGLE) || autoEnableAngle)) {
|
/* Disable stabilised modes initially, will be enabled as required with priority ANGLE > HORIZON > ANGLEHOLD
|
||||||
// bumpless transfer to Level mode
|
* MANUAL mode has priority over these modes except when ANGLE auto enabled */
|
||||||
canUseHorizonMode = false;
|
DISABLE_FLIGHT_MODE(ANGLE_MODE);
|
||||||
|
DISABLE_FLIGHT_MODE(HORIZON_MODE);
|
||||||
|
DISABLE_FLIGHT_MODE(ANGLEHOLD_MODE);
|
||||||
|
|
||||||
if (!FLIGHT_MODE(ANGLE_MODE)) {
|
if (sensors(SENSOR_ACC) && (!FLIGHT_MODE(MANUAL_MODE) || autoEnableAngle)) {
|
||||||
|
if (IS_RC_MODE_ACTIVE(BOXANGLE) || autoEnableAngle) {
|
||||||
ENABLE_FLIGHT_MODE(ANGLE_MODE);
|
ENABLE_FLIGHT_MODE(ANGLE_MODE);
|
||||||
}
|
} else if (IS_RC_MODE_ACTIVE(BOXHORIZON)) {
|
||||||
} else {
|
|
||||||
DISABLE_FLIGHT_MODE(ANGLE_MODE); // failsafe support
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_RC_MODE_ACTIVE(BOXHORIZON) && canUseHorizonMode) {
|
|
||||||
|
|
||||||
DISABLE_FLIGHT_MODE(ANGLE_MODE);
|
|
||||||
|
|
||||||
if (!FLIGHT_MODE(HORIZON_MODE)) {
|
|
||||||
ENABLE_FLIGHT_MODE(HORIZON_MODE);
|
ENABLE_FLIGHT_MODE(HORIZON_MODE);
|
||||||
|
} else if (STATE(AIRPLANE) && IS_RC_MODE_ACTIVE(BOXANGLEHOLD)) {
|
||||||
|
ENABLE_FLIGHT_MODE(ANGLEHOLD_MODE);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
DISABLE_FLIGHT_MODE(HORIZON_MODE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE)) {
|
if (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE)) {
|
||||||
|
@ -710,18 +703,14 @@ void processRx(timeUs_t currentTimeUs)
|
||||||
|
|
||||||
/* Flaperon mode */
|
/* Flaperon mode */
|
||||||
if (IS_RC_MODE_ACTIVE(BOXFLAPERON) && STATE(FLAPERON_AVAILABLE)) {
|
if (IS_RC_MODE_ACTIVE(BOXFLAPERON) && STATE(FLAPERON_AVAILABLE)) {
|
||||||
if (!FLIGHT_MODE(FLAPERON)) {
|
ENABLE_FLIGHT_MODE(FLAPERON);
|
||||||
ENABLE_FLIGHT_MODE(FLAPERON);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DISABLE_FLIGHT_MODE(FLAPERON);
|
DISABLE_FLIGHT_MODE(FLAPERON);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turn assistant mode */
|
/* Turn assistant mode */
|
||||||
if (IS_RC_MODE_ACTIVE(BOXTURNASSIST)) {
|
if (IS_RC_MODE_ACTIVE(BOXTURNASSIST)) {
|
||||||
if (!FLIGHT_MODE(TURN_ASSISTANT)) {
|
ENABLE_FLIGHT_MODE(TURN_ASSISTANT);
|
||||||
ENABLE_FLIGHT_MODE(TURN_ASSISTANT);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DISABLE_FLIGHT_MODE(TURN_ASSISTANT);
|
DISABLE_FLIGHT_MODE(TURN_ASSISTANT);
|
||||||
}
|
}
|
||||||
|
@ -740,9 +729,7 @@ void processRx(timeUs_t currentTimeUs)
|
||||||
#if defined(USE_MAG)
|
#if defined(USE_MAG)
|
||||||
if (sensors(SENSOR_ACC) || sensors(SENSOR_MAG)) {
|
if (sensors(SENSOR_ACC) || sensors(SENSOR_MAG)) {
|
||||||
if (IS_RC_MODE_ACTIVE(BOXHEADFREE) && STATE(MULTIROTOR)) {
|
if (IS_RC_MODE_ACTIVE(BOXHEADFREE) && STATE(MULTIROTOR)) {
|
||||||
if (!FLIGHT_MODE(HEADFREE_MODE)) {
|
ENABLE_FLIGHT_MODE(HEADFREE_MODE);
|
||||||
ENABLE_FLIGHT_MODE(HEADFREE_MODE);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DISABLE_FLIGHT_MODE(HEADFREE_MODE);
|
DISABLE_FLIGHT_MODE(HEADFREE_MODE);
|
||||||
}
|
}
|
||||||
|
@ -760,7 +747,7 @@ void processRx(timeUs_t currentTimeUs)
|
||||||
} else {
|
} else {
|
||||||
DISABLE_FLIGHT_MODE(MANUAL_MODE);
|
DISABLE_FLIGHT_MODE(MANUAL_MODE);
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
DISABLE_FLIGHT_MODE(MANUAL_MODE);
|
DISABLE_FLIGHT_MODE(MANUAL_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,52 +768,52 @@ void processRx(timeUs_t currentTimeUs)
|
||||||
}
|
}
|
||||||
else if (rcControlsConfig()->airmodeHandlingType == STICK_CENTER) {
|
else if (rcControlsConfig()->airmodeHandlingType == STICK_CENTER) {
|
||||||
if (throttleIsLow) {
|
if (throttleIsLow) {
|
||||||
if (STATE(AIRMODE_ACTIVE)) {
|
if (STATE(AIRMODE_ACTIVE)) {
|
||||||
if ((rollPitchStatus == CENTERED) || (ifMotorstopFeatureEnabled() && !STATE(FIXED_WING_LEGACY))) {
|
if ((rollPitchStatus == CENTERED) || (ifMotorstopFeatureEnabled() && !STATE(FIXED_WING_LEGACY))) {
|
||||||
ENABLE_STATE(ANTI_WINDUP);
|
ENABLE_STATE(ANTI_WINDUP);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
pidResetErrorAccumulators();
|
pidResetErrorAccumulators();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (rcControlsConfig()->airmodeHandlingType == STICK_CENTER_ONCE) {
|
else if (rcControlsConfig()->airmodeHandlingType == STICK_CENTER_ONCE) {
|
||||||
if (throttleIsLow) {
|
if (throttleIsLow) {
|
||||||
if (STATE(AIRMODE_ACTIVE)) {
|
if (STATE(AIRMODE_ACTIVE)) {
|
||||||
if ((rollPitchStatus == CENTERED) && !STATE(ANTI_WINDUP_DEACTIVATED)) {
|
if ((rollPitchStatus == CENTERED) && !STATE(ANTI_WINDUP_DEACTIVATED)) {
|
||||||
ENABLE_STATE(ANTI_WINDUP);
|
ENABLE_STATE(ANTI_WINDUP);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
pidResetErrorAccumulators();
|
pidResetErrorAccumulators();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
if (rollPitchStatus != CENTERED) {
|
if (rollPitchStatus != CENTERED) {
|
||||||
ENABLE_STATE(ANTI_WINDUP_DEACTIVATED);
|
ENABLE_STATE(ANTI_WINDUP_DEACTIVATED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (rcControlsConfig()->airmodeHandlingType == THROTTLE_THRESHOLD) {
|
else if (rcControlsConfig()->airmodeHandlingType == THROTTLE_THRESHOLD) {
|
||||||
DISABLE_STATE(ANTI_WINDUP);
|
DISABLE_STATE(ANTI_WINDUP);
|
||||||
//This case applies only to MR when Airmode management is throttle threshold activated
|
//This case applies only to MR when Airmode management is throttle threshold activated
|
||||||
if (throttleIsLow && !STATE(AIRMODE_ACTIVE)) {
|
if (throttleIsLow && !STATE(AIRMODE_ACTIVE)) {
|
||||||
pidResetErrorAccumulators();
|
pidResetErrorAccumulators();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
if (currentMixerConfig.platformType == PLATFORM_AIRPLANE) {
|
if (currentMixerConfig.platformType == PLATFORM_AIRPLANE) {
|
||||||
DISABLE_FLIGHT_MODE(HEADFREE_MODE);
|
DISABLE_FLIGHT_MODE(HEADFREE_MODE);
|
||||||
|
@ -849,6 +836,8 @@ void processRx(timeUs_t currentTimeUs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// Sound a beeper if the flight mode state has changed
|
||||||
|
updateFlightModeChangeBeeper();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function for loop trigger
|
// Function for loop trigger
|
||||||
|
|
|
@ -462,9 +462,9 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
|
||||||
sbufWriteU16(dst, packSensorStatus());
|
sbufWriteU16(dst, packSensorStatus());
|
||||||
sbufWriteU16(dst, averageSystemLoadPercent);
|
sbufWriteU16(dst, averageSystemLoadPercent);
|
||||||
sbufWriteU8(dst, (getConfigBatteryProfile() << 4) | getConfigProfile());
|
sbufWriteU8(dst, (getConfigBatteryProfile() << 4) | getConfigProfile());
|
||||||
sbufWriteU8(dst, getConfigMixerProfile());
|
|
||||||
sbufWriteU32(dst, armingFlags);
|
sbufWriteU32(dst, armingFlags);
|
||||||
sbufWriteData(dst, &mspBoxModeFlags, sizeof(mspBoxModeFlags));
|
sbufWriteData(dst, &mspBoxModeFlags, sizeof(mspBoxModeFlags));
|
||||||
|
sbufWriteU8(dst, getConfigMixerProfile());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -478,7 +478,7 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
#ifdef USE_MAG
|
#ifdef USE_MAG
|
||||||
sbufWriteU16(dst, mag.magADC[i]);
|
sbufWriteU16(dst, lrintf(mag.magADC[i]));
|
||||||
#else
|
#else
|
||||||
sbufWriteU16(dst, 0);
|
sbufWriteU16(dst, 0);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1083,7 +1083,7 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
|
||||||
legacyLedConfig |= ledConfig->led_function << shiftCount;
|
legacyLedConfig |= ledConfig->led_function << shiftCount;
|
||||||
shiftCount += 4;
|
shiftCount += 4;
|
||||||
legacyLedConfig |= (ledConfig->led_overlay & 0x3F) << (shiftCount);
|
legacyLedConfig |= (ledConfig->led_overlay & 0x3F) << (shiftCount);
|
||||||
shiftCount += 6;
|
shiftCount += 6;
|
||||||
legacyLedConfig |= (ledConfig->led_color) << (shiftCount);
|
legacyLedConfig |= (ledConfig->led_color) << (shiftCount);
|
||||||
shiftCount += 4;
|
shiftCount += 4;
|
||||||
legacyLedConfig |= (ledConfig->led_direction) << (shiftCount);
|
legacyLedConfig |= (ledConfig->led_direction) << (shiftCount);
|
||||||
|
@ -2585,6 +2585,29 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
|
||||||
if (sbufBytesRemaining(src) > 0) {
|
if (sbufBytesRemaining(src) > 0) {
|
||||||
vtxSettingsConfigMutable()->lowPowerDisarm = sbufReadU8(src);
|
vtxSettingsConfigMutable()->lowPowerDisarm = sbufReadU8(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////
|
||||||
|
// this code is taken from BF, it's hack for HDZERO VTX MSP frame
|
||||||
|
// API version 1.42 - this parameter kept separate since clients may already be supplying
|
||||||
|
if (sbufBytesRemaining(src) >= 2) {
|
||||||
|
sbufReadU16(src); //skip pitModeFreq
|
||||||
|
}
|
||||||
|
|
||||||
|
// API version 1.42 - extensions for non-encoded versions of the band, channel or frequency
|
||||||
|
if (sbufBytesRemaining(src) >= 4) {
|
||||||
|
uint8_t newBand = sbufReadU8(src);
|
||||||
|
const uint8_t newChannel = sbufReadU8(src);
|
||||||
|
vtxSettingsConfigMutable()->band = newBand;
|
||||||
|
vtxSettingsConfigMutable()->channel = newChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if (sbufBytesRemaining(src) >= 4) {
|
||||||
|
sbufRead8(src); // freq_l
|
||||||
|
sbufRead8(src); // freq_h
|
||||||
|
sbufRead8(src); // band count
|
||||||
|
sbufRead8(src); // channel count
|
||||||
|
}*/
|
||||||
|
// //////////////////////////////////////////////////////////
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2831,7 +2854,7 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
|
||||||
ledConfig->led_position = legacyConfig & 0xFF;
|
ledConfig->led_position = legacyConfig & 0xFF;
|
||||||
ledConfig->led_function = (legacyConfig >> 8) & 0xF;
|
ledConfig->led_function = (legacyConfig >> 8) & 0xF;
|
||||||
ledConfig->led_overlay = (legacyConfig >> 12) & 0x3F;
|
ledConfig->led_overlay = (legacyConfig >> 12) & 0x3F;
|
||||||
ledConfig->led_color = (legacyConfig >> 18) & 0xF;
|
ledConfig->led_color = (legacyConfig >> 18) & 0xF;
|
||||||
ledConfig->led_direction = (legacyConfig >> 22) & 0x3F;
|
ledConfig->led_direction = (legacyConfig >> 22) & 0x3F;
|
||||||
ledConfig->led_params = (legacyConfig >> 28) & 0xF;
|
ledConfig->led_params = (legacyConfig >> 28) & 0xF;
|
||||||
|
|
||||||
|
@ -3400,7 +3423,7 @@ static bool mspParameterGroupsCommand(sbuf_t *dst, sbuf_t *src)
|
||||||
bool isOSDTypeSupportedBySimulator(void)
|
bool isOSDTypeSupportedBySimulator(void)
|
||||||
{
|
{
|
||||||
#ifdef USE_OSD
|
#ifdef USE_OSD
|
||||||
displayPort_t *osdDisplayPort = osdGetDisplayPort();
|
displayPort_t *osdDisplayPort = osdGetDisplayPort();
|
||||||
return (!!osdDisplayPort && !!osdDisplayPort->vTable->readChar);
|
return (!!osdDisplayPort && !!osdDisplayPort->vTable->readChar);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
@ -3623,132 +3646,130 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu
|
||||||
|
|
||||||
#ifdef USE_SIMULATOR
|
#ifdef USE_SIMULATOR
|
||||||
case MSP_SIMULATOR:
|
case MSP_SIMULATOR:
|
||||||
tmp_u8 = sbufReadU8(src); // Get the Simulator MSP version
|
tmp_u8 = sbufReadU8(src); // Get the Simulator MSP version
|
||||||
|
|
||||||
// Check the MSP version of simulator
|
// Check the MSP version of simulator
|
||||||
if (tmp_u8 != SIMULATOR_MSP_VERSION) {
|
if (tmp_u8 != SIMULATOR_MSP_VERSION) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
simulatorData.flags = sbufReadU8(src);
|
simulatorData.flags = sbufReadU8(src);
|
||||||
|
|
||||||
if (!SIMULATOR_HAS_OPTION(HITL_ENABLE)) {
|
if (!SIMULATOR_HAS_OPTION(HITL_ENABLE)) {
|
||||||
|
|
||||||
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once
|
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once
|
||||||
DISABLE_ARMING_FLAG(SIMULATOR_MODE_HITL);
|
DISABLE_ARMING_FLAG(SIMULATOR_MODE_HITL);
|
||||||
|
|
||||||
#ifdef USE_BARO
|
#ifdef USE_BARO
|
||||||
if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) {
|
if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) {
|
||||||
baroStartCalibration();
|
baroStartCalibration();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MAG
|
#ifdef USE_MAG
|
||||||
DISABLE_STATE(COMPASS_CALIBRATED);
|
DISABLE_STATE(COMPASS_CALIBRATED);
|
||||||
compassInit();
|
compassInit();
|
||||||
#endif
|
#endif
|
||||||
simulatorData.flags = HITL_RESET_FLAGS;
|
simulatorData.flags = HITL_RESET_FLAGS;
|
||||||
// Review: Many states were affected. Reboot?
|
// Review: Many states were affected. Reboot?
|
||||||
|
|
||||||
disarm(DISARM_SWITCH); // Disarm to prevent motor output!!!
|
disarm(DISARM_SWITCH); // Disarm to prevent motor output!!!
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once
|
if (!ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once
|
||||||
#ifdef USE_BARO
|
#ifdef USE_BARO
|
||||||
if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) {
|
if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) {
|
||||||
sensorsSet(SENSOR_BARO);
|
sensorsSet(SENSOR_BARO);
|
||||||
setTaskEnabled(TASK_BARO, true);
|
setTaskEnabled(TASK_BARO, true);
|
||||||
DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE);
|
DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE);
|
||||||
baroStartCalibration();
|
baroStartCalibration();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MAG
|
#ifdef USE_MAG
|
||||||
if (compassConfig()->mag_hardware != MAG_NONE) {
|
if (compassConfig()->mag_hardware != MAG_NONE) {
|
||||||
sensorsSet(SENSOR_MAG);
|
sensorsSet(SENSOR_MAG);
|
||||||
ENABLE_STATE(COMPASS_CALIBRATED);
|
ENABLE_STATE(COMPASS_CALIBRATED);
|
||||||
DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE);
|
DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE);
|
||||||
mag.magADC[X] = 800;
|
mag.magADC[X] = 800;
|
||||||
mag.magADC[Y] = 0;
|
mag.magADC[Y] = 0;
|
||||||
mag.magADC[Z] = 0;
|
mag.magADC[Z] = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
ENABLE_ARMING_FLAG(SIMULATOR_MODE_HITL);
|
ENABLE_ARMING_FLAG(SIMULATOR_MODE_HITL);
|
||||||
ENABLE_STATE(ACCELEROMETER_CALIBRATED);
|
ENABLE_STATE(ACCELEROMETER_CALIBRATED);
|
||||||
LOG_DEBUG(SYSTEM, "Simulator enabled");
|
LOG_DEBUG(SYSTEM, "Simulator enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataSize >= 14) {
|
if (dataSize >= 14) {
|
||||||
|
|
||||||
if (feature(FEATURE_GPS) && SIMULATOR_HAS_OPTION(HITL_HAS_NEW_GPS_DATA)) {
|
if (feature(FEATURE_GPS) && SIMULATOR_HAS_OPTION(HITL_HAS_NEW_GPS_DATA)) {
|
||||||
gpsSol.fixType = sbufReadU8(src);
|
gpsSolDRV.fixType = sbufReadU8(src);
|
||||||
gpsSol.hdop = gpsSol.fixType == GPS_NO_FIX ? 9999 : 100;
|
gpsSolDRV.hdop = gpsSolDRV.fixType == GPS_NO_FIX ? 9999 : 100;
|
||||||
gpsSol.flags.hasNewData = true;
|
gpsSolDRV.numSat = sbufReadU8(src);
|
||||||
gpsSol.numSat = sbufReadU8(src);
|
|
||||||
|
|
||||||
if (gpsSol.fixType != GPS_NO_FIX) {
|
if (gpsSolDRV.fixType != GPS_NO_FIX) {
|
||||||
gpsSol.flags.validVelNE = true;
|
gpsSolDRV.flags.validVelNE = true;
|
||||||
gpsSol.flags.validVelD = true;
|
gpsSolDRV.flags.validVelD = true;
|
||||||
gpsSol.flags.validEPE = true;
|
gpsSolDRV.flags.validEPE = true;
|
||||||
gpsSol.flags.validTime = false;
|
gpsSolDRV.flags.validTime = false;
|
||||||
|
|
||||||
gpsSol.llh.lat = sbufReadU32(src);
|
gpsSolDRV.llh.lat = sbufReadU32(src);
|
||||||
gpsSol.llh.lon = sbufReadU32(src);
|
gpsSolDRV.llh.lon = sbufReadU32(src);
|
||||||
gpsSol.llh.alt = sbufReadU32(src);
|
gpsSolDRV.llh.alt = sbufReadU32(src);
|
||||||
gpsSol.groundSpeed = (int16_t)sbufReadU16(src);
|
gpsSolDRV.groundSpeed = (int16_t)sbufReadU16(src);
|
||||||
gpsSol.groundCourse = (int16_t)sbufReadU16(src);
|
gpsSolDRV.groundCourse = (int16_t)sbufReadU16(src);
|
||||||
|
|
||||||
gpsSol.velNED[X] = (int16_t)sbufReadU16(src);
|
gpsSolDRV.velNED[X] = (int16_t)sbufReadU16(src);
|
||||||
gpsSol.velNED[Y] = (int16_t)sbufReadU16(src);
|
gpsSolDRV.velNED[Y] = (int16_t)sbufReadU16(src);
|
||||||
gpsSol.velNED[Z] = (int16_t)sbufReadU16(src);
|
gpsSolDRV.velNED[Z] = (int16_t)sbufReadU16(src);
|
||||||
|
|
||||||
gpsSol.eph = 100;
|
gpsSolDRV.eph = 100;
|
||||||
gpsSol.epv = 100;
|
gpsSolDRV.epv = 100;
|
||||||
|
} else {
|
||||||
ENABLE_STATE(GPS_FIX);
|
sbufAdvance(src, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
|
||||||
} else {
|
}
|
||||||
sbufAdvance(src, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
|
|
||||||
}
|
|
||||||
// Feed data to navigation
|
// Feed data to navigation
|
||||||
gpsProcessNewSolutionData();
|
gpsProcessNewDriverData();
|
||||||
} else {
|
gpsProcessNewSolutionData(false);
|
||||||
sbufAdvance(src, sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
|
} else {
|
||||||
}
|
sbufAdvance(src, sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
|
||||||
|
}
|
||||||
if (!SIMULATOR_HAS_OPTION(HITL_USE_IMU)) {
|
|
||||||
attitude.values.roll = (int16_t)sbufReadU16(src);
|
|
||||||
attitude.values.pitch = (int16_t)sbufReadU16(src);
|
|
||||||
attitude.values.yaw = (int16_t)sbufReadU16(src);
|
|
||||||
} else {
|
|
||||||
sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!SIMULATOR_HAS_OPTION(HITL_USE_IMU)) {
|
||||||
|
attitude.values.roll = (int16_t)sbufReadU16(src);
|
||||||
|
attitude.values.pitch = (int16_t)sbufReadU16(src);
|
||||||
|
attitude.values.yaw = (int16_t)sbufReadU16(src);
|
||||||
|
} else {
|
||||||
|
sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the acceleration in 1G units
|
// Get the acceleration in 1G units
|
||||||
acc.accADCf[X] = ((int16_t)sbufReadU16(src)) / 1000.0f;
|
acc.accADCf[X] = ((int16_t)sbufReadU16(src)) / 1000.0f;
|
||||||
acc.accADCf[Y] = ((int16_t)sbufReadU16(src)) / 1000.0f;
|
acc.accADCf[Y] = ((int16_t)sbufReadU16(src)) / 1000.0f;
|
||||||
acc.accADCf[Z] = ((int16_t)sbufReadU16(src)) / 1000.0f;
|
acc.accADCf[Z] = ((int16_t)sbufReadU16(src)) / 1000.0f;
|
||||||
acc.accVibeSq[X] = 0.0f;
|
acc.accVibeSq[X] = 0.0f;
|
||||||
acc.accVibeSq[Y] = 0.0f;
|
acc.accVibeSq[Y] = 0.0f;
|
||||||
acc.accVibeSq[Z] = 0.0f;
|
acc.accVibeSq[Z] = 0.0f;
|
||||||
|
|
||||||
// Get the angular velocity in DPS
|
// Get the angular velocity in DPS
|
||||||
gyro.gyroADCf[X] = ((int16_t)sbufReadU16(src)) / 16.0f;
|
gyro.gyroADCf[X] = ((int16_t)sbufReadU16(src)) / 16.0f;
|
||||||
gyro.gyroADCf[Y] = ((int16_t)sbufReadU16(src)) / 16.0f;
|
gyro.gyroADCf[Y] = ((int16_t)sbufReadU16(src)) / 16.0f;
|
||||||
gyro.gyroADCf[Z] = ((int16_t)sbufReadU16(src)) / 16.0f;
|
gyro.gyroADCf[Z] = ((int16_t)sbufReadU16(src)) / 16.0f;
|
||||||
|
|
||||||
if (sensors(SENSOR_BARO)) {
|
if (sensors(SENSOR_BARO)) {
|
||||||
baro.baroPressure = (int32_t)sbufReadU32(src);
|
baro.baroPressure = (int32_t)sbufReadU32(src);
|
||||||
baro.baroTemperature = DEGREES_TO_CENTIDEGREES(SIMULATOR_BARO_TEMP);
|
baro.baroTemperature = DEGREES_TO_CENTIDEGREES(SIMULATOR_BARO_TEMP);
|
||||||
} else {
|
} else {
|
||||||
sbufAdvance(src, sizeof(uint32_t));
|
sbufAdvance(src, sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sensors(SENSOR_MAG)) {
|
if (sensors(SENSOR_MAG)) {
|
||||||
mag.magADC[X] = ((int16_t)sbufReadU16(src)) / 20; // 16000 / 20 = 800uT
|
mag.magADC[X] = ((int16_t)sbufReadU16(src)) / 20; // 16000 / 20 = 800uT
|
||||||
mag.magADC[Y] = ((int16_t)sbufReadU16(src)) / 20; //note that mag failure is simulated by setting all readings to zero
|
mag.magADC[Y] = ((int16_t)sbufReadU16(src)) / 20; //note that mag failure is simulated by setting all readings to zero
|
||||||
mag.magADC[Z] = ((int16_t)sbufReadU16(src)) / 20;
|
mag.magADC[Z] = ((int16_t)sbufReadU16(src)) / 20;
|
||||||
} else {
|
} else {
|
||||||
sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT);
|
sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SIMULATOR_HAS_OPTION(HITL_EXT_BATTERY_VOLTAGE)) {
|
if (SIMULATOR_HAS_OPTION(HITL_EXT_BATTERY_VOLTAGE)) {
|
||||||
simulatorData.vbat = sbufReadU8(src);
|
simulatorData.vbat = sbufReadU8(src);
|
||||||
|
@ -3758,44 +3779,44 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu
|
||||||
|
|
||||||
if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED)) {
|
if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED)) {
|
||||||
simulatorData.airSpeed = sbufReadU16(src);
|
simulatorData.airSpeed = sbufReadU16(src);
|
||||||
} else {
|
} else {
|
||||||
if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) {
|
if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) {
|
||||||
sbufReadU16(src);
|
sbufReadU16(src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) {
|
if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) {
|
||||||
simulatorData.flags |= ((uint16_t)sbufReadU8(src)) << 8;
|
simulatorData.flags |= ((uint16_t)sbufReadU8(src)) << 8;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DISABLE_STATE(GPS_FIX);
|
DISABLE_STATE(GPS_FIX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_ROLL]);
|
sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_ROLL]);
|
||||||
sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_PITCH]);
|
sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_PITCH]);
|
||||||
sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_YAW]);
|
sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_YAW]);
|
||||||
sbufWriteU16(dst, (uint16_t)(ARMING_FLAG(ARMED) ? simulatorData.input[INPUT_STABILIZED_THROTTLE] : -500));
|
sbufWriteU16(dst, (uint16_t)(ARMING_FLAG(ARMED) ? simulatorData.input[INPUT_STABILIZED_THROTTLE] : -500));
|
||||||
|
|
||||||
simulatorData.debugIndex++;
|
simulatorData.debugIndex++;
|
||||||
if (simulatorData.debugIndex == 8) {
|
if (simulatorData.debugIndex == 8) {
|
||||||
simulatorData.debugIndex = 0;
|
simulatorData.debugIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_u8 = simulatorData.debugIndex |
|
tmp_u8 = simulatorData.debugIndex |
|
||||||
((mixerConfig()->platformType == PLATFORM_AIRPLANE) ? 128 : 0) |
|
((mixerConfig()->platformType == PLATFORM_AIRPLANE) ? 128 : 0) |
|
||||||
(ARMING_FLAG(ARMED) ? 64 : 0) |
|
(ARMING_FLAG(ARMED) ? 64 : 0) |
|
||||||
(!feature(FEATURE_OSD) ? 32: 0) |
|
(!feature(FEATURE_OSD) ? 32: 0) |
|
||||||
(!isOSDTypeSupportedBySimulator() ? 16 : 0);
|
(!isOSDTypeSupportedBySimulator() ? 16 : 0);
|
||||||
|
|
||||||
sbufWriteU8(dst, tmp_u8);
|
sbufWriteU8(dst, tmp_u8);
|
||||||
sbufWriteU32(dst, debug[simulatorData.debugIndex]);
|
sbufWriteU32(dst, debug[simulatorData.debugIndex]);
|
||||||
|
|
||||||
sbufWriteU16(dst, attitude.values.roll);
|
sbufWriteU16(dst, attitude.values.roll);
|
||||||
sbufWriteU16(dst, attitude.values.pitch);
|
sbufWriteU16(dst, attitude.values.pitch);
|
||||||
sbufWriteU16(dst, attitude.values.yaw);
|
sbufWriteU16(dst, attitude.values.yaw);
|
||||||
|
|
||||||
mspWriteSimulatorOSD(dst);
|
mspWriteSimulatorOSD(dst);
|
||||||
|
|
||||||
*ret = MSP_RESULT_ACK;
|
*ret = MSP_RESULT_ACK;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -102,6 +102,7 @@ static const box_t boxes[CHECKBOX_ITEM_COUNT + 1] = {
|
||||||
{ .boxId = BOXMULTIFUNCTION, .boxName = "MULTI FUNCTION", .permanentId = 61 },
|
{ .boxId = BOXMULTIFUNCTION, .boxName = "MULTI FUNCTION", .permanentId = 61 },
|
||||||
{ .boxId = BOXMIXERPROFILE, .boxName = "MIXER PROFILE 2", .permanentId = 62 },
|
{ .boxId = BOXMIXERPROFILE, .boxName = "MIXER PROFILE 2", .permanentId = 62 },
|
||||||
{ .boxId = BOXMIXERTRANSITION, .boxName = "MIXER TRANSITION", .permanentId = 63 },
|
{ .boxId = BOXMIXERTRANSITION, .boxName = "MIXER TRANSITION", .permanentId = 63 },
|
||||||
|
{ .boxId = BOXANGLEHOLD, .boxName = "ANGLE HOLD", .permanentId = 64 },
|
||||||
{ .boxId = CHECKBOX_ITEM_COUNT, .boxName = NULL, .permanentId = 0xFF }
|
{ .boxId = CHECKBOX_ITEM_COUNT, .boxName = NULL, .permanentId = 0xFF }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -259,7 +260,7 @@ void initActiveBoxIds(void)
|
||||||
ADD_ACTIVE_BOX(BOXNAVALTHOLD);
|
ADD_ACTIVE_BOX(BOXNAVALTHOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (STATE(AIRPLANE) || STATE(ROVER) || STATE(BOAT) ||
|
if (STATE(AIRPLANE) || STATE(ROVER) || STATE(BOAT) ||
|
||||||
platformTypeConfigured(PLATFORM_AIRPLANE) || platformTypeConfigured(PLATFORM_ROVER) || platformTypeConfigured(PLATFORM_BOAT)) {
|
platformTypeConfigured(PLATFORM_AIRPLANE) || platformTypeConfigured(PLATFORM_ROVER) || platformTypeConfigured(PLATFORM_BOAT)) {
|
||||||
ADD_ACTIVE_BOX(BOXMANUAL);
|
ADD_ACTIVE_BOX(BOXMANUAL);
|
||||||
}
|
}
|
||||||
|
@ -279,6 +280,9 @@ void initActiveBoxIds(void)
|
||||||
if (sensors(SENSOR_BARO)) {
|
if (sensors(SENSOR_BARO)) {
|
||||||
ADD_ACTIVE_BOX(BOXAUTOLEVEL);
|
ADD_ACTIVE_BOX(BOXAUTOLEVEL);
|
||||||
}
|
}
|
||||||
|
if (sensors(SENSOR_ACC)) {
|
||||||
|
ADD_ACTIVE_BOX(BOXANGLEHOLD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -432,6 +436,7 @@ void packBoxModeFlags(boxBitmask_t * mspBoxModeFlags)
|
||||||
CHECK_ACTIVE_BOX(IS_ENABLED(currentMixerProfileIndex), BOXMIXERPROFILE);
|
CHECK_ACTIVE_BOX(IS_ENABLED(currentMixerProfileIndex), BOXMIXERPROFILE);
|
||||||
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXMIXERTRANSITION)), BOXMIXERTRANSITION);
|
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXMIXERTRANSITION)), BOXMIXERTRANSITION);
|
||||||
#endif
|
#endif
|
||||||
|
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXANGLEHOLD)), BOXANGLEHOLD);
|
||||||
memset(mspBoxModeFlags, 0, sizeof(boxBitmask_t));
|
memset(mspBoxModeFlags, 0, sizeof(boxBitmask_t));
|
||||||
for (uint32_t i = 0; i < activeBoxIdCount; i++) {
|
for (uint32_t i = 0; i < activeBoxIdCount; i++) {
|
||||||
if (activeBoxes[activeBoxIds[i]]) {
|
if (activeBoxes[activeBoxIds[i]]) {
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include "io/osd.h"
|
#include "io/osd.h"
|
||||||
#include "io/serial.h"
|
#include "io/serial.h"
|
||||||
#include "io/rcdevice_cam.h"
|
#include "io/rcdevice_cam.h"
|
||||||
|
#include "io/osd_joystick.h"
|
||||||
#include "io/smartport_master.h"
|
#include "io/smartport_master.h"
|
||||||
#include "io/vtx.h"
|
#include "io/vtx.h"
|
||||||
#include "io/vtx_msp.h"
|
#include "io/vtx_msp.h"
|
||||||
|
@ -393,8 +394,12 @@ void fcTasksInit(void)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_RCDEVICE
|
#ifdef USE_RCDEVICE
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
setTaskEnabled(TASK_RCDEVICE, rcdeviceIsEnabled() || osdJoystickEnabled());
|
||||||
|
#else
|
||||||
setTaskEnabled(TASK_RCDEVICE, rcdeviceIsEnabled());
|
setTaskEnabled(TASK_RCDEVICE, rcdeviceIsEnabled());
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#ifdef USE_PROGRAMMING_FRAMEWORK
|
#ifdef USE_PROGRAMMING_FRAMEWORK
|
||||||
setTaskEnabled(TASK_PROGRAMMING_FRAMEWORK, true);
|
setTaskEnabled(TASK_PROGRAMMING_FRAMEWORK, true);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef USE_MULTI_FUNCTIONS
|
#ifdef USE_MULTI_FUNCTIONS
|
||||||
|
|
||||||
extern uint8_t multiFunctionFlags;
|
extern uint8_t multiFunctionFlags;
|
||||||
|
|
|
@ -119,8 +119,7 @@ throttleStatus_e FAST_CODE NOINLINE calculateThrottleStatus(throttleStatusType_e
|
||||||
value = rcCommand[THROTTLE];
|
value = rcCommand[THROTTLE];
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint16_t mid_throttle_deadband = rcControlsConfig()->mid_throttle_deadband;
|
bool midThrottle = value > (reversibleMotorsConfig()->deadband_low) && value < (reversibleMotorsConfig()->deadband_high);
|
||||||
bool midThrottle = value > (PWM_RANGE_MIDDLE - mid_throttle_deadband) && value < (PWM_RANGE_MIDDLE + mid_throttle_deadband);
|
|
||||||
if ((feature(FEATURE_REVERSIBLE_MOTORS) && midThrottle) || (!feature(FEATURE_REVERSIBLE_MOTORS) && (value < rxConfig()->mincheck))) {
|
if ((feature(FEATURE_REVERSIBLE_MOTORS) && midThrottle) || (!feature(FEATURE_REVERSIBLE_MOTORS) && (value < rxConfig()->mincheck))) {
|
||||||
return THROTTLE_LOW;
|
return THROTTLE_LOW;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ typedef enum {
|
||||||
BOXMULTIFUNCTION = 52,
|
BOXMULTIFUNCTION = 52,
|
||||||
BOXMIXERPROFILE = 53,
|
BOXMIXERPROFILE = 53,
|
||||||
BOXMIXERTRANSITION = 54,
|
BOXMIXERTRANSITION = 54,
|
||||||
|
BOXANGLEHOLD = 55,
|
||||||
CHECKBOX_ITEM_COUNT
|
CHECKBOX_ITEM_COUNT
|
||||||
} boxId_e;
|
} boxId_e;
|
||||||
|
|
||||||
|
|
|
@ -91,31 +91,16 @@ armingFlag_e isArmingDisabledReason(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the given flight mode. A beep is sounded if the flight mode
|
* Called at Rx update rate. Beeper sounded if flight mode state has changed.
|
||||||
* has changed. Returns the new 'flightModeFlags' value.
|
|
||||||
*/
|
*/
|
||||||
uint32_t enableFlightMode(flightModeFlags_e mask)
|
void updateFlightModeChangeBeeper(void)
|
||||||
{
|
{
|
||||||
uint32_t oldVal = flightModeFlags;
|
static uint32_t previousFlightModeFlags = 0;
|
||||||
|
|
||||||
flightModeFlags |= (mask);
|
if (flightModeFlags != previousFlightModeFlags) {
|
||||||
if (flightModeFlags != oldVal)
|
|
||||||
beeperConfirmationBeeps(1);
|
beeperConfirmationBeeps(1);
|
||||||
return flightModeFlags;
|
}
|
||||||
}
|
previousFlightModeFlags = flightModeFlags;
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables the given flight mode. A beep is sounded if the flight mode
|
|
||||||
* has changed. Returns the new 'flightModeFlags' value.
|
|
||||||
*/
|
|
||||||
uint32_t disableFlightMode(flightModeFlags_e mask)
|
|
||||||
{
|
|
||||||
uint32_t oldVal = flightModeFlags;
|
|
||||||
|
|
||||||
flightModeFlags &= ~(mask);
|
|
||||||
if (flightModeFlags != oldVal)
|
|
||||||
beeperConfirmationBeeps(1);
|
|
||||||
return flightModeFlags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sensors(uint32_t mask)
|
bool sensors(uint32_t mask)
|
||||||
|
@ -173,6 +158,8 @@ flightModeForTelemetry_e getFlightModeForTelemetry(void)
|
||||||
if (FLIGHT_MODE(HORIZON_MODE))
|
if (FLIGHT_MODE(HORIZON_MODE))
|
||||||
return FLM_HORIZON;
|
return FLM_HORIZON;
|
||||||
|
|
||||||
|
if (FLIGHT_MODE(ANGLEHOLD_MODE))
|
||||||
|
return FLM_ANGLEHOLD;
|
||||||
|
|
||||||
return STATE(AIRMODE_ACTIVE) ? FLM_ACRO_AIR : FLM_ACRO;
|
return STATE(AIRMODE_ACTIVE) ? FLM_ACRO_AIR : FLM_ACRO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,12 +104,13 @@ typedef enum {
|
||||||
TURN_ASSISTANT = (1 << 14),
|
TURN_ASSISTANT = (1 << 14),
|
||||||
TURTLE_MODE = (1 << 15),
|
TURTLE_MODE = (1 << 15),
|
||||||
SOARING_MODE = (1 << 16),
|
SOARING_MODE = (1 << 16),
|
||||||
|
ANGLEHOLD_MODE = (1 << 17),
|
||||||
} flightModeFlags_e;
|
} flightModeFlags_e;
|
||||||
|
|
||||||
extern uint32_t flightModeFlags;
|
extern uint32_t flightModeFlags;
|
||||||
|
|
||||||
#define DISABLE_FLIGHT_MODE(mask) disableFlightMode(mask)
|
#define DISABLE_FLIGHT_MODE(mask) (flightModeFlags &= ~(mask))
|
||||||
#define ENABLE_FLIGHT_MODE(mask) enableFlightMode(mask)
|
#define ENABLE_FLIGHT_MODE(mask) (flightModeFlags |= (mask))
|
||||||
#define FLIGHT_MODE(mask) (flightModeFlags & (mask))
|
#define FLIGHT_MODE(mask) (flightModeFlags & (mask))
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -123,6 +124,9 @@ typedef enum {
|
||||||
NAV_MOTOR_STOP_OR_IDLE = (1 << 7), // navigation requests MOTOR_STOP or motor idle regardless of throttle stick, will only activate if MOTOR_STOP feature is available
|
NAV_MOTOR_STOP_OR_IDLE = (1 << 7), // navigation requests MOTOR_STOP or motor idle regardless of throttle stick, will only activate if MOTOR_STOP feature is available
|
||||||
COMPASS_CALIBRATED = (1 << 8),
|
COMPASS_CALIBRATED = (1 << 8),
|
||||||
ACCELEROMETER_CALIBRATED = (1 << 9),
|
ACCELEROMETER_CALIBRATED = (1 << 9),
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
GPS_ESTIMATED_FIX = (1 << 10),
|
||||||
|
#endif
|
||||||
NAV_CRUISE_BRAKING = (1 << 11),
|
NAV_CRUISE_BRAKING = (1 << 11),
|
||||||
NAV_CRUISE_BRAKING_BOOST = (1 << 12),
|
NAV_CRUISE_BRAKING_BOOST = (1 << 12),
|
||||||
NAV_CRUISE_BRAKING_LOCKED = (1 << 13),
|
NAV_CRUISE_BRAKING_LOCKED = (1 << 13),
|
||||||
|
@ -162,6 +166,7 @@ typedef enum {
|
||||||
FLM_CRUISE,
|
FLM_CRUISE,
|
||||||
FLM_LAUNCH,
|
FLM_LAUNCH,
|
||||||
FLM_FAILSAFE,
|
FLM_FAILSAFE,
|
||||||
|
FLM_ANGLEHOLD,
|
||||||
FLM_COUNT
|
FLM_COUNT
|
||||||
} flightModeForTelemetry_e;
|
} flightModeForTelemetry_e;
|
||||||
|
|
||||||
|
@ -200,8 +205,7 @@ extern simulatorData_t simulatorData;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t enableFlightMode(flightModeFlags_e mask);
|
void updateFlightModeChangeBeeper(void);
|
||||||
uint32_t disableFlightMode(flightModeFlags_e mask);
|
|
||||||
|
|
||||||
bool sensors(uint32_t mask);
|
bool sensors(uint32_t mask);
|
||||||
void sensorsSet(uint32_t mask);
|
void sensorsSet(uint32_t mask);
|
||||||
|
|
128
src/main/fc/settings.yaml
Executable file → Normal file
|
@ -50,7 +50,7 @@ tables:
|
||||||
values: ["AUTO", "EGNOS", "WAAS", "MSAS", "GAGAN", "SPAN", "NONE"]
|
values: ["AUTO", "EGNOS", "WAAS", "MSAS", "GAGAN", "SPAN", "NONE"]
|
||||||
enum: sbasMode_e
|
enum: sbasMode_e
|
||||||
- name: gps_dyn_model
|
- name: gps_dyn_model
|
||||||
values: ["PEDESTRIAN", "AIR_1G", "AIR_4G"]
|
values: ["PEDESTRIAN","AUTOMOTIVE", "AIR_1G", "AIR_2G", "AIR_4G"]
|
||||||
enum: gpsDynModel_e
|
enum: gpsDynModel_e
|
||||||
- name: reset_type
|
- name: reset_type
|
||||||
values: ["NEVER", "FIRST_ARM", "EACH_ARM"]
|
values: ["NEVER", "FIRST_ARM", "EACH_ARM"]
|
||||||
|
@ -192,7 +192,10 @@ tables:
|
||||||
enum: gpsBaudRate_e
|
enum: gpsBaudRate_e
|
||||||
- name: nav_mc_althold_throttle
|
- name: nav_mc_althold_throttle
|
||||||
values: ["STICK", "MID_STICK", "HOVER"]
|
values: ["STICK", "MID_STICK", "HOVER"]
|
||||||
enum: navMcAltHoldThrottle_e
|
enum: navMcAltHoldThrottle_e
|
||||||
|
- name: led_pin_pwm_mode
|
||||||
|
values: ["SHARED_LOW", "SHARED_HIGH", "LOW", "HIGH"]
|
||||||
|
enum: led_pin_pwm_mode_e
|
||||||
|
|
||||||
constants:
|
constants:
|
||||||
RPYL_PID_MIN: 0
|
RPYL_PID_MIN: 0
|
||||||
|
@ -585,7 +588,7 @@ groups:
|
||||||
members:
|
members:
|
||||||
- name: pitot_hardware
|
- name: pitot_hardware
|
||||||
description: "Selection of pitot hardware."
|
description: "Selection of pitot hardware."
|
||||||
default_value: "VIRTUAL"
|
default_value: "NONE"
|
||||||
table: pitot_hardware
|
table: pitot_hardware
|
||||||
- name: pitot_lpf_milli_hz
|
- name: pitot_lpf_milli_hz
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -835,6 +838,12 @@ groups:
|
||||||
default_value: 0
|
default_value: 0
|
||||||
min: -1
|
min: -1
|
||||||
max: 600
|
max: 600
|
||||||
|
- name: failsafe_gps_fix_estimation_delay
|
||||||
|
description: "Controls whether waypoint mission is allowed to proceed with gps fix estimation. Sets the time delay in seconds between gps fix lost event and RTH activation. Minimum delay is 7 seconds. If set to -1 the mission will continue until the end. With default setting(7), waypoint mission is aborted and switched to RTH with 7 seconds delay. RTH is done with GPS Fix estimation."
|
||||||
|
condition: USE_GPS_FIX_ESTIMATION
|
||||||
|
default_value: 7
|
||||||
|
min: -1
|
||||||
|
max: 600
|
||||||
|
|
||||||
- name: PG_LIGHTS_CONFIG
|
- name: PG_LIGHTS_CONFIG
|
||||||
type: lightsConfig_t
|
type: lightsConfig_t
|
||||||
|
@ -1183,7 +1192,7 @@ groups:
|
||||||
min: 0
|
min: 0
|
||||||
max: 200
|
max: 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- name: PG_REVERSIBLE_MOTORS_CONFIG
|
- name: PG_REVERSIBLE_MOTORS_CONFIG
|
||||||
type: reversibleMotorsConfig_t
|
type: reversibleMotorsConfig_t
|
||||||
|
@ -1460,6 +1469,12 @@ groups:
|
||||||
default_value: ADAPTIVE
|
default_value: ADAPTIVE
|
||||||
field: inertia_comp_method
|
field: inertia_comp_method
|
||||||
table: imu_inertia_comp_method
|
table: imu_inertia_comp_method
|
||||||
|
- name: ahrs_gps_yaw_weight
|
||||||
|
description: "Arhs gps yaw weight when mag is avaliable, 0 means no gps yaw, 100 means equal weight as compass"
|
||||||
|
default_value: 100
|
||||||
|
field: gps_yaw_weight
|
||||||
|
min: 0
|
||||||
|
max: 500
|
||||||
|
|
||||||
- name: PG_ARMING_CONFIG
|
- name: PG_ARMING_CONFIG
|
||||||
type: armingConfig_t
|
type: armingConfig_t
|
||||||
|
@ -1605,8 +1620,8 @@ groups:
|
||||||
table: gps_sbas_mode
|
table: gps_sbas_mode
|
||||||
type: uint8_t
|
type: uint8_t
|
||||||
- name: gps_dyn_model
|
- name: gps_dyn_model
|
||||||
description: "GPS navigation model: Pedestrian, Air_1g, Air_4g. Default is AIR_1G. Use pedestrian with caution, can cause flyaways with fast flying."
|
description: "GPS navigation model: Pedestrian, Automotive, Air<1g, Air<2g, Air<4g. Default is AIR_2G. Use pedestrian/Automotive with caution, can cause flyaways with fast flying."
|
||||||
default_value: "AIR_1G"
|
default_value: "AIR_2G"
|
||||||
field: dynModel
|
field: dynModel
|
||||||
table: gps_dyn_model
|
table: gps_dyn_model
|
||||||
type: uint8_t
|
type: uint8_t
|
||||||
|
@ -1727,7 +1742,7 @@ groups:
|
||||||
min: RPYL_PID_MIN
|
min: RPYL_PID_MIN
|
||||||
max: RPYL_PID_MAX
|
max: RPYL_PID_MAX
|
||||||
- name: mc_cd_pitch
|
- name: mc_cd_pitch
|
||||||
description: "Multicopter Control Derivative gain for PITCH"
|
description: "Multicopter Control Derivative gain for PITCH (known as 'Feed Forward' in Betaflight). The CD intoduces a term to the PID controller that is the magnitude of the Setpoint change. Fast inputs produce a high CD gain to help push the MC into a move; in advance of the P-gain if set high enough."
|
||||||
default_value: 60
|
default_value: 60
|
||||||
field: bank_mc.pid[PID_PITCH].FF
|
field: bank_mc.pid[PID_PITCH].FF
|
||||||
min: RPYL_PID_MIN
|
min: RPYL_PID_MIN
|
||||||
|
@ -1751,7 +1766,7 @@ groups:
|
||||||
min: RPYL_PID_MIN
|
min: RPYL_PID_MIN
|
||||||
max: RPYL_PID_MAX
|
max: RPYL_PID_MAX
|
||||||
- name: mc_cd_roll
|
- name: mc_cd_roll
|
||||||
description: "Multicopter Control Derivative gain for ROLL"
|
description: "Multicopter Control Derivative gain for ROLL (known as 'Feed Forward' in Betaflight). The CD intoduces a term to the PID controller that is the magnitude of the Setpoint change. Fast inputs produce a high CD gain to help push the MC into a move; in advance of the P-gain if set high enough."
|
||||||
default_value: 60
|
default_value: 60
|
||||||
field: bank_mc.pid[PID_ROLL].FF
|
field: bank_mc.pid[PID_ROLL].FF
|
||||||
min: RPYL_PID_MIN
|
min: RPYL_PID_MIN
|
||||||
|
@ -1775,7 +1790,7 @@ groups:
|
||||||
min: RPYL_PID_MIN
|
min: RPYL_PID_MIN
|
||||||
max: RPYL_PID_MAX
|
max: RPYL_PID_MAX
|
||||||
- name: mc_cd_yaw
|
- name: mc_cd_yaw
|
||||||
description: "Multicopter Control Derivative gain for YAW"
|
description: "Multicopter Control Derivative gain for YAW (known as 'Feed Forward' in Betaflight). The CD intoduces a term to the PID controller that is the magnitude of the Setpoint change. Fast inputs produce a high CD gain to help push the MC into a move; in advance of the P-gain if set high enough."
|
||||||
default_value: 60
|
default_value: 60
|
||||||
field: bank_mc.pid[PID_YAW].FF
|
field: bank_mc.pid[PID_YAW].FF
|
||||||
min: RPYL_PID_MIN
|
min: RPYL_PID_MIN
|
||||||
|
@ -2192,7 +2207,7 @@ groups:
|
||||||
table: pidTypeTable
|
table: pidTypeTable
|
||||||
default_value: AUTO
|
default_value: AUTO
|
||||||
- name: mc_cd_lpf_hz
|
- name: mc_cd_lpf_hz
|
||||||
description: "Cutoff frequency for Control Derivative. Lower value smoother reaction on fast stick movements. With higher values, response will be more aggressive, jerky"
|
description: "Cutoff frequency for Control Derivative. This controls the cutoff for the LPF that is applied to the CD (Feed Forward) signal to the PID controller. Lower value will produce a smoother CD gain to the controller, but it will be more delayed. Higher values will produce CD gain that may have more noise in the signal depending on your RC link but wil be less delayed."
|
||||||
default_value: 30
|
default_value: 30
|
||||||
field: controlDerivativeLpfHz
|
field: controlDerivativeLpfHz
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -2282,6 +2297,12 @@ groups:
|
||||||
default_value: OFF
|
default_value: OFF
|
||||||
field: allow_dead_reckoning
|
field: allow_dead_reckoning
|
||||||
type: bool
|
type: bool
|
||||||
|
- name: inav_allow_gps_fix_estimation
|
||||||
|
description: "Defines if inav will estimate GPS fix with magnetometer and barometer on GPS outages. Enables navigation and RTH without GPS fix on fixed wing. Also see failsafe_gps_fix_estimation_delay."
|
||||||
|
condition: USE_GPS_FIX_ESTIMATION
|
||||||
|
default_value: OFF
|
||||||
|
field: allow_gps_fix_estimation
|
||||||
|
type: bool
|
||||||
- name: inav_reset_altitude
|
- name: inav_reset_altitude
|
||||||
description: "Defines when relative estimated altitude is reset to zero. Variants - `NEVER` (once reference is acquired it's used regardless); `FIRST_ARM` (keep altitude at zero until firstly armed), `EACH_ARM` (altitude is reset to zero on each arming)"
|
description: "Defines when relative estimated altitude is reset to zero. Variants - `NEVER` (once reference is acquired it's used regardless); `FIRST_ARM` (keep altitude at zero until firstly armed), `EACH_ARM` (altitude is reset to zero on each arming)"
|
||||||
default_value: "FIRST_ARM"
|
default_value: "FIRST_ARM"
|
||||||
|
@ -2319,23 +2340,23 @@ groups:
|
||||||
max: 100
|
max: 100
|
||||||
default_value: 2.0
|
default_value: 2.0
|
||||||
- name: inav_w_z_baro_p
|
- name: inav_w_z_baro_p
|
||||||
description: "Weight of barometer measurements in estimated altitude and climb rate"
|
description: "Weight of barometer measurements in estimated altitude and climb rate. Setting is used on both airplanes and multirotors."
|
||||||
field: w_z_baro_p
|
field: w_z_baro_p
|
||||||
min: 0
|
min: 0
|
||||||
max: 10
|
max: 10
|
||||||
default_value: 0.35
|
default_value: 0.4
|
||||||
- name: inav_w_z_gps_p
|
- name: inav_w_z_gps_p
|
||||||
description: "Weight of GPS altitude measurements in estimated altitude. Setting is used only of airplanes"
|
description: "Weight of GPS altitude measurements in estimated altitude. Setting is used on both airplanes and multirotors."
|
||||||
field: w_z_gps_p
|
field: w_z_gps_p
|
||||||
min: 0
|
min: 0
|
||||||
max: 10
|
max: 10
|
||||||
default_value: 0.2
|
default_value: 0.4
|
||||||
- name: inav_w_z_gps_v
|
- name: inav_w_z_gps_v
|
||||||
description: "Weight of GPS climb rate measurements in estimated climb rate. Setting is used on both airplanes and multirotors."
|
description: "Weight of GPS climb rate measurements in estimated climb rate. Setting is used on both airplanes and multirotors."
|
||||||
field: w_z_gps_v
|
field: w_z_gps_v
|
||||||
min: 0
|
min: 0
|
||||||
max: 10
|
max: 10
|
||||||
default_value: 0.1
|
default_value: 0.8
|
||||||
- name: inav_w_xy_gps_p
|
- name: inav_w_xy_gps_p
|
||||||
description: "Weight of GPS coordinates in estimated UAV position and speed."
|
description: "Weight of GPS coordinates in estimated UAV position and speed."
|
||||||
default_value: 1.0
|
default_value: 1.0
|
||||||
|
@ -2360,11 +2381,6 @@ groups:
|
||||||
field: w_xy_res_v
|
field: w_xy_res_v
|
||||||
min: 0
|
min: 0
|
||||||
max: 10
|
max: 10
|
||||||
- name: inav_w_xyz_acc_p
|
|
||||||
field: w_xyz_acc_p
|
|
||||||
min: 0
|
|
||||||
max: 1
|
|
||||||
default_value: 1.0
|
|
||||||
- name: inav_w_acc_bias
|
- name: inav_w_acc_bias
|
||||||
description: "Weight for accelerometer drift estimation"
|
description: "Weight for accelerometer drift estimation"
|
||||||
default_value: 0.01
|
default_value: 0.01
|
||||||
|
@ -2483,6 +2499,12 @@ groups:
|
||||||
field: general.auto_speed
|
field: general.auto_speed
|
||||||
min: 10
|
min: 10
|
||||||
max: 2000
|
max: 2000
|
||||||
|
- name: nav_min_ground_speed
|
||||||
|
description: "Minimum ground speed for navigation flight modes [m/s]. Default 7 m/s."
|
||||||
|
default_value: 7
|
||||||
|
field: general.min_ground_speed
|
||||||
|
min: 6
|
||||||
|
max: 50
|
||||||
- name: nav_max_auto_speed
|
- name: nav_max_auto_speed
|
||||||
description: "Maximum speed allowed in fully autonomous modes (RTH, WP) [cm/s] [Multirotor only]"
|
description: "Maximum speed allowed in fully autonomous modes (RTH, WP) [cm/s] [Multirotor only]"
|
||||||
default_value: 1000
|
default_value: 1000
|
||||||
|
@ -2577,6 +2599,12 @@ groups:
|
||||||
default_value: "ALWAYS"
|
default_value: "ALWAYS"
|
||||||
field: general.flags.rth_allow_landing
|
field: general.flags.rth_allow_landing
|
||||||
table: nav_rth_allow_landing
|
table: nav_rth_allow_landing
|
||||||
|
- name: nav_rth_fs_landing_delay
|
||||||
|
description: "If landing is active on Failsafe and this is above 0. The aircraft will hover or loiter for X seconds before performing the landing. If the battery enters the warning or critical levels, the land will proceed. Default = 0 [seconds]"
|
||||||
|
default_value: 0
|
||||||
|
min: 0
|
||||||
|
max: 1800
|
||||||
|
field: general.rth_fs_landing_delay
|
||||||
- name: nav_rth_alt_mode
|
- name: nav_rth_alt_mode
|
||||||
description: "Configure how the aircraft will manage altitude on the way home, see Navigation modes on wiki for more details"
|
description: "Configure how the aircraft will manage altitude on the way home, see Navigation modes on wiki for more details"
|
||||||
default_value: "AT_LEAST"
|
default_value: "AT_LEAST"
|
||||||
|
@ -2655,7 +2683,7 @@ groups:
|
||||||
type: bool
|
type: bool
|
||||||
- name: nav_cruise_yaw_rate
|
- name: nav_cruise_yaw_rate
|
||||||
description: "Max YAW rate when NAV COURSE HOLD/CRUISE mode is enabled. Set to 0 to disable on fixed wing (Note: On multirotor setting to 0 will disable Course Hold/Cruise mode completely) [dps]"
|
description: "Max YAW rate when NAV COURSE HOLD/CRUISE mode is enabled. Set to 0 to disable on fixed wing (Note: On multirotor setting to 0 will disable Course Hold/Cruise mode completely) [dps]"
|
||||||
default_value: 20
|
default_value: 60
|
||||||
field: general.cruise_yaw_rate
|
field: general.cruise_yaw_rate
|
||||||
min: 0
|
min: 0
|
||||||
max: 120
|
max: 120
|
||||||
|
@ -3589,7 +3617,7 @@ groups:
|
||||||
min: 1000
|
min: 1000
|
||||||
max: 5000
|
max: 5000
|
||||||
default_value: 1500
|
default_value: 1500
|
||||||
|
|
||||||
- name: osd_switch_indicator_zero_name
|
- name: osd_switch_indicator_zero_name
|
||||||
description: "Character to use for OSD switch incicator 0."
|
description: "Character to use for OSD switch incicator 0."
|
||||||
field: osd_switch_indicator0_name
|
field: osd_switch_indicator0_name
|
||||||
|
@ -3813,10 +3841,10 @@ groups:
|
||||||
condition: USE_VTX_SMARTAUDIO || USE_VTX_TRAMP
|
condition: USE_VTX_SMARTAUDIO || USE_VTX_TRAMP
|
||||||
members:
|
members:
|
||||||
- name: vtx_band
|
- name: vtx_band
|
||||||
description: "Configure the VTX band. Set to zero to use `vtx_freq`. Bands: 1: A, 2: B, 3: E, 4: F, 5: Race."
|
description: "Configure the VTX band. Bands: 1: A, 2: B, 3: E, 4: F, 5: Race."
|
||||||
default_value: 1
|
default_value: 1
|
||||||
field: band
|
field: band
|
||||||
min: VTX_SETTINGS_NO_BAND
|
min: VTX_SETTINGS_MIN_BAND
|
||||||
max: VTX_SETTINGS_MAX_BAND
|
max: VTX_SETTINGS_MAX_BAND
|
||||||
- name: vtx_channel
|
- name: vtx_channel
|
||||||
description: "Channel to use within the configured `vtx_band`. Valid values are [1, 8]."
|
description: "Channel to use within the configured `vtx_band`. Valid values are [1, 8]."
|
||||||
|
@ -4025,3 +4053,55 @@ groups:
|
||||||
default_value: 1.2
|
default_value: 1.2
|
||||||
field: attnFilterCutoff
|
field: attnFilterCutoff
|
||||||
max: 100
|
max: 100
|
||||||
|
|
||||||
|
- name: PG_LEDPIN_CONFIG
|
||||||
|
type: ledPinConfig_t
|
||||||
|
headers: ["drivers/light_ws2811strip.h"]
|
||||||
|
members:
|
||||||
|
- name: led_pin_pwm_mode
|
||||||
|
condition: USE_LED_STRIP
|
||||||
|
description: "PWM mode of LED pin."
|
||||||
|
default_value: "SHARED_LOW"
|
||||||
|
field: led_pin_pwm_mode
|
||||||
|
table: led_pin_pwm_mode
|
||||||
|
|
||||||
|
- name: PG_OSD_JOYSTICK_CONFIG
|
||||||
|
type: osdJoystickConfig_t
|
||||||
|
headers: ["io/osd_joystick.h"]
|
||||||
|
condition: USE_RCDEVICE && USE_LED_STRIP
|
||||||
|
members:
|
||||||
|
- name: osd_joystick_enabled
|
||||||
|
description: "Enable OSD Joystick emulation"
|
||||||
|
default_value: OFF
|
||||||
|
field: osd_joystick_enabled
|
||||||
|
type: bool
|
||||||
|
- name: osd_joystick_down
|
||||||
|
description: "PWM value for DOWN key"
|
||||||
|
default_value: 0
|
||||||
|
field: osd_joystick_down
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
- name: osd_joystick_up
|
||||||
|
description: "PWM value for UP key"
|
||||||
|
default_value: 48
|
||||||
|
field: osd_joystick_up
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
- name: osd_joystick_left
|
||||||
|
description: "PWM value for LEFT key"
|
||||||
|
default_value: 63
|
||||||
|
field: osd_joystick_left
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
- name: osd_joystick_right
|
||||||
|
description: "PWM value for RIGHT key"
|
||||||
|
default_value: 28
|
||||||
|
field: osd_joystick_right
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
- name: osd_joystick_enter
|
||||||
|
description: "PWM value for ENTER key"
|
||||||
|
default_value: 75
|
||||||
|
field: osd_joystick_enter
|
||||||
|
min: 0
|
||||||
|
max: 100
|
||||||
|
|
|
@ -82,6 +82,9 @@ PG_RESET_TEMPLATE(failsafeConfig_t, failsafeConfig,
|
||||||
.failsafe_min_distance = SETTING_FAILSAFE_MIN_DISTANCE_DEFAULT, // No minimum distance for failsafe by default
|
.failsafe_min_distance = SETTING_FAILSAFE_MIN_DISTANCE_DEFAULT, // No minimum distance for failsafe by default
|
||||||
.failsafe_min_distance_procedure = SETTING_FAILSAFE_MIN_DISTANCE_PROCEDURE_DEFAULT, // default minimum distance failsafe procedure
|
.failsafe_min_distance_procedure = SETTING_FAILSAFE_MIN_DISTANCE_PROCEDURE_DEFAULT, // default minimum distance failsafe procedure
|
||||||
.failsafe_mission_delay = SETTING_FAILSAFE_MISSION_DELAY_DEFAULT, // Time delay before Failsafe activated during WP mission (s)
|
.failsafe_mission_delay = SETTING_FAILSAFE_MISSION_DELAY_DEFAULT, // Time delay before Failsafe activated during WP mission (s)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
.failsafe_gps_fix_estimation_delay = SETTING_FAILSAFE_GPS_FIX_ESTIMATION_DELAY_DEFAULT, // Time delay before Failsafe activated when GPS Fix estimation is allied
|
||||||
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -350,7 +353,13 @@ static failsafeProcedure_e failsafeChooseFailsafeProcedure(void)
|
||||||
|
|
||||||
// Craft is closer than minimum failsafe procedure distance (if set to non-zero)
|
// Craft is closer than minimum failsafe procedure distance (if set to non-zero)
|
||||||
// GPS must also be working, and home position set
|
// GPS must also be working, and home position set
|
||||||
if (failsafeConfig()->failsafe_min_distance > 0 && sensors(SENSOR_GPS) && STATE(GPS_FIX) && STATE(GPS_FIX_HOME)) {
|
if (failsafeConfig()->failsafe_min_distance > 0 &&
|
||||||
|
((sensors(SENSOR_GPS) && STATE(GPS_FIX))
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && STATE(GPS_FIX_HOME)) {
|
||||||
|
|
||||||
// get the distance to the original arming point
|
// get the distance to the original arming point
|
||||||
uint32_t distance = calculateDistanceToDestination(&posControl.rthState.originalHomePosition);
|
uint32_t distance = calculateDistanceToDestination(&posControl.rthState.originalHomePosition);
|
||||||
if (distance < failsafeConfig()->failsafe_min_distance) {
|
if (distance < failsafeConfig()->failsafe_min_distance) {
|
||||||
|
@ -362,6 +371,28 @@ static failsafeProcedure_e failsafeChooseFailsafeProcedure(void)
|
||||||
return failsafeConfig()->failsafe_procedure;
|
return failsafeConfig()->failsafe_procedure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
bool checkGPSFixFailsafe(void)
|
||||||
|
{
|
||||||
|
if (STATE(GPS_ESTIMATED_FIX) && (FLIGHT_MODE(NAV_WP_MODE) || isWaypointMissionRTHActive()) && (failsafeConfig()->failsafe_gps_fix_estimation_delay >= 0)) {
|
||||||
|
if (!failsafeState.wpModeGPSFixEstimationDelayedFailsafeStart) {
|
||||||
|
failsafeState.wpModeGPSFixEstimationDelayedFailsafeStart = millis();
|
||||||
|
} else if ((millis() - failsafeState.wpModeGPSFixEstimationDelayedFailsafeStart) > (MILLIS_PER_SECOND * (uint16_t)MAX(failsafeConfig()->failsafe_gps_fix_estimation_delay,7))) {
|
||||||
|
if ( !posControl.flags.forcedRTHActivated ) {
|
||||||
|
failsafeSetActiveProcedure(FAILSAFE_PROCEDURE_RTH);
|
||||||
|
failsafeActivate(FAILSAFE_RETURN_TO_HOME);
|
||||||
|
activateForcedRTH();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failsafeState.wpModeGPSFixEstimationDelayedFailsafeStart = 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void failsafeUpdateState(void)
|
void failsafeUpdateState(void)
|
||||||
{
|
{
|
||||||
if (!failsafeIsMonitoring() || failsafeIsSuspended()) {
|
if (!failsafeIsMonitoring() || failsafeIsSuspended()) {
|
||||||
|
@ -390,6 +421,12 @@ void failsafeUpdateState(void)
|
||||||
if (!throttleStickIsLow()) {
|
if (!throttleStickIsLow()) {
|
||||||
failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
|
failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if ( checkGPSFixFailsafe() ) {
|
||||||
|
reprocessState = true;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
if (!receivingRxDataAndNotFailsafeMode) {
|
if (!receivingRxDataAndNotFailsafeMode) {
|
||||||
if ((failsafeConfig()->failsafe_throttle_low_delay && (millis() > failsafeState.throttleLowPeriod)) || STATE(NAV_MOTOR_STOP_OR_IDLE)) {
|
if ((failsafeConfig()->failsafe_throttle_low_delay && (millis() > failsafeState.throttleLowPeriod)) || STATE(NAV_MOTOR_STOP_OR_IDLE)) {
|
||||||
// JustDisarm: throttle was LOW for at least 'failsafe_throttle_low_delay' seconds or waiting for launch
|
// JustDisarm: throttle was LOW for at least 'failsafe_throttle_low_delay' seconds or waiting for launch
|
||||||
|
@ -458,7 +495,15 @@ void failsafeUpdateState(void)
|
||||||
} else if (failsafeChooseFailsafeProcedure() != FAILSAFE_PROCEDURE_NONE) { // trigger new failsafe procedure if changed
|
} else if (failsafeChooseFailsafeProcedure() != FAILSAFE_PROCEDURE_NONE) { // trigger new failsafe procedure if changed
|
||||||
failsafeState.phase = FAILSAFE_RX_LOSS_DETECTED;
|
failsafeState.phase = FAILSAFE_RX_LOSS_DETECTED;
|
||||||
reprocessState = true;
|
reprocessState = true;
|
||||||
|
}
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
else {
|
||||||
|
if ( checkGPSFixFailsafe() ) {
|
||||||
|
reprocessState = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FAILSAFE_RETURN_TO_HOME:
|
case FAILSAFE_RETURN_TO_HOME:
|
||||||
|
|
|
@ -43,6 +43,9 @@ typedef struct failsafeConfig_s {
|
||||||
uint16_t failsafe_min_distance; // Minimum distance required for failsafe procedure to be taken. 1 step = 1 centimeter. 0 = Regular failsafe_procedure always active (default)
|
uint16_t failsafe_min_distance; // Minimum distance required for failsafe procedure to be taken. 1 step = 1 centimeter. 0 = Regular failsafe_procedure always active (default)
|
||||||
uint8_t failsafe_min_distance_procedure; // selected minimum distance failsafe procedure is 0: auto-landing, 1: Drop it, 2: Return To Home (RTH)
|
uint8_t failsafe_min_distance_procedure; // selected minimum distance failsafe procedure is 0: auto-landing, 1: Drop it, 2: Return To Home (RTH)
|
||||||
int16_t failsafe_mission_delay; // Time delay before Failsafe triggered when WP mission in progress (s)
|
int16_t failsafe_mission_delay; // Time delay before Failsafe triggered when WP mission in progress (s)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
int16_t failsafe_gps_fix_estimation_delay; // Time delay before Failsafe triggered when GPX Fix estimation is applied (s)
|
||||||
|
#endif
|
||||||
} failsafeConfig_t;
|
} failsafeConfig_t;
|
||||||
|
|
||||||
PG_DECLARE(failsafeConfig_t, failsafeConfig);
|
PG_DECLARE(failsafeConfig_t, failsafeConfig);
|
||||||
|
@ -149,6 +152,9 @@ typedef struct failsafeState_s {
|
||||||
timeMs_t receivingRxDataPeriod; // period for the required period of valid rxData
|
timeMs_t receivingRxDataPeriod; // period for the required period of valid rxData
|
||||||
timeMs_t receivingRxDataPeriodPreset; // preset for the required period of valid rxData
|
timeMs_t receivingRxDataPeriodPreset; // preset for the required period of valid rxData
|
||||||
timeMs_t wpModeDelayedFailsafeStart; // waypoint mission delayed failsafe timer start time
|
timeMs_t wpModeDelayedFailsafeStart; // waypoint mission delayed failsafe timer start time
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
timeMs_t wpModeGPSFixEstimationDelayedFailsafeStart; // waypoint mission delayed failsafe timer start time on GPS fix estimation
|
||||||
|
#endif
|
||||||
failsafeProcedure_e activeProcedure;
|
failsafeProcedure_e activeProcedure;
|
||||||
failsafePhase_e phase;
|
failsafePhase_e phase;
|
||||||
failsafeRxLinkState_e rxLinkState;
|
failsafeRxLinkState_e rxLinkState;
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
|
|
||||||
#include "flight/imu.h"
|
#include "flight/imu.h"
|
||||||
#include "flight/mixer.h"
|
#include "flight/mixer.h"
|
||||||
|
#include "flight/mixer_profile.h"
|
||||||
#include "flight/pid.h"
|
#include "flight/pid.h"
|
||||||
#if defined(USE_WIND_ESTIMATOR)
|
#if defined(USE_WIND_ESTIMATOR)
|
||||||
#include "flight/wind_estimator.h"
|
#include "flight/wind_estimator.h"
|
||||||
|
@ -77,6 +78,9 @@
|
||||||
|
|
||||||
#define SPIN_RATE_LIMIT 20
|
#define SPIN_RATE_LIMIT 20
|
||||||
#define MAX_ACC_NEARNESS 0.2 // 20% or G error soft-accepted (0.8-1.2G)
|
#define MAX_ACC_NEARNESS 0.2 // 20% or G error soft-accepted (0.8-1.2G)
|
||||||
|
#define MAX_MAG_NEARNESS 0.25 // 25% or magnetic field error soft-accepted (0.75-1.25)
|
||||||
|
#define COS10DEG 0.985f
|
||||||
|
#define COS20DEG 0.940f
|
||||||
#define IMU_ROTATION_LPF 3 // Hz
|
#define IMU_ROTATION_LPF 3 // Hz
|
||||||
FASTRAM fpVector3_t imuMeasuredAccelBF;
|
FASTRAM fpVector3_t imuMeasuredAccelBF;
|
||||||
FASTRAM fpVector3_t imuMeasuredRotationBF;
|
FASTRAM fpVector3_t imuMeasuredRotationBF;
|
||||||
|
@ -111,6 +115,8 @@ FASTRAM bool gpsHeadingInitialized;
|
||||||
|
|
||||||
FASTRAM bool imuUpdated = false;
|
FASTRAM bool imuUpdated = false;
|
||||||
|
|
||||||
|
static float imuCalculateAccelerometerWeightNearness(fpVector3_t* accBF);
|
||||||
|
|
||||||
PG_REGISTER_WITH_RESET_TEMPLATE(imuConfig_t, imuConfig, PG_IMU_CONFIG, 2);
|
PG_REGISTER_WITH_RESET_TEMPLATE(imuConfig_t, imuConfig, PG_IMU_CONFIG, 2);
|
||||||
|
|
||||||
PG_RESET_TEMPLATE(imuConfig_t, imuConfig,
|
PG_RESET_TEMPLATE(imuConfig_t, imuConfig,
|
||||||
|
@ -122,7 +128,8 @@ PG_RESET_TEMPLATE(imuConfig_t, imuConfig,
|
||||||
.acc_ignore_rate = SETTING_AHRS_ACC_IGNORE_RATE_DEFAULT,
|
.acc_ignore_rate = SETTING_AHRS_ACC_IGNORE_RATE_DEFAULT,
|
||||||
.acc_ignore_slope = SETTING_AHRS_ACC_IGNORE_SLOPE_DEFAULT,
|
.acc_ignore_slope = SETTING_AHRS_ACC_IGNORE_SLOPE_DEFAULT,
|
||||||
.gps_yaw_windcomp = SETTING_AHRS_GPS_YAW_WINDCOMP_DEFAULT,
|
.gps_yaw_windcomp = SETTING_AHRS_GPS_YAW_WINDCOMP_DEFAULT,
|
||||||
.inertia_comp_method = SETTING_AHRS_INERTIA_COMP_METHOD_DEFAULT
|
.inertia_comp_method = SETTING_AHRS_INERTIA_COMP_METHOD_DEFAULT,
|
||||||
|
.gps_yaw_weight = SETTING_AHRS_GPS_YAW_WEIGHT_DEFAULT
|
||||||
);
|
);
|
||||||
|
|
||||||
STATIC_UNIT_TESTED void imuComputeRotationMatrix(void)
|
STATIC_UNIT_TESTED void imuComputeRotationMatrix(void)
|
||||||
|
@ -323,7 +330,20 @@ static void imuCheckAndResetOrientationQuaternion(const fpQuaternion_t * quat, c
|
||||||
|
|
||||||
bool isGPSTrustworthy(void)
|
bool isGPSTrustworthy(void)
|
||||||
{
|
{
|
||||||
return sensors(SENSOR_GPS) && STATE(GPS_FIX) && gpsSol.numSat >= 6;
|
return (sensors(SENSOR_GPS) && STATE(GPS_FIX) && gpsSol.numSat >= 6)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float imuCalculateMcCogWeight(void)
|
||||||
|
{
|
||||||
|
float wCoG = imuCalculateAccelerometerWeightNearness(&imuMeasuredAccelBF);
|
||||||
|
float rotRateMagnitude = fast_fsqrtf(vectorNormSquared(&imuMeasuredRotationBFFiltered));
|
||||||
|
const float rateSlopeMax = DEGREES_TO_RADIANS((imuConfig()->acc_ignore_rate)) * 4.0f;
|
||||||
|
wCoG *= scaleRangef(constrainf(rotRateMagnitude, 0.0f, rateSlopeMax), 0.0f, rateSlopeMax, 1.0f, 0.0f);
|
||||||
|
return wCoG;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVector3_t * accBF, const fpVector3_t * magBF, bool useCOG, float courseOverGround, float accWScaler, float magWScaler)
|
static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVector3_t * accBF, const fpVector3_t * magBF, bool useCOG, float courseOverGround, float accWScaler, float magWScaler)
|
||||||
|
@ -339,11 +359,15 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
|
||||||
/* Step 1: Yaw correction */
|
/* Step 1: Yaw correction */
|
||||||
// Use measured magnetic field vector
|
// Use measured magnetic field vector
|
||||||
if (magBF || useCOG) {
|
if (magBF || useCOG) {
|
||||||
static const fpVector3_t vForward = { .v = { 1.0f, 0.0f, 0.0f } };
|
float wMag = 1.0f;
|
||||||
|
float wCoG = 1.0f;
|
||||||
|
if(magBF){wCoG *= imuConfig()->gps_yaw_weight / 100.0f;}
|
||||||
|
|
||||||
fpVector3_t vErr = { .v = { 0.0f, 0.0f, 0.0f } };
|
fpVector3_t vMagErr = { .v = { 0.0f, 0.0f, 0.0f } };
|
||||||
|
fpVector3_t vCoGErr = { .v = { 0.0f, 0.0f, 0.0f } };
|
||||||
|
|
||||||
if (magBF && vectorNormSquared(magBF) > 0.01f) {
|
if (magBF && vectorNormSquared(magBF) > 0.01f) {
|
||||||
|
wMag *= bellCurve((fast_fsqrtf(vectorNormSquared(magBF)) - 1024.0f) / 1024.0f, MAX_MAG_NEARNESS);
|
||||||
fpVector3_t vMag;
|
fpVector3_t vMag;
|
||||||
|
|
||||||
// For magnetometer correction we make an assumption that magnetic field is perpendicular to gravity (ignore Z-component in EF).
|
// For magnetometer correction we make an assumption that magnetic field is perpendicular to gravity (ignore Z-component in EF).
|
||||||
|
@ -369,13 +393,20 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
|
||||||
|
|
||||||
// Reference mag field vector heading is Magnetic North in EF. We compute that by rotating True North vector by declination and assuming Z-component is zero
|
// Reference mag field vector heading is Magnetic North in EF. We compute that by rotating True North vector by declination and assuming Z-component is zero
|
||||||
// magnetometer error is cross product between estimated magnetic north and measured magnetic north (calculated in EF)
|
// magnetometer error is cross product between estimated magnetic north and measured magnetic north (calculated in EF)
|
||||||
vectorCrossProduct(&vErr, &vMag, &vCorrectedMagNorth);
|
vectorCrossProduct(&vMagErr, &vMag, &vCorrectedMagNorth);
|
||||||
|
|
||||||
// Rotate error back into body frame
|
// Rotate error back into body frame
|
||||||
quaternionRotateVector(&vErr, &vErr, &orientation);
|
quaternionRotateVector(&vMagErr, &vMagErr, &orientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (useCOG) {
|
if (useCOG) {
|
||||||
|
fpVector3_t vForward = { .v = { 0.0f, 0.0f, 0.0f } };
|
||||||
|
//vForward as trust vector
|
||||||
|
if (STATE(MULTIROTOR) && (!isMixerTransitionMixing)){
|
||||||
|
vForward.z = 1.0f;
|
||||||
|
}else{
|
||||||
|
vForward.x = 1.0f;
|
||||||
|
}
|
||||||
fpVector3_t vHeadingEF;
|
fpVector3_t vHeadingEF;
|
||||||
|
|
||||||
// Use raw heading error (from GPS or whatever else)
|
// Use raw heading error (from GPS or whatever else)
|
||||||
|
@ -386,6 +417,7 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
|
||||||
// (Rxx; Ryx) - measured (estimated) heading vector (EF)
|
// (Rxx; Ryx) - measured (estimated) heading vector (EF)
|
||||||
// (-cos(COG), sin(COG)) - reference heading vector (EF)
|
// (-cos(COG), sin(COG)) - reference heading vector (EF)
|
||||||
|
|
||||||
|
float airSpeed = gpsSol.groundSpeed;
|
||||||
// Compute heading vector in EF from scalar CoG,x axis of accelerometer is pointing backwards.
|
// Compute heading vector in EF from scalar CoG,x axis of accelerometer is pointing backwards.
|
||||||
fpVector3_t vCoG = { .v = { -cos_approx(courseOverGround), sin_approx(courseOverGround), 0.0f } };
|
fpVector3_t vCoG = { .v = { -cos_approx(courseOverGround), sin_approx(courseOverGround), 0.0f } };
|
||||||
#if defined(USE_WIND_ESTIMATOR)
|
#if defined(USE_WIND_ESTIMATOR)
|
||||||
|
@ -395,12 +427,21 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
|
||||||
vectorScale(&vCoG, &vCoG, gpsSol.groundSpeed);
|
vectorScale(&vCoG, &vCoG, gpsSol.groundSpeed);
|
||||||
vCoG.x += getEstimatedWindSpeed(X);
|
vCoG.x += getEstimatedWindSpeed(X);
|
||||||
vCoG.y -= getEstimatedWindSpeed(Y);
|
vCoG.y -= getEstimatedWindSpeed(Y);
|
||||||
|
airSpeed = fast_fsqrtf(vectorNormSquared(&vCoG));
|
||||||
vectorNormalize(&vCoG, &vCoG);
|
vectorNormalize(&vCoG, &vCoG);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
wCoG *= scaleRangef(constrainf((airSpeed+gpsSol.groundSpeed)/2, 400, 1000), 400, 1000, 0.0f, 1.0f);
|
||||||
// Rotate Forward vector from BF to EF - will yield Heading vector in Earth frame
|
// Rotate Forward vector from BF to EF - will yield Heading vector in Earth frame
|
||||||
quaternionRotateVectorInv(&vHeadingEF, &vForward, &orientation);
|
quaternionRotateVectorInv(&vHeadingEF, &vForward, &orientation);
|
||||||
|
|
||||||
|
if (STATE(MULTIROTOR)){
|
||||||
|
//when multicopter`s orientation or speed is changing rapidly. less weight on gps heading
|
||||||
|
wCoG *= imuCalculateMcCogWeight();
|
||||||
|
//scale accroading to multirotor`s tilt angle
|
||||||
|
wCoG *= scaleRangef(constrainf(vHeadingEF.z, COS20DEG, COS10DEG), COS20DEG, COS10DEG, 1.0f, 0.0f);
|
||||||
|
//for inverted flying, wCoG is lowered by imuCalculateMcCogWeight no additional processing needed
|
||||||
|
}
|
||||||
vHeadingEF.z = 0.0f;
|
vHeadingEF.z = 0.0f;
|
||||||
|
|
||||||
// We zeroed out vHeadingEF.z - make sure the whole vector didn't go to zero
|
// We zeroed out vHeadingEF.z - make sure the whole vector didn't go to zero
|
||||||
|
@ -409,13 +450,16 @@ static void imuMahonyAHRSupdate(float dt, const fpVector3_t * gyroBF, const fpVe
|
||||||
vectorNormalize(&vHeadingEF, &vHeadingEF);
|
vectorNormalize(&vHeadingEF, &vHeadingEF);
|
||||||
|
|
||||||
// error is cross product between reference heading and estimated heading (calculated in EF)
|
// error is cross product between reference heading and estimated heading (calculated in EF)
|
||||||
vectorCrossProduct(&vErr, &vCoG, &vHeadingEF);
|
vectorCrossProduct(&vCoGErr, &vCoG, &vHeadingEF);
|
||||||
|
|
||||||
// Rotate error back into body frame
|
// Rotate error back into body frame
|
||||||
quaternionRotateVector(&vErr, &vErr, &orientation);
|
quaternionRotateVector(&vCoGErr, &vCoGErr, &orientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fpVector3_t vErr = { .v = { 0.0f, 0.0f, 0.0f } };
|
||||||
|
vectorScale(&vMagErr, &vMagErr, wMag);
|
||||||
|
vectorScale(&vCoGErr, &vCoGErr, wCoG);
|
||||||
|
vectorAdd(&vErr, &vMagErr, &vCoGErr);
|
||||||
// Compute and apply integral feedback if enabled
|
// Compute and apply integral feedback if enabled
|
||||||
if (imuRuntimeConfig.dcm_ki_mag > 0.0f) {
|
if (imuRuntimeConfig.dcm_ki_mag > 0.0f) {
|
||||||
// Stop integrating if spinning beyond the certain limit
|
// Stop integrating if spinning beyond the certain limit
|
||||||
|
@ -535,10 +579,10 @@ STATIC_UNIT_TESTED void imuUpdateEulerAngles(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static float imuCalculateAccelerometerWeightNearness(void)
|
static float imuCalculateAccelerometerWeightNearness(fpVector3_t* accBF)
|
||||||
{
|
{
|
||||||
fpVector3_t accBFNorm;
|
fpVector3_t accBFNorm;
|
||||||
vectorScale(&accBFNorm, &compansatedGravityBF, 1.0f / GRAVITY_CMSS);
|
vectorScale(&accBFNorm, accBF, 1.0f / GRAVITY_CMSS);
|
||||||
const float accMagnitudeSq = vectorNormSquared(&accBFNorm);
|
const float accMagnitudeSq = vectorNormSquared(&accBFNorm);
|
||||||
const float accWeight_Nearness = bellCurve(fast_fsqrtf(accMagnitudeSq) - 1.0f, MAX_ACC_NEARNESS);
|
const float accWeight_Nearness = bellCurve(fast_fsqrtf(accMagnitudeSq) - 1.0f, MAX_ACC_NEARNESS);
|
||||||
return accWeight_Nearness;
|
return accWeight_Nearness;
|
||||||
|
@ -672,36 +716,29 @@ static void imuCalculateEstimatedAttitude(float dT)
|
||||||
bool useMag = false;
|
bool useMag = false;
|
||||||
bool useCOG = false;
|
bool useCOG = false;
|
||||||
#if defined(USE_GPS)
|
#if defined(USE_GPS)
|
||||||
if (STATE(FIXED_WING_LEGACY)) {
|
bool canUseCOG = isGPSHeadingValid();
|
||||||
bool canUseCOG = isGPSHeadingValid();
|
|
||||||
|
|
||||||
// Prefer compass (if available)
|
// Use compass (if available)
|
||||||
if (canUseMAG) {
|
if (canUseMAG) {
|
||||||
useMag = true;
|
useMag = true;
|
||||||
gpsHeadingInitialized = true; // GPS heading initialised from MAG, continue on GPS if compass fails
|
gpsHeadingInitialized = true; // GPS heading initialised from MAG, continue on GPS if compass fails
|
||||||
}
|
|
||||||
else if (canUseCOG) {
|
|
||||||
if (gpsHeadingInitialized) {
|
|
||||||
courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse);
|
|
||||||
useCOG = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Re-initialize quaternion from known Roll, Pitch and GPS heading
|
|
||||||
imuComputeQuaternionFromRPY(attitude.values.roll, attitude.values.pitch, gpsSol.groundCourse);
|
|
||||||
gpsHeadingInitialized = true;
|
|
||||||
|
|
||||||
// Force reset of heading hold target
|
|
||||||
resetHeadingHoldTarget(DECIDEGREES_TO_DEGREES(attitude.values.yaw));
|
|
||||||
}
|
|
||||||
} else if (!ARMING_FLAG(ARMED)) {
|
|
||||||
gpsHeadingInitialized = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
// Use GPS (if available)
|
||||||
// Multicopters don't use GPS heading
|
if (canUseCOG) {
|
||||||
if (canUseMAG) {
|
if (gpsHeadingInitialized) {
|
||||||
useMag = true;
|
courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse);
|
||||||
|
useCOG = true;
|
||||||
}
|
}
|
||||||
|
else if (!canUseMAG) {
|
||||||
|
// Re-initialize quaternion from known Roll, Pitch and GPS heading
|
||||||
|
imuComputeQuaternionFromRPY(attitude.values.roll, attitude.values.pitch, gpsSol.groundCourse);
|
||||||
|
gpsHeadingInitialized = true;
|
||||||
|
|
||||||
|
// Force reset of heading hold target
|
||||||
|
resetHeadingHoldTarget(DECIDEGREES_TO_DEGREES(attitude.values.yaw));
|
||||||
|
}
|
||||||
|
} else if (!ARMING_FLAG(ARMED)) {
|
||||||
|
gpsHeadingInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
imuCalculateFilters(dT);
|
imuCalculateFilters(dT);
|
||||||
|
@ -751,7 +788,7 @@ static void imuCalculateEstimatedAttitude(float dT)
|
||||||
}
|
}
|
||||||
compansatedGravityBF = imuMeasuredAccelBF
|
compansatedGravityBF = imuMeasuredAccelBF
|
||||||
#endif
|
#endif
|
||||||
float accWeight = imuGetPGainScaleFactor() * imuCalculateAccelerometerWeightNearness();
|
float accWeight = imuGetPGainScaleFactor() * imuCalculateAccelerometerWeightNearness(&compansatedGravityBF);
|
||||||
accWeight = accWeight * imuCalculateAccelerometerWeightRateIgnore(acc_ignore_slope_multipiler);
|
accWeight = accWeight * imuCalculateAccelerometerWeightRateIgnore(acc_ignore_slope_multipiler);
|
||||||
const bool useAcc = (accWeight > 0.001f);
|
const bool useAcc = (accWeight > 0.001f);
|
||||||
|
|
||||||
|
@ -806,6 +843,7 @@ void imuUpdateAttitude(timeUs_t currentTimeUs)
|
||||||
acc.accADCf[Z] = 0.0f;
|
acc.accADCf[Z] = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool isImuReady(void)
|
bool isImuReady(void)
|
||||||
{
|
{
|
||||||
|
@ -814,7 +852,7 @@ bool isImuReady(void)
|
||||||
|
|
||||||
bool isImuHeadingValid(void)
|
bool isImuHeadingValid(void)
|
||||||
{
|
{
|
||||||
return (sensors(SENSOR_MAG) && STATE(COMPASS_CALIBRATED)) || (STATE(FIXED_WING_LEGACY) && gpsHeadingInitialized);
|
return (sensors(SENSOR_MAG) && STATE(COMPASS_CALIBRATED)) || gpsHeadingInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
float calculateCosTiltAngle(void)
|
float calculateCosTiltAngle(void)
|
||||||
|
|
|
@ -54,6 +54,7 @@ typedef struct imuConfig_s {
|
||||||
uint8_t acc_ignore_slope;
|
uint8_t acc_ignore_slope;
|
||||||
uint8_t gps_yaw_windcomp;
|
uint8_t gps_yaw_windcomp;
|
||||||
uint8_t inertia_comp_method;
|
uint8_t inertia_comp_method;
|
||||||
|
uint16_t gps_yaw_weight;
|
||||||
} imuConfig_t;
|
} imuConfig_t;
|
||||||
|
|
||||||
PG_DECLARE(imuConfig_t, imuConfig);
|
PG_DECLARE(imuConfig_t, imuConfig);
|
||||||
|
|
|
@ -159,6 +159,8 @@ typedef void (*pidControllerFnPtr)(pidState_t *pidState, flight_dynamics_index_t
|
||||||
static EXTENDED_FASTRAM pidControllerFnPtr pidControllerApplyFn;
|
static EXTENDED_FASTRAM pidControllerFnPtr pidControllerApplyFn;
|
||||||
static EXTENDED_FASTRAM filterApplyFnPtr dTermLpfFilterApplyFn;
|
static EXTENDED_FASTRAM filterApplyFnPtr dTermLpfFilterApplyFn;
|
||||||
static EXTENDED_FASTRAM bool levelingEnabled = false;
|
static EXTENDED_FASTRAM bool levelingEnabled = false;
|
||||||
|
static EXTENDED_FASTRAM bool restartAngleHoldMode = true;
|
||||||
|
static EXTENDED_FASTRAM bool angleHoldIsLevel = false;
|
||||||
|
|
||||||
#define FIXED_WING_LEVEL_TRIM_MAX_ANGLE 10.0f // Max angle auto trimming can demand
|
#define FIXED_WING_LEVEL_TRIM_MAX_ANGLE 10.0f // Max angle auto trimming can demand
|
||||||
#define FIXED_WING_LEVEL_TRIM_DIVIDER 50.0f
|
#define FIXED_WING_LEVEL_TRIM_DIVIDER 50.0f
|
||||||
|
@ -838,7 +840,7 @@ static void FAST_CODE NOINLINE pidApplyMulticopterRateController(pidState_t *pid
|
||||||
|
|
||||||
pidState->errorGyroIf += (itermErrorRate * pidState->kI * antiWindupScaler * dT)
|
pidState->errorGyroIf += (itermErrorRate * pidState->kI * antiWindupScaler * dT)
|
||||||
+ ((newOutputLimited - newOutput) * pidState->kT * antiWindupScaler * dT);
|
+ ((newOutputLimited - newOutput) * pidState->kT * antiWindupScaler * dT);
|
||||||
|
|
||||||
if (pidProfile()->pidItermLimitPercent != 0){
|
if (pidProfile()->pidItermLimitPercent != 0){
|
||||||
float itermLimit = pidState->pidSumLimit * pidProfile()->pidItermLimitPercent * 0.01f;
|
float itermLimit = pidState->pidSumLimit * pidProfile()->pidItermLimitPercent * 0.01f;
|
||||||
pidState->errorGyroIf = constrainf(pidState->errorGyroIf, -itermLimit, +itermLimit);
|
pidState->errorGyroIf = constrainf(pidState->errorGyroIf, -itermLimit, +itermLimit);
|
||||||
|
@ -1064,9 +1066,62 @@ void checkItermFreezingActive(pidState_t *pidState, flight_dynamics_index_t axis
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAngleHoldLevel(void)
|
||||||
|
{
|
||||||
|
return angleHoldIsLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateAngleHold(float *angleTarget, uint8_t axis)
|
||||||
|
{
|
||||||
|
int8_t navAngleHoldAxis = navCheckActiveAngleHoldAxis();
|
||||||
|
|
||||||
|
if (!restartAngleHoldMode) { // set restart flag when anglehold is inactive
|
||||||
|
restartAngleHoldMode = !FLIGHT_MODE(ANGLEHOLD_MODE) && navAngleHoldAxis == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((FLIGHT_MODE(ANGLEHOLD_MODE) || axis == navAngleHoldAxis) && !isFlightAxisAngleOverrideActive(axis)) {
|
||||||
|
/* angleHoldTarget stores attitude values using a zero datum when level.
|
||||||
|
* This requires angleHoldTarget pitch to be corrected for fixedWingLevelTrim so it is 0
|
||||||
|
* when the craft is level even though attitude pitch is non zero in this case.
|
||||||
|
* angleTarget pitch is corrected back to fixedWingLevelTrim datum on return from function */
|
||||||
|
|
||||||
|
static int16_t angleHoldTarget[2];
|
||||||
|
|
||||||
|
if (restartAngleHoldMode) { // set target attitude to current attitude on activation
|
||||||
|
angleHoldTarget[FD_ROLL] = attitude.raw[FD_ROLL];
|
||||||
|
angleHoldTarget[FD_PITCH] = attitude.raw[FD_PITCH] + DEGREES_TO_DECIDEGREES(fixedWingLevelTrim);
|
||||||
|
restartAngleHoldMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set flag indicating anglehold is level
|
||||||
|
if (FLIGHT_MODE(ANGLEHOLD_MODE)) {
|
||||||
|
angleHoldIsLevel = angleHoldTarget[FD_ROLL] == 0 && angleHoldTarget[FD_PITCH] == 0;
|
||||||
|
} else {
|
||||||
|
angleHoldIsLevel = angleHoldTarget[navAngleHoldAxis] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t bankLimit = pidProfile()->max_angle_inclination[axis];
|
||||||
|
|
||||||
|
// use Nav bank angle limits if Nav active
|
||||||
|
if (navAngleHoldAxis == FD_ROLL) {
|
||||||
|
bankLimit = DEGREES_TO_DECIDEGREES(navConfig()->fw.max_bank_angle);
|
||||||
|
} else if (navAngleHoldAxis == FD_PITCH) {
|
||||||
|
bankLimit = DEGREES_TO_DECIDEGREES(navConfig()->fw.max_climb_angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t levelTrim = axis == FD_PITCH ? DEGREES_TO_DECIDEGREES(fixedWingLevelTrim) : 0;
|
||||||
|
if (calculateRollPitchCenterStatus() == CENTERED) {
|
||||||
|
angleHoldTarget[axis] = ABS(angleHoldTarget[axis]) < 30 ? 0 : angleHoldTarget[axis]; // snap to level when within 3 degs of level
|
||||||
|
*angleTarget = constrain(angleHoldTarget[axis] - levelTrim, -bankLimit, bankLimit);
|
||||||
|
} else {
|
||||||
|
*angleTarget = constrain(attitude.raw[axis] + *angleTarget + levelTrim, -bankLimit, bankLimit);
|
||||||
|
angleHoldTarget[axis] = attitude.raw[axis] + levelTrim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FAST_CODE pidController(float dT)
|
void FAST_CODE pidController(float dT)
|
||||||
{
|
{
|
||||||
|
|
||||||
const float dT_inv = 1.0f / dT;
|
const float dT_inv = 1.0f / dT;
|
||||||
|
|
||||||
if (!pidFiltersConfigured) {
|
if (!pidFiltersConfigured) {
|
||||||
|
@ -1115,21 +1170,30 @@ void FAST_CODE pidController(float dT)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Run control for ANGLE_MODE, HORIZON_MODE, and HEADING_LOCK
|
// Step 3: Run control for ANGLE_MODE, HORIZON_MODE and ANGLEHOLD_MODE
|
||||||
const float horizonRateMagnitude = calcHorizonRateMagnitude();
|
const float horizonRateMagnitude = FLIGHT_MODE(HORIZON_MODE) ? calcHorizonRateMagnitude() : 0.0f;
|
||||||
levelingEnabled = false;
|
levelingEnabled = false;
|
||||||
|
angleHoldIsLevel = false;
|
||||||
|
|
||||||
for (uint8_t axis = FD_ROLL; axis <= FD_PITCH; axis++) {
|
for (uint8_t axis = FD_ROLL; axis <= FD_PITCH; axis++) {
|
||||||
if (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE) || isFlightAxisAngleOverrideActive(axis)) {
|
if (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE) || FLIGHT_MODE(ANGLEHOLD_MODE) || isFlightAxisAngleOverrideActive(axis)) {
|
||||||
//If axis angle override, get the correct angle from Logic Conditions
|
// If axis angle override, get the correct angle from Logic Conditions
|
||||||
float angleTarget = getFlightAxisAngleOverride(axis, computePidLevelTarget(axis));
|
float angleTarget = getFlightAxisAngleOverride(axis, computePidLevelTarget(axis));
|
||||||
|
|
||||||
//Apply the Level PID controller
|
if (STATE(AIRPLANE)) { // update anglehold mode
|
||||||
|
updateAngleHold(&angleTarget, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the Level PID controller
|
||||||
pidLevel(angleTarget, &pidState[axis], axis, horizonRateMagnitude, dT);
|
pidLevel(angleTarget, &pidState[axis], axis, horizonRateMagnitude, dT);
|
||||||
canUseFpvCameraMix = false; // FPVANGLEMIX is incompatible with ANGLE/HORIZON
|
canUseFpvCameraMix = false; // FPVANGLEMIX is incompatible with ANGLE/HORIZON
|
||||||
levelingEnabled = true;
|
levelingEnabled = true;
|
||||||
|
} else {
|
||||||
|
restartAngleHoldMode = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply Turn Assistance
|
||||||
if ((FLIGHT_MODE(TURN_ASSISTANT) || navigationRequiresTurnAssistance()) && (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE))) {
|
if ((FLIGHT_MODE(TURN_ASSISTANT) || navigationRequiresTurnAssistance()) && (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE))) {
|
||||||
float bankAngleTarget = DECIDEGREES_TO_RADIANS(pidRcCommandToAngle(rcCommand[FD_ROLL], pidProfile()->max_angle_inclination[FD_ROLL]));
|
float bankAngleTarget = DECIDEGREES_TO_RADIANS(pidRcCommandToAngle(rcCommand[FD_ROLL], pidProfile()->max_angle_inclination[FD_ROLL]));
|
||||||
float pitchAngleTarget = DECIDEGREES_TO_RADIANS(pidRcCommandToAngle(rcCommand[FD_PITCH], pidProfile()->max_angle_inclination[FD_PITCH]));
|
float pitchAngleTarget = DECIDEGREES_TO_RADIANS(pidRcCommandToAngle(rcCommand[FD_PITCH], pidProfile()->max_angle_inclination[FD_PITCH]));
|
||||||
|
@ -1137,6 +1201,7 @@ void FAST_CODE pidController(float dT)
|
||||||
canUseFpvCameraMix = false; // FPVANGLEMIX is incompatible with TURN_ASSISTANT
|
canUseFpvCameraMix = false; // FPVANGLEMIX is incompatible with TURN_ASSISTANT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply FPV camera mix
|
||||||
if (canUseFpvCameraMix && IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX) && currentControlRateProfile->misc.fpvCamAngleDegrees && STATE(MULTIROTOR)) {
|
if (canUseFpvCameraMix && IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX) && currentControlRateProfile->misc.fpvCamAngleDegrees && STATE(MULTIROTOR)) {
|
||||||
pidApplyFpvCameraAngleMix(pidState, currentControlRateProfile->misc.fpvCamAngleDegrees);
|
pidApplyFpvCameraAngleMix(pidState, currentControlRateProfile->misc.fpvCamAngleDegrees);
|
||||||
}
|
}
|
||||||
|
@ -1272,7 +1337,7 @@ bool isFixedWingLevelTrimActive(void)
|
||||||
return IS_RC_MODE_ACTIVE(BOXAUTOLEVEL) && !areSticksDeflected() &&
|
return IS_RC_MODE_ACTIVE(BOXAUTOLEVEL) && !areSticksDeflected() &&
|
||||||
(FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE)) &&
|
(FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE)) &&
|
||||||
!FLIGHT_MODE(SOARING_MODE) && !FLIGHT_MODE(MANUAL_MODE) &&
|
!FLIGHT_MODE(SOARING_MODE) && !FLIGHT_MODE(MANUAL_MODE) &&
|
||||||
!navigationIsControllingAltitude();
|
!navigationIsControllingAltitude() && !(navCheckActiveAngleHoldAxis() == FD_PITCH && !angleHoldIsLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateFixedWingLevelTrim(timeUs_t currentTimeUs)
|
void updateFixedWingLevelTrim(timeUs_t currentTimeUs)
|
||||||
|
|
|
@ -220,3 +220,4 @@ pidType_e pidIndexGetType(pidIndex_e pidIndex);
|
||||||
bool isFixedWingLevelTrimActive(void);
|
bool isFixedWingLevelTrimActive(void);
|
||||||
void updateFixedWingLevelTrim(timeUs_t currentTimeUs);
|
void updateFixedWingLevelTrim(timeUs_t currentTimeUs);
|
||||||
float getFixedWingLevelTrim(void);
|
float getFixedWingLevelTrim(void);
|
||||||
|
bool isAngleHoldLevel(void);
|
||||||
|
|
|
@ -385,20 +385,6 @@ void servoMixer(float dT)
|
||||||
servo[target] += ((int32_t)inputLimited * currentServoMixer[i].rate) / 100;
|
servo[target] += ((int32_t)inputLimited * currentServoMixer[i].rate) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* When not armed, apply servo low position to all outputs that include a throttle or stabilized throttle in the mix
|
|
||||||
*/
|
|
||||||
if (!ARMING_FLAG(ARMED)) {
|
|
||||||
for (int i = 0; i < servoRuleCount; i++) {
|
|
||||||
const uint8_t target = currentServoMixer[i].targetChannel;
|
|
||||||
const uint8_t from = currentServoMixer[i].inputSource;
|
|
||||||
|
|
||||||
if (from == INPUT_STABILIZED_THROTTLE || from == INPUT_RC_THROTTLE) {
|
|
||||||
servo[target] = motorConfig()->mincommand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
|
for (int i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -429,6 +415,20 @@ void servoMixer(float dT)
|
||||||
*/
|
*/
|
||||||
servo[i] = constrain(servo[i], servoParams(i)->min, servoParams(i)->max);
|
servo[i] = constrain(servo[i], servoParams(i)->min, servoParams(i)->max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When not armed, apply servo low position to all outputs that include a throttle or stabilizet throttle in the mix
|
||||||
|
*/
|
||||||
|
if (!ARMING_FLAG(ARMED)) {
|
||||||
|
for (int i = 0; i < servoRuleCount; i++) {
|
||||||
|
const uint8_t target = currentServoMixer[i].targetChannel;
|
||||||
|
const uint8_t from = currentServoMixer[i].inputSource;
|
||||||
|
|
||||||
|
if (from == INPUT_STABILIZED_THROTTLE || from == INPUT_RC_THROTTLE) {
|
||||||
|
servo[target] = motorConfig()->mincommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SERVO_AUTOTRIM_TIMER_MS 2000
|
#define SERVO_AUTOTRIM_TIMER_MS 2000
|
||||||
|
|
|
@ -52,7 +52,11 @@ static float lastFuselageDirection[XYZ_AXIS_COUNT];
|
||||||
|
|
||||||
bool isEstimatedWindSpeedValid(void)
|
bool isEstimatedWindSpeedValid(void)
|
||||||
{
|
{
|
||||||
return hasValidWindEstimate;
|
return hasValidWindEstimate
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX) //use any wind estimate with GPS fix estimation.
|
||||||
|
#endif
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getEstimatedWindSpeed(int axis)
|
float getEstimatedWindSpeed(int axis)
|
||||||
|
@ -83,15 +87,18 @@ void updateWindEstimator(timeUs_t currentTimeUs)
|
||||||
static float lastValidEstimateAltitude = 0.0f;
|
static float lastValidEstimateAltitude = 0.0f;
|
||||||
float currentAltitude = gpsSol.llh.alt / 100.0f; // altitude in m
|
float currentAltitude = gpsSol.llh.alt / 100.0f; // altitude in m
|
||||||
|
|
||||||
if ((US2S(currentTimeUs - lastValidWindEstimate) + WINDESTIMATOR_ALTITUDE_SCALE * fabsf(currentAltitude - lastValidEstimateAltitude)) > WINDESTIMATOR_TIMEOUT)
|
if ((US2S(currentTimeUs - lastValidWindEstimate) + WINDESTIMATOR_ALTITUDE_SCALE * fabsf(currentAltitude - lastValidEstimateAltitude)) > WINDESTIMATOR_TIMEOUT) {
|
||||||
{
|
|
||||||
hasValidWindEstimate = false;
|
hasValidWindEstimate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!STATE(FIXED_WING_LEGACY) ||
|
if (!STATE(FIXED_WING_LEGACY) ||
|
||||||
!isGPSHeadingValid() ||
|
!isGPSHeadingValid() ||
|
||||||
!gpsSol.flags.validVelNE ||
|
!gpsSol.flags.validVelNE ||
|
||||||
!gpsSol.flags.validVelD) {
|
!gpsSol.flags.validVelD
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,12 @@
|
||||||
#include "fc/runtime_config.h"
|
#include "fc/runtime_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "fc/rc_modes.h"
|
||||||
|
|
||||||
#include "sensors/sensors.h"
|
#include "sensors/sensors.h"
|
||||||
#include "sensors/compass.h"
|
#include "sensors/compass.h"
|
||||||
|
#include "sensors/barometer.h"
|
||||||
|
#include "sensors/pitotmeter.h"
|
||||||
|
|
||||||
#include "io/serial.h"
|
#include "io/serial.h"
|
||||||
#include "io/gps.h"
|
#include "io/gps.h"
|
||||||
|
@ -54,6 +58,7 @@
|
||||||
#include "io/gps_ublox.h"
|
#include "io/gps_ublox.h"
|
||||||
|
|
||||||
#include "navigation/navigation.h"
|
#include "navigation/navigation.h"
|
||||||
|
#include "navigation/navigation_private.h"
|
||||||
|
|
||||||
#include "config/feature.h"
|
#include "config/feature.h"
|
||||||
|
|
||||||
|
@ -61,6 +66,13 @@
|
||||||
#include "fc/runtime_config.h"
|
#include "fc/runtime_config.h"
|
||||||
#include "fc/settings.h"
|
#include "fc/settings.h"
|
||||||
|
|
||||||
|
#include "flight/imu.h"
|
||||||
|
#include "flight/wind_estimator.h"
|
||||||
|
#include "flight/pid.h"
|
||||||
|
#include "flight/mixer.h"
|
||||||
|
|
||||||
|
#include "programming/logic_condition.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool isDriverBased;
|
bool isDriverBased;
|
||||||
portMode_t portMode; // Port mode RX/TX (only for serial based)
|
portMode_t portMode; // Port mode RX/TX (only for serial based)
|
||||||
|
@ -71,7 +83,13 @@ typedef struct {
|
||||||
// GPS public data
|
// GPS public data
|
||||||
gpsReceiverData_t gpsState;
|
gpsReceiverData_t gpsState;
|
||||||
gpsStatistics_t gpsStats;
|
gpsStatistics_t gpsStats;
|
||||||
gpsSolutionData_t gpsSol;
|
|
||||||
|
//it is not safe to access gpsSolDRV which is filled in gps thread by driver.
|
||||||
|
//gpsSolDRV can be accessed only after driver signalled that data is ready
|
||||||
|
//we copy gpsSolDRV to gpsSol, process by "Disable GPS logic condition" and "GPS Fix estimation" features
|
||||||
|
//and use it in the rest of code.
|
||||||
|
gpsSolutionData_t gpsSolDRV; //filled by driver
|
||||||
|
gpsSolutionData_t gpsSol; //used in the rest of the code
|
||||||
|
|
||||||
// Map gpsBaudRate_e index to baudRate_e
|
// Map gpsBaudRate_e index to baudRate_e
|
||||||
baudRate_e gpsToSerialBaudRate[GPS_BAUDRATE_COUNT] = { BAUD_115200, BAUD_57600, BAUD_38400, BAUD_19200, BAUD_9600, BAUD_230400, BAUD_460800, BAUD_921600 };
|
baudRate_e gpsToSerialBaudRate[GPS_BAUDRATE_COUNT] = { BAUD_115200, BAUD_57600, BAUD_38400, BAUD_19200, BAUD_9600, BAUD_230400, BAUD_460800, BAUD_921600 };
|
||||||
|
@ -203,22 +221,177 @@ void gpsSetProtocolTimeout(timeMs_t timeoutMs)
|
||||||
gpsState.timeoutMs = timeoutMs;
|
gpsState.timeoutMs = timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpsProcessNewSolutionData(void)
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
bool canEstimateGPSFix(void)
|
||||||
{
|
{
|
||||||
// Set GPS fix flag only if we have 3D fix
|
#if defined(USE_GPS) && defined(USE_BARO)
|
||||||
if (gpsSol.fixType == GPS_FIX_3D && gpsSol.numSat >= gpsConfig()->gpsMinSats) {
|
|
||||||
ENABLE_STATE(GPS_FIX);
|
//we do not check neither sensors(SENSOR_GPS) nor FEATURE(FEATURE_GPS) because:
|
||||||
}
|
//1) checking STATE(GPS_FIX_HOME) is enough to ensure that GPS sensor was initialized once
|
||||||
else {
|
//2) sensors(SENSOR_GPS) is false on GPS timeout. We also want to support GPS timeouts, not just lost fix
|
||||||
/* When no fix available - reset flags as well */
|
return positionEstimationConfig()->allow_gps_fix_estimation && STATE(AIRPLANE) &&
|
||||||
|
sensors(SENSOR_BARO) && baroIsHealthy() &&
|
||||||
|
ARMING_FLAG(WAS_EVER_ARMED) && STATE(GPS_FIX_HOME);
|
||||||
|
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
void processDisableGPSFix(void)
|
||||||
|
{
|
||||||
|
static int32_t last_lat = 0;
|
||||||
|
static int32_t last_lon = 0;
|
||||||
|
static int32_t last_alt = 0;
|
||||||
|
|
||||||
|
if (LOGIC_CONDITION_GLOBAL_FLAG(LOGIC_CONDITION_GLOBAL_FLAG_DISABLE_GPS_FIX)) {
|
||||||
|
gpsSol.fixType = GPS_NO_FIX;
|
||||||
|
gpsSol.hdop = 9999;
|
||||||
|
gpsSol.numSat = 0;
|
||||||
|
|
||||||
gpsSol.flags.validVelNE = false;
|
gpsSol.flags.validVelNE = false;
|
||||||
gpsSol.flags.validVelD = false;
|
gpsSol.flags.validVelD = false;
|
||||||
gpsSol.flags.validEPE = false;
|
gpsSol.flags.validEPE = false;
|
||||||
DISABLE_STATE(GPS_FIX);
|
gpsSol.flags.validTime = false;
|
||||||
|
|
||||||
|
//freeze coordinates
|
||||||
|
gpsSol.llh.lat = last_lat;
|
||||||
|
gpsSol.llh.lon = last_lon;
|
||||||
|
gpsSol.llh.alt = last_alt;
|
||||||
|
} else {
|
||||||
|
last_lat = gpsSol.llh.lat;
|
||||||
|
last_lon = gpsSol.llh.lon;
|
||||||
|
last_alt = gpsSol.llh.alt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
//called after gpsSolDRV is copied to gpsSol and processed by "Disable GPS Fix logical condition"
|
||||||
|
void updateEstimatedGPSFix(void)
|
||||||
|
{
|
||||||
|
static uint32_t lastUpdateMs = 0;
|
||||||
|
static int32_t estimated_lat = 0;
|
||||||
|
static int32_t estimated_lon = 0;
|
||||||
|
static int32_t estimated_alt = 0;
|
||||||
|
|
||||||
|
uint32_t t = millis();
|
||||||
|
int32_t dt = t - lastUpdateMs;
|
||||||
|
lastUpdateMs = t;
|
||||||
|
|
||||||
|
bool sensorHasFix = gpsSol.fixType == GPS_FIX_3D && gpsSol.numSat >= gpsConfig()->gpsMinSats;
|
||||||
|
|
||||||
|
if (sensorHasFix || !canEstimateGPSFix()) {
|
||||||
|
estimated_lat = gpsSol.llh.lat;
|
||||||
|
estimated_lon = gpsSol.llh.lon;
|
||||||
|
estimated_alt = posControl.gpsOrigin.alt + baro.BaroAlt;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sensor as ready and available
|
gpsSol.fixType = GPS_FIX_3D;
|
||||||
sensorsSet(SENSOR_GPS);
|
gpsSol.hdop = 99;
|
||||||
|
gpsSol.numSat = 99;
|
||||||
|
|
||||||
|
gpsSol.eph = 100;
|
||||||
|
gpsSol.epv = 100;
|
||||||
|
|
||||||
|
gpsSol.flags.validVelNE = true;
|
||||||
|
gpsSol.flags.validVelD = false; //do not provide velocity.z
|
||||||
|
gpsSol.flags.validEPE = true;
|
||||||
|
gpsSol.flags.validTime = false;
|
||||||
|
|
||||||
|
float speed = pidProfile()->fixedWingReferenceAirspeed;
|
||||||
|
|
||||||
|
#ifdef USE_PITOT
|
||||||
|
if (sensors(SENSOR_PITOT) && pitotIsHealthy()) {
|
||||||
|
speed = getAirspeedEstimate();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float velX = rMat[0][0] * speed;
|
||||||
|
float velY = -rMat[1][0] * speed;
|
||||||
|
// here (velX, velY) is estimated horizontal speed without wind influence = airspeed, cm/sec in NEU frame
|
||||||
|
|
||||||
|
if (isEstimatedWindSpeedValid()) {
|
||||||
|
velX += getEstimatedWindSpeed(X);
|
||||||
|
velY += getEstimatedWindSpeed(Y);
|
||||||
|
}
|
||||||
|
// here (velX, velY) is estimated horizontal speed with wind influence = ground speed
|
||||||
|
|
||||||
|
if (STATE(LANDING_DETECTED) || ((posControl.navState == NAV_STATE_RTH_LANDING) && (getThrottlePercent(false) == 0))) {
|
||||||
|
velX = 0;
|
||||||
|
velY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
estimated_lat += (int32_t)( velX * dt / (DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR * 1000 ) );
|
||||||
|
estimated_lon += (int32_t)( velY * dt / (DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR * 1000 * posControl.gpsOrigin.scale) );
|
||||||
|
estimated_alt = posControl.gpsOrigin.alt + baro.BaroAlt;
|
||||||
|
|
||||||
|
gpsSol.llh.lat = estimated_lat;
|
||||||
|
gpsSol.llh.lon = estimated_lon;
|
||||||
|
gpsSol.llh.alt = estimated_alt;
|
||||||
|
|
||||||
|
gpsSol.groundSpeed = (int16_t)fast_fsqrtf(velX * velX + velY * velY);
|
||||||
|
|
||||||
|
float groundCourse = atan2_approx(velY, velX); // atan2 returns [-M_PI, M_PI], with 0 indicating the vector points in the X direction
|
||||||
|
if (groundCourse < 0) {
|
||||||
|
groundCourse += 2 * M_PIf;
|
||||||
|
}
|
||||||
|
gpsSol.groundCourse = RADIANS_TO_DECIDEGREES(groundCourse);
|
||||||
|
|
||||||
|
gpsSol.velNED[X] = (int16_t)(velX);
|
||||||
|
gpsSol.velNED[Y] = (int16_t)(velY);
|
||||||
|
gpsSol.velNED[Z] = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void gpsProcessNewDriverData(void)
|
||||||
|
{
|
||||||
|
gpsSol = gpsSolDRV;
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
processDisableGPSFix();
|
||||||
|
updateEstimatedGPSFix();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//called after:
|
||||||
|
//1)driver copies gpsSolDRV to gpsSol
|
||||||
|
//2)gpsSol is processed by "Disable GPS logical switch"
|
||||||
|
//3)gpsSol is processed by GPS Fix estimator - updateEstimatedGPSFix()
|
||||||
|
//On GPS sensor timeout - called after updateEstimatedGPSFix()
|
||||||
|
void gpsProcessNewSolutionData(bool timeout)
|
||||||
|
{
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if ( gpsSol.numSat == 99 ) {
|
||||||
|
ENABLE_STATE(GPS_ESTIMATED_FIX);
|
||||||
|
DISABLE_STATE(GPS_FIX);
|
||||||
|
} else {
|
||||||
|
DISABLE_STATE(GPS_ESTIMATED_FIX);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set GPS fix flag only if we have 3D fix
|
||||||
|
if (gpsSol.fixType == GPS_FIX_3D && gpsSol.numSat >= gpsConfig()->gpsMinSats) {
|
||||||
|
ENABLE_STATE(GPS_FIX);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* When no fix available - reset flags as well */
|
||||||
|
gpsSol.flags.validVelNE = false;
|
||||||
|
gpsSol.flags.validVelD = false;
|
||||||
|
gpsSol.flags.validEPE = false;
|
||||||
|
DISABLE_STATE(GPS_FIX);
|
||||||
|
}
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!timeout) {
|
||||||
|
// Data came from GPS sensor - set sensor as ready and available (it may still not have GPS fix)
|
||||||
|
sensorsSet(SENSOR_GPS);
|
||||||
|
}
|
||||||
|
|
||||||
// Pass on GPS update to NAV and IMU
|
// Pass on GPS update to NAV and IMU
|
||||||
onNewGPSData();
|
onNewGPSData();
|
||||||
|
@ -237,20 +410,35 @@ void gpsProcessNewSolutionData(void)
|
||||||
gpsSol.flags.gpsHeartbeat = !gpsSol.flags.gpsHeartbeat;
|
gpsSol.flags.gpsHeartbeat = !gpsSol.flags.gpsHeartbeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpsResetSolution(void)
|
static void gpsResetSolution(gpsSolutionData_t* gpsSol)
|
||||||
{
|
{
|
||||||
gpsSol.eph = 9999;
|
gpsSol->eph = 9999;
|
||||||
gpsSol.epv = 9999;
|
gpsSol->epv = 9999;
|
||||||
gpsSol.numSat = 0;
|
gpsSol->numSat = 0;
|
||||||
gpsSol.hdop = 9999;
|
gpsSol->hdop = 9999;
|
||||||
|
|
||||||
gpsSol.fixType = GPS_NO_FIX;
|
gpsSol->fixType = GPS_NO_FIX;
|
||||||
|
|
||||||
gpsSol.flags.validVelNE = false;
|
gpsSol->flags.validVelNE = false;
|
||||||
gpsSol.flags.validVelD = false;
|
gpsSol->flags.validVelD = false;
|
||||||
gpsSol.flags.validMag = false;
|
gpsSol->flags.validEPE = false;
|
||||||
gpsSol.flags.validEPE = false;
|
gpsSol->flags.validTime = false;
|
||||||
gpsSol.flags.validTime = false;
|
}
|
||||||
|
|
||||||
|
void gpsTryEstimateOnTimeout(void)
|
||||||
|
{
|
||||||
|
gpsResetSolution(&gpsSol);
|
||||||
|
DISABLE_STATE(GPS_FIX);
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if ( canEstimateGPSFix() ) {
|
||||||
|
updateEstimatedGPSFix();
|
||||||
|
|
||||||
|
if (gpsSol.fixType == GPS_FIX_3D) { //estimation kicked in
|
||||||
|
gpsProcessNewSolutionData(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpsPreInit(void)
|
void gpsPreInit(void)
|
||||||
|
@ -269,7 +457,8 @@ void gpsInit(void)
|
||||||
gpsStats.timeouts = 0;
|
gpsStats.timeouts = 0;
|
||||||
|
|
||||||
// Reset solution, timeout and prepare to start
|
// Reset solution, timeout and prepare to start
|
||||||
gpsResetSolution();
|
gpsResetSolution(&gpsSolDRV);
|
||||||
|
gpsResetSolution(&gpsSol);
|
||||||
gpsSetProtocolTimeout(gpsState.baseTimeoutMs);
|
gpsSetProtocolTimeout(gpsState.baseTimeoutMs);
|
||||||
gpsSetState(GPS_UNKNOWN);
|
gpsSetState(GPS_UNKNOWN);
|
||||||
|
|
||||||
|
@ -345,27 +534,21 @@ bool gpsUpdate(void)
|
||||||
|
|
||||||
#ifdef USE_SIMULATOR
|
#ifdef USE_SIMULATOR
|
||||||
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
|
if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
|
||||||
if ( SIMULATOR_HAS_OPTION(HITL_GPS_TIMEOUT))
|
if ( SIMULATOR_HAS_OPTION(HITL_GPS_TIMEOUT)) {
|
||||||
{
|
|
||||||
gpsSetState(GPS_LOST_COMMUNICATION);
|
gpsSetState(GPS_LOST_COMMUNICATION);
|
||||||
sensorsClear(SENSOR_GPS);
|
sensorsClear(SENSOR_GPS);
|
||||||
gpsStats.timeouts = 5;
|
gpsStats.timeouts = 5;
|
||||||
return false;
|
gpsTryEstimateOnTimeout();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
gpsSetState(GPS_RUNNING);
|
gpsSetState(GPS_RUNNING);
|
||||||
sensorsSet(SENSOR_GPS);
|
sensorsSet(SENSOR_GPS);
|
||||||
bool res = gpsSol.flags.hasNewData;
|
|
||||||
gpsSol.flags.hasNewData = false;
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
bool res = gpsSol.flags.hasNewData;
|
||||||
|
gpsSol.flags.hasNewData = false;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Assume that we don't have new data this run
|
|
||||||
gpsSol.flags.hasNewData = false;
|
|
||||||
|
|
||||||
switch (gpsState.state) {
|
switch (gpsState.state) {
|
||||||
default:
|
default:
|
||||||
case GPS_INITIALIZING:
|
case GPS_INITIALIZING:
|
||||||
|
@ -373,10 +556,9 @@ bool gpsUpdate(void)
|
||||||
if ((millis() - gpsState.lastStateSwitchMs) >= GPS_INIT_DELAY) {
|
if ((millis() - gpsState.lastStateSwitchMs) >= GPS_INIT_DELAY) {
|
||||||
// Reset internals
|
// Reset internals
|
||||||
DISABLE_STATE(GPS_FIX);
|
DISABLE_STATE(GPS_FIX);
|
||||||
gpsSol.fixType = GPS_NO_FIX;
|
|
||||||
|
|
||||||
// Reset solution
|
// Reset solution
|
||||||
gpsResetSolution();
|
gpsResetSolution(&gpsSolDRV);
|
||||||
|
|
||||||
// Call GPS protocol reset handler
|
// Call GPS protocol reset handler
|
||||||
gpsProviders[gpsState.gpsConfig->provider].restart();
|
gpsProviders[gpsState.gpsConfig->provider].restart();
|
||||||
|
@ -406,7 +588,13 @@ bool gpsUpdate(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gpsSol.flags.hasNewData;
|
if ( !sensors(SENSOR_GPS) ) {
|
||||||
|
gpsTryEstimateOnTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool res = gpsSol.flags.hasNewData;
|
||||||
|
gpsSol.flags.hasNewData = false;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpsEnablePassthrough(serialPort_t *gpsPassthroughPort)
|
void gpsEnablePassthrough(serialPort_t *gpsPassthroughPort)
|
||||||
|
@ -453,7 +641,11 @@ bool isGPSHealthy(void)
|
||||||
|
|
||||||
bool isGPSHeadingValid(void)
|
bool isGPSHeadingValid(void)
|
||||||
{
|
{
|
||||||
return sensors(SENSOR_GPS) && STATE(GPS_FIX) && gpsSol.numSat >= 6 && gpsSol.groundSpeed >= 300;
|
return ((STATE(GPS_FIX) && gpsSol.numSat >= 6)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && gpsSol.groundSpeed >= 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,7 +76,9 @@ typedef enum {
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GPS_DYNMODEL_PEDESTRIAN = 0,
|
GPS_DYNMODEL_PEDESTRIAN = 0,
|
||||||
|
GPS_DYNMODEL_AUTOMOTIVE,
|
||||||
GPS_DYNMODEL_AIR_1G,
|
GPS_DYNMODEL_AIR_1G,
|
||||||
|
GPS_DYNMODEL_AIR_2G,
|
||||||
GPS_DYNMODEL_AIR_4G,
|
GPS_DYNMODEL_AIR_4G,
|
||||||
} gpsDynModel_e;
|
} gpsDynModel_e;
|
||||||
|
|
||||||
|
@ -124,7 +126,6 @@ typedef struct gpsSolutionData_s {
|
||||||
bool gpsHeartbeat; // Toggle each update
|
bool gpsHeartbeat; // Toggle each update
|
||||||
bool validVelNE;
|
bool validVelNE;
|
||||||
bool validVelD;
|
bool validVelD;
|
||||||
bool validMag;
|
|
||||||
bool validEPE; // EPH/EPV values are valid - actual accuracy
|
bool validEPE; // EPH/EPV values are valid - actual accuracy
|
||||||
bool validTime;
|
bool validTime;
|
||||||
} flags;
|
} flags;
|
||||||
|
@ -133,7 +134,6 @@ typedef struct gpsSolutionData_s {
|
||||||
uint8_t numSat;
|
uint8_t numSat;
|
||||||
|
|
||||||
gpsLocation_t llh;
|
gpsLocation_t llh;
|
||||||
int16_t magData[3];
|
|
||||||
int16_t velNED[3];
|
int16_t velNED[3];
|
||||||
|
|
||||||
int16_t groundSpeed;
|
int16_t groundSpeed;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
#if defined(USE_GPS_FAKE)
|
#if defined(USE_GPS_FAKE)
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ void gpsFakeRestart(void)
|
||||||
|
|
||||||
void gpsFakeHandle(void)
|
void gpsFakeHandle(void)
|
||||||
{
|
{
|
||||||
gpsProcessNewSolutionData();
|
gpsProcessNewSolutionData(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpsFakeSet(
|
void gpsFakeSet(
|
||||||
|
@ -62,37 +63,38 @@ void gpsFakeSet(
|
||||||
int16_t velNED_Z,
|
int16_t velNED_Z,
|
||||||
time_t time)
|
time_t time)
|
||||||
{
|
{
|
||||||
gpsSol.fixType = fixType;
|
gpsSolDRV.fixType = fixType;
|
||||||
gpsSol.hdop = gpsSol.fixType == GPS_NO_FIX ? 9999 : 100;
|
gpsSolDRV.hdop = gpsSol.fixType == GPS_NO_FIX ? 9999 : 100;
|
||||||
gpsSol.numSat = numSat;
|
gpsSolDRV.numSat = numSat;
|
||||||
|
|
||||||
gpsSol.llh.lat = lat;
|
gpsSolDRV.llh.lat = lat;
|
||||||
gpsSol.llh.lon = lon;
|
gpsSolDRV.llh.lon = lon;
|
||||||
gpsSol.llh.alt = alt;
|
gpsSolDRV.llh.alt = alt;
|
||||||
gpsSol.groundSpeed = groundSpeed;
|
gpsSolDRV.groundSpeed = groundSpeed;
|
||||||
gpsSol.groundCourse = groundCourse;
|
gpsSolDRV.groundCourse = groundCourse;
|
||||||
gpsSol.velNED[X] = velNED_X;
|
gpsSolDRV.velNED[X] = velNED_X;
|
||||||
gpsSol.velNED[Y] = velNED_Y;
|
gpsSolDRV.velNED[Y] = velNED_Y;
|
||||||
gpsSol.velNED[Z] = velNED_Z;
|
gpsSolDRV.velNED[Z] = velNED_Z;
|
||||||
gpsSol.eph = 100;
|
gpsSolDRV.eph = 100;
|
||||||
gpsSol.epv = 100;
|
gpsSolDRV.epv = 100;
|
||||||
gpsSol.flags.validVelNE = true;
|
gpsSolDRV.flags.validVelNE = true;
|
||||||
gpsSol.flags.validVelD = true;
|
gpsSolDRV.flags.validVelD = true;
|
||||||
gpsSol.flags.validEPE = true;
|
gpsSolDRV.flags.validEPE = true;
|
||||||
gpsSol.flags.hasNewData = true;
|
|
||||||
|
|
||||||
if (time) {
|
if (time) {
|
||||||
struct tm* gTime = gmtime(&time);
|
struct tm* gTime = gmtime(&time);
|
||||||
|
|
||||||
gpsSol.time.year = (uint16_t)(gTime->tm_year + 1900);
|
gpsSolDRV.time.year = (uint16_t)(gTime->tm_year + 1900);
|
||||||
gpsSol.time.month = (uint16_t)(gTime->tm_mon + 1);
|
gpsSolDRV.time.month = (uint16_t)(gTime->tm_mon + 1);
|
||||||
gpsSol.time.day = (uint8_t)gTime->tm_mday;
|
gpsSolDRV.time.day = (uint8_t)gTime->tm_mday;
|
||||||
gpsSol.time.hours = (uint8_t)gTime->tm_hour;
|
gpsSolDRV.time.hours = (uint8_t)gTime->tm_hour;
|
||||||
gpsSol.time.minutes = (uint8_t)gTime->tm_min;
|
gpsSolDRV.time.minutes = (uint8_t)gTime->tm_min;
|
||||||
gpsSol.time.seconds = (uint8_t)gTime->tm_sec;
|
gpsSolDRV.time.seconds = (uint8_t)gTime->tm_sec;
|
||||||
gpsSol.time.millis = 0;
|
gpsSolDRV.time.millis = 0;
|
||||||
gpsSol.flags.validTime = gpsSol.fixType >= 3;
|
gpsSolDRV.flags.validTime = gpsSol.fixType >= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpsProcessNewDriverData();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -63,7 +63,7 @@ void gpsRestartMSP(void)
|
||||||
void gpsHandleMSP(void)
|
void gpsHandleMSP(void)
|
||||||
{
|
{
|
||||||
if (newDataReady) {
|
if (newDataReady) {
|
||||||
gpsProcessNewSolutionData();
|
gpsProcessNewSolutionData(false);
|
||||||
newDataReady = false;
|
newDataReady = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,33 +81,34 @@ void mspGPSReceiveNewData(const uint8_t * bufferPtr)
|
||||||
{
|
{
|
||||||
const mspSensorGpsDataMessage_t * pkt = (const mspSensorGpsDataMessage_t *)bufferPtr;
|
const mspSensorGpsDataMessage_t * pkt = (const mspSensorGpsDataMessage_t *)bufferPtr;
|
||||||
|
|
||||||
gpsSol.fixType = gpsMapFixType(pkt->fixType);
|
gpsSolDRV.fixType = gpsMapFixType(pkt->fixType);
|
||||||
gpsSol.numSat = pkt->satellitesInView;
|
gpsSolDRV.numSat = pkt->satellitesInView;
|
||||||
gpsSol.llh.lon = pkt->longitude;
|
gpsSolDRV.llh.lon = pkt->longitude;
|
||||||
gpsSol.llh.lat = pkt->latitude;
|
gpsSolDRV.llh.lat = pkt->latitude;
|
||||||
gpsSol.llh.alt = pkt->mslAltitude;
|
gpsSolDRV.llh.alt = pkt->mslAltitude;
|
||||||
gpsSol.velNED[X] = pkt->nedVelNorth;
|
gpsSolDRV.velNED[X] = pkt->nedVelNorth;
|
||||||
gpsSol.velNED[Y] = pkt->nedVelEast;
|
gpsSolDRV.velNED[Y] = pkt->nedVelEast;
|
||||||
gpsSol.velNED[Z] = pkt->nedVelDown;
|
gpsSolDRV.velNED[Z] = pkt->nedVelDown;
|
||||||
gpsSol.groundSpeed = calc_length_pythagorean_2D((float)pkt->nedVelNorth, (float)pkt->nedVelEast);
|
gpsSolDRV.groundSpeed = calc_length_pythagorean_2D((float)pkt->nedVelNorth, (float)pkt->nedVelEast);
|
||||||
gpsSol.groundCourse = pkt->groundCourse / 10; // in deg * 10
|
gpsSolDRV.groundCourse = pkt->groundCourse / 10; // in deg * 10
|
||||||
gpsSol.eph = gpsConstrainEPE(pkt->horizontalPosAccuracy / 10);
|
gpsSolDRV.eph = gpsConstrainEPE(pkt->horizontalPosAccuracy / 10);
|
||||||
gpsSol.epv = gpsConstrainEPE(pkt->verticalPosAccuracy / 10);
|
gpsSolDRV.epv = gpsConstrainEPE(pkt->verticalPosAccuracy / 10);
|
||||||
gpsSol.hdop = gpsConstrainHDOP(pkt->hdop);
|
gpsSolDRV.hdop = gpsConstrainHDOP(pkt->hdop);
|
||||||
gpsSol.flags.validVelNE = true;
|
gpsSolDRV.flags.validVelNE = true;
|
||||||
gpsSol.flags.validVelD = true;
|
gpsSolDRV.flags.validVelD = true;
|
||||||
gpsSol.flags.validEPE = true;
|
gpsSolDRV.flags.validEPE = true;
|
||||||
|
|
||||||
gpsSol.time.year = pkt->year;
|
gpsSolDRV.time.year = pkt->year;
|
||||||
gpsSol.time.month = pkt->month;
|
gpsSolDRV.time.month = pkt->month;
|
||||||
gpsSol.time.day = pkt->day;
|
gpsSolDRV.time.day = pkt->day;
|
||||||
gpsSol.time.hours = pkt->hour;
|
gpsSolDRV.time.hours = pkt->hour;
|
||||||
gpsSol.time.minutes = pkt->min;
|
gpsSolDRV.time.minutes = pkt->min;
|
||||||
gpsSol.time.seconds = pkt->sec;
|
gpsSolDRV.time.seconds = pkt->sec;
|
||||||
gpsSol.time.millis = 0;
|
gpsSolDRV.time.millis = 0;
|
||||||
|
|
||||||
gpsSol.flags.validTime = (pkt->fixType >= 3);
|
gpsSolDRV.flags.validTime = (pkt->fixType >= 3);
|
||||||
|
|
||||||
|
gpsProcessNewDriverData();
|
||||||
newDataReady = true;
|
newDataReady = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -61,6 +61,7 @@ typedef struct {
|
||||||
} gpsReceiverData_t;
|
} gpsReceiverData_t;
|
||||||
|
|
||||||
extern gpsReceiverData_t gpsState;
|
extern gpsReceiverData_t gpsState;
|
||||||
|
extern gpsSolutionData_t gpsSolDRV;
|
||||||
|
|
||||||
extern baudRate_e gpsToSerialBaudRate[GPS_BAUDRATE_COUNT];
|
extern baudRate_e gpsToSerialBaudRate[GPS_BAUDRATE_COUNT];
|
||||||
|
|
||||||
|
@ -70,7 +71,8 @@ extern void gpsFinalizeChangeBaud(void);
|
||||||
extern uint16_t gpsConstrainEPE(uint32_t epe);
|
extern uint16_t gpsConstrainEPE(uint32_t epe);
|
||||||
extern uint16_t gpsConstrainHDOP(uint32_t hdop);
|
extern uint16_t gpsConstrainHDOP(uint32_t hdop);
|
||||||
|
|
||||||
void gpsProcessNewSolutionData(void);
|
void gpsProcessNewDriverData(void);
|
||||||
|
void gpsProcessNewSolutionData(bool);
|
||||||
void gpsSetProtocolTimeout(timeMs_t timeoutMs);
|
void gpsSetProtocolTimeout(timeMs_t timeoutMs);
|
||||||
|
|
||||||
extern void gpsRestartUBLOX(void);
|
extern void gpsRestartUBLOX(void);
|
||||||
|
|
|
@ -251,8 +251,8 @@ static const uint8_t default_payload[] = {
|
||||||
0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GNSSID_SBAS 1
|
#define GNSSID_SBAS 1
|
||||||
#define GNSSID_GALILEO 2
|
#define GNSSID_GALILEO 2
|
||||||
#define GNSSID_BEIDOU 3
|
#define GNSSID_BEIDOU 3
|
||||||
#define GNSSID_GZSS 5
|
#define GNSSID_GZSS 5
|
||||||
#define GNSSID_GLONASS 6
|
#define GNSSID_GLONASS 6
|
||||||
|
@ -410,18 +410,18 @@ static void configureGNSS10(void)
|
||||||
|
|
||||||
static void configureGNSS(void)
|
static void configureGNSS(void)
|
||||||
{
|
{
|
||||||
int blocksUsed = 0;
|
int blocksUsed = 0;
|
||||||
send_buffer.message.header.msg_class = CLASS_CFG;
|
send_buffer.message.header.msg_class = CLASS_CFG;
|
||||||
send_buffer.message.header.msg_id = MSG_CFG_GNSS; // message deprecated in protocol > 23.01, should use UBX-CFG-VALSET/UBX-CFG-VALGET
|
send_buffer.message.header.msg_id = MSG_CFG_GNSS; // message deprecated in protocol > 23.01, should use UBX-CFG-VALSET/UBX-CFG-VALGET
|
||||||
send_buffer.message.payload.gnss.msgVer = 0;
|
send_buffer.message.payload.gnss.msgVer = 0;
|
||||||
send_buffer.message.payload.gnss.numTrkChHw = 0; // read only, so unset
|
send_buffer.message.payload.gnss.numTrkChHw = 0; // read only, so unset
|
||||||
send_buffer.message.payload.gnss.numTrkChUse = 0xFF; // If set to 0xFF will use hardware max
|
send_buffer.message.payload.gnss.numTrkChUse = 0xFF; // If set to 0xFF will use hardware max
|
||||||
|
|
||||||
/* SBAS, always generated */
|
/* SBAS, always generated */
|
||||||
blocksUsed += configureGNSS_SBAS(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
blocksUsed += configureGNSS_SBAS(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
||||||
|
|
||||||
/* Galileo */
|
/* Galileo */
|
||||||
blocksUsed += configureGNSS_GALILEO(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
blocksUsed += configureGNSS_GALILEO(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
||||||
|
|
||||||
/* BeiDou */
|
/* BeiDou */
|
||||||
blocksUsed += configureGNSS_BEIDOU(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
blocksUsed += configureGNSS_BEIDOU(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
||||||
|
@ -429,9 +429,9 @@ static void configureGNSS(void)
|
||||||
/* GLONASS */
|
/* GLONASS */
|
||||||
blocksUsed += configureGNSS_GLONASS(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
blocksUsed += configureGNSS_GLONASS(&send_buffer.message.payload.gnss.config[blocksUsed]);
|
||||||
|
|
||||||
send_buffer.message.payload.gnss.numConfigBlocks = blocksUsed;
|
send_buffer.message.payload.gnss.numConfigBlocks = blocksUsed;
|
||||||
send_buffer.message.header.length = (sizeof(ubx_gnss_msg_t) + sizeof(ubx_gnss_element_t) * blocksUsed);
|
send_buffer.message.header.length = (sizeof(ubx_gnss_msg_t) + sizeof(ubx_gnss_element_t)* blocksUsed);
|
||||||
sendConfigMessageUBLOX();
|
sendConfigMessageUBLOX();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configureNAV5(uint8_t dynModel, uint8_t fixMode)
|
static void configureNAV5(uint8_t dynModel, uint8_t fixMode)
|
||||||
|
@ -538,84 +538,84 @@ static bool gpsParseFrameUBLOX(void)
|
||||||
{
|
{
|
||||||
switch (_msg_id) {
|
switch (_msg_id) {
|
||||||
case MSG_POSLLH:
|
case MSG_POSLLH:
|
||||||
gpsSol.llh.lon = _buffer.posllh.longitude;
|
gpsSolDRV.llh.lon = _buffer.posllh.longitude;
|
||||||
gpsSol.llh.lat = _buffer.posllh.latitude;
|
gpsSolDRV.llh.lat = _buffer.posllh.latitude;
|
||||||
gpsSol.llh.alt = _buffer.posllh.altitude_msl / 10; //alt in cm
|
gpsSolDRV.llh.alt = _buffer.posllh.altitude_msl / 10; //alt in cm
|
||||||
gpsSol.eph = gpsConstrainEPE(_buffer.posllh.horizontal_accuracy / 10);
|
gpsSolDRV.eph = gpsConstrainEPE(_buffer.posllh.horizontal_accuracy / 10);
|
||||||
gpsSol.epv = gpsConstrainEPE(_buffer.posllh.vertical_accuracy / 10);
|
gpsSolDRV.epv = gpsConstrainEPE(_buffer.posllh.vertical_accuracy / 10);
|
||||||
gpsSol.flags.validEPE = true;
|
gpsSolDRV.flags.validEPE = true;
|
||||||
if (next_fix_type != GPS_NO_FIX)
|
if (next_fix_type != GPS_NO_FIX)
|
||||||
gpsSol.fixType = next_fix_type;
|
gpsSolDRV.fixType = next_fix_type;
|
||||||
_new_position = true;
|
_new_position = true;
|
||||||
break;
|
break;
|
||||||
case MSG_STATUS:
|
case MSG_STATUS:
|
||||||
next_fix_type = gpsMapFixType(_buffer.status.fix_status & NAV_STATUS_FIX_VALID, _buffer.status.fix_type);
|
next_fix_type = gpsMapFixType(_buffer.status.fix_status & NAV_STATUS_FIX_VALID, _buffer.status.fix_type);
|
||||||
if (next_fix_type == GPS_NO_FIX)
|
if (next_fix_type == GPS_NO_FIX)
|
||||||
gpsSol.fixType = GPS_NO_FIX;
|
gpsSolDRV.fixType = GPS_NO_FIX;
|
||||||
break;
|
break;
|
||||||
case MSG_SOL:
|
case MSG_SOL:
|
||||||
next_fix_type = gpsMapFixType(_buffer.solution.fix_status & NAV_STATUS_FIX_VALID, _buffer.solution.fix_type);
|
next_fix_type = gpsMapFixType(_buffer.solution.fix_status & NAV_STATUS_FIX_VALID, _buffer.solution.fix_type);
|
||||||
if (next_fix_type == GPS_NO_FIX)
|
if (next_fix_type == GPS_NO_FIX)
|
||||||
gpsSol.fixType = GPS_NO_FIX;
|
gpsSolDRV.fixType = GPS_NO_FIX;
|
||||||
gpsSol.numSat = _buffer.solution.satellites;
|
gpsSolDRV.numSat = _buffer.solution.satellites;
|
||||||
gpsSol.hdop = gpsConstrainHDOP(_buffer.solution.position_DOP);
|
gpsSolDRV.hdop = gpsConstrainHDOP(_buffer.solution.position_DOP);
|
||||||
break;
|
break;
|
||||||
case MSG_VELNED:
|
case MSG_VELNED:
|
||||||
gpsSol.groundSpeed = _buffer.velned.speed_2d; // cm/s
|
gpsSolDRV.groundSpeed = _buffer.velned.speed_2d; // cm/s
|
||||||
gpsSol.groundCourse = (uint16_t) (_buffer.velned.heading_2d / 10000); // Heading 2D deg * 100000 rescaled to deg * 10
|
gpsSolDRV.groundCourse = (uint16_t) (_buffer.velned.heading_2d / 10000); // Heading 2D deg * 100000 rescaled to deg * 10
|
||||||
gpsSol.velNED[X] = _buffer.velned.ned_north;
|
gpsSolDRV.velNED[X] = _buffer.velned.ned_north;
|
||||||
gpsSol.velNED[Y] = _buffer.velned.ned_east;
|
gpsSolDRV.velNED[Y] = _buffer.velned.ned_east;
|
||||||
gpsSol.velNED[Z] = _buffer.velned.ned_down;
|
gpsSolDRV.velNED[Z] = _buffer.velned.ned_down;
|
||||||
gpsSol.flags.validVelNE = true;
|
gpsSolDRV.flags.validVelNE = true;
|
||||||
gpsSol.flags.validVelD = true;
|
gpsSolDRV.flags.validVelD = true;
|
||||||
_new_speed = true;
|
_new_speed = true;
|
||||||
break;
|
break;
|
||||||
case MSG_TIMEUTC:
|
case MSG_TIMEUTC:
|
||||||
if (UBX_VALID_GPS_DATE_TIME(_buffer.timeutc.valid)) {
|
if (UBX_VALID_GPS_DATE_TIME(_buffer.timeutc.valid)) {
|
||||||
gpsSol.time.year = _buffer.timeutc.year;
|
gpsSolDRV.time.year = _buffer.timeutc.year;
|
||||||
gpsSol.time.month = _buffer.timeutc.month;
|
gpsSolDRV.time.month = _buffer.timeutc.month;
|
||||||
gpsSol.time.day = _buffer.timeutc.day;
|
gpsSolDRV.time.day = _buffer.timeutc.day;
|
||||||
gpsSol.time.hours = _buffer.timeutc.hour;
|
gpsSolDRV.time.hours = _buffer.timeutc.hour;
|
||||||
gpsSol.time.minutes = _buffer.timeutc.min;
|
gpsSolDRV.time.minutes = _buffer.timeutc.min;
|
||||||
gpsSol.time.seconds = _buffer.timeutc.sec;
|
gpsSolDRV.time.seconds = _buffer.timeutc.sec;
|
||||||
gpsSol.time.millis = _buffer.timeutc.nano / (1000*1000);
|
gpsSolDRV.time.millis = _buffer.timeutc.nano / (1000*1000);
|
||||||
|
|
||||||
gpsSol.flags.validTime = true;
|
gpsSolDRV.flags.validTime = true;
|
||||||
} else {
|
} else {
|
||||||
gpsSol.flags.validTime = false;
|
gpsSolDRV.flags.validTime = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_PVT:
|
case MSG_PVT:
|
||||||
next_fix_type = gpsMapFixType(_buffer.pvt.fix_status & NAV_STATUS_FIX_VALID, _buffer.pvt.fix_type);
|
next_fix_type = gpsMapFixType(_buffer.pvt.fix_status & NAV_STATUS_FIX_VALID, _buffer.pvt.fix_type);
|
||||||
gpsSol.fixType = next_fix_type;
|
gpsSolDRV.fixType = next_fix_type;
|
||||||
gpsSol.llh.lon = _buffer.pvt.longitude;
|
gpsSolDRV.llh.lon = _buffer.pvt.longitude;
|
||||||
gpsSol.llh.lat = _buffer.pvt.latitude;
|
gpsSolDRV.llh.lat = _buffer.pvt.latitude;
|
||||||
gpsSol.llh.alt = _buffer.pvt.altitude_msl / 10; //alt in cm
|
gpsSolDRV.llh.alt = _buffer.pvt.altitude_msl / 10; //alt in cm
|
||||||
gpsSol.velNED[X]=_buffer.pvt.ned_north / 10; // to cm/s
|
gpsSolDRV.velNED[X]=_buffer.pvt.ned_north / 10; // to cm/s
|
||||||
gpsSol.velNED[Y]=_buffer.pvt.ned_east / 10; // to cm/s
|
gpsSolDRV.velNED[Y]=_buffer.pvt.ned_east / 10; // to cm/s
|
||||||
gpsSol.velNED[Z]=_buffer.pvt.ned_down / 10; // to cm/s
|
gpsSolDRV.velNED[Z]=_buffer.pvt.ned_down / 10; // to cm/s
|
||||||
gpsSol.groundSpeed = _buffer.pvt.speed_2d / 10; // to cm/s
|
gpsSolDRV.groundSpeed = _buffer.pvt.speed_2d / 10; // to cm/s
|
||||||
gpsSol.groundCourse = (uint16_t) (_buffer.pvt.heading_2d / 10000); // Heading 2D deg * 100000 rescaled to deg * 10
|
gpsSolDRV.groundCourse = (uint16_t) (_buffer.pvt.heading_2d / 10000); // Heading 2D deg * 100000 rescaled to deg * 10
|
||||||
gpsSol.numSat = _buffer.pvt.satellites;
|
gpsSolDRV.numSat = _buffer.pvt.satellites;
|
||||||
gpsSol.eph = gpsConstrainEPE(_buffer.pvt.horizontal_accuracy / 10);
|
gpsSolDRV.eph = gpsConstrainEPE(_buffer.pvt.horizontal_accuracy / 10);
|
||||||
gpsSol.epv = gpsConstrainEPE(_buffer.pvt.vertical_accuracy / 10);
|
gpsSolDRV.epv = gpsConstrainEPE(_buffer.pvt.vertical_accuracy / 10);
|
||||||
gpsSol.hdop = gpsConstrainHDOP(_buffer.pvt.position_DOP);
|
gpsSolDRV.hdop = gpsConstrainHDOP(_buffer.pvt.position_DOP);
|
||||||
gpsSol.flags.validVelNE = true;
|
gpsSolDRV.flags.validVelNE = true;
|
||||||
gpsSol.flags.validVelD = true;
|
gpsSolDRV.flags.validVelD = true;
|
||||||
gpsSol.flags.validEPE = true;
|
gpsSolDRV.flags.validEPE = true;
|
||||||
|
|
||||||
if (UBX_VALID_GPS_DATE_TIME(_buffer.pvt.valid)) {
|
if (UBX_VALID_GPS_DATE_TIME(_buffer.pvt.valid)) {
|
||||||
gpsSol.time.year = _buffer.pvt.year;
|
gpsSolDRV.time.year = _buffer.pvt.year;
|
||||||
gpsSol.time.month = _buffer.pvt.month;
|
gpsSolDRV.time.month = _buffer.pvt.month;
|
||||||
gpsSol.time.day = _buffer.pvt.day;
|
gpsSolDRV.time.day = _buffer.pvt.day;
|
||||||
gpsSol.time.hours = _buffer.pvt.hour;
|
gpsSolDRV.time.hours = _buffer.pvt.hour;
|
||||||
gpsSol.time.minutes = _buffer.pvt.min;
|
gpsSolDRV.time.minutes = _buffer.pvt.min;
|
||||||
gpsSol.time.seconds = _buffer.pvt.sec;
|
gpsSolDRV.time.seconds = _buffer.pvt.sec;
|
||||||
gpsSol.time.millis = _buffer.pvt.nano / (1000*1000);
|
gpsSolDRV.time.millis = _buffer.pvt.nano / (1000*1000);
|
||||||
|
|
||||||
gpsSol.flags.validTime = true;
|
gpsSolDRV.flags.validTime = true;
|
||||||
} else {
|
} else {
|
||||||
gpsSol.flags.validTime = false;
|
gpsSolDRV.flags.validTime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_new_position = true;
|
_new_position = true;
|
||||||
|
@ -649,7 +649,7 @@ static bool gpsParseFrameUBLOX(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int j = 40; j < _payload_length; j += 30) {
|
for(int j = 40; j < _payload_length; j += 30) {
|
||||||
if (strnstr((const char *)(_buffer.bytes + j), "PROTVER", 30)) {
|
if (strnstr((const char *)(_buffer.bytes + j), "PROTVER", 30)) {
|
||||||
gpsDecodeProtocolVersion((const char *)(_buffer.bytes + j), 30);
|
gpsDecodeProtocolVersion((const char *)(_buffer.bytes + j), 30);
|
||||||
break;
|
break;
|
||||||
|
@ -800,10 +800,16 @@ STATIC_PROTOTHREAD(gpsConfigure)
|
||||||
case GPS_DYNMODEL_PEDESTRIAN:
|
case GPS_DYNMODEL_PEDESTRIAN:
|
||||||
configureNAV5(UBX_DYNMODEL_PEDESTRIAN, UBX_FIXMODE_AUTO);
|
configureNAV5(UBX_DYNMODEL_PEDESTRIAN, UBX_FIXMODE_AUTO);
|
||||||
break;
|
break;
|
||||||
case GPS_DYNMODEL_AIR_1G: // Default to this
|
case GPS_DYNMODEL_AUTOMOTIVE:
|
||||||
default:
|
configureNAV5(UBX_DYNMODEL_AUTOMOVITE, UBX_FIXMODE_AUTO);
|
||||||
|
break;
|
||||||
|
case GPS_DYNMODEL_AIR_1G:
|
||||||
configureNAV5(UBX_DYNMODEL_AIR_1G, UBX_FIXMODE_AUTO);
|
configureNAV5(UBX_DYNMODEL_AIR_1G, UBX_FIXMODE_AUTO);
|
||||||
break;
|
break;
|
||||||
|
case GPS_DYNMODEL_AIR_2G: // Default to this
|
||||||
|
default:
|
||||||
|
configureNAV5(UBX_DYNMODEL_AIR_2G, UBX_FIXMODE_AUTO);
|
||||||
|
break;
|
||||||
case GPS_DYNMODEL_AIR_4G:
|
case GPS_DYNMODEL_AIR_4G:
|
||||||
configureNAV5(UBX_DYNMODEL_AIR_4G, UBX_FIXMODE_AUTO);
|
configureNAV5(UBX_DYNMODEL_AIR_4G, UBX_FIXMODE_AUTO);
|
||||||
break;
|
break;
|
||||||
|
@ -948,19 +954,19 @@ STATIC_PROTOTHREAD(gpsConfigure)
|
||||||
|
|
||||||
// Configure GNSS for M8N and later
|
// Configure GNSS for M8N and later
|
||||||
if (gpsState.hwVersion >= UBX_HW_VERSION_UBLOX8) {
|
if (gpsState.hwVersion >= UBX_HW_VERSION_UBLOX8) {
|
||||||
gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT);
|
gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT);
|
||||||
if(gpsState.hwVersion >= UBX_HW_VERSION_UBLOX10 || (gpsState.swVersionMajor>=23 && gpsState.swVersionMinor >= 1)) {
|
if(gpsState.hwVersion >= UBX_HW_VERSION_UBLOX10 || (gpsState.swVersionMajor>=23 && gpsState.swVersionMinor >= 1)) {
|
||||||
configureGNSS10();
|
configureGNSS10();
|
||||||
} else {
|
} else {
|
||||||
configureGNSS();
|
configureGNSS();
|
||||||
}
|
}
|
||||||
ptWaitTimeout((_ack_state == UBX_ACK_GOT_ACK || _ack_state == UBX_ACK_GOT_NAK), GPS_CFG_CMD_TIMEOUT_MS);
|
ptWaitTimeout((_ack_state == UBX_ACK_GOT_ACK || _ack_state == UBX_ACK_GOT_NAK), GPS_CFG_CMD_TIMEOUT_MS);
|
||||||
|
|
||||||
if(_ack_state == UBX_ACK_GOT_NAK) {
|
if(_ack_state == UBX_ACK_GOT_NAK) {
|
||||||
gpsConfigMutable()->ubloxUseGalileo = SETTING_GPS_UBLOX_USE_GALILEO_DEFAULT;
|
gpsConfigMutable()->ubloxUseGalileo = SETTING_GPS_UBLOX_USE_GALILEO_DEFAULT;
|
||||||
gpsConfigMutable()->ubloxUseBeidou = SETTING_GPS_UBLOX_USE_BEIDOU_DEFAULT;
|
gpsConfigMutable()->ubloxUseBeidou = SETTING_GPS_UBLOX_USE_BEIDOU_DEFAULT;
|
||||||
gpsConfigMutable()->ubloxUseGlonass = SETTING_GPS_UBLOX_USE_GLONASS_DEFAULT;
|
gpsConfigMutable()->ubloxUseGlonass = SETTING_GPS_UBLOX_USE_GLONASS_DEFAULT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ptEnd(0);
|
ptEnd(0);
|
||||||
|
@ -980,6 +986,7 @@ STATIC_PROTOTHREAD(gpsProtocolReceiverThread)
|
||||||
while (serialRxBytesWaiting(gpsState.gpsPort)) {
|
while (serialRxBytesWaiting(gpsState.gpsPort)) {
|
||||||
uint8_t newChar = serialRead(gpsState.gpsPort);
|
uint8_t newChar = serialRead(gpsState.gpsPort);
|
||||||
if (gpsNewFrameUBLOX(newChar)) {
|
if (gpsNewFrameUBLOX(newChar)) {
|
||||||
|
gpsProcessNewDriverData();
|
||||||
ptSemaphoreSignal(semNewDataReady);
|
ptSemaphoreSignal(semNewDataReady);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1038,16 +1045,19 @@ STATIC_PROTOTHREAD(gpsProtocolStateThread)
|
||||||
pollVersion();
|
pollVersion();
|
||||||
gpsState.autoConfigStep++;
|
gpsState.autoConfigStep++;
|
||||||
ptWaitTimeout((gpsState.hwVersion != UBX_HW_VERSION_UNKNOWN), GPS_CFG_CMD_TIMEOUT_MS);
|
ptWaitTimeout((gpsState.hwVersion != UBX_HW_VERSION_UNKNOWN), GPS_CFG_CMD_TIMEOUT_MS);
|
||||||
} while (gpsState.autoConfigStep < GPS_VERSION_RETRY_TIMES && gpsState.hwVersion == UBX_HW_VERSION_UNKNOWN);
|
} while(gpsState.autoConfigStep < GPS_VERSION_RETRY_TIMES && gpsState.hwVersion == UBX_HW_VERSION_UNKNOWN);
|
||||||
|
|
||||||
gpsState.autoConfigStep = 0;
|
gpsState.autoConfigStep = 0;
|
||||||
ubx_capabilities.supported = ubx_capabilities.enabledGnss = ubx_capabilities.defaultGnss = 0;
|
ubx_capabilities.supported = ubx_capabilities.enabledGnss = ubx_capabilities.defaultGnss = 0;
|
||||||
do {
|
// M7 and earlier will never get pass this step, so skip it (#9440).
|
||||||
pollGnssCapabilities();
|
// UBLOX documents that this is M8N and later
|
||||||
gpsState.autoConfigStep++;
|
if (gpsState.hwVersion > UBX_HW_VERSION_UBLOX7) {
|
||||||
ptWaitTimeout((ubx_capabilities.capMaxGnss != 0), GPS_CFG_CMD_TIMEOUT_MS);
|
do {
|
||||||
} while (gpsState.autoConfigStep < GPS_VERSION_RETRY_TIMES && ubx_capabilities.capMaxGnss == 0);
|
pollGnssCapabilities();
|
||||||
|
gpsState.autoConfigStep++;
|
||||||
|
ptWaitTimeout((ubx_capabilities.capMaxGnss != 0), GPS_CFG_CMD_TIMEOUT_MS);
|
||||||
|
} while (gpsState.autoConfigStep < GPS_VERSION_RETRY_TIMES && ubx_capabilities.capMaxGnss == 0);
|
||||||
|
}
|
||||||
// Configure GPS module if enabled
|
// Configure GPS module if enabled
|
||||||
if (gpsState.gpsConfig->autoConfig) {
|
if (gpsState.gpsConfig->autoConfig) {
|
||||||
// Configure GPS
|
// Configure GPS
|
||||||
|
@ -1060,7 +1070,7 @@ STATIC_PROTOTHREAD(gpsProtocolStateThread)
|
||||||
// GPS is ready - execute the gpsProcessNewSolutionData() based on gpsProtocolReceiverThread semaphore
|
// GPS is ready - execute the gpsProcessNewSolutionData() based on gpsProtocolReceiverThread semaphore
|
||||||
while (1) {
|
while (1) {
|
||||||
ptSemaphoreWait(semNewDataReady);
|
ptSemaphoreWait(semNewDataReady);
|
||||||
gpsProcessNewSolutionData();
|
gpsProcessNewSolutionData(false);
|
||||||
|
|
||||||
if ((gpsState.gpsConfig->provider == GPS_UBLOX || gpsState.gpsConfig->provider == GPS_UBLOX7PLUS)) {
|
if ((gpsState.gpsConfig->provider == GPS_UBLOX || gpsState.gpsConfig->provider == GPS_UBLOX7PLUS)) {
|
||||||
if ((millis() - gpsState.lastCapaPoolMs) > GPS_CAPA_INTERVAL) {
|
if ((millis() - gpsState.lastCapaPoolMs) > GPS_CAPA_INTERVAL) {
|
||||||
|
|
|
@ -34,7 +34,9 @@ extern "C" {
|
||||||
#define GPS_CAPA_INTERVAL 5000
|
#define GPS_CAPA_INTERVAL 5000
|
||||||
|
|
||||||
#define UBX_DYNMODEL_PEDESTRIAN 3
|
#define UBX_DYNMODEL_PEDESTRIAN 3
|
||||||
|
#define UBX_DYNMODEL_AUTOMOVITE 4
|
||||||
#define UBX_DYNMODEL_AIR_1G 6
|
#define UBX_DYNMODEL_AIR_1G 6
|
||||||
|
#define UBX_DYNMODEL_AIR_2G 7
|
||||||
#define UBX_DYNMODEL_AIR_4G 8
|
#define UBX_DYNMODEL_AIR_4G 8
|
||||||
|
|
||||||
#define UBX_FIXMODE_2D_ONLY 1
|
#define UBX_FIXMODE_2D_ONLY 1
|
||||||
|
|
|
@ -154,8 +154,9 @@
|
||||||
|
|
||||||
#define OSD_MIN_FONT_VERSION 3
|
#define OSD_MIN_FONT_VERSION 3
|
||||||
|
|
||||||
static timeMs_t notify_settings_saved = 0;
|
static timeMs_t linearDescentMessageMs = 0;
|
||||||
static bool savingSettings = false;
|
static timeMs_t notify_settings_saved = 0;
|
||||||
|
static bool savingSettings = false;
|
||||||
|
|
||||||
static unsigned currentLayout = 0;
|
static unsigned currentLayout = 0;
|
||||||
static int layoutOverride = -1;
|
static int layoutOverride = -1;
|
||||||
|
@ -546,7 +547,7 @@ void osdFormatAltitudeSymbol(char *buff, int32_t alt)
|
||||||
buff[0] = ' ';
|
buff[0] = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it and change the values
|
#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it and change the values
|
||||||
if (isBfCompatibleVideoSystem(osdConfig())) {
|
if (isBfCompatibleVideoSystem(osdConfig())) {
|
||||||
totalDigits++;
|
totalDigits++;
|
||||||
digits++;
|
digits++;
|
||||||
|
@ -641,7 +642,7 @@ static inline void osdFormatFlyTime(char *buff, textAttributes_t *attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trim whitespace from string.
|
* Trim whitespace from string.
|
||||||
* Used in Stats screen on lines with multiple values.
|
* Used in Stats screen on lines with multiple values.
|
||||||
*/
|
*/
|
||||||
char *osdFormatTrimWhiteSpace(char *buff)
|
char *osdFormatTrimWhiteSpace(char *buff)
|
||||||
|
@ -652,7 +653,7 @@ char *osdFormatTrimWhiteSpace(char *buff)
|
||||||
while(isspace((unsigned char)*buff)) buff++;
|
while(isspace((unsigned char)*buff)) buff++;
|
||||||
|
|
||||||
// All spaces?
|
// All spaces?
|
||||||
if(*buff == 0)
|
if(*buff == 0)
|
||||||
return buff;
|
return buff;
|
||||||
|
|
||||||
// Trim trailing spaces
|
// Trim trailing spaces
|
||||||
|
@ -1000,6 +1001,9 @@ static const char * divertingToSafehomeMessage(void)
|
||||||
|
|
||||||
static const char * navigationStateMessage(void)
|
static const char * navigationStateMessage(void)
|
||||||
{
|
{
|
||||||
|
if (!posControl.rthState.rthLinearDescentActive && linearDescentMessageMs != 0)
|
||||||
|
linearDescentMessageMs = 0;
|
||||||
|
|
||||||
switch (NAV_Status.state) {
|
switch (NAV_Status.state) {
|
||||||
case MW_NAV_STATE_NONE:
|
case MW_NAV_STATE_NONE:
|
||||||
break;
|
break;
|
||||||
|
@ -1011,7 +1015,13 @@ static const char * navigationStateMessage(void)
|
||||||
if (posControl.flags.rthTrackbackActive) {
|
if (posControl.flags.rthTrackbackActive) {
|
||||||
return OSD_MESSAGE_STR(OSD_MSG_RTH_TRACKBACK);
|
return OSD_MESSAGE_STR(OSD_MSG_RTH_TRACKBACK);
|
||||||
} else {
|
} else {
|
||||||
return OSD_MESSAGE_STR(OSD_MSG_HEADING_HOME);
|
if (posControl.rthState.rthLinearDescentActive && (linearDescentMessageMs == 0 || linearDescentMessageMs > millis())) {
|
||||||
|
if (linearDescentMessageMs == 0)
|
||||||
|
linearDescentMessageMs = millis() + 5000; // Show message for 5 seconds.
|
||||||
|
|
||||||
|
return OSD_MESSAGE_STR(OSD_MSG_RTH_LINEAR_DESCENT);
|
||||||
|
} else
|
||||||
|
return OSD_MESSAGE_STR(OSD_MSG_HEADING_HOME);
|
||||||
}
|
}
|
||||||
case MW_NAV_STATE_HOLD_INFINIT:
|
case MW_NAV_STATE_HOLD_INFINIT:
|
||||||
// Used by HOLD flight modes. No information to add.
|
// Used by HOLD flight modes. No information to add.
|
||||||
|
@ -1047,11 +1057,18 @@ static const char * navigationStateMessage(void)
|
||||||
case MW_NAV_STATE_LANDED:
|
case MW_NAV_STATE_LANDED:
|
||||||
return OSD_MESSAGE_STR(OSD_MSG_LANDED);
|
return OSD_MESSAGE_STR(OSD_MSG_LANDED);
|
||||||
case MW_NAV_STATE_LAND_SETTLE:
|
case MW_NAV_STATE_LAND_SETTLE:
|
||||||
|
{
|
||||||
|
// If there is a FS landing delay occurring. That is handled by the calling function.
|
||||||
|
if (posControl.landingDelay > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
return OSD_MESSAGE_STR(OSD_MSG_PREPARING_LAND);
|
return OSD_MESSAGE_STR(OSD_MSG_PREPARING_LAND);
|
||||||
|
}
|
||||||
case MW_NAV_STATE_LAND_START_DESCENT:
|
case MW_NAV_STATE_LAND_START_DESCENT:
|
||||||
// Not used
|
// Not used
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,7 +1116,7 @@ void osdCrosshairPosition(uint8_t *x, uint8_t *y)
|
||||||
* Check if this OSD layout is using scaled or unscaled throttle.
|
* Check if this OSD layout is using scaled or unscaled throttle.
|
||||||
* If both are used, it will default to scaled.
|
* If both are used, it will default to scaled.
|
||||||
*/
|
*/
|
||||||
bool osdUsingScaledThrottle(void)
|
bool osdUsingScaledThrottle(void)
|
||||||
{
|
{
|
||||||
bool usingScaledThrottle = OSD_VISIBLE(osdLayoutsConfig()->item_pos[currentLayout][OSD_SCALED_THROTTLE_POS]);
|
bool usingScaledThrottle = OSD_VISIBLE(osdLayoutsConfig()->item_pos[currentLayout][OSD_SCALED_THROTTLE_POS]);
|
||||||
bool usingRCThrottle = OSD_VISIBLE(osdLayoutsConfig()->item_pos[currentLayout][OSD_THROTTLE_POS]);
|
bool usingRCThrottle = OSD_VISIBLE(osdLayoutsConfig()->item_pos[currentLayout][OSD_THROTTLE_POS]);
|
||||||
|
@ -1323,7 +1340,11 @@ static void osdDrawMap(int referenceHeading, uint16_t referenceSym, uint16_t cen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (STATE(GPS_FIX)) {
|
if (STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
|
||||||
int directionToPoi = osdGetHeadingAngle(poiDirection - referenceHeading);
|
int directionToPoi = osdGetHeadingAngle(poiDirection - referenceHeading);
|
||||||
float poiAngle = DEGREES_TO_RADIANS(directionToPoi);
|
float poiAngle = DEGREES_TO_RADIANS(directionToPoi);
|
||||||
|
@ -1437,7 +1458,11 @@ static void osdDisplayTelemetry(void)
|
||||||
static uint16_t trk_bearing = 0;
|
static uint16_t trk_bearing = 0;
|
||||||
|
|
||||||
if (ARMING_FLAG(ARMED)) {
|
if (ARMING_FLAG(ARMED)) {
|
||||||
if (STATE(GPS_FIX)){
|
if (STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
){
|
||||||
if (GPS_distanceToHome > 5) {
|
if (GPS_distanceToHome > 5) {
|
||||||
trk_bearing = GPS_directionToHome;
|
trk_bearing = GPS_directionToHome;
|
||||||
trk_bearing += 360 + 180;
|
trk_bearing += 360 + 180;
|
||||||
|
@ -1726,7 +1751,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
tfp_sprintf(buff, "%5d", (int)getMAhDrawn()); // Use 5 digits to allow packs below 100Ah
|
tfp_sprintf(buff, "%5d", (int)getMAhDrawn()); // Use 5 digits to allow packs below 100Ah
|
||||||
buff[5] = SYM_MAH;
|
buff[5] = SYM_MAH;
|
||||||
buff[6] = '\0';
|
buff[6] = '\0';
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (osdFormatCentiNumber(buff, getMAhDrawn() * 100, 1000, 0, (mah_digits - 2), mah_digits, false)) {
|
if (osdFormatCentiNumber(buff, getMAhDrawn() * 100, 1000, 0, (mah_digits - 2), mah_digits, false)) {
|
||||||
|
@ -1760,7 +1785,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
tfp_sprintf(buff, " NF");
|
tfp_sprintf(buff, " NF");
|
||||||
else if (currentBatteryProfile->capacity.unit == BAT_CAPACITY_UNIT_MAH) {
|
else if (currentBatteryProfile->capacity.unit == BAT_CAPACITY_UNIT_MAH) {
|
||||||
uint8_t mah_digits = osdConfig()->mAh_precision; // Initialize to config value
|
uint8_t mah_digits = osdConfig()->mAh_precision; // Initialize to config value
|
||||||
|
|
||||||
#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it
|
#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it
|
||||||
if (isBfCompatibleVideoSystem(osdConfig())) {
|
if (isBfCompatibleVideoSystem(osdConfig())) {
|
||||||
//BFcompat is unable to work with scaled values and it only has mAh symbol to work with
|
//BFcompat is unable to work with scaled values and it only has mAh symbol to work with
|
||||||
|
@ -1768,7 +1793,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
buff[5] = SYM_MAH;
|
buff[5] = SYM_MAH;
|
||||||
buff[6] = '\0';
|
buff[6] = '\0';
|
||||||
unitsDrawn = true;
|
unitsDrawn = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (osdFormatCentiNumber(buff, getBatteryRemainingCapacity() * 100, 1000, 0, (mah_digits - 2), mah_digits, false)) {
|
if (osdFormatCentiNumber(buff, getBatteryRemainingCapacity() * 100, 1000, 0, (mah_digits - 2), mah_digits, false)) {
|
||||||
|
@ -1785,8 +1810,8 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
osdFormatCentiNumber(buff + 1, getBatteryRemainingCapacity() / 10, 0, 2, 0, 3, false);
|
osdFormatCentiNumber(buff + 1, getBatteryRemainingCapacity() / 10, 0, 2, 0, 3, false);
|
||||||
|
|
||||||
if (!unitsDrawn) {
|
if (!unitsDrawn) {
|
||||||
buff[4] = currentBatteryProfile->capacity.unit == BAT_CAPACITY_UNIT_MAH ? SYM_MAH : SYM_WH;
|
buff[4] = currentBatteryProfile->capacity.unit == BAT_CAPACITY_UNIT_MAH ? SYM_MAH : SYM_WH;
|
||||||
buff[5] = '\0';
|
buff[5] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batteryUsesCapacityThresholds()) {
|
if (batteryUsesCapacityThresholds()) {
|
||||||
|
@ -1815,6 +1840,12 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
buff[0] = SYM_SAT_L;
|
buff[0] = SYM_SAT_L;
|
||||||
buff[1] = SYM_SAT_R;
|
buff[1] = SYM_SAT_R;
|
||||||
tfp_sprintf(buff + 2, "%2d", gpsSol.numSat);
|
tfp_sprintf(buff + 2, "%2d", gpsSol.numSat);
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if (STATE(GPS_ESTIMATED_FIX)) {
|
||||||
|
strcpy(buff + 2, "ES");
|
||||||
|
TEXT_ATTRIBUTES_ADD_BLINK(elemAttr);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
if (!STATE(GPS_FIX)) {
|
if (!STATE(GPS_FIX)) {
|
||||||
hardwareSensorStatus_e sensorStatus = getHwGPSStatus();
|
hardwareSensorStatus_e sensorStatus = getHwGPSStatus();
|
||||||
if (sensorStatus == HW_SENSOR_UNAVAILABLE || sensorStatus == HW_SENSOR_UNHEALTHY) {
|
if (sensorStatus == HW_SENSOR_UNAVAILABLE || sensorStatus == HW_SENSOR_UNHEALTHY) {
|
||||||
|
@ -1872,7 +1903,11 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
|
|
||||||
case OSD_HOME_DIR:
|
case OSD_HOME_DIR:
|
||||||
{
|
{
|
||||||
if (STATE(GPS_FIX) && STATE(GPS_FIX_HOME) && isImuHeadingValid()) {
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && STATE(GPS_FIX_HOME) && isImuHeadingValid()) {
|
||||||
if (GPS_distanceToHome < (navConfig()->general.min_rth_distance / 100) ) {
|
if (GPS_distanceToHome < (navConfig()->general.min_rth_distance / 100) ) {
|
||||||
displayWriteChar(osdDisplayPort, elemPosX, elemPosY, SYM_HOME_NEAR);
|
displayWriteChar(osdDisplayPort, elemPosX, elemPosY, SYM_HOME_NEAR);
|
||||||
}
|
}
|
||||||
|
@ -2094,8 +2129,8 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
int8_t blankPos;
|
int8_t blankPos;
|
||||||
for (blankPos = 2; blankPos >= 0; blankPos--) {
|
for (blankPos = 2; blankPos >= 0; blankPos--) {
|
||||||
if (buff[blankPos] == SYM_BLANK) {
|
if (buff[blankPos] == SYM_BLANK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blankPos >= 0 || OSD_ALTERNATING_CHOICES(600, 2) == 0) {
|
if (blankPos >= 0 || OSD_ALTERNATING_CHOICES(600, 2) == 0) {
|
||||||
blankPos = blankPos < 0 ? 0 : blankPos;
|
blankPos = blankPos < 0 ? 0 : blankPos;
|
||||||
|
@ -2258,7 +2293,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
p = " WP ";
|
p = " WP ";
|
||||||
else if (FLIGHT_MODE(NAV_ALTHOLD_MODE) && navigationRequiresAngleMode()) {
|
else if (FLIGHT_MODE(NAV_ALTHOLD_MODE) && navigationRequiresAngleMode()) {
|
||||||
// If navigationRequiresAngleMode() returns false when ALTHOLD is active,
|
// If navigationRequiresAngleMode() returns false when ALTHOLD is active,
|
||||||
// it means it can be combined with ANGLE, HORIZON, ACRO, etc...
|
// it means it can be combined with ANGLE, HORIZON, ANGLEHOLD, ACRO, etc...
|
||||||
// and its display is handled by OSD_MESSAGES rather than OSD_FLYMODE.
|
// and its display is handled by OSD_MESSAGES rather than OSD_FLYMODE.
|
||||||
p = " AH ";
|
p = " AH ";
|
||||||
}
|
}
|
||||||
|
@ -2266,6 +2301,8 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
p = "ANGL";
|
p = "ANGL";
|
||||||
else if (FLIGHT_MODE(HORIZON_MODE))
|
else if (FLIGHT_MODE(HORIZON_MODE))
|
||||||
p = "HOR ";
|
p = "HOR ";
|
||||||
|
else if (FLIGHT_MODE(ANGLEHOLD_MODE))
|
||||||
|
p = "ANGH";
|
||||||
|
|
||||||
displayWrite(osdDisplayPort, elemPosX, elemPosY, p);
|
displayWrite(osdDisplayPort, elemPosX, elemPosY, p);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2416,11 +2453,19 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
osdCrosshairPosition(&elemPosX, &elemPosY);
|
osdCrosshairPosition(&elemPosX, &elemPosY);
|
||||||
osdHudDrawCrosshair(osdGetDisplayPortCanvas(), elemPosX, elemPosY);
|
osdHudDrawCrosshair(osdGetDisplayPortCanvas(), elemPosX, elemPosY);
|
||||||
|
|
||||||
if (osdConfig()->hud_homing && STATE(GPS_FIX) && STATE(GPS_FIX_HOME) && isImuHeadingValid()) {
|
if (osdConfig()->hud_homing && (STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && STATE(GPS_FIX_HOME) && isImuHeadingValid()) {
|
||||||
osdHudDrawHoming(elemPosX, elemPosY);
|
osdHudDrawHoming(elemPosX, elemPosY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (STATE(GPS_FIX) && isImuHeadingValid()) {
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && isImuHeadingValid()) {
|
||||||
|
|
||||||
if (osdConfig()->hud_homepoint || osdConfig()->hud_radar_disp > 0 || osdConfig()->hud_wp_disp > 0) {
|
if (osdConfig()->hud_homepoint || osdConfig()->hud_radar_disp > 0 || osdConfig()->hud_wp_disp > 0) {
|
||||||
osdHudClear();
|
osdHudClear();
|
||||||
|
@ -3044,7 +3089,11 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
digits = 4U;
|
digits = 4U;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (STATE(GPS_FIX) && gpsSol.groundSpeed > 0) {
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && gpsSol.groundSpeed > 0) {
|
||||||
if (efficiencyTimeDelta >= EFFICIENCY_UPDATE_INTERVAL) {
|
if (efficiencyTimeDelta >= EFFICIENCY_UPDATE_INTERVAL) {
|
||||||
value = pt1FilterApply4(&eFilterState, ((float)getAmperage() / gpsSol.groundSpeed) / 0.0036f,
|
value = pt1FilterApply4(&eFilterState, ((float)getAmperage() / gpsSol.groundSpeed) / 0.0036f,
|
||||||
1, US2S(efficiencyTimeDelta));
|
1, US2S(efficiencyTimeDelta));
|
||||||
|
@ -3066,7 +3115,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
tfp_sprintf(buff, "%s%c", buff, SYM_AH_MI);
|
tfp_sprintf(buff, "%s%c", buff, SYM_AH_MI);
|
||||||
}
|
}
|
||||||
if (!efficiencyValid) {
|
if (!efficiencyValid) {
|
||||||
buff[0] = buff[1] = buff[2] = buff[3] = '-';
|
buff[0] = buff[1] = buff[2] = buff[3] = '-';
|
||||||
buff[digits] = SYM_MAH_MI_0; // This will overwrite the "-" at buff[3] if not in BFCOMPAT mode
|
buff[digits] = SYM_MAH_MI_0; // This will overwrite the "-" at buff[3] if not in BFCOMPAT mode
|
||||||
buff[digits + 1] = SYM_MAH_MI_1;
|
buff[digits + 1] = SYM_MAH_MI_1;
|
||||||
buff[digits + 2] = '\0';
|
buff[digits + 2] = '\0';
|
||||||
|
@ -3080,7 +3129,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
tfp_sprintf(buff, "%s%c", buff, SYM_AH_NM);
|
tfp_sprintf(buff, "%s%c", buff, SYM_AH_NM);
|
||||||
}
|
}
|
||||||
if (!efficiencyValid) {
|
if (!efficiencyValid) {
|
||||||
buff[0] = buff[1] = buff[2] = buff[3] = '-';
|
buff[0] = buff[1] = buff[2] = buff[3] = '-';
|
||||||
buff[digits] = SYM_MAH_NM_0;
|
buff[digits] = SYM_MAH_NM_0;
|
||||||
buff[digits + 1] = SYM_MAH_NM_1;
|
buff[digits + 1] = SYM_MAH_NM_1;
|
||||||
buff[digits + 2] = '\0';
|
buff[digits + 2] = '\0';
|
||||||
|
@ -3096,7 +3145,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
tfp_sprintf(buff, "%s%c", buff, SYM_AH_KM);
|
tfp_sprintf(buff, "%s%c", buff, SYM_AH_KM);
|
||||||
}
|
}
|
||||||
if (!efficiencyValid) {
|
if (!efficiencyValid) {
|
||||||
buff[0] = buff[1] = buff[2] = buff[3] = '-';
|
buff[0] = buff[1] = buff[2] = buff[3] = '-';
|
||||||
buff[digits] = SYM_MAH_KM_0;
|
buff[digits] = SYM_MAH_KM_0;
|
||||||
buff[digits + 1] = SYM_MAH_KM_1;
|
buff[digits + 1] = SYM_MAH_KM_1;
|
||||||
buff[digits + 2] = '\0';
|
buff[digits + 2] = '\0';
|
||||||
|
@ -3115,7 +3164,11 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
int32_t value = 0;
|
int32_t value = 0;
|
||||||
timeUs_t currentTimeUs = micros();
|
timeUs_t currentTimeUs = micros();
|
||||||
timeDelta_t efficiencyTimeDelta = cmpTimeUs(currentTimeUs, efficiencyUpdated);
|
timeDelta_t efficiencyTimeDelta = cmpTimeUs(currentTimeUs, efficiencyUpdated);
|
||||||
if (STATE(GPS_FIX) && gpsSol.groundSpeed > 0) {
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && gpsSol.groundSpeed > 0) {
|
||||||
if (efficiencyTimeDelta >= EFFICIENCY_UPDATE_INTERVAL) {
|
if (efficiencyTimeDelta >= EFFICIENCY_UPDATE_INTERVAL) {
|
||||||
value = pt1FilterApply4(&eFilterState, ((float)getPower() / gpsSol.groundSpeed) / 0.0036f,
|
value = pt1FilterApply4(&eFilterState, ((float)getPower() / gpsSol.groundSpeed) / 0.0036f,
|
||||||
1, US2S(efficiencyTimeDelta));
|
1, US2S(efficiencyTimeDelta));
|
||||||
|
@ -3267,7 +3320,11 @@ static bool osdDrawSingleElement(uint8_t item)
|
||||||
STATIC_ASSERT(GPS_DEGREES_DIVIDER == OLC_DEG_MULTIPLIER, invalid_olc_deg_multiplier);
|
STATIC_ASSERT(GPS_DEGREES_DIVIDER == OLC_DEG_MULTIPLIER, invalid_olc_deg_multiplier);
|
||||||
int digits = osdConfig()->plus_code_digits;
|
int digits = osdConfig()->plus_code_digits;
|
||||||
int digitsRemoved = osdConfig()->plus_code_short * 2;
|
int digitsRemoved = osdConfig()->plus_code_short * 2;
|
||||||
if (STATE(GPS_FIX)) {
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
)) {
|
||||||
olc_encode(gpsSol.llh.lat, gpsSol.llh.lon, digits, buff, sizeof(buff));
|
olc_encode(gpsSol.llh.lat, gpsSol.llh.lon, digits, buff, sizeof(buff));
|
||||||
} else {
|
} else {
|
||||||
// +codes with > 8 digits have a + at the 9th digit
|
// +codes with > 8 digits have a + at the 9th digit
|
||||||
|
@ -3685,7 +3742,7 @@ void osdDrawNextElement(void)
|
||||||
elementIndex = osdIncElementIndex(elementIndex);
|
elementIndex = osdIncElementIndex(elementIndex);
|
||||||
} while (!osdDrawSingleElement(elementIndex) && index != elementIndex);
|
} while (!osdDrawSingleElement(elementIndex) && index != elementIndex);
|
||||||
|
|
||||||
// Draw artificial horizon + tracking telemtry last
|
// Draw artificial horizon + tracking telemetry last
|
||||||
osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON);
|
osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON);
|
||||||
if (osdConfig()->telemetry>0){
|
if (osdConfig()->telemetry>0){
|
||||||
osdDisplayTelemetry();
|
osdDisplayTelemetry();
|
||||||
|
@ -3995,7 +4052,7 @@ uint8_t drawLogos(bool singular, uint8_t row) {
|
||||||
|
|
||||||
if (logoSpacing > 0 && ((osdDisplayPort->cols % 2) != (logoSpacing % 2))) {
|
if (logoSpacing > 0 && ((osdDisplayPort->cols % 2) != (logoSpacing % 2))) {
|
||||||
logoSpacing++; // Add extra 1 character space between logos, if the odd/even of the OSD cols doesn't match the odd/even of the logo spacing
|
logoSpacing++; // Add extra 1 character space between logos, if the odd/even of the OSD cols doesn't match the odd/even of the logo spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw Logo(s)
|
// Draw Logo(s)
|
||||||
if (usePilotLogo && !singular) {
|
if (usePilotLogo && !singular) {
|
||||||
|
@ -4023,9 +4080,9 @@ uint8_t drawLogos(bool singular, uint8_t row) {
|
||||||
logoRow = row;
|
logoRow = row;
|
||||||
if (singular) {
|
if (singular) {
|
||||||
logo_x = logoColOffset;
|
logo_x = logoColOffset;
|
||||||
} else {
|
} else {
|
||||||
logo_x = logoColOffset + SYM_LOGO_WIDTH + logoSpacing;
|
logo_x = logoColOffset + SYM_LOGO_WIDTH + logoSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t lRow = 0; lRow < SYM_LOGO_HEIGHT; lRow++) {
|
for (uint8_t lRow = 0; lRow < SYM_LOGO_HEIGHT; lRow++) {
|
||||||
for (uint8_t lCol = 0; lCol < SYM_LOGO_WIDTH; lCol++) {
|
for (uint8_t lCol = 0; lCol < SYM_LOGO_WIDTH; lCol++) {
|
||||||
|
@ -4036,7 +4093,7 @@ uint8_t drawLogos(bool singular, uint8_t row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return logoRow;
|
return logoRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t drawStats(uint8_t row) {
|
uint8_t drawStats(uint8_t row) {
|
||||||
#ifdef USE_STATS
|
#ifdef USE_STATS
|
||||||
|
@ -4267,7 +4324,7 @@ static void osdUpdateStats(void)
|
||||||
static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
{
|
{
|
||||||
const char * disarmReasonStr[DISARM_REASON_COUNT] = { "UNKNOWN", "TIMEOUT", "STICKS", "SWITCH", "SWITCH", "KILLSW", "FAILSAFE", "NAV SYS", "LANDING"};
|
const char * disarmReasonStr[DISARM_REASON_COUNT] = { "UNKNOWN", "TIMEOUT", "STICKS", "SWITCH", "SWITCH", "KILLSW", "FAILSAFE", "NAV SYS", "LANDING"};
|
||||||
uint8_t top = 1; // Start one line down leaving space at the top of the screen.
|
uint8_t top = 1; // Start one line down leaving space at the top of the screen.
|
||||||
size_t multiValueLengthOffset = 0;
|
size_t multiValueLengthOffset = 0;
|
||||||
|
|
||||||
const uint8_t statNameX = osdDisplayIsHD() ? 11 : 1;
|
const uint8_t statNameX = osdDisplayIsHD() ? 11 : 1;
|
||||||
|
@ -4296,7 +4353,7 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
osdLeftAlignString(buff);
|
osdLeftAlignString(buff);
|
||||||
strcat(osdFormatTrimWhiteSpace(buff),"/");
|
strcat(osdFormatTrimWhiteSpace(buff),"/");
|
||||||
multiValueLengthOffset = strlen(buff);
|
multiValueLengthOffset = strlen(buff);
|
||||||
displayWrite(osdDisplayPort, statValuesX, top, buff);
|
displayWrite(osdDisplayPort, statValuesX, top, buff);
|
||||||
osdGenerateAverageVelocityStr(buff);
|
osdGenerateAverageVelocityStr(buff);
|
||||||
osdLeftAlignString(buff);
|
osdLeftAlignString(buff);
|
||||||
displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff);
|
displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff);
|
||||||
|
@ -4333,7 +4390,7 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
osdLeftAlignString(buff);
|
osdLeftAlignString(buff);
|
||||||
strcat(osdFormatTrimWhiteSpace(buff), "%/");
|
strcat(osdFormatTrimWhiteSpace(buff), "%/");
|
||||||
multiValueLengthOffset = strlen(buff);
|
multiValueLengthOffset = strlen(buff);
|
||||||
displayWrite(osdDisplayPort, statValuesX, top, buff);
|
displayWrite(osdDisplayPort, statValuesX, top, buff);
|
||||||
itoa(stats.min_rssi_dbm, buff, 10);
|
itoa(stats.min_rssi_dbm, buff, 10);
|
||||||
tfp_sprintf(buff, "%s%c", buff, SYM_DBM);
|
tfp_sprintf(buff, "%s%c", buff, SYM_DBM);
|
||||||
osdLeftAlignString(buff);
|
osdLeftAlignString(buff);
|
||||||
|
@ -4348,7 +4405,7 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
itoa(stats.min_rssi_dbm, buff, 10);
|
itoa(stats.min_rssi_dbm, buff, 10);
|
||||||
tfp_sprintf(buff, "%s%c", buff, SYM_DBM);
|
tfp_sprintf(buff, "%s%c", buff, SYM_DBM);
|
||||||
displayWrite(osdDisplayPort, statValuesX, top++, buff);
|
displayWrite(osdDisplayPort, statValuesX, top++, buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayWrite(osdDisplayPort, statNameX, top, "MIN LQ :");
|
displayWrite(osdDisplayPort, statNameX, top, "MIN LQ :");
|
||||||
itoa(stats.min_lq, buff, 10);
|
itoa(stats.min_lq, buff, 10);
|
||||||
|
@ -4360,7 +4417,7 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
itoa(stats.min_rssi, buff, 10);
|
itoa(stats.min_rssi, buff, 10);
|
||||||
strcat(buff, "%");
|
strcat(buff, "%");
|
||||||
displayWrite(osdDisplayPort, statValuesX, top++, buff);
|
displayWrite(osdDisplayPort, statValuesX, top++, buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayWrite(osdDisplayPort, statNameX, top, "FLY TIME :");
|
displayWrite(osdDisplayPort, statNameX, top, "FLY TIME :");
|
||||||
uint16_t flySeconds = getFlightTime();
|
uint16_t flySeconds = getFlightTime();
|
||||||
|
@ -4412,13 +4469,13 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
bool efficiencyValid = totalDistance >= 10000;
|
bool efficiencyValid = totalDistance >= 10000;
|
||||||
if (feature(FEATURE_GPS)) {
|
if (feature(FEATURE_GPS)) {
|
||||||
displayWrite(osdDisplayPort, statNameX, top, "AVG EFFICIENCY :");
|
displayWrite(osdDisplayPort, statNameX, top, "AVG EFFICIENCY :");
|
||||||
uint8_t digits = 3U; // Total number of digits (including decimal point)
|
uint8_t digits = 3U; // Total number of digits (including decimal point)
|
||||||
#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it and change the values
|
#ifndef DISABLE_MSP_BF_COMPAT // IF BFCOMPAT is not supported, there's no need to check for it and change the values
|
||||||
if (isBfCompatibleVideoSystem(osdConfig())) {
|
if (isBfCompatibleVideoSystem(osdConfig())) {
|
||||||
// Add one digit so no switch to scaled decimal occurs above 99
|
// Add one digit so no switch to scaled decimal occurs above 99
|
||||||
digits = 4U;
|
digits = 4U;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
switch (osdConfig()->units) {
|
switch (osdConfig()->units) {
|
||||||
case OSD_UNIT_UK:
|
case OSD_UNIT_UK:
|
||||||
FALLTHROUGH;
|
FALLTHROUGH;
|
||||||
|
@ -4507,9 +4564,9 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
displayWrite(osdDisplayPort, statNameX, top, "MIN/MAX Z G-FORCE:");
|
displayWrite(osdDisplayPort, statNameX, top, "MIN/MAX Z G-FORCE:");
|
||||||
osdFormatCentiNumber(buff, acc_extremes_min * 100, 0, 2, 0, 4, false);
|
osdFormatCentiNumber(buff, acc_extremes_min * 100, 0, 2, 0, 4, false);
|
||||||
osdLeftAlignString(buff);
|
osdLeftAlignString(buff);
|
||||||
strcat(osdFormatTrimWhiteSpace(buff),"/");
|
strcat(osdFormatTrimWhiteSpace(buff),"/");
|
||||||
multiValueLengthOffset = strlen(buff);
|
multiValueLengthOffset = strlen(buff);
|
||||||
displayWrite(osdDisplayPort, statValuesX, top, buff);
|
displayWrite(osdDisplayPort, statValuesX, top, buff);
|
||||||
osdFormatCentiNumber(buff, acc_extremes_max * 100, 0, 2, 0, 3, false);
|
osdFormatCentiNumber(buff, acc_extremes_max * 100, 0, 2, 0, 3, false);
|
||||||
osdLeftAlignString(buff);
|
osdLeftAlignString(buff);
|
||||||
displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff);
|
displayWrite(osdDisplayPort, statValuesX + multiValueLengthOffset, top++, buff);
|
||||||
|
@ -4531,10 +4588,10 @@ static void osdShowStats(bool isSinglePageStatsCompatible, uint8_t page)
|
||||||
// HD arming screen. based on the minimum HD OSD grid size of 50 x 18
|
// HD arming screen. based on the minimum HD OSD grid size of 50 x 18
|
||||||
static void osdShowHDArmScreen(void)
|
static void osdShowHDArmScreen(void)
|
||||||
{
|
{
|
||||||
dateTime_t dt;
|
dateTime_t dt;
|
||||||
char buf[MAX(osdDisplayPort->cols, FORMATTED_DATE_TIME_BUFSIZE)];
|
char buf[MAX(osdDisplayPort->cols, FORMATTED_DATE_TIME_BUFSIZE)];
|
||||||
char buf2[MAX(osdDisplayPort->cols, FORMATTED_DATE_TIME_BUFSIZE)];
|
char buf2[MAX(osdDisplayPort->cols, FORMATTED_DATE_TIME_BUFSIZE)];
|
||||||
char craftNameBuf[MAX_NAME_LENGTH];
|
char craftNameBuf[MAX_NAME_LENGTH];
|
||||||
char versionBuf[osdDisplayPort->cols];
|
char versionBuf[osdDisplayPort->cols];
|
||||||
uint8_t safehomeRow = 0;
|
uint8_t safehomeRow = 0;
|
||||||
uint8_t armScreenRow = 2; // Start at row 2
|
uint8_t armScreenRow = 2; // Start at row 2
|
||||||
|
@ -4574,7 +4631,7 @@ static void osdShowHDArmScreen(void)
|
||||||
#if defined(USE_GPS)
|
#if defined(USE_GPS)
|
||||||
if (feature(FEATURE_GPS)) {
|
if (feature(FEATURE_GPS)) {
|
||||||
if (STATE(GPS_FIX_HOME)) {
|
if (STATE(GPS_FIX_HOME)) {
|
||||||
if (osdConfig()->osd_home_position_arm_screen) {
|
if (osdConfig()->osd_home_position_arm_screen){
|
||||||
osdFormatCoordinate(buf, SYM_LAT, GPS_home.lat);
|
osdFormatCoordinate(buf, SYM_LAT, GPS_home.lat);
|
||||||
osdFormatCoordinate(buf2, SYM_LON, GPS_home.lon);
|
osdFormatCoordinate(buf2, SYM_LON, GPS_home.lon);
|
||||||
uint8_t gap = 1;
|
uint8_t gap = 1;
|
||||||
|
@ -4630,7 +4687,7 @@ static void osdShowHDArmScreen(void)
|
||||||
if (armScreenRow < (osdDisplayPort->rows - 4))
|
if (armScreenRow < (osdDisplayPort->rows - 4))
|
||||||
armScreenRow = drawStats(armScreenRow);
|
armScreenRow = drawStats(armScreenRow);
|
||||||
#endif // USE_STATS
|
#endif // USE_STATS
|
||||||
}
|
}
|
||||||
|
|
||||||
static void osdShowSDArmScreen(void)
|
static void osdShowSDArmScreen(void)
|
||||||
{
|
{
|
||||||
|
@ -4857,27 +4914,27 @@ static void osdRefresh(timeUs_t currentTimeUs)
|
||||||
|
|
||||||
// Manual paging stick commands are only applicable to multi-page stats.
|
// Manual paging stick commands are only applicable to multi-page stats.
|
||||||
// ******************************
|
// ******************************
|
||||||
// For single-page stats, this effectively disables the ability to cancel the
|
// For single-page stats, this effectively disables the ability to cancel the
|
||||||
// automatic paging/updates with the stick commands. So unless stats_page_auto_swap_time
|
// automatic paging/updates with the stick commands. So unless stats_page_auto_swap_time
|
||||||
// is set to 0 or greater than 4 (saved settings display interval is 5 seconds), then
|
// is set to 0 or greater than 4 (saved settings display interval is 5 seconds), then
|
||||||
// "Saved Settings" should display if it is active within the refresh interval.
|
// "Saved Settings" should display if it is active within the refresh interval.
|
||||||
// ******************************
|
// ******************************
|
||||||
// With multi-page stats, "Saved Settings" could also be missed if the user
|
// With multi-page stats, "Saved Settings" could also be missed if the user
|
||||||
// has canceled automatic paging using the stick commands, because that is only
|
// has canceled automatic paging using the stick commands, because that is only
|
||||||
// updated when osdShowStats() is called. So, in that case, they would only see
|
// updated when osdShowStats() is called. So, in that case, they would only see
|
||||||
// the "Saved Settings" message if they happen to manually change pages using the
|
// the "Saved Settings" message if they happen to manually change pages using the
|
||||||
// stick commands within the interval the message is displayed.
|
// stick commands within the interval the message is displayed.
|
||||||
bool manualPageUpRequested = false;
|
bool manualPageUpRequested = false;
|
||||||
bool manualPageDownRequested = false;
|
bool manualPageDownRequested = false;
|
||||||
if (!statsSinglePageCompatible) {
|
if (!statsSinglePageCompatible) {
|
||||||
// These methods ensure the paging stick commands are held for a brief period
|
// These methods ensure the paging stick commands are held for a brief period
|
||||||
// Otherwise it can result in a race condition where the stats are
|
// Otherwise it can result in a race condition where the stats are
|
||||||
// updated too quickly and can result in partial blanks, etc.
|
// updated too quickly and can result in partial blanks, etc.
|
||||||
if (osdIsPageUpStickCommandHeld()) {
|
if (osdIsPageUpStickCommandHeld()) {
|
||||||
manualPageUpRequested = true;
|
manualPageUpRequested = true;
|
||||||
statsAutoPagingEnabled = false;
|
statsAutoPagingEnabled = false;
|
||||||
} else if (osdIsPageDownStickCommandHeld()) {
|
} else if (osdIsPageDownStickCommandHeld()) {
|
||||||
manualPageDownRequested = true;
|
manualPageDownRequested = true;
|
||||||
statsAutoPagingEnabled = false;
|
statsAutoPagingEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4900,16 +4957,16 @@ static void osdRefresh(timeUs_t currentTimeUs)
|
||||||
// Process manual page change events for multi-page stats.
|
// Process manual page change events for multi-page stats.
|
||||||
if (manualPageUpRequested) {
|
if (manualPageUpRequested) {
|
||||||
osdShowStats(statsSinglePageCompatible, 1);
|
osdShowStats(statsSinglePageCompatible, 1);
|
||||||
statsCurrentPage = 1;
|
statsCurrentPage = 1;
|
||||||
} else if (manualPageDownRequested) {
|
} else if (manualPageDownRequested) {
|
||||||
osdShowStats(statsSinglePageCompatible, 0);
|
osdShowStats(statsSinglePageCompatible, 0);
|
||||||
statsCurrentPage = 0;
|
statsCurrentPage = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle events when either "Splash", "Armed" or "Stats" screens are displayed.
|
// Handle events when either "Splash", "Armed" or "Stats" screens are displayed.
|
||||||
if ((currentTimeUs > resumeRefreshAt) || OSD_RESUME_UPDATES_STICK_COMMAND) {
|
if ((currentTimeUs > resumeRefreshAt) || OSD_RESUME_UPDATES_STICK_COMMAND) {
|
||||||
// Time elapsed or canceled by stick commands.
|
// Time elapsed or canceled by stick commands.
|
||||||
// Exit to normal OSD operation.
|
// Exit to normal OSD operation.
|
||||||
displayClearScreen(osdDisplayPort);
|
displayClearScreen(osdDisplayPort);
|
||||||
|
@ -4919,7 +4976,7 @@ static void osdRefresh(timeUs_t currentTimeUs)
|
||||||
// Continue "Splash", "Armed" or "Stats" screens.
|
// Continue "Splash", "Armed" or "Stats" screens.
|
||||||
displayHeartbeat(osdDisplayPort);
|
displayHeartbeat(osdDisplayPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5122,6 +5179,11 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter
|
||||||
|
|
||||||
messages[messageCount++] = messageBuf;
|
messages[messageCount++] = messageBuf;
|
||||||
}
|
}
|
||||||
|
} else if (NAV_Status.state == MW_NAV_STATE_LAND_SETTLE && posControl.landingDelay > 0) {
|
||||||
|
uint16_t remainingHoldSec = MS2S(posControl.landingDelay - millis());
|
||||||
|
tfp_sprintf(messageBuf, "LANDING DELAY: %3u SECONDS", remainingHoldSec);
|
||||||
|
|
||||||
|
messages[messageCount++] = messageBuf;
|
||||||
} else {
|
} else {
|
||||||
const char *navStateMessage = navigationStateMessage();
|
const char *navStateMessage = navigationStateMessage();
|
||||||
if (navStateMessage) {
|
if (navStateMessage) {
|
||||||
|
@ -5156,10 +5218,9 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (FLIGHT_MODE(NAV_ALTHOLD_MODE) && !navigationRequiresAngleMode()) {
|
if (FLIGHT_MODE(NAV_ALTHOLD_MODE) && !navigationRequiresAngleMode()) {
|
||||||
// ALTHOLD might be enabled alongside ANGLE/HORIZON/ACRO
|
// ALTHOLD might be enabled alongside ANGLE/HORIZON/ANGLEHOLD/ACRO
|
||||||
// when it doesn't require ANGLE mode (required only in FW
|
// when it doesn't require ANGLE mode (required only in FW
|
||||||
// right now). If if requires ANGLE, its display is handled
|
// right now). If it requires ANGLE, its display is handled by OSD_FLYMODE.
|
||||||
// by OSD_FLYMODE.
|
|
||||||
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ALTITUDE_HOLD);
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ALTITUDE_HOLD);
|
||||||
}
|
}
|
||||||
if (STATE(MULTIROTOR) && FLIGHT_MODE(NAV_COURSE_HOLD_MODE)) {
|
if (STATE(MULTIROTOR) && FLIGHT_MODE(NAV_COURSE_HOLD_MODE)) {
|
||||||
|
@ -5182,7 +5243,7 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isFixedWingLevelTrimActive()) {
|
if (isFixedWingLevelTrimActive()) {
|
||||||
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_AUTOLEVEL);
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_AUTOLEVEL);
|
||||||
}
|
}
|
||||||
if (FLIGHT_MODE(HEADFREE_MODE)) {
|
if (FLIGHT_MODE(HEADFREE_MODE)) {
|
||||||
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_HEADFREE);
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_HEADFREE);
|
||||||
|
@ -5196,6 +5257,16 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter
|
||||||
if (STATE(LANDING_DETECTED)) {
|
if (STATE(LANDING_DETECTED)) {
|
||||||
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_LANDED);
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_LANDED);
|
||||||
}
|
}
|
||||||
|
if (IS_RC_MODE_ACTIVE(BOXANGLEHOLD)) {
|
||||||
|
int8_t navAngleHoldAxis = navCheckActiveAngleHoldAxis();
|
||||||
|
if (isAngleHoldLevel()) {
|
||||||
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_LEVEL);
|
||||||
|
} else if (navAngleHoldAxis == FD_ROLL) {
|
||||||
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_ROLL);
|
||||||
|
} else if (navAngleHoldAxis == FD_PITCH) {
|
||||||
|
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_PITCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ARMING_FLAG(ARMING_DISABLED_ALL_FLAGS)) {
|
} else if (ARMING_FLAG(ARMING_DISABLED_ALL_FLAGS)) {
|
||||||
|
|
|
@ -93,6 +93,7 @@
|
||||||
#define OSD_MSG_RTH_CLIMB "ADJUSTING RTH ALTITUDE"
|
#define OSD_MSG_RTH_CLIMB "ADJUSTING RTH ALTITUDE"
|
||||||
#define OSD_MSG_RTH_TRACKBACK "RTH BACK TRACKING"
|
#define OSD_MSG_RTH_TRACKBACK "RTH BACK TRACKING"
|
||||||
#define OSD_MSG_HEADING_HOME "EN ROUTE TO HOME"
|
#define OSD_MSG_HEADING_HOME "EN ROUTE TO HOME"
|
||||||
|
#define OSD_MSG_RTH_LINEAR_DESCENT "BEGIN LINEAR DESCENT"
|
||||||
#define OSD_MSG_WP_FINISHED "WP END>HOLDING POSITION"
|
#define OSD_MSG_WP_FINISHED "WP END>HOLDING POSITION"
|
||||||
#define OSD_MSG_WP_LANDED "WP END>LANDED"
|
#define OSD_MSG_WP_LANDED "WP END>LANDED"
|
||||||
#define OSD_MSG_PREPARE_NEXT_WP "PREPARING FOR NEXT WAYPOINT"
|
#define OSD_MSG_PREPARE_NEXT_WP "PREPARING FOR NEXT WAYPOINT"
|
||||||
|
@ -118,6 +119,9 @@
|
||||||
#define OSD_MSG_UNABLE_ARM "UNABLE TO ARM"
|
#define OSD_MSG_UNABLE_ARM "UNABLE TO ARM"
|
||||||
#define OSD_MSG_SAVING_SETTNGS "** SAVING SETTINGS **"
|
#define OSD_MSG_SAVING_SETTNGS "** SAVING SETTINGS **"
|
||||||
#define OSD_MSG_SETTINGS_SAVED "** SETTINGS SAVED **"
|
#define OSD_MSG_SETTINGS_SAVED "** SETTINGS SAVED **"
|
||||||
|
#define OSD_MSG_ANGLEHOLD_ROLL "(ANGLEHOLD ROLL)"
|
||||||
|
#define OSD_MSG_ANGLEHOLD_PITCH "(ANGLEHOLD PITCH)"
|
||||||
|
#define OSD_MSG_ANGLEHOLD_LEVEL "(ANGLEHOLD LEVEL)"
|
||||||
|
|
||||||
#ifdef USE_DEV_TOOLS
|
#ifdef USE_DEV_TOOLS
|
||||||
#define OSD_MSG_GRD_TEST_MODE "GRD TEST > MOTORS DISABLED"
|
#define OSD_MSG_GRD_TEST_MODE "GRD TEST > MOTORS DISABLED"
|
||||||
|
@ -413,7 +417,7 @@ typedef struct osdConfig_s {
|
||||||
|
|
||||||
#ifdef USE_WIND_ESTIMATOR
|
#ifdef USE_WIND_ESTIMATOR
|
||||||
bool estimations_wind_compensation; // use wind compensation for estimated remaining flight/distance
|
bool estimations_wind_compensation; // use wind compensation for estimated remaining flight/distance
|
||||||
#endif
|
#endif
|
||||||
uint8_t coordinate_digits;
|
uint8_t coordinate_digits;
|
||||||
bool osd_failsafe_switch_layout;
|
bool osd_failsafe_switch_layout;
|
||||||
uint8_t plus_code_digits; // Number of digits to use in OSD_PLUS_CODE
|
uint8_t plus_code_digits; // Number of digits to use in OSD_PLUS_CODE
|
||||||
|
|
|
@ -300,6 +300,7 @@ static void djiPackBoxModeBitmask(boxBitmask_t * flightModeBitmask)
|
||||||
case FLM_ALTITUDE_HOLD:
|
case FLM_ALTITUDE_HOLD:
|
||||||
case FLM_POSITION_HOLD:
|
case FLM_POSITION_HOLD:
|
||||||
case FLM_MISSION:
|
case FLM_MISSION:
|
||||||
|
case FLM_ANGLEHOLD:
|
||||||
default:
|
default:
|
||||||
// Unsupported ATM, keep at ANGLE
|
// Unsupported ATM, keep at ANGLE
|
||||||
bitArraySet(flightModeBitmask->bits, 1); // DJI: 1 << 1 : ANGLE
|
bitArraySet(flightModeBitmask->bits, 1); // DJI: 1 << 1 : ANGLE
|
||||||
|
@ -786,7 +787,11 @@ static void osdDJIEfficiencyMahPerKM(char *buff)
|
||||||
timeUs_t currentTimeUs = micros();
|
timeUs_t currentTimeUs = micros();
|
||||||
timeDelta_t efficiencyTimeDelta = cmpTimeUs(currentTimeUs, efficiencyUpdated);
|
timeDelta_t efficiencyTimeDelta = cmpTimeUs(currentTimeUs, efficiencyUpdated);
|
||||||
|
|
||||||
if (STATE(GPS_FIX) && gpsSol.groundSpeed > 0) {
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && gpsSol.groundSpeed > 0) {
|
||||||
if (efficiencyTimeDelta >= EFFICIENCY_UPDATE_INTERVAL) {
|
if (efficiencyTimeDelta >= EFFICIENCY_UPDATE_INTERVAL) {
|
||||||
value = pt1FilterApply4(&eFilterState, ((float)getAmperage() / gpsSol.groundSpeed) / 0.0036f,
|
value = pt1FilterApply4(&eFilterState, ((float)getAmperage() / gpsSol.groundSpeed) / 0.0036f,
|
||||||
1, US2S(efficiencyTimeDelta));
|
1, US2S(efficiencyTimeDelta));
|
||||||
|
|
74
src/main/io/osd_joystick.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include "common/crc.h"
|
||||||
|
#include "common/maths.h"
|
||||||
|
#include "common/streambuf.h"
|
||||||
|
#include "common/utils.h"
|
||||||
|
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
#include "config/parameter_group.h"
|
||||||
|
#include "config/parameter_group_ids.h"
|
||||||
|
#include "fc/settings.h"
|
||||||
|
#include "fc/runtime_config.h"
|
||||||
|
|
||||||
|
#include "drivers/time.h"
|
||||||
|
#include "drivers/light_ws2811strip.h"
|
||||||
|
|
||||||
|
#include "io/serial.h"
|
||||||
|
#include "io/rcdevice.h"
|
||||||
|
|
||||||
|
#include "osd_joystick.h"
|
||||||
|
|
||||||
|
#ifdef USE_RCDEVICE
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
|
||||||
|
|
||||||
|
PG_REGISTER_WITH_RESET_TEMPLATE(osdJoystickConfig_t, osdJoystickConfig, PG_OSD_JOYSTICK_CONFIG, 0);
|
||||||
|
|
||||||
|
PG_RESET_TEMPLATE(osdJoystickConfig_t, osdJoystickConfig,
|
||||||
|
.osd_joystick_enabled = SETTING_OSD_JOYSTICK_ENABLED_DEFAULT,
|
||||||
|
.osd_joystick_down = SETTING_OSD_JOYSTICK_DOWN_DEFAULT,
|
||||||
|
.osd_joystick_up = SETTING_OSD_JOYSTICK_UP_DEFAULT,
|
||||||
|
.osd_joystick_left = SETTING_OSD_JOYSTICK_LEFT_DEFAULT,
|
||||||
|
.osd_joystick_right = SETTING_OSD_JOYSTICK_RIGHT_DEFAULT,
|
||||||
|
.osd_joystick_enter = SETTING_OSD_JOYSTICK_ENTER_DEFAULT
|
||||||
|
);
|
||||||
|
|
||||||
|
bool osdJoystickEnabled(void) {
|
||||||
|
return osdJoystickConfig()->osd_joystick_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void osdJoystickSimulate5KeyButtonPress(uint8_t operation) {
|
||||||
|
switch (operation) {
|
||||||
|
case RCDEVICE_CAM_KEY_ENTER:
|
||||||
|
ledPinStartPWM( osdJoystickConfig()->osd_joystick_enter );
|
||||||
|
break;
|
||||||
|
case RCDEVICE_CAM_KEY_LEFT:
|
||||||
|
ledPinStartPWM( osdJoystickConfig()->osd_joystick_left );
|
||||||
|
break;
|
||||||
|
case RCDEVICE_CAM_KEY_UP:
|
||||||
|
ledPinStartPWM( osdJoystickConfig()->osd_joystick_up );
|
||||||
|
break;
|
||||||
|
case RCDEVICE_CAM_KEY_RIGHT:
|
||||||
|
ledPinStartPWM( osdJoystickConfig()->osd_joystick_right );
|
||||||
|
break;
|
||||||
|
case RCDEVICE_CAM_KEY_DOWN:
|
||||||
|
ledPinStartPWM( osdJoystickConfig()->osd_joystick_down );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void osdJoystickSimulate5KeyButtonRelease(void) {
|
||||||
|
ledPinStopPWM();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
26
src/main/io/osd_joystick.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config/parameter_group.h"
|
||||||
|
|
||||||
|
#ifdef USE_RCDEVICE
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
|
||||||
|
typedef struct osdJoystickConfig_s {
|
||||||
|
bool osd_joystick_enabled;
|
||||||
|
uint8_t osd_joystick_down;
|
||||||
|
uint8_t osd_joystick_up;
|
||||||
|
uint8_t osd_joystick_left;
|
||||||
|
uint8_t osd_joystick_right;
|
||||||
|
uint8_t osd_joystick_enter;
|
||||||
|
} osdJoystickConfig_t;
|
||||||
|
|
||||||
|
PG_DECLARE(osdJoystickConfig_t, osdJoystickConfig);
|
||||||
|
|
||||||
|
bool osdJoystickEnabled(void);
|
||||||
|
|
||||||
|
// 5 key osd cable simulation
|
||||||
|
void osdJoystickSimulate5KeyButtonPress(uint8_t operation);
|
||||||
|
void osdJoystickSimulate5KeyButtonRelease(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "io/beeper.h"
|
#include "io/beeper.h"
|
||||||
#include "io/rcdevice_cam.h"
|
#include "io/rcdevice_cam.h"
|
||||||
|
#include "io/osd_joystick.h"
|
||||||
|
|
||||||
#include "rx/rx.h"
|
#include "rx/rx.h"
|
||||||
|
|
||||||
|
@ -47,6 +48,14 @@ bool waitingDeviceResponse = false;
|
||||||
|
|
||||||
static bool isFeatureSupported(uint8_t feature)
|
static bool isFeatureSupported(uint8_t feature)
|
||||||
{
|
{
|
||||||
|
#ifndef UNIT_TEST
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
if (!rcdeviceIsEnabled() && osdJoystickEnabled() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
if (camDevice->info.features & feature) {
|
if (camDevice->info.features & feature) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +81,7 @@ static void rcdeviceCameraControlProcess(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t behavior = RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION;
|
uint8_t behavior = RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION;
|
||||||
|
uint8_t behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION;
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case BOXCAMERA1:
|
case BOXCAMERA1:
|
||||||
if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_WIFI_BUTTON)) {
|
if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_WIFI_BUTTON)) {
|
||||||
|
@ -81,11 +91,13 @@ static void rcdeviceCameraControlProcess(void)
|
||||||
if (!ARMING_FLAG(ARMED)) {
|
if (!ARMING_FLAG(ARMED)) {
|
||||||
behavior = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN;
|
behavior = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN;
|
||||||
}
|
}
|
||||||
|
behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BOXCAMERA2:
|
case BOXCAMERA2:
|
||||||
if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_POWER_BUTTON)) {
|
if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_POWER_BUTTON)) {
|
||||||
behavior = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN;
|
behavior = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN;
|
||||||
|
behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BOXCAMERA3:
|
case BOXCAMERA3:
|
||||||
|
@ -94,16 +106,43 @@ static void rcdeviceCameraControlProcess(void)
|
||||||
if (!ARMING_FLAG(ARMED)) {
|
if (!ARMING_FLAG(ARMED)) {
|
||||||
behavior = RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE;
|
behavior = RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE;
|
||||||
}
|
}
|
||||||
|
behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (behavior != RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION) {
|
if ((behavior != RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION) && rcdeviceIsEnabled()) {
|
||||||
runcamDeviceSimulateCameraButton(camDevice, behavior);
|
runcamDeviceSimulateCameraButton(camDevice, behavior);
|
||||||
switchStates[switchIndex].isActivated = true;
|
switchStates[switchIndex].isActivated = true;
|
||||||
}
|
}
|
||||||
|
#ifndef UNIT_TEST
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
else if ((behavior1 != RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION) && osdJoystickEnabled()) {
|
||||||
|
switch (behavior1) {
|
||||||
|
case RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN:
|
||||||
|
osdJoystickSimulate5KeyButtonPress(RCDEVICE_CAM_KEY_ENTER);
|
||||||
|
break;
|
||||||
|
case RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN:
|
||||||
|
osdJoystickSimulate5KeyButtonPress(RCDEVICE_CAM_KEY_UP);
|
||||||
|
break;
|
||||||
|
case RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE:
|
||||||
|
osdJoystickSimulate5KeyButtonPress(RCDEVICE_CAM_KEY_DOWN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switchStates[switchIndex].isActivated = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
UNUSED(behavior1);
|
||||||
} else {
|
} else {
|
||||||
|
#ifndef UNIT_TEST
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
if (osdJoystickEnabled() && switchStates[switchIndex].isActivated) {
|
||||||
|
osdJoystickSimulate5KeyButtonRelease();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
switchStates[switchIndex].isActivated = false;
|
switchStates[switchIndex].isActivated = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,14 +264,24 @@ static void rcdevice5KeySimulationProcess(timeUs_t currentTimeUs)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (camDevice->serialPort == 0 || ARMING_FLAG(ARMED)) {
|
if (ARMING_FLAG(ARMED)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isButtonPressed) {
|
if (isButtonPressed) {
|
||||||
if (IS_MID(YAW) && IS_MID(PITCH) && IS_MID(ROLL)) {
|
if (IS_MID(YAW) && IS_MID(PITCH) && IS_MID(ROLL)) {
|
||||||
rcdeviceSend5KeyOSDCableSimualtionEvent(RCDEVICE_CAM_KEY_RELEASE);
|
if ( rcdeviceIsEnabled() ) {
|
||||||
waitingDeviceResponse = true;
|
rcdeviceSend5KeyOSDCableSimualtionEvent(RCDEVICE_CAM_KEY_RELEASE);
|
||||||
|
waitingDeviceResponse = true;
|
||||||
|
}
|
||||||
|
#ifndef UNIT_TEST
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
else if (osdJoystickEnabled()) {
|
||||||
|
osdJoystickSimulate5KeyButtonRelease();
|
||||||
|
isButtonPressed = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (waitingDeviceResponse) {
|
if (waitingDeviceResponse) {
|
||||||
|
@ -266,16 +315,33 @@ static void rcdevice5KeySimulationProcess(timeUs_t currentTimeUs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key != RCDEVICE_CAM_KEY_NONE) {
|
if (key != RCDEVICE_CAM_KEY_NONE) {
|
||||||
rcdeviceSend5KeyOSDCableSimualtionEvent(key);
|
if ( rcdeviceIsEnabled() ) {
|
||||||
|
rcdeviceSend5KeyOSDCableSimualtionEvent(key);
|
||||||
|
waitingDeviceResponse = true;
|
||||||
|
}
|
||||||
|
#ifndef UNIT_TEST
|
||||||
|
#ifdef USE_LED_STRIP
|
||||||
|
else if (osdJoystickEnabled()) {
|
||||||
|
if ( key == RCDEVICE_CAM_KEY_CONNECTION_OPEN ) {
|
||||||
|
rcdeviceInMenu = true;
|
||||||
|
} else if ( key == RCDEVICE_CAM_KEY_CONNECTION_CLOSE ) {
|
||||||
|
rcdeviceInMenu = false;
|
||||||
|
} else {
|
||||||
|
osdJoystickSimulate5KeyButtonPress(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
isButtonPressed = true;
|
isButtonPressed = true;
|
||||||
waitingDeviceResponse = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rcdeviceUpdate(timeUs_t currentTimeUs)
|
void rcdeviceUpdate(timeUs_t currentTimeUs)
|
||||||
{
|
{
|
||||||
rcdeviceReceive(currentTimeUs);
|
if ( rcdeviceIsEnabled() ) {
|
||||||
|
rcdeviceReceive(currentTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
rcdeviceCameraControlProcess();
|
rcdeviceCameraControlProcess();
|
||||||
|
|
||||||
|
|
|
@ -575,16 +575,27 @@ const char * const trampPowerNames_5G8_800[VTX_TRAMP_5G8_MAX_POWER_COUNT + 1] =
|
||||||
const uint16_t trampPowerTable_1G3_800[VTX_TRAMP_1G3_MAX_POWER_COUNT] = { 25, 200, 800 };
|
const uint16_t trampPowerTable_1G3_800[VTX_TRAMP_1G3_MAX_POWER_COUNT] = { 25, 200, 800 };
|
||||||
const char * const trampPowerNames_1G3_800[VTX_TRAMP_1G3_MAX_POWER_COUNT + 1] = { "---", "25 ", "200", "800" };
|
const char * const trampPowerNames_1G3_800[VTX_TRAMP_1G3_MAX_POWER_COUNT + 1] = { "---", "25 ", "200", "800" };
|
||||||
|
|
||||||
|
const uint16_t trampPowerTable_1G3_2000[VTX_TRAMP_1G3_MAX_POWER_COUNT] = { 25, 200, 2000 };
|
||||||
|
const char * const trampPowerNames_1G3_2000[VTX_TRAMP_1G3_MAX_POWER_COUNT + 1] = { "---", "25 ", "200", "2000" };
|
||||||
|
|
||||||
static void vtxProtoUpdatePowerMetadata(uint16_t maxPower)
|
static void vtxProtoUpdatePowerMetadata(uint16_t maxPower)
|
||||||
{
|
{
|
||||||
switch (vtxSettingsConfig()->frequencyGroup) {
|
switch (vtxSettingsConfig()->frequencyGroup) {
|
||||||
case FREQUENCYGROUP_1G3:
|
case FREQUENCYGROUP_1G3:
|
||||||
vtxState.metadata.powerTablePtr = trampPowerTable_1G3_800;
|
if (maxPower >= 2000) {
|
||||||
vtxState.metadata.powerTableCount = VTX_TRAMP_1G3_MAX_POWER_COUNT;
|
vtxState.metadata.powerTablePtr = trampPowerTable_1G3_2000;
|
||||||
|
vtxState.metadata.powerTableCount = VTX_TRAMP_1G3_MAX_POWER_COUNT;
|
||||||
|
|
||||||
impl_vtxDevice.capability.powerNames = (char **)trampPowerNames_1G3_800;
|
impl_vtxDevice.capability.powerNames = (char **)trampPowerNames_1G3_2000;
|
||||||
impl_vtxDevice.capability.powerCount = VTX_TRAMP_1G3_MAX_POWER_COUNT;
|
impl_vtxDevice.capability.powerCount = VTX_TRAMP_1G3_MAX_POWER_COUNT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vtxState.metadata.powerTablePtr = trampPowerTable_1G3_800;
|
||||||
|
vtxState.metadata.powerTableCount = VTX_TRAMP_1G3_MAX_POWER_COUNT;
|
||||||
|
|
||||||
|
impl_vtxDevice.capability.powerNames = (char **)trampPowerNames_1G3_800;
|
||||||
|
impl_vtxDevice.capability.powerCount = VTX_TRAMP_1G3_MAX_POWER_COUNT;
|
||||||
|
}
|
||||||
impl_vtxDevice.capability.bandCount = VTX_TRAMP_1G3_BAND_COUNT;
|
impl_vtxDevice.capability.bandCount = VTX_TRAMP_1G3_BAND_COUNT;
|
||||||
impl_vtxDevice.capability.channelCount = VTX_TRAMP_1G3_CHANNEL_COUNT;
|
impl_vtxDevice.capability.channelCount = VTX_TRAMP_1G3_CHANNEL_COUNT;
|
||||||
impl_vtxDevice.capability.bandNames = (char **)vtx1G3BandNames;
|
impl_vtxDevice.capability.bandNames = (char **)vtx1G3BandNames;
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
|
|
||||||
#include "navigation/navigation.h"
|
#include "navigation/navigation.h"
|
||||||
#include "navigation/navigation_private.h"
|
#include "navigation/navigation_private.h"
|
||||||
|
#include "navigation/rth_trackback.h"
|
||||||
|
|
||||||
#include "rx/rx.h"
|
#include "rx/rx.h"
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ STATIC_ASSERT(NAV_MAX_WAYPOINTS < 254, NAV_MAX_WAYPOINTS_exceeded_allowable_rang
|
||||||
PG_REGISTER_ARRAY(navWaypoint_t, NAV_MAX_WAYPOINTS, nonVolatileWaypointList, PG_WAYPOINT_MISSION_STORAGE, 2);
|
PG_REGISTER_ARRAY(navWaypoint_t, NAV_MAX_WAYPOINTS, nonVolatileWaypointList, PG_WAYPOINT_MISSION_STORAGE, 2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PG_REGISTER_WITH_RESET_TEMPLATE(navConfig_t, navConfig, PG_NAV_CONFIG, 5);
|
PG_REGISTER_WITH_RESET_TEMPLATE(navConfig_t, navConfig, PG_NAV_CONFIG, 6);
|
||||||
|
|
||||||
PG_RESET_TEMPLATE(navConfig_t, navConfig,
|
PG_RESET_TEMPLATE(navConfig_t, navConfig,
|
||||||
.general = {
|
.general = {
|
||||||
|
@ -132,6 +133,7 @@ PG_RESET_TEMPLATE(navConfig_t, navConfig,
|
||||||
#endif
|
#endif
|
||||||
.waypoint_load_on_boot = SETTING_NAV_WP_LOAD_ON_BOOT_DEFAULT, // load waypoints automatically during boot
|
.waypoint_load_on_boot = SETTING_NAV_WP_LOAD_ON_BOOT_DEFAULT, // load waypoints automatically during boot
|
||||||
.auto_speed = SETTING_NAV_AUTO_SPEED_DEFAULT, // speed in autonomous modes (3 m/s = 10.8 km/h)
|
.auto_speed = SETTING_NAV_AUTO_SPEED_DEFAULT, // speed in autonomous modes (3 m/s = 10.8 km/h)
|
||||||
|
.min_ground_speed = SETTING_NAV_MIN_GROUND_SPEED_DEFAULT, // Minimum ground speed (m/s)
|
||||||
.max_auto_speed = SETTING_NAV_MAX_AUTO_SPEED_DEFAULT, // max allowed speed autonomous modes
|
.max_auto_speed = SETTING_NAV_MAX_AUTO_SPEED_DEFAULT, // max allowed speed autonomous modes
|
||||||
.max_manual_speed = SETTING_NAV_MANUAL_SPEED_DEFAULT,
|
.max_manual_speed = SETTING_NAV_MANUAL_SPEED_DEFAULT,
|
||||||
.land_slowdown_minalt = SETTING_NAV_LAND_SLOWDOWN_MINALT_DEFAULT, // altitude in centimeters
|
.land_slowdown_minalt = SETTING_NAV_LAND_SLOWDOWN_MINALT_DEFAULT, // altitude in centimeters
|
||||||
|
@ -153,6 +155,7 @@ PG_RESET_TEMPLATE(navConfig_t, navConfig,
|
||||||
.auto_disarm_delay = SETTING_NAV_AUTO_DISARM_DELAY_DEFAULT, // 2000 ms - time delay to disarm when auto disarm after landing enabled
|
.auto_disarm_delay = SETTING_NAV_AUTO_DISARM_DELAY_DEFAULT, // 2000 ms - time delay to disarm when auto disarm after landing enabled
|
||||||
.rth_linear_descent_start_distance = SETTING_NAV_RTH_LINEAR_DESCENT_START_DISTANCE_DEFAULT,
|
.rth_linear_descent_start_distance = SETTING_NAV_RTH_LINEAR_DESCENT_START_DISTANCE_DEFAULT,
|
||||||
.cruise_yaw_rate = SETTING_NAV_CRUISE_YAW_RATE_DEFAULT, // 20dps
|
.cruise_yaw_rate = SETTING_NAV_CRUISE_YAW_RATE_DEFAULT, // 20dps
|
||||||
|
.rth_fs_landing_delay = SETTING_NAV_RTH_FS_LANDING_DELAY_DEFAULT, // Delay before landing in FS. 0 = immedate landing
|
||||||
},
|
},
|
||||||
|
|
||||||
// MC-specific
|
// MC-specific
|
||||||
|
@ -257,10 +260,8 @@ static void resetJumpCounter(void);
|
||||||
static void clearJumpCounters(void);
|
static void clearJumpCounters(void);
|
||||||
|
|
||||||
static void calculateAndSetActiveWaypoint(const navWaypoint_t * waypoint);
|
static void calculateAndSetActiveWaypoint(const navWaypoint_t * waypoint);
|
||||||
static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t * pos);
|
|
||||||
void calculateInitialHoldPosition(fpVector3_t * pos);
|
void calculateInitialHoldPosition(fpVector3_t * pos);
|
||||||
void calculateFarAwayTarget(fpVector3_t * farAwayPos, int32_t bearing, int32_t distance);
|
void calculateFarAwayTarget(fpVector3_t * farAwayPos, int32_t bearing, int32_t distance);
|
||||||
static bool isWaypointReached(const fpVector3_t * waypointPos, const int32_t * waypointBearing);
|
|
||||||
bool isWaypointAltitudeReached(void);
|
bool isWaypointAltitudeReached(void);
|
||||||
static void mapWaypointToLocalPosition(fpVector3_t * localPos, const navWaypoint_t * waypoint, geoAltitudeConversionMode_e altConv);
|
static void mapWaypointToLocalPosition(fpVector3_t * localPos, const navWaypoint_t * waypoint, geoAltitudeConversionMode_e altConv);
|
||||||
static navigationFSMEvent_t nextForNonGeoStates(void);
|
static navigationFSMEvent_t nextForNonGeoStates(void);
|
||||||
|
@ -272,11 +273,6 @@ bool validateRTHSanityChecker(void);
|
||||||
void updateHomePosition(void);
|
void updateHomePosition(void);
|
||||||
bool abortLaunchAllowed(void);
|
bool abortLaunchAllowed(void);
|
||||||
|
|
||||||
static bool rthAltControlStickOverrideCheck(unsigned axis);
|
|
||||||
|
|
||||||
static void updateRthTrackback(bool forceSaveTrackPoint);
|
|
||||||
static fpVector3_t * rthGetTrackbackPos(void);
|
|
||||||
|
|
||||||
/*************************************************************************************************/
|
/*************************************************************************************************/
|
||||||
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState);
|
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState);
|
||||||
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState);
|
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState);
|
||||||
|
@ -1228,6 +1224,9 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigati
|
||||||
{
|
{
|
||||||
UNUSED(previousState);
|
UNUSED(previousState);
|
||||||
|
|
||||||
|
if (navConfig()->general.flags.rth_use_linear_descent && posControl.rthState.rthLinearDescentActive)
|
||||||
|
posControl.rthState.rthLinearDescentActive = false;
|
||||||
|
|
||||||
if ((posControl.flags.estHeadingStatus == EST_NONE) || (posControl.flags.estAltStatus == EST_NONE) || !STATE(GPS_FIX_HOME)) {
|
if ((posControl.flags.estHeadingStatus == EST_NONE) || (posControl.flags.estAltStatus == EST_NONE) || !STATE(GPS_FIX_HOME)) {
|
||||||
// Heading sensor, altitude sensor and HOME fix are mandatory for RTH. If not satisfied - switch to emergency landing
|
// Heading sensor, altitude sensor and HOME fix are mandatory for RTH. If not satisfied - switch to emergency landing
|
||||||
// Relevant to failsafe forced RTH only. Switched RTH blocked in selectNavEventFromBoxModeInput if sensors unavailable.
|
// Relevant to failsafe forced RTH only. Switched RTH blocked in selectNavEventFromBoxModeInput if sensors unavailable.
|
||||||
|
@ -1253,16 +1252,11 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigati
|
||||||
setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos, posControl.actualState.yaw, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING);
|
setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos, posControl.actualState.yaw, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING);
|
||||||
|
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
|
return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
|
||||||
}
|
} else {
|
||||||
else {
|
if (rthTrackBackIsActive() && rth_trackback.activePointIndex >= 0 && !isWaypointMissionRTHActive()) {
|
||||||
// Switch to RTH trackback
|
rthTrackBackUpdate(true); // save final trackpoint for altitude and max trackback distance reference
|
||||||
bool trackbackActive = navConfig()->general.flags.rth_trackback_mode == RTH_TRACKBACK_ON ||
|
|
||||||
(navConfig()->general.flags.rth_trackback_mode == RTH_TRACKBACK_FS && posControl.flags.forcedRTHActivated);
|
|
||||||
|
|
||||||
if (trackbackActive && posControl.activeRthTBPointIndex >= 0 && !isWaypointMissionRTHActive()) {
|
|
||||||
updateRthTrackback(true); // save final trackpoint for altitude and max trackback distance reference
|
|
||||||
posControl.flags.rthTrackbackActive = true;
|
posControl.flags.rthTrackbackActive = true;
|
||||||
calculateAndSetActiveWaypointToLocalPosition(rthGetTrackbackPos());
|
calculateAndSetActiveWaypointToLocalPosition(getRthTrackBackPosition());
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_TRACKBACK;
|
return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_TRACKBACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,36 +1371,8 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_TRACKBACK(navigatio
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING;
|
return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (posControl.flags.estPosStatus >= EST_USABLE) {
|
if (rthTrackBackSetNewPosition()) {
|
||||||
const int32_t distFromStartTrackback = calculateDistanceToDestination(&posControl.rthTBPointsList[posControl.rthTBLastSavedIndex]) / 100;
|
return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE;
|
||||||
#ifdef USE_MULTI_FUNCTIONS
|
|
||||||
const bool overrideTrackback = rthAltControlStickOverrideCheck(ROLL) || MULTI_FUNC_FLAG(MF_SUSPEND_TRACKBACK);
|
|
||||||
#else
|
|
||||||
const bool overrideTrackback = rthAltControlStickOverrideCheck(ROLL);
|
|
||||||
#endif
|
|
||||||
const bool cancelTrackback = distFromStartTrackback > navConfig()->general.rth_trackback_distance ||
|
|
||||||
(overrideTrackback && !posControl.flags.forcedRTHActivated);
|
|
||||||
|
|
||||||
if (posControl.activeRthTBPointIndex < 0 || cancelTrackback) {
|
|
||||||
posControl.rthTBWrapAroundCounter = posControl.activeRthTBPointIndex = -1;
|
|
||||||
posControl.flags.rthTrackbackActive = false;
|
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE; // procede to home after final trackback point
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWaypointReached(&posControl.activeWaypoint.pos, &posControl.activeWaypoint.bearing)) {
|
|
||||||
posControl.activeRthTBPointIndex--;
|
|
||||||
|
|
||||||
if (posControl.rthTBWrapAroundCounter > -1 && posControl.activeRthTBPointIndex < 0) {
|
|
||||||
posControl.activeRthTBPointIndex = NAV_RTH_TRACKBACK_POINTS - 1;
|
|
||||||
}
|
|
||||||
calculateAndSetActiveWaypointToLocalPosition(rthGetTrackbackPos());
|
|
||||||
|
|
||||||
if (posControl.activeRthTBPointIndex - posControl.rthTBWrapAroundCounter == 0) {
|
|
||||||
posControl.rthTBWrapAroundCounter = posControl.activeRthTBPointIndex = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setDesiredPosition(rthGetTrackbackPos(), 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_BEARING);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NAV_FSM_EVENT_NONE;
|
return NAV_FSM_EVENT_NONE;
|
||||||
|
@ -1433,6 +1399,7 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigatio
|
||||||
|
|
||||||
if (homeDistance <= METERS_TO_CENTIMETERS(navConfig()->general.rth_linear_descent_start_distance)) {
|
if (homeDistance <= METERS_TO_CENTIMETERS(navConfig()->general.rth_linear_descent_start_distance)) {
|
||||||
posControl.rthState.rthFinalAltitude = posControl.rthState.homePosition.pos.z + navConfig()->general.rth_home_altitude;
|
posControl.rthState.rthFinalAltitude = posControl.rthState.homePosition.pos.z + navConfig()->general.rth_home_altitude;
|
||||||
|
posControl.rthState.rthLinearDescentActive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1443,6 +1410,12 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigatio
|
||||||
if (isWaypointReached(tmpHomePos, 0)) {
|
if (isWaypointReached(tmpHomePos, 0)) {
|
||||||
// Successfully reached position target - update XYZ-position
|
// Successfully reached position target - update XYZ-position
|
||||||
setDesiredPosition(tmpHomePos, posControl.rthState.homePosition.heading, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING);
|
setDesiredPosition(tmpHomePos, posControl.rthState.homePosition.heading, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING);
|
||||||
|
|
||||||
|
posControl.landingDelay = 0;
|
||||||
|
|
||||||
|
if (navConfig()->general.flags.rth_use_linear_descent && posControl.rthState.rthLinearDescentActive)
|
||||||
|
posControl.rthState.rthLinearDescentActive = false;
|
||||||
|
|
||||||
return NAV_FSM_EVENT_SUCCESS; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
|
return NAV_FSM_EVENT_SUCCESS; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
|
||||||
} else {
|
} else {
|
||||||
setDesiredPosition(tmpHomePos, 0, NAV_POS_UPDATE_Z | NAV_POS_UPDATE_XY);
|
setDesiredPosition(tmpHomePos, 0, NAV_POS_UPDATE_Z | NAV_POS_UPDATE_XY);
|
||||||
|
@ -1471,9 +1444,24 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LAND
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING;
|
return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If position ok OR within valid timeout - continue
|
// Action delay before landing if in FS and option enabled
|
||||||
|
bool pauseLanding = false;
|
||||||
|
navRTHAllowLanding_e allow = navConfig()->general.flags.rth_allow_landing;
|
||||||
|
if ((allow == NAV_RTH_ALLOW_LANDING_ALWAYS || allow == NAV_RTH_ALLOW_LANDING_FS_ONLY) && FLIGHT_MODE(FAILSAFE_MODE) && navConfig()->general.rth_fs_landing_delay > 0) {
|
||||||
|
if (posControl.landingDelay == 0)
|
||||||
|
posControl.landingDelay = millis() + S2MS(navConfig()->general.rth_fs_landing_delay);
|
||||||
|
|
||||||
|
batteryState_e batteryState = getBatteryState();
|
||||||
|
|
||||||
|
if (millis() < posControl.landingDelay && batteryState != BATTERY_WARNING && batteryState != BATTERY_CRITICAL)
|
||||||
|
pauseLanding = true;
|
||||||
|
else
|
||||||
|
posControl.landingDelay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If landing is not temporarily paused (FS only), position ok, OR within valid timeout - continue
|
||||||
// Wait until target heading is reached for MR (with 15 deg margin for error), or continue for Fixed Wing
|
// Wait until target heading is reached for MR (with 15 deg margin for error), or continue for Fixed Wing
|
||||||
if ((ABS(wrap_18000(posControl.rthState.homePosition.heading - posControl.actualState.yaw)) < DEGREES_TO_CENTIDEGREES(15)) || STATE(FIXED_WING_LEGACY)) {
|
if (!pauseLanding && ((ABS(wrap_18000(posControl.rthState.homePosition.heading - posControl.actualState.yaw)) < DEGREES_TO_CENTIDEGREES(15)) || STATE(FIXED_WING_LEGACY))) {
|
||||||
resetLandingDetector(); // force reset landing detector just in case
|
resetLandingDetector(); // force reset landing detector just in case
|
||||||
updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET);
|
updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET);
|
||||||
return navigationRTHAllowsLanding() ? NAV_FSM_EVENT_SUCCESS : NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME; // success = land
|
return navigationRTHAllowsLanding() ? NAV_FSM_EVENT_SUCCESS : NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME; // success = land
|
||||||
|
@ -2382,7 +2370,7 @@ static bool getLocalPosNextWaypoint(fpVector3_t * nextWpPos)
|
||||||
* Check if waypoint is/was reached.
|
* Check if waypoint is/was reached.
|
||||||
* waypointBearing stores initial bearing to waypoint
|
* waypointBearing stores initial bearing to waypoint
|
||||||
*-----------------------------------------------------------*/
|
*-----------------------------------------------------------*/
|
||||||
static bool isWaypointReached(const fpVector3_t * waypointPos, const int32_t * waypointBearing)
|
bool isWaypointReached(const fpVector3_t *waypointPos, const int32_t *waypointBearing)
|
||||||
{
|
{
|
||||||
posControl.wpDistance = calculateDistanceToDestination(waypointPos);
|
posControl.wpDistance = calculateDistanceToDestination(waypointPos);
|
||||||
|
|
||||||
|
@ -2506,8 +2494,19 @@ bool validateRTHSanityChecker(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if (STATE(GPS_ESTIMATED_FIX)) {
|
||||||
|
//disable sanity checks in GPS estimation mode
|
||||||
|
//when estimated GPS fix is replaced with real fix, coordinates may jump
|
||||||
|
posControl.rthSanityChecker.minimalDistanceToHome = 1e10f;
|
||||||
|
//schedule check in 5 seconds after getting real GPS fix, when position estimation coords stabilise after jump
|
||||||
|
posControl.rthSanityChecker.lastCheckTime = currentTimeMs + 5000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Check at 10Hz rate
|
// Check at 10Hz rate
|
||||||
if ((currentTimeMs - posControl.rthSanityChecker.lastCheckTime) > 100) {
|
if ( ((int32_t)(currentTimeMs - posControl.rthSanityChecker.lastCheckTime)) > 100) {
|
||||||
const float currentDistanceToHome = calculateDistanceToDestination(&posControl.rthState.homePosition.pos);
|
const float currentDistanceToHome = calculateDistanceToDestination(&posControl.rthState.homePosition.pos);
|
||||||
posControl.rthSanityChecker.lastCheckTime = currentTimeMs;
|
posControl.rthSanityChecker.lastCheckTime = currentTimeMs;
|
||||||
|
|
||||||
|
@ -2731,12 +2730,13 @@ void updateHomePosition(void)
|
||||||
* Climb First override limited to Fixed Wing only
|
* Climb First override limited to Fixed Wing only
|
||||||
* Roll also cancels RTH trackback on Fixed Wing and Multirotor
|
* Roll also cancels RTH trackback on Fixed Wing and Multirotor
|
||||||
*-----------------------------------------------------------*/
|
*-----------------------------------------------------------*/
|
||||||
static bool rthAltControlStickOverrideCheck(unsigned axis)
|
bool rthAltControlStickOverrideCheck(uint8_t axis)
|
||||||
{
|
{
|
||||||
if (!navConfig()->general.flags.rth_alt_control_override || posControl.flags.forcedRTHActivated ||
|
if (!navConfig()->general.flags.rth_alt_control_override || posControl.flags.forcedRTHActivated ||
|
||||||
(axis == ROLL && STATE(MULTIROTOR) && !posControl.flags.rthTrackbackActive)) {
|
(axis == ROLL && STATE(MULTIROTOR) && !posControl.flags.rthTrackbackActive)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static timeMs_t rthOverrideStickHoldStartTime[2];
|
static timeMs_t rthOverrideStickHoldStartTime[2];
|
||||||
|
|
||||||
if (rxGetChannelValue(axis) > rxConfig()->maxcheck) {
|
if (rxGetChannelValue(axis) > rxConfig()->maxcheck) {
|
||||||
|
@ -2775,110 +2775,6 @@ static bool rthAltControlStickOverrideCheck(unsigned axis)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------
|
|
||||||
* == RTH Trackback ==
|
|
||||||
* Saves track during flight which is used during RTH to back track
|
|
||||||
* along arrival route rather than immediately heading directly toward home.
|
|
||||||
* Max desired trackback distance set by user or limited by number of available points.
|
|
||||||
* Reverts to normal RTH heading direct to home when end of track reached.
|
|
||||||
* Trackpoints logged with precedence for course/altitude changes. Distance based changes
|
|
||||||
* only logged if no course/altitude changes logged over an extended distance.
|
|
||||||
* Tracking suspended during fixed wing loiter (PosHold and WP Mode timed hold).
|
|
||||||
* --------------------------------------------------------------------------------- */
|
|
||||||
static void updateRthTrackback(bool forceSaveTrackPoint)
|
|
||||||
{
|
|
||||||
static bool suspendTracking = false;
|
|
||||||
bool fwLoiterIsActive = STATE(AIRPLANE) && (NAV_Status.state == MW_NAV_STATE_HOLD_TIMED || FLIGHT_MODE(NAV_POSHOLD_MODE));
|
|
||||||
if (!fwLoiterIsActive && suspendTracking) {
|
|
||||||
suspendTracking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (navConfig()->general.flags.rth_trackback_mode == RTH_TRACKBACK_OFF || FLIGHT_MODE(NAV_RTH_MODE) || !ARMING_FLAG(ARMED) || suspendTracking) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record trackback points based on significant change in course/altitude until
|
|
||||||
// points limit reached. Overwrite older points from then on.
|
|
||||||
if (posControl.flags.estPosStatus >= EST_USABLE && posControl.flags.estAltStatus >= EST_USABLE) {
|
|
||||||
static int32_t previousTBTripDist; // cm
|
|
||||||
static int16_t previousTBCourse; // degrees
|
|
||||||
static int16_t previousTBAltitude; // meters
|
|
||||||
static uint8_t distanceCounter = 0;
|
|
||||||
bool saveTrackpoint = forceSaveTrackPoint;
|
|
||||||
bool GPSCourseIsValid = isGPSHeadingValid();
|
|
||||||
|
|
||||||
// start recording when some distance from home, 50m seems reasonable.
|
|
||||||
if (posControl.activeRthTBPointIndex < 0) {
|
|
||||||
saveTrackpoint = posControl.homeDistance > METERS_TO_CENTIMETERS(50);
|
|
||||||
|
|
||||||
previousTBCourse = CENTIDEGREES_TO_DEGREES(posControl.actualState.cog);
|
|
||||||
previousTBTripDist = posControl.totalTripDistance;
|
|
||||||
} else {
|
|
||||||
// Minimum distance increment between course change track points when GPS course valid - set to 10m
|
|
||||||
const bool distanceIncrement = posControl.totalTripDistance - previousTBTripDist > METERS_TO_CENTIMETERS(10);
|
|
||||||
|
|
||||||
// Altitude change
|
|
||||||
if (ABS(previousTBAltitude - CENTIMETERS_TO_METERS(posControl.actualState.abs.pos.z)) > 10) { // meters
|
|
||||||
saveTrackpoint = true;
|
|
||||||
} else if (distanceIncrement && GPSCourseIsValid) {
|
|
||||||
// Course change - set to 45 degrees
|
|
||||||
if (ABS(wrap_18000(DEGREES_TO_CENTIDEGREES(DECIDEGREES_TO_DEGREES(gpsSol.groundCourse) - previousTBCourse))) > DEGREES_TO_CENTIDEGREES(45)) {
|
|
||||||
saveTrackpoint = true;
|
|
||||||
} else if (distanceCounter >= 9) {
|
|
||||||
// Distance based trackpoint logged if at least 10 distance increments occur without altitude or course change
|
|
||||||
// and deviation from projected course path > 20m
|
|
||||||
float distToPrevPoint = calculateDistanceToDestination(&posControl.rthTBPointsList[posControl.activeRthTBPointIndex]);
|
|
||||||
|
|
||||||
fpVector3_t virtualCoursePoint;
|
|
||||||
virtualCoursePoint.x = posControl.rthTBPointsList[posControl.activeRthTBPointIndex].x + distToPrevPoint * cos_approx(DEGREES_TO_RADIANS(previousTBCourse));
|
|
||||||
virtualCoursePoint.y = posControl.rthTBPointsList[posControl.activeRthTBPointIndex].y + distToPrevPoint * sin_approx(DEGREES_TO_RADIANS(previousTBCourse));
|
|
||||||
|
|
||||||
saveTrackpoint = calculateDistanceToDestination(&virtualCoursePoint) > METERS_TO_CENTIMETERS(20);
|
|
||||||
}
|
|
||||||
distanceCounter++;
|
|
||||||
previousTBTripDist = posControl.totalTripDistance;
|
|
||||||
} else if (!GPSCourseIsValid) {
|
|
||||||
// if no reliable course revert to basic distance logging based on direct distance from last point - set to 20m
|
|
||||||
saveTrackpoint = calculateDistanceToDestination(&posControl.rthTBPointsList[posControl.activeRthTBPointIndex]) > METERS_TO_CENTIMETERS(20);
|
|
||||||
previousTBTripDist = posControl.totalTripDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suspend tracking during loiter on fixed wing. Save trackpoint at start of loiter.
|
|
||||||
if (distanceCounter && fwLoiterIsActive) {
|
|
||||||
saveTrackpoint = suspendTracking = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// when trackpoint store full, overwrite from start of store using 'rthTBWrapAroundCounter' to track overwrite position
|
|
||||||
if (saveTrackpoint) {
|
|
||||||
if (posControl.activeRthTBPointIndex == (NAV_RTH_TRACKBACK_POINTS - 1)) { // wraparound to start
|
|
||||||
posControl.rthTBWrapAroundCounter = posControl.activeRthTBPointIndex = 0;
|
|
||||||
} else {
|
|
||||||
posControl.activeRthTBPointIndex++;
|
|
||||||
if (posControl.rthTBWrapAroundCounter > -1) { // track wraparound overwrite position after store first filled
|
|
||||||
posControl.rthTBWrapAroundCounter = posControl.activeRthTBPointIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
posControl.rthTBPointsList[posControl.activeRthTBPointIndex] = posControl.actualState.abs.pos;
|
|
||||||
|
|
||||||
posControl.rthTBLastSavedIndex = posControl.activeRthTBPointIndex;
|
|
||||||
previousTBAltitude = CENTIMETERS_TO_METERS(posControl.actualState.abs.pos.z);
|
|
||||||
previousTBCourse = GPSCourseIsValid ? DECIDEGREES_TO_DEGREES(gpsSol.groundCourse) : previousTBCourse;
|
|
||||||
distanceCounter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static fpVector3_t * rthGetTrackbackPos(void)
|
|
||||||
{
|
|
||||||
// ensure trackback altitude never lower than altitude of start point
|
|
||||||
if (posControl.rthTBPointsList[posControl.activeRthTBPointIndex].z < posControl.rthTBPointsList[posControl.rthTBLastSavedIndex].z) {
|
|
||||||
posControl.rthTBPointsList[posControl.activeRthTBPointIndex].z = posControl.rthTBPointsList[posControl.rthTBLastSavedIndex].z;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &posControl.rthTBPointsList[posControl.activeRthTBPointIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
* Update flight statistics
|
* Update flight statistics
|
||||||
*-----------------------------------------------------------*/
|
*-----------------------------------------------------------*/
|
||||||
|
@ -3552,7 +3448,7 @@ static void mapWaypointToLocalPosition(fpVector3_t * localPos, const navWaypoint
|
||||||
geoConvertGeodeticToLocal(localPos, &posControl.gpsOrigin, &wpLLH, altConv);
|
geoConvertGeodeticToLocal(localPos, &posControl.gpsOrigin, &wpLLH, altConv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t * pos)
|
void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t *pos)
|
||||||
{
|
{
|
||||||
// Calculate bearing towards waypoint and store it in waypoint bearing parameter (this will further be used to detect missed waypoints)
|
// Calculate bearing towards waypoint and store it in waypoint bearing parameter (this will further be used to detect missed waypoints)
|
||||||
if (isWaypointNavTrackingActive() && !(posControl.activeWaypoint.pos.x == pos->x && posControl.activeWaypoint.pos.y == pos->y)) {
|
if (isWaypointNavTrackingActive() && !(posControl.activeWaypoint.pos.x == pos->x && posControl.activeWaypoint.pos.y == pos->y)) {
|
||||||
|
@ -3645,7 +3541,7 @@ bool isWaypointNavTrackingActive(void)
|
||||||
// is set from current position not previous WP. Works for WP Restart intermediate WP as well as first mission WP.
|
// is set from current position not previous WP. Works for WP Restart intermediate WP as well as first mission WP.
|
||||||
// (NAV_WP_MODE flag isn't set until WP initialisation is finished, i.e. after calculateAndSetActiveWaypoint called)
|
// (NAV_WP_MODE flag isn't set until WP initialisation is finished, i.e. after calculateAndSetActiveWaypoint called)
|
||||||
|
|
||||||
return FLIGHT_MODE(NAV_WP_MODE) || (posControl.flags.rthTrackbackActive && posControl.activeRthTBPointIndex != posControl.rthTBLastSavedIndex);
|
return FLIGHT_MODE(NAV_WP_MODE) || (posControl.flags.rthTrackbackActive && rth_trackback.activePointIndex != rth_trackback.lastSavedIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
|
@ -3702,9 +3598,7 @@ void applyWaypointNavigationAndAltitudeHold(void)
|
||||||
// ensure WP missions always restart from first waypoint after disarm
|
// ensure WP missions always restart from first waypoint after disarm
|
||||||
posControl.activeWaypointIndex = posControl.startWpIndex;
|
posControl.activeWaypointIndex = posControl.startWpIndex;
|
||||||
// Reset RTH trackback
|
// Reset RTH trackback
|
||||||
posControl.activeRthTBPointIndex = -1;
|
resetRthTrackBack();
|
||||||
posControl.flags.rthTrackbackActive = false;
|
|
||||||
posControl.rthTBWrapAroundCounter = -1;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3814,7 +3708,6 @@ void checkManualEmergencyLandingControl(bool forcedActivation)
|
||||||
|
|
||||||
static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
|
static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
|
||||||
{
|
{
|
||||||
static bool canActivateWaypoint = false;
|
|
||||||
static bool canActivateLaunchMode = false;
|
static bool canActivateLaunchMode = false;
|
||||||
|
|
||||||
//We can switch modes only when ARMED
|
//We can switch modes only when ARMED
|
||||||
|
@ -3862,10 +3755,20 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
|
||||||
}
|
}
|
||||||
posControl.rthSanityChecker.rthSanityOK = true;
|
posControl.rthSanityChecker.rthSanityOK = true;
|
||||||
|
|
||||||
// Keep canActivateWaypoint flag at FALSE if there is no mission loaded
|
/* WP mission activation control:
|
||||||
// Also block WP mission if we are executing RTH
|
* canActivateWaypoint & waypointWasActivated are used to prevent WP mission
|
||||||
if (!isWaypointMissionValid() || isExecutingRTH) {
|
* auto restarting after interruption by Manual or RTH modes.
|
||||||
|
* WP mode must be deselected before it can be reactivated again. */
|
||||||
|
static bool waypointWasActivated = false;
|
||||||
|
const bool isWpMissionLoaded = isWaypointMissionValid();
|
||||||
|
bool canActivateWaypoint = isWpMissionLoaded && !posControl.flags.wpMissionPlannerActive; // Block activation if using WP Mission Planner
|
||||||
|
|
||||||
|
if (waypointWasActivated && !FLIGHT_MODE(NAV_WP_MODE)) {
|
||||||
canActivateWaypoint = false;
|
canActivateWaypoint = false;
|
||||||
|
if (!IS_RC_MODE_ACTIVE(BOXNAVWP)) {
|
||||||
|
canActivateWaypoint = true;
|
||||||
|
waypointWasActivated = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Airplane specific modes */
|
/* Airplane specific modes */
|
||||||
|
@ -3895,30 +3798,26 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
|
||||||
/* Soaring mode, disables altitude control in Position hold and Course hold modes.
|
/* Soaring mode, disables altitude control in Position hold and Course hold modes.
|
||||||
* Pitch allowed to freefloat within defined Angle mode deadband */
|
* Pitch allowed to freefloat within defined Angle mode deadband */
|
||||||
if (IS_RC_MODE_ACTIVE(BOXSOARING) && (FLIGHT_MODE(NAV_POSHOLD_MODE) || FLIGHT_MODE(NAV_COURSE_HOLD_MODE))) {
|
if (IS_RC_MODE_ACTIVE(BOXSOARING) && (FLIGHT_MODE(NAV_POSHOLD_MODE) || FLIGHT_MODE(NAV_COURSE_HOLD_MODE))) {
|
||||||
if (!FLIGHT_MODE(SOARING_MODE)) {
|
ENABLE_FLIGHT_MODE(SOARING_MODE);
|
||||||
ENABLE_FLIGHT_MODE(SOARING_MODE);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DISABLE_FLIGHT_MODE(SOARING_MODE);
|
DISABLE_FLIGHT_MODE(SOARING_MODE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failsafe_RTH (can override MANUAL)
|
/* If we request forced RTH - attempt to activate it no matter what
|
||||||
|
* This might switch to emergency landing controller if GPS is unavailable */
|
||||||
if (posControl.flags.forcedRTHActivated) {
|
if (posControl.flags.forcedRTHActivated) {
|
||||||
// If we request forced RTH - attempt to activate it no matter what
|
|
||||||
// This might switch to emergency landing controller if GPS is unavailable
|
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_RTH;
|
return NAV_FSM_EVENT_SWITCH_TO_RTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pilot-triggered RTH (can override MANUAL), also fall-back for WP if there is no mission loaded
|
/* Pilot-triggered RTH, also fall-back for WP if there is no mission loaded.
|
||||||
* Prevent MANUAL falling back to RTH if selected during active mission (canActivateWaypoint is set false on MANUAL selection)
|
* WP prevented from falling back to RTH if WP mission planner is active */
|
||||||
* Also prevent WP falling back to RTH if WP mission planner is active */
|
const bool wpRthFallbackIsActive = IS_RC_MODE_ACTIVE(BOXNAVWP) && !isWpMissionLoaded && !posControl.flags.wpMissionPlannerActive;
|
||||||
const bool blockWPFallback = IS_RC_MODE_ACTIVE(BOXMANUAL) || posControl.flags.wpMissionPlannerActive;
|
if (IS_RC_MODE_ACTIVE(BOXNAVRTH) || wpRthFallbackIsActive) {
|
||||||
if (IS_RC_MODE_ACTIVE(BOXNAVRTH) || (IS_RC_MODE_ACTIVE(BOXNAVWP) && !canActivateWaypoint && !blockWPFallback)) {
|
|
||||||
// Check for isExecutingRTH to prevent switching our from RTH in case of a brief GPS loss
|
// Check for isExecutingRTH to prevent switching our from RTH in case of a brief GPS loss
|
||||||
// If don't keep this, loss of any of the canActivateNavigation && canActivateAltHold
|
// Without this loss of any of the canActivateNavigation && canActivateAltHold
|
||||||
// will kick us out of RTH state machine via NAV_FSM_EVENT_SWITCH_TO_IDLE and will prevent any of the fall-back
|
// will kick us out of RTH state machine via NAV_FSM_EVENT_SWITCH_TO_IDLE and will prevent any of the fall-back
|
||||||
// logic to kick in (waiting for GPS on airplanes, switch to emergency landing etc)
|
// logic kicking in (waiting for GPS on airplanes, switch to emergency landing etc)
|
||||||
if (isExecutingRTH || (canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME))) {
|
if (isExecutingRTH || (canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME))) {
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_RTH;
|
return NAV_FSM_EVENT_SWITCH_TO_RTH;
|
||||||
}
|
}
|
||||||
|
@ -3926,24 +3825,20 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
|
||||||
|
|
||||||
// MANUAL mode has priority over WP/PH/AH
|
// MANUAL mode has priority over WP/PH/AH
|
||||||
if (IS_RC_MODE_ACTIVE(BOXMANUAL)) {
|
if (IS_RC_MODE_ACTIVE(BOXMANUAL)) {
|
||||||
canActivateWaypoint = false; // Block WP mode if we are in PASSTHROUGH mode
|
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_IDLE;
|
return NAV_FSM_EVENT_SWITCH_TO_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pilot-activated waypoint mission. Fall-back to RTH in case of no mission loaded
|
// Pilot-activated waypoint mission. Fall-back to RTH if no mission loaded.
|
||||||
// Block activation if using WP Mission Planner
|
// Also check multimission mission change status before activating WP mode.
|
||||||
// Also check multimission mission change status before activating WP mode
|
|
||||||
#ifdef USE_MULTI_MISSION
|
#ifdef USE_MULTI_MISSION
|
||||||
if (updateWpMissionChange() && IS_RC_MODE_ACTIVE(BOXNAVWP) && !posControl.flags.wpMissionPlannerActive) {
|
if (updateWpMissionChange() && IS_RC_MODE_ACTIVE(BOXNAVWP) && canActivateWaypoint) {
|
||||||
#else
|
#else
|
||||||
if (IS_RC_MODE_ACTIVE(BOXNAVWP) && !posControl.flags.wpMissionPlannerActive) {
|
if (IS_RC_MODE_ACTIVE(BOXNAVWP) && canActivateWaypoint) {
|
||||||
#endif
|
#endif
|
||||||
if (FLIGHT_MODE(NAV_WP_MODE) || (canActivateWaypoint && canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME)))
|
if (FLIGHT_MODE(NAV_WP_MODE) || (canActivateNavigation && canActivateAltHold && STATE(GPS_FIX_HOME))) {
|
||||||
|
waypointWasActivated = true;
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT;
|
return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Arm the state variable if the WP BOX mode is not enabled
|
|
||||||
canActivateWaypoint = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD)) {
|
if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD)) {
|
||||||
|
@ -3974,8 +3869,6 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
|
||||||
return NAV_FSM_EVENT_SWITCH_TO_ALTHOLD;
|
return NAV_FSM_EVENT_SWITCH_TO_ALTHOLD;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
canActivateWaypoint = false;
|
|
||||||
|
|
||||||
// Launch mode can be activated if feature FW_LAUNCH is enabled or BOX is turned on prior to arming (avoid switching to LAUNCH in flight)
|
// Launch mode can be activated if feature FW_LAUNCH is enabled or BOX is turned on prior to arming (avoid switching to LAUNCH in flight)
|
||||||
canActivateLaunchMode = isNavLaunchEnabled() && (!sensors(SENSOR_GPS) || (sensors(SENSOR_GPS) && !isGPSHeadingValid()));
|
canActivateLaunchMode = isNavLaunchEnabled() && (!sensors(SENSOR_GPS) || (sensors(SENSOR_GPS) && !isGPSHeadingValid()));
|
||||||
}
|
}
|
||||||
|
@ -4056,7 +3949,8 @@ bool navigationPositionEstimateIsHealthy(void)
|
||||||
|
|
||||||
navArmingBlocker_e navigationIsBlockingArming(bool *usedBypass)
|
navArmingBlocker_e navigationIsBlockingArming(bool *usedBypass)
|
||||||
{
|
{
|
||||||
const bool navBoxModesEnabled = IS_RC_MODE_ACTIVE(BOXNAVRTH) || IS_RC_MODE_ACTIVE(BOXNAVWP) || IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD) || (STATE(FIXED_WING_LEGACY) && IS_RC_MODE_ACTIVE(BOXNAVALTHOLD)) || (STATE(FIXED_WING_LEGACY) && (IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD) || IS_RC_MODE_ACTIVE(BOXNAVCRUISE)));
|
const bool navBoxModesEnabled = IS_RC_MODE_ACTIVE(BOXNAVRTH) || IS_RC_MODE_ACTIVE(BOXNAVWP) || IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD) ||
|
||||||
|
IS_RC_MODE_ACTIVE(BOXNAVCRUISE) || IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD) || (STATE(FIXED_WING_LEGACY) && IS_RC_MODE_ACTIVE(BOXNAVALTHOLD));
|
||||||
|
|
||||||
if (usedBypass) {
|
if (usedBypass) {
|
||||||
*usedBypass = false;
|
*usedBypass = false;
|
||||||
|
@ -4144,7 +4038,11 @@ void updateWpMissionPlanner(void)
|
||||||
{
|
{
|
||||||
static timeMs_t resetTimerStart = 0;
|
static timeMs_t resetTimerStart = 0;
|
||||||
if (IS_RC_MODE_ACTIVE(BOXPLANWPMISSION) && !(FLIGHT_MODE(NAV_WP_MODE) || isWaypointMissionRTHActive())) {
|
if (IS_RC_MODE_ACTIVE(BOXPLANWPMISSION) && !(FLIGHT_MODE(NAV_WP_MODE) || isWaypointMissionRTHActive())) {
|
||||||
const bool positionTrusted = posControl.flags.estAltStatus == EST_TRUSTED && posControl.flags.estPosStatus == EST_TRUSTED && STATE(GPS_FIX);
|
const bool positionTrusted = posControl.flags.estAltStatus == EST_TRUSTED && posControl.flags.estPosStatus == EST_TRUSTED && (STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
posControl.flags.wpMissionPlannerActive = true;
|
posControl.flags.wpMissionPlannerActive = true;
|
||||||
if (millis() - resetTimerStart < 1000 && navConfig()->general.flags.mission_planner_reset) {
|
if (millis() - resetTimerStart < 1000 && navConfig()->general.flags.mission_planner_reset) {
|
||||||
|
@ -4232,7 +4130,7 @@ void updateWaypointsAndNavigationMode(void)
|
||||||
updateWpMissionPlanner();
|
updateWpMissionPlanner();
|
||||||
|
|
||||||
// Update RTH trackback
|
// Update RTH trackback
|
||||||
updateRthTrackback(false);
|
rthTrackBackUpdate(false);
|
||||||
|
|
||||||
//Update Blackbox data
|
//Update Blackbox data
|
||||||
navCurrentState = (int16_t)posControl.navPersistentId;
|
navCurrentState = (int16_t)posControl.navPersistentId;
|
||||||
|
@ -4568,3 +4466,26 @@ int32_t navigationGetHeadingError(void)
|
||||||
{
|
{
|
||||||
return wrap_18000(posControl.desiredState.yaw - posControl.actualState.cog);
|
return wrap_18000(posControl.desiredState.yaw - posControl.actualState.cog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int8_t navCheckActiveAngleHoldAxis(void)
|
||||||
|
{
|
||||||
|
int8_t activeAxis = -1;
|
||||||
|
|
||||||
|
if (IS_RC_MODE_ACTIVE(BOXANGLEHOLD)) {
|
||||||
|
navigationFSMStateFlags_t stateFlags = navGetCurrentStateFlags();
|
||||||
|
bool altholdActive = stateFlags & NAV_REQUIRE_ANGLE_FW && !(stateFlags & NAV_REQUIRE_ANGLE);
|
||||||
|
|
||||||
|
if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE) && !FLIGHT_MODE(NAV_ALTHOLD_MODE)) {
|
||||||
|
activeAxis = FD_PITCH;
|
||||||
|
} else if (altholdActive) {
|
||||||
|
activeAxis = FD_ROLL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeAxis;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getActiveWpNumber(void)
|
||||||
|
{
|
||||||
|
return NAV_Status.activeWpNumber;
|
||||||
|
}
|
||||||
|
|
|
@ -203,12 +203,15 @@ typedef struct positionEstimationConfig_s {
|
||||||
float w_xy_res_v;
|
float w_xy_res_v;
|
||||||
|
|
||||||
float w_acc_bias; // Weight (cutoff frequency) for accelerometer bias estimation. 0 to disable.
|
float w_acc_bias; // Weight (cutoff frequency) for accelerometer bias estimation. 0 to disable.
|
||||||
float w_xyz_acc_p;
|
|
||||||
|
|
||||||
float max_eph_epv; // Max estimated position error acceptable for estimation (cm)
|
float max_eph_epv; // Max estimated position error acceptable for estimation (cm)
|
||||||
float baro_epv; // Baro position error
|
float baro_epv; // Baro position error
|
||||||
|
|
||||||
uint8_t use_gps_no_baro;
|
uint8_t use_gps_no_baro;
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
uint8_t allow_gps_fix_estimation;
|
||||||
|
#endif
|
||||||
} positionEstimationConfig_t;
|
} positionEstimationConfig_t;
|
||||||
|
|
||||||
PG_DECLARE(positionEstimationConfig_t, positionEstimationConfig);
|
PG_DECLARE(positionEstimationConfig_t, positionEstimationConfig);
|
||||||
|
@ -245,6 +248,7 @@ typedef struct navConfig_s {
|
||||||
#endif
|
#endif
|
||||||
bool waypoint_load_on_boot; // load waypoints automatically during boot
|
bool waypoint_load_on_boot; // load waypoints automatically during boot
|
||||||
uint16_t auto_speed; // autonomous navigation speed cm/sec
|
uint16_t auto_speed; // autonomous navigation speed cm/sec
|
||||||
|
uint8_t min_ground_speed; // Minimum navigation ground speed [m/s]
|
||||||
uint16_t max_auto_speed; // maximum allowed autonomous navigation speed cm/sec
|
uint16_t max_auto_speed; // maximum allowed autonomous navigation speed cm/sec
|
||||||
uint16_t max_manual_speed; // manual velocity control max horizontal speed
|
uint16_t max_manual_speed; // manual velocity control max horizontal speed
|
||||||
uint16_t land_minalt_vspd; // Final RTH landing descent rate under minalt
|
uint16_t land_minalt_vspd; // Final RTH landing descent rate under minalt
|
||||||
|
@ -266,6 +270,7 @@ typedef struct navConfig_s {
|
||||||
uint16_t auto_disarm_delay; // safety time delay for landing detector
|
uint16_t auto_disarm_delay; // safety time delay for landing detector
|
||||||
uint16_t rth_linear_descent_start_distance; // Distance from home to start the linear descent (0 = immediately)
|
uint16_t rth_linear_descent_start_distance; // Distance from home to start the linear descent (0 = immediately)
|
||||||
uint8_t cruise_yaw_rate; // Max yaw rate (dps) when CRUISE MODE is enabled
|
uint8_t cruise_yaw_rate; // Max yaw rate (dps) when CRUISE MODE is enabled
|
||||||
|
uint16_t rth_fs_landing_delay; // Delay upon reaching home before starting landing if in FS (0 = immediate)
|
||||||
} general;
|
} general;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -575,6 +580,9 @@ float geoCalculateMagDeclination(const gpsLocation_t * llh); // degrees units
|
||||||
// Select absolute or relative altitude based on WP mission flag setting
|
// Select absolute or relative altitude based on WP mission flag setting
|
||||||
geoAltitudeConversionMode_e waypointMissionAltConvMode(geoAltitudeDatumFlag_e datumFlag);
|
geoAltitudeConversionMode_e waypointMissionAltConvMode(geoAltitudeDatumFlag_e datumFlag);
|
||||||
|
|
||||||
|
void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t *pos);
|
||||||
|
bool isWaypointReached(const fpVector3_t * waypointPos, const int32_t * waypointBearing);
|
||||||
|
|
||||||
/* Distance/bearing calculation */
|
/* Distance/bearing calculation */
|
||||||
bool navCalculatePathToDestination(navDestinationPath_t *result, const fpVector3_t * destinationPos); // NOT USED
|
bool navCalculatePathToDestination(navDestinationPath_t *result, const fpVector3_t * destinationPos); // NOT USED
|
||||||
uint32_t distanceToFirstWP(void);
|
uint32_t distanceToFirstWP(void);
|
||||||
|
@ -596,7 +604,7 @@ bool isFixedWingAutoThrottleManuallyIncreased(void);
|
||||||
bool navigationIsFlyingAutonomousMode(void);
|
bool navigationIsFlyingAutonomousMode(void);
|
||||||
bool navigationIsExecutingAnEmergencyLanding(void);
|
bool navigationIsExecutingAnEmergencyLanding(void);
|
||||||
bool navigationIsControllingAltitude(void);
|
bool navigationIsControllingAltitude(void);
|
||||||
/* Returns true iff navConfig()->general.flags.rth_allow_landing is NAV_RTH_ALLOW_LANDING_ALWAYS
|
/* Returns true if navConfig()->general.flags.rth_allow_landing is NAV_RTH_ALLOW_LANDING_ALWAYS
|
||||||
* or if it's NAV_RTH_ALLOW_LANDING_FAILSAFE and failsafe mode is active.
|
* or if it's NAV_RTH_ALLOW_LANDING_FAILSAFE and failsafe mode is active.
|
||||||
*/
|
*/
|
||||||
bool navigationRTHAllowsLanding(void);
|
bool navigationRTHAllowsLanding(void);
|
||||||
|
@ -627,6 +635,10 @@ bool isEstimatedAglTrusted(void);
|
||||||
|
|
||||||
void checkManualEmergencyLandingControl(bool forcedActivation);
|
void checkManualEmergencyLandingControl(bool forcedActivation);
|
||||||
float updateBaroAltitudeRate(float newBaroAltRate, bool updateValue);
|
float updateBaroAltitudeRate(float newBaroAltRate, bool updateValue);
|
||||||
|
bool rthAltControlStickOverrideCheck(uint8_t axis);
|
||||||
|
|
||||||
|
int8_t navCheckActiveAngleHoldAxis(void);
|
||||||
|
uint8_t getActiveWpNumber(void);
|
||||||
|
|
||||||
/* Returns the heading recorded when home position was acquired.
|
/* Returns the heading recorded when home position was acquired.
|
||||||
* Note that the navigation system uses deg*100 as unit and angles
|
* Note that the navigation system uses deg*100 as unit and angles
|
||||||
|
|
|
@ -60,9 +60,8 @@
|
||||||
#define NAV_FW_BASE_PITCH_CUTOFF_FREQUENCY_HZ 2.0f
|
#define NAV_FW_BASE_PITCH_CUTOFF_FREQUENCY_HZ 2.0f
|
||||||
#define NAV_FW_BASE_ROLL_CUTOFF_FREQUENCY_HZ 10.0f
|
#define NAV_FW_BASE_ROLL_CUTOFF_FREQUENCY_HZ 10.0f
|
||||||
|
|
||||||
// If we are going slower than NAV_FW_MIN_VEL_SPEED_BOOST - boost throttle to fight against the wind
|
// If we are going slower than the minimum ground speed (navConfig()->general.min_ground_speed) - boost throttle to fight against the wind
|
||||||
#define NAV_FW_THROTTLE_SPEED_BOOST_GAIN 1.5f
|
#define NAV_FW_THROTTLE_SPEED_BOOST_GAIN 1.5f
|
||||||
#define NAV_FW_MIN_VEL_SPEED_BOOST 700.0f // 7 m/s
|
|
||||||
|
|
||||||
// If this is enabled navigation won't be applied if velocity is below 3 m/s
|
// If this is enabled navigation won't be applied if velocity is below 3 m/s
|
||||||
//#define NAV_FW_LIMIT_MIN_FLY_VELOCITY
|
//#define NAV_FW_LIMIT_MIN_FLY_VELOCITY
|
||||||
|
@ -518,8 +517,8 @@ void applyFixedWingPositionController(timeUs_t currentTimeUs)
|
||||||
// POSITION_TARGET_UPDATE_RATE_HZ should be chosen keeping in mind that position target shouldn't be reached until next pos update occurs
|
// POSITION_TARGET_UPDATE_RATE_HZ should be chosen keeping in mind that position target shouldn't be reached until next pos update occurs
|
||||||
// FIXME: verify the above
|
// FIXME: verify the above
|
||||||
calculateVirtualPositionTarget_FW(HZ2S(MIN_POSITION_UPDATE_RATE_HZ) * 2);
|
calculateVirtualPositionTarget_FW(HZ2S(MIN_POSITION_UPDATE_RATE_HZ) * 2);
|
||||||
|
|
||||||
updatePositionHeadingController_FW(currentTimeUs, deltaMicrosPositionUpdate);
|
updatePositionHeadingController_FW(currentTimeUs, deltaMicrosPositionUpdate);
|
||||||
|
needToCalculateCircularLoiter = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Position update has not occurred in time (first iteration or glitch), reset altitude controller
|
// Position update has not occurred in time (first iteration or glitch), reset altitude controller
|
||||||
|
@ -552,10 +551,10 @@ int16_t applyFixedWingMinSpeedController(timeUs_t currentTimeUs)
|
||||||
previousTimePositionUpdate = currentTimeUs;
|
previousTimePositionUpdate = currentTimeUs;
|
||||||
|
|
||||||
if (deltaMicrosPositionUpdate < MAX_POSITION_UPDATE_INTERVAL_US) {
|
if (deltaMicrosPositionUpdate < MAX_POSITION_UPDATE_INTERVAL_US) {
|
||||||
float velThrottleBoost = (NAV_FW_MIN_VEL_SPEED_BOOST - posControl.actualState.velXY) * NAV_FW_THROTTLE_SPEED_BOOST_GAIN * US2S(deltaMicrosPositionUpdate);
|
float velThrottleBoost = ((navConfig()->general.min_ground_speed * 100.0f) - posControl.actualState.velXY) * NAV_FW_THROTTLE_SPEED_BOOST_GAIN * US2S(deltaMicrosPositionUpdate);
|
||||||
|
|
||||||
// If we are in the deadband of 50cm/s - don't update speed boost
|
// If we are in the deadband of 50cm/s - don't update speed boost
|
||||||
if (fabsf(posControl.actualState.velXY - NAV_FW_MIN_VEL_SPEED_BOOST) > 50) {
|
if (fabsf(posControl.actualState.velXY - (navConfig()->general.min_ground_speed * 100.0f)) > 50) {
|
||||||
throttleSpeedAdjustment += velThrottleBoost;
|
throttleSpeedAdjustment += velThrottleBoost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,7 +724,7 @@ bool isFixedWingLandingDetected(void)
|
||||||
|
|
||||||
// Check horizontal and vertical velocities are low (cm/s)
|
// Check horizontal and vertical velocities are low (cm/s)
|
||||||
bool velCondition = fabsf(navGetCurrentActualPositionAndVelocity()->vel.z) < (50.0f * sensitivity) &&
|
bool velCondition = fabsf(navGetCurrentActualPositionAndVelocity()->vel.z) < (50.0f * sensitivity) &&
|
||||||
posControl.actualState.velXY < (100.0f * sensitivity);
|
( posControl.actualState.velXY < (100.0f * sensitivity));
|
||||||
// Check angular rates are low (degs/s)
|
// Check angular rates are low (degs/s)
|
||||||
bool gyroCondition = averageAbsGyroRates() < (2.0f * sensitivity);
|
bool gyroCondition = averageAbsGyroRates() < (2.0f * sensitivity);
|
||||||
DEBUG_SET(DEBUG_LANDING, 2, velCondition);
|
DEBUG_SET(DEBUG_LANDING, 2, velCondition);
|
||||||
|
|
|
@ -70,8 +70,6 @@ PG_RESET_TEMPLATE(positionEstimationConfig_t, positionEstimationConfig,
|
||||||
|
|
||||||
.max_surface_altitude = SETTING_INAV_MAX_SURFACE_ALTITUDE_DEFAULT,
|
.max_surface_altitude = SETTING_INAV_MAX_SURFACE_ALTITUDE_DEFAULT,
|
||||||
|
|
||||||
.w_xyz_acc_p = SETTING_INAV_W_XYZ_ACC_P_DEFAULT,
|
|
||||||
|
|
||||||
.w_z_baro_p = SETTING_INAV_W_Z_BARO_P_DEFAULT,
|
.w_z_baro_p = SETTING_INAV_W_Z_BARO_P_DEFAULT,
|
||||||
|
|
||||||
.w_z_surface_p = SETTING_INAV_W_Z_SURFACE_P_DEFAULT,
|
.w_z_surface_p = SETTING_INAV_W_Z_SURFACE_P_DEFAULT,
|
||||||
|
@ -92,7 +90,10 @@ PG_RESET_TEMPLATE(positionEstimationConfig_t, positionEstimationConfig,
|
||||||
.w_acc_bias = SETTING_INAV_W_ACC_BIAS_DEFAULT,
|
.w_acc_bias = SETTING_INAV_W_ACC_BIAS_DEFAULT,
|
||||||
|
|
||||||
.max_eph_epv = SETTING_INAV_MAX_EPH_EPV_DEFAULT,
|
.max_eph_epv = SETTING_INAV_MAX_EPH_EPV_DEFAULT,
|
||||||
.baro_epv = SETTING_INAV_BARO_EPV_DEFAULT
|
.baro_epv = SETTING_INAV_BARO_EPV_DEFAULT,
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
.allow_gps_fix_estimation = SETTING_INAV_ALLOW_GPS_FIX_ESTIMATION_DEFAULT
|
||||||
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
#define resetTimer(tim, currentTimeUs) { (tim)->deltaTime = 0; (tim)->lastTriggeredTime = currentTimeUs; }
|
#define resetTimer(tim, currentTimeUs) { (tim)->deltaTime = 0; (tim)->lastTriggeredTime = currentTimeUs; }
|
||||||
|
@ -170,6 +171,15 @@ static bool detectGPSGlitch(timeUs_t currentTimeUs)
|
||||||
|
|
||||||
bool isGlitching = false;
|
bool isGlitching = false;
|
||||||
|
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if (STATE(GPS_ESTIMATED_FIX)) {
|
||||||
|
//disable sanity checks in GPS estimation mode
|
||||||
|
//when estimated GPS fix is replaced with real fix, coordinates may jump
|
||||||
|
previousTime = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (previousTime == 0) {
|
if (previousTime == 0) {
|
||||||
isGlitching = false;
|
isGlitching = false;
|
||||||
}
|
}
|
||||||
|
@ -221,8 +231,16 @@ void onNewGPSData(void)
|
||||||
newLLH.lon = gpsSol.llh.lon;
|
newLLH.lon = gpsSol.llh.lon;
|
||||||
newLLH.alt = gpsSol.llh.alt;
|
newLLH.alt = gpsSol.llh.alt;
|
||||||
|
|
||||||
if (sensors(SENSOR_GPS)) {
|
if (sensors(SENSOR_GPS)
|
||||||
if (!STATE(GPS_FIX)) {
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
if (!(STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
)) {
|
||||||
isFirstGPSUpdate = true;
|
isFirstGPSUpdate = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -389,28 +407,30 @@ static bool gravityCalibrationComplete(void)
|
||||||
|
|
||||||
return zeroCalibrationIsCompleteS(&posEstimator.imu.gravityCalibration);
|
return zeroCalibrationIsCompleteS(&posEstimator.imu.gravityCalibration);
|
||||||
}
|
}
|
||||||
|
#define ACC_VIB_FACTOR_S 1.0f
|
||||||
|
#define ACC_VIB_FACTOR_E 3.0f
|
||||||
static void updateIMUEstimationWeight(const float dt)
|
static void updateIMUEstimationWeight(const float dt)
|
||||||
{
|
{
|
||||||
bool isAccClipped = accIsClipped();
|
static float acc_clip_factor = 1.0f;
|
||||||
|
// If accelerometer measurement is clipped - drop the acc weight to 0.3
|
||||||
// If accelerometer measurement is clipped - drop the acc weight to zero
|
|
||||||
// and gradually restore weight back to 1.0 over time
|
// and gradually restore weight back to 1.0 over time
|
||||||
if (isAccClipped) {
|
if (accIsClipped()) {
|
||||||
posEstimator.imu.accWeightFactor = 0.0f;
|
acc_clip_factor = 0.5f;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const float relAlpha = dt / (dt + INAV_ACC_CLIPPING_RC_CONSTANT);
|
const float relAlpha = dt / (dt + INAV_ACC_CLIPPING_RC_CONSTANT);
|
||||||
posEstimator.imu.accWeightFactor = posEstimator.imu.accWeightFactor * (1.0f - relAlpha) + 1.0f * relAlpha;
|
acc_clip_factor = acc_clip_factor * (1.0f - relAlpha) + 1.0f * relAlpha;
|
||||||
}
|
}
|
||||||
|
// Update accelerometer weight based on vibration levels and clipping
|
||||||
|
float acc_vibration_factor = scaleRangef(constrainf(accGetVibrationLevel(),ACC_VIB_FACTOR_S,ACC_VIB_FACTOR_E),ACC_VIB_FACTOR_S,ACC_VIB_FACTOR_E,1.0f,0.3f); // g/s
|
||||||
|
posEstimator.imu.accWeightFactor = acc_vibration_factor * acc_clip_factor;
|
||||||
// DEBUG_VIBE[0-3] are used in IMU
|
// DEBUG_VIBE[0-3] are used in IMU
|
||||||
DEBUG_SET(DEBUG_VIBE, 4, posEstimator.imu.accWeightFactor * 1000);
|
DEBUG_SET(DEBUG_VIBE, 4, posEstimator.imu.accWeightFactor * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
float navGetAccelerometerWeight(void)
|
float navGetAccelerometerWeight(void)
|
||||||
{
|
{
|
||||||
const float accWeightScaled = posEstimator.imu.accWeightFactor * positionEstimationConfig()->w_xyz_acc_p;
|
const float accWeightScaled = posEstimator.imu.accWeightFactor;
|
||||||
DEBUG_SET(DEBUG_VIBE, 5, accWeightScaled * 1000);
|
DEBUG_SET(DEBUG_VIBE, 5, accWeightScaled * 1000);
|
||||||
|
|
||||||
return accWeightScaled;
|
return accWeightScaled;
|
||||||
|
@ -502,7 +522,11 @@ static bool navIsAccelerationUsable(void)
|
||||||
|
|
||||||
static bool navIsHeadingUsable(void)
|
static bool navIsHeadingUsable(void)
|
||||||
{
|
{
|
||||||
if (sensors(SENSOR_GPS)) {
|
if (sensors(SENSOR_GPS)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
// If we have GPS - we need true IMU north (valid heading)
|
// If we have GPS - we need true IMU north (valid heading)
|
||||||
return isImuHeadingValid();
|
return isImuHeadingValid();
|
||||||
}
|
}
|
||||||
|
@ -517,7 +541,11 @@ static uint32_t calculateCurrentValidityFlags(timeUs_t currentTimeUs)
|
||||||
/* Figure out if we have valid position data from our data sources */
|
/* Figure out if we have valid position data from our data sources */
|
||||||
uint32_t newFlags = 0;
|
uint32_t newFlags = 0;
|
||||||
|
|
||||||
if (sensors(SENSOR_GPS) && posControl.gpsOrigin.valid &&
|
if ((sensors(SENSOR_GPS)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
|
#endif
|
||||||
|
) && posControl.gpsOrigin.valid &&
|
||||||
((currentTimeUs - posEstimator.gps.lastUpdateTime) <= MS2US(INAV_GPS_TIMEOUT_MS)) &&
|
((currentTimeUs - posEstimator.gps.lastUpdateTime) <= MS2US(INAV_GPS_TIMEOUT_MS)) &&
|
||||||
(posEstimator.gps.eph < positionEstimationConfig()->max_eph_epv)) {
|
(posEstimator.gps.eph < positionEstimationConfig()->max_eph_epv)) {
|
||||||
if (posEstimator.gps.epv < positionEstimationConfig()->max_eph_epv) {
|
if (posEstimator.gps.epv < positionEstimationConfig()->max_eph_epv) {
|
||||||
|
@ -553,13 +581,12 @@ static uint32_t calculateCurrentValidityFlags(timeUs_t currentTimeUs)
|
||||||
|
|
||||||
static void estimationPredict(estimationContext_t * ctx)
|
static void estimationPredict(estimationContext_t * ctx)
|
||||||
{
|
{
|
||||||
const float accWeight = navGetAccelerometerWeight();
|
|
||||||
|
|
||||||
/* Prediction step: Z-axis */
|
/* Prediction step: Z-axis */
|
||||||
if ((ctx->newFlags & EST_Z_VALID)) {
|
if ((ctx->newFlags & EST_Z_VALID)) {
|
||||||
posEstimator.est.pos.z += posEstimator.est.vel.z * ctx->dt;
|
posEstimator.est.pos.z += posEstimator.est.vel.z * ctx->dt;
|
||||||
posEstimator.est.pos.z += posEstimator.imu.accelNEU.z * sq(ctx->dt) / 2.0f * accWeight;
|
posEstimator.est.pos.z += posEstimator.imu.accelNEU.z * sq(ctx->dt) / 2.0f;
|
||||||
posEstimator.est.vel.z += posEstimator.imu.accelNEU.z * ctx->dt * sq(accWeight);
|
posEstimator.est.vel.z += posEstimator.imu.accelNEU.z * ctx->dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prediction step: XY-axis */
|
/* Prediction step: XY-axis */
|
||||||
|
@ -570,10 +597,10 @@ static void estimationPredict(estimationContext_t * ctx)
|
||||||
|
|
||||||
// If heading is valid, accelNEU is valid as well. Account for acceleration
|
// If heading is valid, accelNEU is valid as well. Account for acceleration
|
||||||
if (navIsHeadingUsable() && navIsAccelerationUsable()) {
|
if (navIsHeadingUsable() && navIsAccelerationUsable()) {
|
||||||
posEstimator.est.pos.x += posEstimator.imu.accelNEU.x * sq(ctx->dt) / 2.0f * accWeight;
|
posEstimator.est.pos.x += posEstimator.imu.accelNEU.x * sq(ctx->dt) / 2.0f;
|
||||||
posEstimator.est.pos.y += posEstimator.imu.accelNEU.y * sq(ctx->dt) / 2.0f * accWeight;
|
posEstimator.est.pos.y += posEstimator.imu.accelNEU.y * sq(ctx->dt) / 2.0f;
|
||||||
posEstimator.est.vel.x += posEstimator.imu.accelNEU.x * ctx->dt * sq(accWeight);
|
posEstimator.est.vel.x += posEstimator.imu.accelNEU.x * ctx->dt;
|
||||||
posEstimator.est.vel.y += posEstimator.imu.accelNEU.y * ctx->dt * sq(accWeight);
|
posEstimator.est.vel.y += posEstimator.imu.accelNEU.y * ctx->dt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -589,7 +616,16 @@ static bool estimationCalculateCorrection_Z(estimationContext_t * ctx)
|
||||||
DEBUG_SET(DEBUG_ALTITUDE, 5, posEstimator.gps.vel.z); // GPS vertical speed
|
DEBUG_SET(DEBUG_ALTITUDE, 5, posEstimator.gps.vel.z); // GPS vertical speed
|
||||||
DEBUG_SET(DEBUG_ALTITUDE, 7, accGetClipCount()); // Clip count
|
DEBUG_SET(DEBUG_ALTITUDE, 7, accGetClipCount()); // Clip count
|
||||||
|
|
||||||
if (ctx->newFlags & EST_BARO_VALID) {
|
bool correctOK = false;
|
||||||
|
|
||||||
|
//ignore baro if difference is too big, baro is probably wrong
|
||||||
|
const float gpsBaroResidual = ctx->newFlags & EST_GPS_Z_VALID ? fabsf(posEstimator.gps.pos.z - posEstimator.baro.alt) : 0.0f;
|
||||||
|
//fade out the baro to prevent sudden jump
|
||||||
|
const float start_epv = positionEstimationConfig()->max_eph_epv;
|
||||||
|
const float end_epv = positionEstimationConfig()->max_eph_epv * 2.0f;
|
||||||
|
const float wBaro = scaleRangef(constrainf(gpsBaroResidual, start_epv, end_epv), start_epv, end_epv, 1.0f, 0.0f);
|
||||||
|
//use both baro and gps
|
||||||
|
if ((ctx->newFlags & EST_BARO_VALID) && (!positionEstimationConfig()->use_gps_no_baro) && (wBaro > 0.01f)) {
|
||||||
timeUs_t currentTimeUs = micros();
|
timeUs_t currentTimeUs = micros();
|
||||||
|
|
||||||
if (!ARMING_FLAG(ARMED)) {
|
if (!ARMING_FLAG(ARMED)) {
|
||||||
|
@ -599,44 +635,33 @@ static bool estimationCalculateCorrection_Z(estimationContext_t * ctx)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (posEstimator.est.vel.z > 15) {
|
if (posEstimator.est.vel.z > 15) {
|
||||||
if (currentTimeUs > posEstimator.state.baroGroundTimeout) {
|
posEstimator.state.isBaroGroundValid = currentTimeUs > posEstimator.state.baroGroundTimeout ? false: true;
|
||||||
posEstimator.state.isBaroGroundValid = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
posEstimator.state.baroGroundTimeout = currentTimeUs + 250000; // 0.25 sec
|
posEstimator.state.baroGroundTimeout = currentTimeUs + 250000; // 0.25 sec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We might be experiencing air cushion effect - use sonar or baro groung altitude to detect it
|
// We might be experiencing air cushion effect during takeoff - use sonar or baro ground altitude to detect it
|
||||||
bool isAirCushionEffectDetected = ARMING_FLAG(ARMED) &&
|
bool isAirCushionEffectDetected = ARMING_FLAG(ARMED) &&
|
||||||
(((ctx->newFlags & EST_SURFACE_VALID) && posEstimator.surface.alt < 20.0f && posEstimator.state.isBaroGroundValid) ||
|
(((ctx->newFlags & EST_SURFACE_VALID) && posEstimator.surface.alt < 20.0f && posEstimator.state.isBaroGroundValid) ||
|
||||||
((ctx->newFlags & EST_BARO_VALID) && posEstimator.state.isBaroGroundValid && posEstimator.baro.alt < posEstimator.state.baroGroundAlt));
|
((ctx->newFlags & EST_BARO_VALID) && posEstimator.state.isBaroGroundValid && posEstimator.baro.alt < posEstimator.state.baroGroundAlt));
|
||||||
|
|
||||||
// Altitude
|
// Altitude
|
||||||
const float baroAltResidual = (isAirCushionEffectDetected ? posEstimator.state.baroGroundAlt : posEstimator.baro.alt) - posEstimator.est.pos.z;
|
const float baroAltResidual = (isAirCushionEffectDetected ? posEstimator.state.baroGroundAlt : posEstimator.baro.alt) - posEstimator.est.pos.z;
|
||||||
ctx->estPosCorr.z += baroAltResidual * positionEstimationConfig()->w_z_baro_p * ctx->dt;
|
ctx->estPosCorr.z += wBaro * baroAltResidual * positionEstimationConfig()->w_z_baro_p * ctx->dt;
|
||||||
ctx->estVelCorr.z += baroAltResidual * sq(positionEstimationConfig()->w_z_baro_p) * ctx->dt;
|
ctx->estVelCorr.z += wBaro * baroAltResidual * sq(positionEstimationConfig()->w_z_baro_p) * ctx->dt;
|
||||||
|
|
||||||
// If GPS is available - also use GPS climb rate
|
|
||||||
if (ctx->newFlags & EST_GPS_Z_VALID) {
|
|
||||||
// Trust GPS velocity only if residual/error is less than 2.5 m/s, scale weight according to gaussian distribution
|
|
||||||
const float gpsRocResidual = posEstimator.gps.vel.z - posEstimator.est.vel.z;
|
|
||||||
const float gpsRocScaler = bellCurve(gpsRocResidual, 250.0f);
|
|
||||||
ctx->estVelCorr.z += gpsRocResidual * positionEstimationConfig()->w_z_gps_v * gpsRocScaler * ctx->dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->newEPV = updateEPE(posEstimator.est.epv, ctx->dt, posEstimator.baro.epv, positionEstimationConfig()->w_z_baro_p);
|
ctx->newEPV = updateEPE(posEstimator.est.epv, ctx->dt, posEstimator.baro.epv, positionEstimationConfig()->w_z_baro_p);
|
||||||
|
|
||||||
// Accelerometer bias
|
// Accelerometer bias
|
||||||
if (!isAirCushionEffectDetected) {
|
if (!isAirCushionEffectDetected) {
|
||||||
ctx->accBiasCorr.z -= baroAltResidual * sq(positionEstimationConfig()->w_z_baro_p);
|
ctx->accBiasCorr.z -= wBaro * baroAltResidual * sq(positionEstimationConfig()->w_z_baro_p);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
correctOK = true;
|
||||||
}
|
}
|
||||||
else if ((STATE(FIXED_WING_LEGACY) || positionEstimationConfig()->use_gps_no_baro) && (ctx->newFlags & EST_GPS_Z_VALID)) {
|
if (ctx->newFlags & EST_GPS_Z_VALID) {
|
||||||
// If baro is not available - use GPS Z for correction on a plane
|
|
||||||
// Reset current estimate to GPS altitude if estimate not valid
|
// Reset current estimate to GPS altitude if estimate not valid
|
||||||
if (!(ctx->newFlags & EST_Z_VALID)) {
|
if (!(ctx->newFlags & EST_Z_VALID)) {
|
||||||
ctx->estPosCorr.z += posEstimator.gps.pos.z - posEstimator.est.pos.z;
|
ctx->estPosCorr.z += posEstimator.gps.pos.z - posEstimator.est.pos.z;
|
||||||
|
@ -646,20 +671,21 @@ static bool estimationCalculateCorrection_Z(estimationContext_t * ctx)
|
||||||
else {
|
else {
|
||||||
// Altitude
|
// Altitude
|
||||||
const float gpsAltResudual = posEstimator.gps.pos.z - posEstimator.est.pos.z;
|
const float gpsAltResudual = posEstimator.gps.pos.z - posEstimator.est.pos.z;
|
||||||
|
const float gpsVelZResudual = posEstimator.gps.vel.z - posEstimator.est.vel.z;
|
||||||
|
|
||||||
ctx->estPosCorr.z += gpsAltResudual * positionEstimationConfig()->w_z_gps_p * ctx->dt;
|
ctx->estPosCorr.z += gpsAltResudual * positionEstimationConfig()->w_z_gps_p * ctx->dt;
|
||||||
ctx->estVelCorr.z += gpsAltResudual * sq(positionEstimationConfig()->w_z_gps_p) * ctx->dt;
|
ctx->estVelCorr.z += gpsAltResudual * sq(positionEstimationConfig()->w_z_gps_p) * ctx->dt;
|
||||||
ctx->estVelCorr.z += (posEstimator.gps.vel.z - posEstimator.est.vel.z) * positionEstimationConfig()->w_z_gps_v * ctx->dt;
|
ctx->estVelCorr.z += gpsVelZResudual * positionEstimationConfig()->w_z_gps_v * ctx->dt;
|
||||||
ctx->newEPV = updateEPE(posEstimator.est.epv, ctx->dt, MAX(posEstimator.gps.epv, gpsAltResudual), positionEstimationConfig()->w_z_gps_p);
|
ctx->newEPV = updateEPE(posEstimator.est.epv, ctx->dt, MAX(posEstimator.gps.epv, gpsAltResudual), positionEstimationConfig()->w_z_gps_p);
|
||||||
|
|
||||||
// Accelerometer bias
|
// Accelerometer bias
|
||||||
ctx->accBiasCorr.z -= gpsAltResudual * sq(positionEstimationConfig()->w_z_gps_p);
|
ctx->accBiasCorr.z -= gpsAltResudual * sq(positionEstimationConfig()->w_z_gps_p);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
correctOK = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return correctOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool estimationCalculateCorrection_XY_GPS(estimationContext_t * ctx)
|
static bool estimationCalculateCorrection_XY_GPS(estimationContext_t * ctx)
|
||||||
|
@ -714,15 +740,14 @@ static bool estimationCalculateCorrection_XY_GPS(estimationContext_t * ctx)
|
||||||
|
|
||||||
static void estimationCalculateGroundCourse(timeUs_t currentTimeUs)
|
static void estimationCalculateGroundCourse(timeUs_t currentTimeUs)
|
||||||
{
|
{
|
||||||
if (STATE(GPS_FIX) && navIsHeadingUsable()) {
|
UNUSED(currentTimeUs);
|
||||||
static timeUs_t lastUpdateTimeUs = 0;
|
if ((STATE(GPS_FIX)
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
if (currentTimeUs - lastUpdateTimeUs >= HZ2US(INAV_COG_UPDATE_RATE_HZ)) { // limit update rate
|
|| STATE(GPS_ESTIMATED_FIX)
|
||||||
const float dt = US2S(currentTimeUs - lastUpdateTimeUs);
|
#endif
|
||||||
uint32_t groundCourse = wrap_36000(RADIANS_TO_CENTIDEGREES(atan2_approx(posEstimator.est.vel.y * dt, posEstimator.est.vel.x * dt)));
|
) && navIsHeadingUsable()) {
|
||||||
posEstimator.est.cog = CENTIDEGREES_TO_DECIDEGREES(groundCourse);
|
uint32_t groundCourse = wrap_36000(RADIANS_TO_CENTIDEGREES(atan2_approx(posEstimator.est.vel.y, posEstimator.est.vel.x)));
|
||||||
lastUpdateTimeUs = currentTimeUs;
|
posEstimator.est.cog = CENTIDEGREES_TO_DECIDEGREES(groundCourse);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -779,7 +804,10 @@ static void updateEstimatedTopic(timeUs_t currentTimeUs)
|
||||||
if (!estZCorrectOk || ctx.newEPV > positionEstimationConfig()->max_eph_epv) {
|
if (!estZCorrectOk || ctx.newEPV > positionEstimationConfig()->max_eph_epv) {
|
||||||
ctx.estVelCorr.z = (0.0f - posEstimator.est.vel.z) * positionEstimationConfig()->w_z_res_v * ctx.dt;
|
ctx.estVelCorr.z = (0.0f - posEstimator.est.vel.z) * positionEstimationConfig()->w_z_res_v * ctx.dt;
|
||||||
}
|
}
|
||||||
|
// Boost the corrections based on accWeight
|
||||||
|
const float accWeight = navGetAccelerometerWeight();
|
||||||
|
vectorScale(&ctx.estPosCorr, &ctx.estPosCorr, 1.0f/accWeight);
|
||||||
|
vectorScale(&ctx.estVelCorr, &ctx.estVelCorr, 1.0f/accWeight);
|
||||||
// Apply corrections
|
// Apply corrections
|
||||||
vectorAdd(&posEstimator.est.pos, &posEstimator.est.pos, &ctx.estPosCorr);
|
vectorAdd(&posEstimator.est.pos, &posEstimator.est.pos, &ctx.estPosCorr);
|
||||||
vectorAdd(&posEstimator.est.vel, &posEstimator.est.vel, &ctx.estVelCorr);
|
vectorAdd(&posEstimator.est.vel, &posEstimator.est.vel, &ctx.estVelCorr);
|
||||||
|
@ -817,13 +845,14 @@ static void publishEstimatedTopic(timeUs_t currentTimeUs)
|
||||||
{
|
{
|
||||||
static navigationTimer_t posPublishTimer;
|
static navigationTimer_t posPublishTimer;
|
||||||
|
|
||||||
/* IMU operates in decidegrees while INAV operates in deg*100
|
|
||||||
* Use course over ground when GPS heading valid */
|
|
||||||
int16_t cogValue = isGPSHeadingValid() ? posEstimator.est.cog : attitude.values.yaw;
|
|
||||||
updateActualHeading(navIsHeadingUsable(), DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw), DECIDEGREES_TO_CENTIDEGREES(cogValue));
|
|
||||||
|
|
||||||
/* Position and velocity are published with INAV_POSITION_PUBLISH_RATE_HZ */
|
/* Position and velocity are published with INAV_POSITION_PUBLISH_RATE_HZ */
|
||||||
if (updateTimer(&posPublishTimer, HZ2US(INAV_POSITION_PUBLISH_RATE_HZ), currentTimeUs)) {
|
if (updateTimer(&posPublishTimer, HZ2US(INAV_POSITION_PUBLISH_RATE_HZ), currentTimeUs)) {
|
||||||
|
/* Publish heading update */
|
||||||
|
/* IMU operates in decidegrees while INAV operates in deg*100
|
||||||
|
* Use course over ground when GPS heading valid */
|
||||||
|
int16_t cogValue = isGPSHeadingValid() ? posEstimator.est.cog : attitude.values.yaw;
|
||||||
|
updateActualHeading(navIsHeadingUsable(), DECIDEGREES_TO_CENTIDEGREES(attitude.values.yaw), DECIDEGREES_TO_CENTIDEGREES(cogValue));
|
||||||
|
|
||||||
/* Publish position update */
|
/* Publish position update */
|
||||||
if (posEstimator.est.eph < positionEstimationConfig()->max_eph_epv) {
|
if (posEstimator.est.eph < positionEstimationConfig()->max_eph_epv) {
|
||||||
// FIXME!!!!!
|
// FIXME!!!!!
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
|
|
||||||
#define INAV_POSITION_PUBLISH_RATE_HZ 50 // Publish position updates at this rate
|
#define INAV_POSITION_PUBLISH_RATE_HZ 50 // Publish position updates at this rate
|
||||||
#define INAV_PITOT_UPDATE_RATE 10
|
#define INAV_PITOT_UPDATE_RATE 10
|
||||||
#define INAV_COG_UPDATE_RATE_HZ 20 // ground course update rate
|
|
||||||
|
|
||||||
#define INAV_GPS_TIMEOUT_MS 1500 // GPS timeout
|
#define INAV_GPS_TIMEOUT_MS 1500 // GPS timeout
|
||||||
#define INAV_BARO_TIMEOUT_MS 200 // Baro timeout
|
#define INAV_BARO_TIMEOUT_MS 200 // Baro timeout
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
#define MC_LAND_DESCEND_THROTTLE 40 // RC pwm units (us)
|
#define MC_LAND_DESCEND_THROTTLE 40 // RC pwm units (us)
|
||||||
#define MC_LAND_SAFE_SURFACE 5.0f // cm
|
#define MC_LAND_SAFE_SURFACE 5.0f // cm
|
||||||
|
|
||||||
#define NAV_RTH_TRACKBACK_POINTS 50 // max number RTH trackback points
|
|
||||||
|
|
||||||
#define MAX_POSITION_UPDATE_INTERVAL_US HZ2US(MIN_POSITION_UPDATE_RATE_HZ) // convenience macro
|
#define MAX_POSITION_UPDATE_INTERVAL_US HZ2US(MIN_POSITION_UPDATE_RATE_HZ) // convenience macro
|
||||||
_Static_assert(MAX_POSITION_UPDATE_INTERVAL_US <= TIMEDELTA_MAX, "deltaMicros can overflow!");
|
_Static_assert(MAX_POSITION_UPDATE_INTERVAL_US <= TIMEDELTA_MAX, "deltaMicros can overflow!");
|
||||||
|
|
||||||
|
@ -350,6 +348,7 @@ typedef struct {
|
||||||
float rthInitialDistance; // Distance when starting flight home
|
float rthInitialDistance; // Distance when starting flight home
|
||||||
fpVector3_t homeTmpWaypoint; // Temporary storage for home target
|
fpVector3_t homeTmpWaypoint; // Temporary storage for home target
|
||||||
fpVector3_t originalHomePosition; // the original rth home - save it, since it could be replaced by safehome or HOME_RESET
|
fpVector3_t originalHomePosition; // the original rth home - save it, since it could be replaced by safehome or HOME_RESET
|
||||||
|
bool rthLinearDescentActive; // Activation status of Linear Descent
|
||||||
} rthState_t;
|
} rthState_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -394,6 +393,7 @@ typedef struct {
|
||||||
rthState_t rthState;
|
rthState_t rthState;
|
||||||
uint32_t homeDistance; // cm
|
uint32_t homeDistance; // cm
|
||||||
int32_t homeDirection; // deg*100
|
int32_t homeDirection; // deg*100
|
||||||
|
timeMs_t landingDelay;
|
||||||
|
|
||||||
/* Safehome parameters */
|
/* Safehome parameters */
|
||||||
safehomeState_t safehomeState;
|
safehomeState_t safehomeState;
|
||||||
|
@ -426,12 +426,6 @@ typedef struct {
|
||||||
timeMs_t wpReachedTime; // Time the waypoint was reached
|
timeMs_t wpReachedTime; // Time the waypoint was reached
|
||||||
bool wpAltitudeReached; // WP altitude achieved
|
bool wpAltitudeReached; // WP altitude achieved
|
||||||
|
|
||||||
/* RTH Trackback */
|
|
||||||
fpVector3_t rthTBPointsList[NAV_RTH_TRACKBACK_POINTS];
|
|
||||||
int8_t rthTBLastSavedIndex; // last trackback point index saved
|
|
||||||
int8_t activeRthTBPointIndex;
|
|
||||||
int8_t rthTBWrapAroundCounter; // stores trackpoint array overwrite index position
|
|
||||||
|
|
||||||
/* Internals & statistics */
|
/* Internals & statistics */
|
||||||
int16_t rcAdjustment[4];
|
int16_t rcAdjustment[4];
|
||||||
float totalTripDistance;
|
float totalTripDistance;
|
||||||
|
|
182
src/main/navigation/rth_trackback.c
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* This file is part of INAV.
|
||||||
|
*
|
||||||
|
* INAV is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* INAV is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with INAV. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------
|
||||||
|
* == RTH Trackback ==
|
||||||
|
* Saves track during flight which is used during RTH to back track
|
||||||
|
* along arrival route rather than immediately heading directly toward home.
|
||||||
|
* Max desired trackback distance set by user or limited by number of available points.
|
||||||
|
* Reverts to normal RTH heading direct to home when end of track reached.
|
||||||
|
* Trackpoints logged with precedence for course/altitude changes. Distance based changes
|
||||||
|
* only logged if no course/altitude changes logged over an extended distance.
|
||||||
|
* Tracking suspended during fixed wing loiter (PosHold and WP Mode timed hold).
|
||||||
|
* --------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include "fc/multifunction.h"
|
||||||
|
#include "fc/rc_controls.h"
|
||||||
|
|
||||||
|
#include "navigation/rth_trackback.h"
|
||||||
|
#include "navigation/navigation.h"
|
||||||
|
#include "navigation/navigation_private.h"
|
||||||
|
|
||||||
|
rth_trackback_t rth_trackback;
|
||||||
|
|
||||||
|
bool rthTrackBackIsActive(void)
|
||||||
|
{
|
||||||
|
return navConfig()->general.flags.rth_trackback_mode == RTH_TRACKBACK_ON || (navConfig()->general.flags.rth_trackback_mode == RTH_TRACKBACK_FS && posControl.flags.forcedRTHActivated);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rthTrackBackUpdate(bool forceSaveTrackPoint)
|
||||||
|
{
|
||||||
|
static bool suspendTracking = false;
|
||||||
|
bool fwLoiterIsActive = STATE(AIRPLANE) && (NAV_Status.state == MW_NAV_STATE_HOLD_TIMED || FLIGHT_MODE(NAV_POSHOLD_MODE));
|
||||||
|
|
||||||
|
if (!fwLoiterIsActive && suspendTracking) {
|
||||||
|
suspendTracking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navConfig()->general.flags.rth_trackback_mode == RTH_TRACKBACK_OFF || FLIGHT_MODE(NAV_RTH_MODE) || !ARMING_FLAG(ARMED) || suspendTracking) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record trackback points based on significant change in course/altitude until points limit reached. Overwrite older points from then on.
|
||||||
|
if (posControl.flags.estPosStatus >= EST_USABLE && posControl.flags.estAltStatus >= EST_USABLE) {
|
||||||
|
static int32_t previousTBTripDist; // cm
|
||||||
|
static int16_t previousTBCourse; // degrees
|
||||||
|
static int16_t previousTBAltitude; // meters
|
||||||
|
static uint8_t distanceCounter = 0;
|
||||||
|
bool saveTrackpoint = forceSaveTrackPoint;
|
||||||
|
bool GPSCourseIsValid = isGPSHeadingValid();
|
||||||
|
|
||||||
|
// Start recording when some distance from home
|
||||||
|
if (rth_trackback.activePointIndex < 0) {
|
||||||
|
saveTrackpoint = posControl.homeDistance > METERS_TO_CENTIMETERS(NAV_RTH_TRACKBACK_MIN_DIST_TO_START);
|
||||||
|
previousTBCourse = CENTIDEGREES_TO_DEGREES(posControl.actualState.cog);
|
||||||
|
previousTBTripDist = posControl.totalTripDistance;
|
||||||
|
} else {
|
||||||
|
// Minimum distance increment between course change track points when GPS course valid
|
||||||
|
const bool distanceIncrement = posControl.totalTripDistance - previousTBTripDist > METERS_TO_CENTIMETERS(NAV_RTH_TRACKBACK_MIN_TRIP_DIST_TO_SAVE);
|
||||||
|
|
||||||
|
// Altitude change
|
||||||
|
if (ABS(previousTBAltitude - CENTIMETERS_TO_METERS(posControl.actualState.abs.pos.z)) > NAV_RTH_TRACKBACK_MIN_Z_DIST_TO_SAVE) {
|
||||||
|
saveTrackpoint = true;
|
||||||
|
} else if (distanceIncrement && GPSCourseIsValid) {
|
||||||
|
// Course change - set to 45 degrees
|
||||||
|
if (ABS(wrap_18000(DEGREES_TO_CENTIDEGREES(DECIDEGREES_TO_DEGREES(gpsSol.groundCourse) - previousTBCourse))) > DEGREES_TO_CENTIDEGREES(45)) {
|
||||||
|
saveTrackpoint = true;
|
||||||
|
} else if (distanceCounter >= 9) {
|
||||||
|
// Distance based trackpoint logged if at least 10 distance increments occur without altitude or course change and deviation from projected course path > 20m
|
||||||
|
float distToPrevPoint = calculateDistanceToDestination(&rth_trackback.pointsList[rth_trackback.activePointIndex]);
|
||||||
|
|
||||||
|
fpVector3_t virtualCoursePoint;
|
||||||
|
virtualCoursePoint.x = rth_trackback.pointsList[rth_trackback.activePointIndex].x + distToPrevPoint * cos_approx(DEGREES_TO_RADIANS(previousTBCourse));
|
||||||
|
virtualCoursePoint.y = rth_trackback.pointsList[rth_trackback.activePointIndex].y + distToPrevPoint * sin_approx(DEGREES_TO_RADIANS(previousTBCourse));
|
||||||
|
|
||||||
|
saveTrackpoint = calculateDistanceToDestination(&virtualCoursePoint) > METERS_TO_CENTIMETERS(NAV_RTH_TRACKBACK_MIN_XY_DIST_TO_SAVE);
|
||||||
|
}
|
||||||
|
distanceCounter++;
|
||||||
|
previousTBTripDist = posControl.totalTripDistance;
|
||||||
|
} else if (!GPSCourseIsValid) {
|
||||||
|
// If no reliable course revert to basic distance logging based on direct distance from last point
|
||||||
|
saveTrackpoint = calculateDistanceToDestination(&rth_trackback.pointsList[rth_trackback.activePointIndex]) > METERS_TO_CENTIMETERS(NAV_RTH_TRACKBACK_MIN_XY_DIST_TO_SAVE);
|
||||||
|
previousTBTripDist = posControl.totalTripDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend tracking during loiter on fixed wing. Save trackpoint at start of loiter.
|
||||||
|
if (distanceCounter && fwLoiterIsActive) {
|
||||||
|
saveTrackpoint = suspendTracking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When trackpoint store full, overwrite from start of store using 'WrapAroundCounter' to track overwrite position
|
||||||
|
if (saveTrackpoint) {
|
||||||
|
if (rth_trackback.activePointIndex == (NAV_RTH_TRACKBACK_POINTS - 1)) { // Wraparound to start
|
||||||
|
rth_trackback.WrapAroundCounter = rth_trackback.activePointIndex = 0;
|
||||||
|
} else {
|
||||||
|
rth_trackback.activePointIndex++;
|
||||||
|
if (rth_trackback.WrapAroundCounter > -1) { // Track wraparound overwrite position after store first filled
|
||||||
|
rth_trackback.WrapAroundCounter = rth_trackback.activePointIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rth_trackback.pointsList[rth_trackback.activePointIndex] = posControl.actualState.abs.pos;
|
||||||
|
rth_trackback.lastSavedIndex = rth_trackback.activePointIndex;
|
||||||
|
previousTBAltitude = CENTIMETERS_TO_METERS(posControl.actualState.abs.pos.z);
|
||||||
|
previousTBCourse = GPSCourseIsValid ? DECIDEGREES_TO_DEGREES(gpsSol.groundCourse) : previousTBCourse;
|
||||||
|
distanceCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rthTrackBackSetNewPosition(void)
|
||||||
|
{
|
||||||
|
if (posControl.flags.estPosStatus == EST_NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t distFromStartTrackback = CENTIMETERS_TO_METERS(calculateDistanceToDestination(&rth_trackback.pointsList[rth_trackback.lastSavedIndex]));
|
||||||
|
|
||||||
|
#ifdef USE_MULTI_FUNCTIONS
|
||||||
|
const bool overrideTrackback = rthAltControlStickOverrideCheck(ROLL) || MULTI_FUNC_FLAG(MF_SUSPEND_TRACKBACK);
|
||||||
|
#else
|
||||||
|
const bool overrideTrackback = rthAltControlStickOverrideCheck(ROLL);
|
||||||
|
#endif
|
||||||
|
const bool cancelTrackback = distFromStartTrackback > navConfig()->general.rth_trackback_distance || (overrideTrackback && !posControl.flags.forcedRTHActivated);
|
||||||
|
|
||||||
|
if (rth_trackback.activePointIndex < 0 || cancelTrackback) {
|
||||||
|
rth_trackback.WrapAroundCounter = rth_trackback.activePointIndex = -1;
|
||||||
|
posControl.flags.rthTrackbackActive = false;
|
||||||
|
return true; // Procede to home after final trackback point
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWaypointReached(&posControl.activeWaypoint.pos, &posControl.activeWaypoint.bearing)) {
|
||||||
|
rth_trackback.activePointIndex--;
|
||||||
|
|
||||||
|
if (rth_trackback.WrapAroundCounter > -1 && rth_trackback.activePointIndex < 0) {
|
||||||
|
rth_trackback.activePointIndex = NAV_RTH_TRACKBACK_POINTS - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateAndSetActiveWaypointToLocalPosition(getRthTrackBackPosition());
|
||||||
|
|
||||||
|
if (rth_trackback.activePointIndex - rth_trackback.WrapAroundCounter == 0) {
|
||||||
|
rth_trackback.WrapAroundCounter = rth_trackback.activePointIndex = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setDesiredPosition(getRthTrackBackPosition(), 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_BEARING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fpVector3_t *getRthTrackBackPosition(void)
|
||||||
|
{
|
||||||
|
// Ensure trackback altitude never lower than altitude of start point
|
||||||
|
if (rth_trackback.pointsList[rth_trackback.activePointIndex].z < rth_trackback.pointsList[rth_trackback.lastSavedIndex].z) {
|
||||||
|
rth_trackback.pointsList[rth_trackback.activePointIndex].z = rth_trackback.pointsList[rth_trackback.lastSavedIndex].z;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rth_trackback.pointsList[rth_trackback.activePointIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetRthTrackBack(void)
|
||||||
|
{
|
||||||
|
rth_trackback.activePointIndex = -1;
|
||||||
|
posControl.flags.rthTrackbackActive = false;
|
||||||
|
rth_trackback.WrapAroundCounter = -1;
|
||||||
|
}
|
42
src/main/navigation/rth_trackback.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* This file is part of INAV.
|
||||||
|
*
|
||||||
|
* INAV is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* INAV is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with INAV. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/vector.h"
|
||||||
|
|
||||||
|
#define NAV_RTH_TRACKBACK_POINTS 50 // max number RTH trackback points
|
||||||
|
#define NAV_RTH_TRACKBACK_MIN_DIST_TO_START 50 // start recording when some distance from home (meters)
|
||||||
|
#define NAV_RTH_TRACKBACK_MIN_XY_DIST_TO_SAVE 20 // minimum XY distance between two points to store in the buffer (meters)
|
||||||
|
#define NAV_RTH_TRACKBACK_MIN_Z_DIST_TO_SAVE 10 // minimum Z distance between two points to store in the buffer (meters)
|
||||||
|
#define NAV_RTH_TRACKBACK_MIN_TRIP_DIST_TO_SAVE 10 // minimum trip distance between two points to store in the buffer (meters)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
fpVector3_t pointsList[NAV_RTH_TRACKBACK_POINTS]; // buffer of points stored
|
||||||
|
int16_t lastSavedIndex; // last trackback point index saved
|
||||||
|
int16_t activePointIndex; // trackback points counter
|
||||||
|
int16_t WrapAroundCounter; // stores trackpoint array overwrite index position
|
||||||
|
} rth_trackback_t;
|
||||||
|
|
||||||
|
extern rth_trackback_t rth_trackback;
|
||||||
|
|
||||||
|
bool rthTrackBackIsActive(void);
|
||||||
|
bool rthTrackBackSetNewPosition(void);
|
||||||
|
void rthTrackBackUpdate(bool forceSaveTrackPoint);
|
||||||
|
fpVector3_t *getRthTrackBackPosition(void);
|
||||||
|
void resetRthTrackBack(void);
|
|
@ -56,6 +56,7 @@
|
||||||
|
|
||||||
#include "io/vtx.h"
|
#include "io/vtx.h"
|
||||||
#include "drivers/vtx_common.h"
|
#include "drivers/vtx_common.h"
|
||||||
|
#include "drivers/light_ws2811strip.h"
|
||||||
|
|
||||||
PG_REGISTER_ARRAY_WITH_RESET_FN(logicCondition_t, MAX_LOGIC_CONDITIONS, logicConditions, PG_LOGIC_CONDITIONS, 4);
|
PG_REGISTER_ARRAY_WITH_RESET_FN(logicCondition_t, MAX_LOGIC_CONDITIONS, logicConditions, PG_LOGIC_CONDITIONS, 4);
|
||||||
|
|
||||||
|
@ -473,8 +474,29 @@ static int logicConditionCompute(
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifdef LED_PIN
|
||||||
|
case LOGIC_CONDITION_LED_PIN_PWM:
|
||||||
|
if (operandA >=0 && operandA <= 100) {
|
||||||
|
ledPinStartPWM((uint8_t)operandA);
|
||||||
|
} else {
|
||||||
|
ledPinStopPWM();
|
||||||
|
}
|
||||||
|
return operandA;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
case LOGIC_CONDITION_DISABLE_GPS_FIX:
|
||||||
|
if (operandA > 0) {
|
||||||
|
LOGIC_CONDITION_GLOBAL_FLAG_ENABLE(LOGIC_CONDITION_GLOBAL_FLAG_DISABLE_GPS_FIX);
|
||||||
|
} else {
|
||||||
|
LOGIC_CONDITION_GLOBAL_FLAG_DISABLE(LOGIC_CONDITION_GLOBAL_FLAG_DISABLE_GPS_FIX);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
@ -656,6 +678,11 @@ static int logicConditionGetFlightOperandValue(int operand) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOGIC_CONDITION_OPERAND_FLIGHT_GPS_SATS:
|
case LOGIC_CONDITION_OPERAND_FLIGHT_GPS_SATS:
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
if ( STATE(GPS_ESTIMATED_FIX) ){
|
||||||
|
return gpsSol.numSat; //99
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
if (getHwGPSStatus() == HW_SENSOR_UNAVAILABLE || getHwGPSStatus() == HW_SENSOR_UNHEALTHY) {
|
if (getHwGPSStatus() == HW_SENSOR_UNAVAILABLE || getHwGPSStatus() == HW_SENSOR_UNHEALTHY) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -849,13 +876,18 @@ static int logicConditionGetFlightModeOperandValue(int operand) {
|
||||||
return (bool) FLIGHT_MODE(HORIZON_MODE);
|
return (bool) FLIGHT_MODE(HORIZON_MODE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_ANGLEHOLD:
|
||||||
|
return (bool) FLIGHT_MODE(ANGLEHOLD_MODE);
|
||||||
|
break;
|
||||||
|
|
||||||
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_AIR:
|
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_AIR:
|
||||||
return (bool) FLIGHT_MODE(AIRMODE_ACTIVE);
|
return (bool) FLIGHT_MODE(AIRMODE_ACTIVE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_ACRO:
|
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_ACRO:
|
||||||
return (((bool) FLIGHT_MODE(ANGLE_MODE) || (bool) FLIGHT_MODE(HORIZON_MODE) || (bool) FLIGHT_MODE(MANUAL_MODE) || (bool) FLIGHT_MODE(NAV_RTH_MODE) ||
|
return (((bool) FLIGHT_MODE(ANGLE_MODE) || (bool) FLIGHT_MODE(HORIZON_MODE) || (bool) FLIGHT_MODE(ANGLEHOLD_MODE) ||
|
||||||
(bool) FLIGHT_MODE(NAV_POSHOLD_MODE) || (bool) FLIGHT_MODE(NAV_COURSE_HOLD_MODE) || (bool) FLIGHT_MODE(NAV_WP_MODE)) == false);
|
(bool) FLIGHT_MODE(MANUAL_MODE) || (bool) FLIGHT_MODE(NAV_RTH_MODE) || (bool) FLIGHT_MODE(NAV_POSHOLD_MODE) ||
|
||||||
|
(bool) FLIGHT_MODE(NAV_COURSE_HOLD_MODE) || (bool) FLIGHT_MODE(NAV_WP_MODE)) == false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_USER1:
|
case LOGIC_CONDITION_OPERAND_FLIGHT_MODE_USER1:
|
||||||
|
|
|
@ -81,7 +81,9 @@ typedef enum {
|
||||||
LOGIC_CONDITION_TIMER = 49,
|
LOGIC_CONDITION_TIMER = 49,
|
||||||
LOGIC_CONDITION_DELTA = 50,
|
LOGIC_CONDITION_DELTA = 50,
|
||||||
LOGIC_CONDITION_APPROX_EQUAL = 51,
|
LOGIC_CONDITION_APPROX_EQUAL = 51,
|
||||||
LOGIC_CONDITION_LAST = 52,
|
LOGIC_CONDITION_LED_PIN_PWM = 52,
|
||||||
|
LOGIC_CONDITION_DISABLE_GPS_FIX = 53,
|
||||||
|
LOGIC_CONDITION_LAST = 54,
|
||||||
} logicOperation_e;
|
} logicOperation_e;
|
||||||
|
|
||||||
typedef enum logicOperandType_s {
|
typedef enum logicOperandType_s {
|
||||||
|
@ -157,6 +159,7 @@ typedef enum {
|
||||||
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_USER4, // 13
|
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_USER4, // 13
|
||||||
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_ACRO, // 14
|
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_ACRO, // 14
|
||||||
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_WAYPOINT_MISSION, // 15
|
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_WAYPOINT_MISSION, // 15
|
||||||
|
LOGIC_CONDITION_OPERAND_FLIGHT_MODE_ANGLEHOLD, // 16
|
||||||
} logicFlightModeOperands_e;
|
} logicFlightModeOperands_e;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -188,6 +191,9 @@ typedef enum {
|
||||||
LOGIC_CONDITION_GLOBAL_FLAG_OVERRIDE_RC_CHANNEL = (1 << 8),
|
LOGIC_CONDITION_GLOBAL_FLAG_OVERRIDE_RC_CHANNEL = (1 << 8),
|
||||||
LOGIC_CONDITION_GLOBAL_FLAG_OVERRIDE_LOITER_RADIUS = (1 << 9),
|
LOGIC_CONDITION_GLOBAL_FLAG_OVERRIDE_LOITER_RADIUS = (1 << 9),
|
||||||
LOGIC_CONDITION_GLOBAL_FLAG_OVERRIDE_FLIGHT_AXIS = (1 << 10),
|
LOGIC_CONDITION_GLOBAL_FLAG_OVERRIDE_FLIGHT_AXIS = (1 << 10),
|
||||||
|
#ifdef USE_GPS_FIX_ESTIMATION
|
||||||
|
LOGIC_CONDITION_GLOBAL_FLAG_DISABLE_GPS_FIX = (1 << 11),
|
||||||
|
#endif
|
||||||
} logicConditionsGlobalFlags_t;
|
} logicConditionsGlobalFlags_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -576,6 +576,7 @@ void accUpdate(void)
|
||||||
// calc difference from this sample and 5hz filtered value, square and filter at 2hz
|
// calc difference from this sample and 5hz filtered value, square and filter at 2hz
|
||||||
const float accDiff = acc.accADCf[axis] - accFloorFilt;
|
const float accDiff = acc.accADCf[axis] - accFloorFilt;
|
||||||
acc.accVibeSq[axis] = pt1FilterApply(&accVibeFilter[axis], accDiff * accDiff);
|
acc.accVibeSq[axis] = pt1FilterApply(&accVibeFilter[axis], accDiff * accDiff);
|
||||||
|
acc.accVibe = fast_fsqrtf(acc.accVibeSq[X] + acc.accVibeSq[Y] + acc.accVibeSq[Z]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter acceleration
|
// Filter acceleration
|
||||||
|
@ -612,7 +613,7 @@ void accGetVibrationLevels(fpVector3_t *accVibeLevels)
|
||||||
|
|
||||||
float accGetVibrationLevel(void)
|
float accGetVibrationLevel(void)
|
||||||
{
|
{
|
||||||
return fast_fsqrtf(acc.accVibeSq[X] + acc.accVibeSq[Y] + acc.accVibeSq[Z]);
|
return acc.accVibe;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t accGetClipCount(void)
|
uint32_t accGetClipCount(void)
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#define GRAVITY_CMSS 980.665f
|
#define GRAVITY_CMSS 980.665f
|
||||||
#define GRAVITY_MSS 9.80665f
|
#define GRAVITY_MSS 9.80665f
|
||||||
|
|
||||||
#define ACC_CLIPPING_THRESHOLD_G 7.9f
|
#define ACC_CLIPPING_THRESHOLD_G 15.9f
|
||||||
#define ACC_VIBE_FLOOR_FILT_HZ 5.0f
|
#define ACC_VIBE_FLOOR_FILT_HZ 5.0f
|
||||||
#define ACC_VIBE_FILT_HZ 2.0f
|
#define ACC_VIBE_FILT_HZ 2.0f
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ typedef struct acc_s {
|
||||||
uint32_t accTargetLooptime;
|
uint32_t accTargetLooptime;
|
||||||
float accADCf[XYZ_AXIS_COUNT]; // acceleration in g
|
float accADCf[XYZ_AXIS_COUNT]; // acceleration in g
|
||||||
float accVibeSq[XYZ_AXIS_COUNT];
|
float accVibeSq[XYZ_AXIS_COUNT];
|
||||||
|
float accVibe;
|
||||||
uint32_t accClipCount;
|
uint32_t accClipCount;
|
||||||
bool isClipped;
|
bool isClipped;
|
||||||
acc_extremes_t extremes[XYZ_AXIS_COUNT];
|
acc_extremes_t extremes[XYZ_AXIS_COUNT];
|
||||||
|
|
|
@ -134,7 +134,6 @@ void pgResetFn_batteryProfiles(batteryProfile_t *instance)
|
||||||
.failsafe_throttle = SETTING_FAILSAFE_THROTTLE_DEFAULT, // default throttle off.
|
.failsafe_throttle = SETTING_FAILSAFE_THROTTLE_DEFAULT, // default throttle off.
|
||||||
|
|
||||||
.nav = {
|
.nav = {
|
||||||
|
|
||||||
.mc = {
|
.mc = {
|
||||||
.hover_throttle = SETTING_NAV_MC_HOVER_THR_DEFAULT,
|
.hover_throttle = SETTING_NAV_MC_HOVER_THR_DEFAULT,
|
||||||
},
|
},
|
||||||
|
@ -147,7 +146,6 @@ void pgResetFn_batteryProfiles(batteryProfile_t *instance)
|
||||||
.launch_throttle = SETTING_NAV_FW_LAUNCH_THR_DEFAULT,
|
.launch_throttle = SETTING_NAV_FW_LAUNCH_THR_DEFAULT,
|
||||||
.launch_idle_throttle = SETTING_NAV_FW_LAUNCH_IDLE_THR_DEFAULT, // Motor idle or MOTOR_STOP
|
.launch_idle_throttle = SETTING_NAV_FW_LAUNCH_IDLE_THR_DEFAULT, // Motor idle or MOTOR_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#if defined(USE_POWER_LIMITS)
|
#if defined(USE_POWER_LIMITS)
|
||||||
|
|
|
@ -485,4 +485,5 @@ void compassUpdate(timeUs_t currentTimeUs)
|
||||||
|
|
||||||
magUpdatedAtLeastOnce = true;
|
magUpdatedAtLeastOnce = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|