Merge branch 'master' into gps_task_time

This commit is contained in:
Michael Keller 2021-05-17 22:44:18 +12:00 committed by GitHub
commit e9b77e9f30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
104 changed files with 1879 additions and 583 deletions

View File

@ -105,7 +105,7 @@ FEATURE_CUT_LEVEL_SUPPLIED := $(FEATURE_CUT_LEVEL)
FEATURE_CUT_LEVEL =
# The list of targets to build for 'pre-push'
PRE_PUSH_TARGET_LIST ?= STM32F405 STM32F411 STM32F7X2 STM32F745 NUCLEOH743 SITL STM32F4DISCOVERY_DEBUG test-representative
PRE_PUSH_TARGET_LIST ?= $(UNIFIED_TARGETS) NUCLEOH743 SITL STM32F4DISCOVERY_DEBUG test-representative
include $(ROOT)/make/targets.mk

View File

@ -52,9 +52,9 @@ stages:
- script: make EXTRA_FLAGS=-Werror test-all
displayName: 'Run all unit tests'
- script: make EXTRA_FLAGS=-Werror all
displayName: 'Build all oficial targets'
displayName: 'Build all official targets'
- script: mkdir release; cp obj/*.hex release/
displayName: 'Copy artefacts'
displayName: 'Copy artifacts'
- task: PublishPipelineArtifact@1
displayName: 'Publish Linux release'
inputs:

View File

@ -175,6 +175,16 @@ amperage_meter_scale = (Imax - Imin) * 100000 / (Tmax + (Tmax * Tmax / 50))
= 205
amperage_meter_offset = Imin * 100 = 280
```
Measuring Imax requires a battery and an ESC that can both deliver and support max current for the duration of the measurement, so it's prone to big inaccuracies. Alternatively, current can be measured at a much lower throttle position and be taken into account in the calculations.
Following the previous example, if we measured an Ibench current of 6A at 30% of throttle (1255 in the motors tab because (0.3*(max_throttle-1000))+1000))
```
Tbench = Tmax * bench_throttle = 850 * 0.3 = 255
amperage_meter_scale = (Ibench - Imin) * 100000 / (Tbench + (Tbench * Tbench / 50))
= (6 - 2.8) * 100000 / (255 + (255 * 255 / 50))
= 205
amperage_meter_offset = Imin * 100 = 280
```
#### Tuning Using Battery Charger Measurement
If you cannot measure current draw directly, you can approximate it indirectly using your battery charger.
However, note it may be difficult to adjust `amperage_meter_offset` using this method unless you can

View File

@ -83,6 +83,7 @@ Click on a command to jump to the relevant documentation page.
| [`color`](LedStrip.md) | configure colors |
| `defaults` | reset to defaults and reboot |
| `dump` | print configurable settings in a pastable form |
| `dma` | configure direct memory access channel |
| `exit` | |
| `feature` | list or -val or val |
| `get` | get variable value |
@ -102,6 +103,7 @@ Click on a command to jump to the relevant documentation page.
| `serialpassthrough` | serial passthrough mode, reset board to exit |
| `set` | name=value or blank or * for list |
| `status` | show system status |
| `timer` | configure timer |
| `version` | show version |
| [`serial`](Serial.md) | configure serial ports |
| [`servo`](Mixer.md) | configure servos |
@ -177,9 +179,9 @@ Click on a variable to jump to the relevant documentation page.
| `hott_alarm_sound_interval` | Battery alarm delay in seconds for Hott telemetry | 0 | 120 | 5 | Master | UINT8 |
| [`bat_capacity`](Battery.md) | Battery capacity in mAH. This value is used in conjunction with the current meter to determine remaining battery capacity. | 0 | 20000 | 0 | Master | UINT16 |
| [`vbat_scale`](Battery.md) | Result is Vbatt in 0.1V steps. 3.3V = ADC Vref, 4095 = 12bit adc, 110 = 11:1 voltage divider (10k:1k) x 10 for 0.1V. Adjust this slightly if reported pack voltage is different from multimeter reading. You can get current voltage by typing "status" in cli. | 0 | 255 | 110 | Master | UINT8 |
| [`vbat_max_cell_voltage`](Battery.md) | Maximum voltage per cell, used for auto-detecting battery voltage in 0.1V units, default is 43 (4.3V) | 10 | 50 | 43 | Master | UINT8 |
| [`vbat_min_cell_voltage`](Battery.md) | Minimum voltage per cell, this triggers battery-critical alarms, in 0.1V units, default is 33 (3.3V) | 10 | 50 | 33 | Master | UINT8 |
| [`vbat_warning_cell_voltage`](Battery.md) | Warning voltage per cell, this triggers battery-warning alarms, in 0.1V units, default is 35 (3.5V) | 10 | 50 | 35 | Master | UINT8 |
| [`vbat_max_cell_voltage`](Battery.md) | Maximum voltage per cell, used for auto-detecting battery voltage in 0.01V units, default is 430 (4.3V) | 100 | 500 | 430 | Master | UINT16 |
| [`vbat_min_cell_voltage`](Battery.md) | Minimum voltage per cell, this triggers battery-critical alarms, in 0.01V units, default is 330 (3.3V) | 100 | 500 | 330 | Master | UINT16 |
| [`vbat_warning_cell_voltage`](Battery.md) | Warning voltage per cell, this triggers battery-warning alarms, in 0.01V units, default is 350 (3.5V) | 100 | 500 | 350 | Master | UINT16 |
| [`vbat_hysteresis`](Battery.md) | Sets the hysteresis value for low-battery alarms, in 0.01V units, default is 1 (0.01V) | 10 | 250 | 1 | Master | UINT8 |
| [`current_meter_scale`](Battery.md) | This sets the output voltage to current scaling for the current sensor in 0.1 mV/A steps. 400 is 40mV/A such as the ACS756 sensor outputs. 183 is the setting for the uberdistro with a 0.25mOhm shunt. | -10000 | 10000 | 400 | Master | INT16 |
| [`current_meter_offset`](Battery.md) | This sets the output offset voltage of the current sensor in millivolts. | 0 | 3300 | 0 | Master | UINT16 |

View File

@ -0,0 +1,16 @@
## Custom Board Configuration Using CLI
Warning: This section covers beyond the normal use-case for Betaflight. It's recommended that you use a pre-made target configuration for a flight controller that has been thoroughly tested before. Those configurations can be found in the [unified targets list](https://github.com/betaflight/unified-targets/tree/master/configs/default).
However, Betaflight can provide support for custom-made flight controllers, assuming it contains supported hardware. Before using Betaflight at all, it's highly recommended that you, first, check to make sure your MCU and your peripherals are supported. Checking the [Supported Sensors](https://github.com/betaflight/betaflight/wiki/Supported-Sensors) and [Hardware Reference](https://github.com/betaflight/betaflight/wiki/Hardware-Reference) wiki pages are good places to look. Betaflight is supported across many STM32 processors. Second, thoroughly test the functionality of your hardware by programming your board in an IDE (i.e. STMCube) or using any other hardware debugging methods/tools.
Assuming your hardware is supported and you've thoroughly tested your peripherals, flash generic firmware into your board. It's recommended to flash betaflight firmware using the configurator and [USB Flashing](https://github.com/betaflight/betaflight/blob/master/docs/USB%20Flashing.md).
To create your own configuration, you must first use the `resource` command in the command line interface to map your peripheral pins on your controller. Use the CLI documentation linked above and other wiki pages for command reference. Then, you use the `set` command to set the bus type, i2c address, lowpass filter frequency/type, baro_hardware, gyro_hardware, etc. This will mainly be dependent on your hardware and will involve a lot of trial and error. Use the `save` command to save your configuration and reboot the controller. If your hardware is not detected, then you need to modify or create more settings in the CLI to accurately describe your hardware to Betaflight to detect it. Use your custom board's and respective peripherals' datasheets and schematics to help you.
Alongside `resource` and `set` commands, there are `timer` and `dma` commands. For certain functions, such as battery voltage monitoring and motor output, you need to associate a timer and/or a Direct Memory Access (DMA) channel to those pins for those to work.
- `# timer`
- `timer <pin> AF(x) [1-3]`
- `# dma`
- `dma [SPI_TX|ADC|pin] [index|pin] [0|1]`
For custom configuration like this outside the normal use-case, it's recommended that you use the pre-made [configs](https://github.com/betaflight/unified-targets/tree/master/configs/default) as a source of reference.

View File

@ -25,11 +25,6 @@
#ifdef GENERATOR_I2S_PRESENT
#include "stm32h7xx_ll_rcc.h"
#endif /* GENERATOR_I2S_PRESENT*/
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif
/** @addtogroup STM32H7xx_LL_Driver
* @{

202
lib/main/google/olc/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,96 @@
Open Location Code
==================
[![Build Status](https://api.travis-ci.org/google/open-location-code.svg?branch=master)](https://travis-ci.org/google/open-location-code)
[![CDNJS](https://img.shields.io/cdnjs/v/openlocationcode.svg)](https://cdnjs.com/libraries/openlocationcode)
Open Location Code is a technology that gives a way of encoding location into a form that is
easier to use than latitude and longitude. The codes generated are called plus codes, as their
distinguishing attribute is that they include a "+" character.
The technology is designed to produce codes that can be used as a replacement for street addresses, especially
in places where buildings aren't numbered or streets aren't named.
Plus codes represent an area, not a point. As digits are added
to a code, the area shrinks, so a long code is more precise than a short
code.
Codes that are similar are located closer together than codes that are
different.
A location can be converted into a code, and a code can be converted back
to a location completely offline.
There are no data tables to lookup or online services required. The
algorithm is publicly available and can be used without restriction.
Links
-----
* [Demonstration site](http://plus.codes/)
* [Mailing list](https://groups.google.com/forum/#!forum/open-location-code)
* [Comparison of existing location encoding systems](https://github.com/google/open-location-code/wiki/Evaluation-of-Location-Encoding-Systems)
* [Open Location Code definition](https://github.com/google/open-location-code/blob/master/docs/olc_definition.adoc)
Description
-----------
Codes are made up of a sequence of digits chosen from a set of 20. The
digits in the code alternate between latitude and longitude. The first
four digits describe a one degree latitude by one degree longitude
area, aligned on degrees. Adding two further digits to the code,
reduces the area to 1/20th of a degree by 1/20th of a degree within the
previous area. And so on - each pair of digits reduces the area to
1/400th of the previous area.
As an example, the Parliament Buildings in Nairobi, Kenya are located at
6GCRPR6C+24. 6GCR is the area from 2S 36E to 1S 37E. PR6C+24 is a 14 meter
wide by 14 meter high area within 6GCR.
A "+" character is used after eight digits, to break the code up into two parts
and to distinguish codes from postal codes.
There will be locations where a 10 digit code is not sufficiently precise, but
refining it by a factor of 20 is i) unnecessarily precise and ii) requires extending
the code by two digits. Instead, after 10 digits, the area is divided
into a 4x5 grid and a single digit used to identify the grid square. A single
grid refinement step reduces the area to approximately 3.5x2.8 meters.
Codes can be shortened relative to a location. This reduces the number of digits
that must be remembered, by using a location to identify an approximate area,
and then generating the nearest matching code. Shortening a code, if possible,
will drop four or more digits from the start of the code. The degree to which a
code can be shortened depends on the proximity of the reference location.
If the reference location is derived from a town or city name, it is dependent
on the accuracy of the geocoding service. Although one service may place
"Zurich" close to the Google office, another may move it by a hundred meters or
more, and this could be enough to prevent the original code being recovered.
Rather than a large city size feature to generate the reference location, it is
better to use smaller, neighbourhood features, that will not have as much
variation in their geocode results.
Guidelines for shortening codes are in the [wiki](https://github.com/google/open-location-code/wiki).
Recovering shortened codes works by providing the short code and a reference
location. This does not need to be the same as the location used to shorten the
code, but it does need to be nearby. Shortened codes always include the "+"
character so it is simple to compute the missing component.
* 8F+GG is missing six leading characters
* 6C8F+GG is missing four leading characters
Example Code
------------
The subdirectories contain sample implementations and tests for different
languages. Each implementation provides the following functions:
* Test a code to see if it is a valid sequence
* Test a code to see if it is a valid full code
Not all valid sequences are valid full codes
* Encode a latitude and longitude to a standard accuracy
(14 meter by 14 meter) code
* Encode a latitude and longitude to a code of any length
* Decode a code to its coordinates: low, high and center
* Shorten a full code relative to a location
* Extend a short code relative to a location

View File

@ -0,0 +1,6 @@
// Be sure to add this to olc.c
// Ignore a bunch of warnings in this sloppy code
#pragma GCC diagnostic ignored "-Wdouble-promotion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wsign-compare"

View File

@ -0,0 +1,7 @@
Open Location Codes
Source from: https://github.com/google/open-location-code
Code use here is unaltered except for adding the following header to olc.c to ignore the numerous compiler warnings for sloppy code:
#include betaflight.h

608
lib/main/google/olc/olc.c Normal file
View File

@ -0,0 +1,608 @@
#include "olc.h"
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include "olc_private.h"
#include "betaflight.h"
#define CORRECT_IF_SEPARATOR(var, info) \
do { \
(var) += (info)->sep_first >= 0 ? 1 : 0; \
} while (0)
// Information about a code, produced by analyse();
typedef struct CodeInfo {
// Original code.
const char* code;
// Total count of characters in the code including padding and separators.
int size;
// Count of valid digits (not including padding or separators).
int len;
// Index of the first separator in the code.
int sep_first;
// Index of the last separator in the code. (If there is only one, same as
// sep_first.)
int sep_last;
// Index of the first padding character in the code.
int pad_first;
// Index of the last padding character in the code. (If there is only one,
// same as pad_first.)
int pad_last;
} CodeInfo;
// Helper functions
static int analyse(const char* code, size_t size, CodeInfo* info);
static int is_short(CodeInfo* info);
static int is_full(CodeInfo* info);
static int decode(CodeInfo* info, OLC_CodeArea* decoded);
static size_t code_length(CodeInfo* info);
static double pow_neg(double base, double exponent);
static double compute_latitude_precision(int length);
static double normalize_longitude(double lon_degrees);
static double adjust_latitude(double lat_degrees, size_t length);
void OLC_GetCenter(const OLC_CodeArea* area, OLC_LatLon* center) {
center->lat = area->lo.lat + (area->hi.lat - area->lo.lat) / 2.0;
if (center->lat > kLatMaxDegrees) {
center->lat = kLatMaxDegrees;
}
center->lon = area->lo.lon + (area->hi.lon - area->lo.lon) / 2.0;
if (center->lon > kLonMaxDegrees) {
center->lon = kLonMaxDegrees;
}
}
size_t OLC_CodeLength(const char* code, size_t size) {
CodeInfo info;
analyse(code, size, &info);
return code_length(&info);
}
int OLC_IsValid(const char* code, size_t size) {
CodeInfo info;
return analyse(code, size, &info) > 0;
}
int OLC_IsShort(const char* code, size_t size) {
CodeInfo info;
if (analyse(code, size, &info) <= 0) {
return 0;
}
return is_short(&info);
}
int OLC_IsFull(const char* code, size_t size) {
CodeInfo info;
if (analyse(code, size, &info) <= 0) {
return 0;
}
return is_full(&info);
}
int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
int maxlen) {
// Limit the maximum number of digits in the code.
if (length > kMaximumDigitCount) {
length = kMaximumDigitCount;
}
// Adjust latitude and longitude so they fall into positive ranges.
double latitude = adjust_latitude(location->lat, length);
double longitude = normalize_longitude(location->lon);
// Build up the code here, then copy it to the passed pointer.
char fullcode[] = "12345678901234567";
// Compute the code.
// This approach converts each value to an integer after multiplying it by
// the final precision. This allows us to use only integer operations, so
// avoiding any accumulation of floating point representation errors.
// Multiply values by their precision and convert to positive without any
// floating point operations.
long long int lat_val = kLatMaxDegrees * 2.5e7;
long long int lng_val = kLonMaxDegrees * 8.192e6;
lat_val += latitude * 2.5e7;
lng_val += longitude * 8.192e6;
size_t pos = kMaximumDigitCount;
// Compute the grid part of the code if necessary.
if (length > kPairCodeLength) {
for (size_t i = 0; i < kGridCodeLength; i++) {
int lat_digit = lat_val % kGridRows;
int lng_digit = lng_val % kGridCols;
int ndx = lat_digit * kGridCols + lng_digit;
fullcode[pos--] = kAlphabet[ndx];
// Note! Integer division.
lat_val /= kGridRows;
lng_val /= kGridCols;
}
} else {
lat_val /= pow(kGridRows, kGridCodeLength);
lng_val /= pow(kGridCols, kGridCodeLength);
}
pos = kPairCodeLength;
// Compute the pair section of the code.
for (size_t i = 0; i < kPairCodeLength / 2; i++) {
int lat_ndx = lat_val % kEncodingBase;
int lng_ndx = lng_val % kEncodingBase;
fullcode[pos--] = kAlphabet[lng_ndx];
fullcode[pos--] = kAlphabet[lat_ndx];
// Note! Integer division.
lat_val /= kEncodingBase;
lng_val /= kEncodingBase;
if (i == 0) {
fullcode[pos--] = kSeparator;
}
}
// Replace digits with padding if necessary.
if (length < kSeparatorPosition) {
for (size_t i = length; i < kSeparatorPosition; i++) {
fullcode[i] = kPaddingCharacter;
}
fullcode[kSeparatorPosition] = kSeparator;
}
// Now copy the full code digits into the buffer.
size_t char_count = length + 1;
if (kSeparatorPosition + 1 > char_count) {
char_count = kSeparatorPosition + 1;
}
for (size_t i = 0; i < char_count; i++) {
code[i] = fullcode[i];
}
// Terminate the buffer.
code[char_count] = '\0';
return char_count;
}
int OLC_EncodeDefault(const OLC_LatLon* location, char* code, int maxlen) {
return OLC_Encode(location, kPairCodeLength, code, maxlen);
}
int OLC_Decode(const char* code, size_t size, OLC_CodeArea* decoded) {
CodeInfo info;
if (analyse(code, size, &info) <= 0) {
return 0;
}
return decode(&info, decoded);
}
int OLC_Shorten(const char* code, size_t size, const OLC_LatLon* reference,
char* shortened, int maxlen) {
CodeInfo info;
if (analyse(code, size, &info) <= 0) {
return 0;
}
if (info.pad_first > 0) {
return 0;
}
if (!is_full(&info)) {
return 0;
}
OLC_CodeArea code_area;
decode(&info, &code_area);
OLC_LatLon center;
OLC_GetCenter(&code_area, &center);
// Ensure that latitude and longitude are valid.
double lat = adjust_latitude(reference->lat, info.len);
double lon = normalize_longitude(reference->lon);
// How close are the latitude and longitude to the code center.
double alat = fabs(center.lat - lat);
double alon = fabs(center.lon - lon);
double range = alat > alon ? alat : alon;
// Yes, magic numbers... sob.
int start = 0;
const double safety_factor = 0.3;
const int removal_lengths[3] = {8, 6, 4};
for (int j = 0; j < sizeof(removal_lengths) / sizeof(removal_lengths[0]);
++j) {
// Check if we're close enough to shorten. The range must be less than
// 1/2 the resolution to shorten at all, and we want to allow some
// safety, so use 0.3 instead of 0.5 as a multiplier.
int removal_length = removal_lengths[j];
double area_edge =
compute_latitude_precision(removal_length) * safety_factor;
if (range < area_edge) {
start = removal_length;
break;
}
}
int pos = 0;
for (int j = start; j < info.size && code[j] != '\0'; ++j) {
shortened[pos++] = code[j];
}
shortened[pos] = '\0';
return pos;
}
int OLC_RecoverNearest(const char* short_code, size_t size,
const OLC_LatLon* reference, char* code, int maxlen) {
CodeInfo info;
if (analyse(short_code, size, &info) <= 0) {
return 0;
}
// Check if it is a full code - then we just convert to upper case.
if (is_full(&info)) {
OLC_CodeArea code_area;
decode(&info, &code_area);
OLC_LatLon center;
OLC_GetCenter(&code_area, &center);
return OLC_Encode(&center, code_area.len, code, maxlen);
}
if (!is_short(&info)) {
return 0;
}
int len = code_length(&info);
// Ensure that latitude and longitude are valid.
double lat = adjust_latitude(reference->lat, len);
double lon = normalize_longitude(reference->lon);
// Compute the number of digits we need to recover.
size_t padding_length = kSeparatorPosition;
if (info.sep_first >= 0) {
padding_length -= info.sep_first;
}
// The resolution (height and width) of the padded area in degrees.
double resolution = pow_neg(kEncodingBase, 2.0 - (padding_length / 2.0));
// Distance from the center to an edge (in degrees).
double half_res = resolution / 2.0;
// Use the reference location to pad the supplied short code and decode it.
OLC_LatLon latlon = {lat, lon};
char encoded[256];
OLC_EncodeDefault(&latlon, encoded, 256);
char new_code[256];
int pos = 0;
for (int j = 0; encoded[j] != '\0'; ++j) {
if (j >= padding_length) {
break;
}
new_code[pos++] = encoded[j];
}
for (int j = 0; j < info.size && short_code[j] != '\0'; ++j) {
new_code[pos++] = short_code[j];
}
new_code[pos] = '\0';
if (analyse(new_code, pos, &info) <= 0) {
return 0;
}
OLC_CodeArea code_area;
decode(&info, &code_area);
OLC_LatLon center;
OLC_GetCenter(&code_area, &center);
// How many degrees latitude is the code from the reference?
if (lat + half_res < center.lat &&
center.lat - resolution > -kLatMaxDegrees) {
// If the proposed code is more than half a cell north of the reference
// location, it's too far, and the best match will be one cell south.
center.lat -= resolution;
} else if (lat - half_res > center.lat &&
center.lat + resolution < kLatMaxDegrees) {
// If the proposed code is more than half a cell south of the reference
// location, it's too far, and the best match will be one cell north.
center.lat += resolution;
}
// How many degrees longitude is the code from the reference?
if (lon + half_res < center.lon) {
center.lon -= resolution;
} else if (lon - half_res > center.lon) {
center.lon += resolution;
}
return OLC_Encode(&center, len + padding_length, code, maxlen);
}
// private functions
static int analyse(const char* code, size_t size, CodeInfo* info) {
memset(info, 0, sizeof(CodeInfo));
// null code is not valid
if (!code) {
return 0;
}
if (!size) {
size = strlen(code);
}
info->code = code;
info->size = size < kMaximumDigitCount ? size : kMaximumDigitCount;
info->sep_first = -1;
info->sep_last = -1;
info->pad_first = -1;
info->pad_last = -1;
int j = 0;
for (j = 0; j <= size && code[j] != '\0'; ++j) {
int ok = 0;
// if this is a padding character, remember it
if (!ok && code[j] == kPaddingCharacter) {
if (info->pad_first < 0) {
info->pad_first = j;
}
info->pad_last = j;
ok = 1;
}
// if this is a separator character, remember it
if (!ok && code[j] == kSeparator) {
if (info->sep_first < 0) {
info->sep_first = j;
}
info->sep_last = j;
ok = 1;
}
// only accept characters in the valid character set
if (!ok && get_alphabet_position(code[j]) >= 0) {
ok = 1;
}
// didn't find anything expected => bail out
if (!ok) {
return 0;
}
}
// so far, code only has valid characters -- good
info->len = j < kMaximumDigitCount ? j : kMaximumDigitCount;
// Cannot be empty
if (info->len <= 0) {
return 0;
}
// The separator is required.
if (info->sep_first < 0) {
return 0;
}
// There can be only one... separator.
if (info->sep_last > info->sep_first) {
return 0;
}
// separator cannot be the only character
if (info->len == 1) {
return 0;
}
// Is the separator in an illegal position?
if (info->sep_first > kSeparatorPosition || (info->sep_first % 2)) {
return 0;
}
// padding cannot be at the initial position
if (info->pad_first == 0) {
return 0;
}
// We can have an even number of padding characters before the separator,
// but then it must be the final character.
if (info->pad_first > 0) {
// Short codes cannot have padding
if (info->sep_first < kSeparatorPosition) {
return 0;
}
// The first padding character needs to be in an odd position.
if (info->pad_first % 2) {
return 0;
}
// With padding, the separator must be the final character
if (info->sep_last < info->len - 1) {
return 0;
}
// After removing padding characters, we mustn't have anything left.
if (info->pad_last < info->sep_first - 1) {
return 0;
}
}
// If there are characters after the separator, make sure there isn't just
// one of them (not legal).
if (info->len - info->sep_first - 1 == 1) {
return 0;
}
return info->len;
}
static int is_short(CodeInfo* info) {
if (info->len <= 0) {
return 0;
}
// if there is a separator, it cannot be beyond the valid position
if (info->sep_first >= kSeparatorPosition) {
return 0;
}
return 1;
}
// checks that the first character of latitude or longitude is valid
static int valid_first_character(CodeInfo* info, int pos, double kMax) {
if (info->len <= pos) {
return 1;
}
// Work out what the first character indicates
size_t firstValue = get_alphabet_position(info->code[pos]);
firstValue *= kEncodingBase;
return firstValue < kMax;
}
static int is_full(CodeInfo* info) {
if (info->len <= 0) {
return 0;
}
// If there are less characters than expected before the separator.
if (info->sep_first < kSeparatorPosition) {
return 0;
}
// check first latitude character, if any
if (!valid_first_character(info, 0, kLatMaxDegreesT2)) {
return 0;
}
// check first longitude character, if any
if (!valid_first_character(info, 1, kLonMaxDegreesT2)) {
return 0;
}
return 1;
}
static int decode(CodeInfo* info, OLC_CodeArea* decoded) {
// Create a copy of the code, skipping padding and separators.
char clean_code[256];
int ci = 0;
for (size_t i = 0; i < info->len + 1; i++) {
if (info->code[i] != kPaddingCharacter && info->code[i] != kSeparator) {
clean_code[ci] = info->code[i];
ci++;
}
}
clean_code[ci] = '\0';
// Initialise the values for each section. We work them out as integers and
// convert them to floats at the end. Using doubles all the way results in
// multiplying small rounding errors until they become significant.
int normal_lat = -kLatMaxDegrees * kPairPrecisionInverse;
int normal_lng = -kLonMaxDegrees * kPairPrecisionInverse;
int extra_lat = 0;
int extra_lng = 0;
// How many digits do we have to process?
size_t digits = strlen(clean_code) < kPairCodeLength ? strlen(clean_code)
: kPairCodeLength;
// Define the place value for the most significant pair.
int pv = pow(kEncodingBase, kPairCodeLength / 2);
for (size_t i = 0; i < digits - 1; i += 2) {
pv /= kEncodingBase;
normal_lat += get_alphabet_position(clean_code[i]) * pv;
normal_lng += get_alphabet_position(clean_code[i + 1]) * pv;
}
// Convert the place value to a float in degrees.
double lat_precision = (double)pv / kPairPrecisionInverse;
double lng_precision = (double)pv / kPairPrecisionInverse;
// Process any extra precision digits.
if (strlen(clean_code) > kPairCodeLength) {
// How many digits do we have to process?
digits = strlen(clean_code) < kMaximumDigitCount ? strlen(clean_code)
: kMaximumDigitCount;
// Initialise the place values for the grid.
int row_pv = pow(kGridRows, kGridCodeLength);
int col_pv = pow(kGridCols, kGridCodeLength);
for (size_t i = kPairCodeLength; i < digits; i++) {
row_pv /= kGridRows;
col_pv /= kGridCols;
int dval = get_alphabet_position(clean_code[i]);
int row = dval / kGridCols;
int col = dval % kGridCols;
extra_lat += row * row_pv;
extra_lng += col * col_pv;
}
// Adjust the precisions from the integer values to degrees.
lat_precision = (double)row_pv / kGridLatPrecisionInverse;
lng_precision = (double)col_pv / kGridLonPrecisionInverse;
}
// Merge the values from the normal and extra precision parts of the code.
// Everything is ints so they all need to be cast to floats.
double lat = (double)normal_lat / kPairPrecisionInverse +
(double)extra_lat / kGridLatPrecisionInverse;
double lng = (double)normal_lng / kPairPrecisionInverse +
(double)extra_lng / kGridLonPrecisionInverse;
decoded->lo.lat = lat;
decoded->lo.lon = lng;
decoded->hi.lat = lat + lat_precision;
decoded->hi.lon = lng + lng_precision;
decoded->len = strlen(clean_code);
return decoded->len;
}
static size_t code_length(CodeInfo* info) {
int len = info->len;
if (info->sep_first >= 0) {
--len;
}
if (info->pad_first >= 0) {
len = info->pad_first;
}
return len;
}
// Raises a number to an exponent, handling negative exponents.
static double pow_neg(double base, double exponent) {
if (exponent == 0) {
return 1;
}
if (exponent > 0) {
return pow(base, exponent);
}
return 1 / pow(base, -exponent);
}
// Compute the latitude precision value for a given code length. Lengths <= 10
// have the same precision for latitude and longitude, but lengths > 10 have
// different precisions due to the grid method having fewer columns than rows.
static double compute_latitude_precision(int length) {
// Magic numbers!
if (length <= kPairCodeLength) {
return pow_neg(kEncodingBase, floor((length / -2) + 2));
}
return pow_neg(kEncodingBase, -3) / pow(kGridRows, length - kPairCodeLength);
}
// Normalize a longitude into the range -180 to 180, not including 180.
static double normalize_longitude(double lon_degrees) {
while (lon_degrees < -kLonMaxDegrees) {
lon_degrees += kLonMaxDegreesT2;
}
while (lon_degrees >= kLonMaxDegrees) {
lon_degrees -= kLonMaxDegreesT2;
}
return lon_degrees;
}
// Adjusts 90 degree latitude to be lower so that a legal OLC code can be
// generated.
static double adjust_latitude(double lat_degrees, size_t length) {
if (lat_degrees < -kLatMaxDegrees) {
lat_degrees = -kLatMaxDegrees;
}
if (lat_degrees > kLatMaxDegrees) {
lat_degrees = kLatMaxDegrees;
}
if (lat_degrees < kLatMaxDegrees) {
return lat_degrees;
}
// Subtract half the code precision to get the latitude into the code area.
double precision = compute_latitude_precision(length);
return lat_degrees - precision / 2;
}

75
lib/main/google/olc/olc.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef OLC_OPENLOCATIONCODE_H_
#define OLC_OPENLOCATIONCODE_H_
#include <stdlib.h>
#define OLC_VERSION_MAJOR 1
#define OLC_VERSION_MINOR 0
#define OLC_VERSION_PATCH 0
// OLC version number: 2.3.4 => 2003004
// Useful for checking against a particular version or above:
//
// #if OLC_VERSION_NUM < OLC_MAKE_VERSION_NUM(1, 0, 2)
// #error UNSUPPORTED OLC VERSION
// #endif
#define OLC_MAKE_VERSION_NUM(major, minor, patch) \
((major * 1000 + minor) * 1000 + patch)
// OLC version string: 2.3.4 => "2.3.4"
#define OLC_MAKE_VERSION_STR_IMPL(major, minor, patch) \
(#major "." #minor "." #patch)
#define OLC_MAKE_VERSION_STR(major, minor, patch) \
OLC_MAKE_VERSION_STR_IMPL(major, minor, patch)
// Current version, as a number and a string
#define OLC_VERSION_NUM \
OLC_MAKE_VERSION_NUM(OLC_VERSION_MAJOR, OLC_VERSION_MINOR, OLC_VERSION_PATCH)
#define OLC_VERSION_STR \
OLC_MAKE_VERSION_STR(OLC_VERSION_MAJOR, OLC_VERSION_MINOR, OLC_VERSION_PATCH)
// A pair of doubles representing latitude / longitude
typedef struct OLC_LatLon {
double lat;
double lon;
} OLC_LatLon;
// An area defined by two corners (lo and hi) and a code length
typedef struct OLC_CodeArea {
OLC_LatLon lo;
OLC_LatLon hi;
size_t len;
} OLC_CodeArea;
// Get the center coordinates for an area
void OLC_GetCenter(const OLC_CodeArea* area, OLC_LatLon* center);
// Get the effective length for a code
size_t OLC_CodeLength(const char* code, size_t size);
// Check for the three obviously-named conditions
int OLC_IsValid(const char* code, size_t size);
int OLC_IsShort(const char* code, size_t size);
int OLC_IsFull(const char* code, size_t size);
// Encode location with given code length (indicates precision) into an OLC
// Return the string length of the code
int OLC_Encode(const OLC_LatLon* location, size_t code_length, char* code,
int maxlen);
// Encode location with default code length into an OLC
// Return the string length of the code
int OLC_EncodeDefault(const OLC_LatLon* location, char* code, int maxlen);
// Decode OLC into the original location
int OLC_Decode(const char* code, size_t size, OLC_CodeArea* decoded);
// Compute a (shorter) OLC for a given code and a reference location
int OLC_Shorten(const char* code, size_t size, const OLC_LatLon* reference,
char* buf, int maxlen);
// Given shorter OLC and reference location, compute original (full length) OLC
int OLC_RecoverNearest(const char* short_code, size_t size,
const OLC_LatLon* reference, char* code, int maxlen);
#endif

View File

@ -0,0 +1,69 @@
/*
* We place these static definitions on a separate header file so that we can
* include the file in both the library and the tests.
*/
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <memory.h>
#define OLC_kEncodingBase 20
#define OLC_kGridCols 4
#define OLC_kLatMaxDegrees 90
#define OLC_kLonMaxDegrees 180
// Separates the first eight digits from the rest of the code.
static const char kSeparator = '+';
// Used to indicate null values before the separator.
static const char kPaddingCharacter = '0';
// Digits used in the codes.
static const char kAlphabet[] = "23456789CFGHJMPQRVWX";
// Number of digits in the alphabet.
static const size_t kEncodingBase = OLC_kEncodingBase;
// The max number of digits returned in a plus code. Roughly 1 x 0.5 cm.
static const size_t kMaximumDigitCount = 15;
// The number of code characters that are lat/lng pairs.
static const size_t kPairCodeLength = 10;
// The number of characters that combine lat and lng into a grid.
// kMaximumDigitCount - kPairCodeLength
static const size_t kGridCodeLength = 5;
// The number of columns in each grid step.
static const size_t kGridCols = OLC_kGridCols;
// The number of rows in each grid step.
static const size_t kGridRows = OLC_kEncodingBase / OLC_kGridCols;
// The number of digits before the separator.
static const size_t kSeparatorPosition = 8;
// Inverse of the precision of the last pair digits (in degrees).
static const size_t kPairPrecisionInverse = 8000;
// Inverse (1/) of the precision of the final grid digits in degrees.
// Latitude is kEncodingBase^3 * kGridRows^kGridCodeLength
static const size_t kGridLatPrecisionInverse = 2.5e7;
// Longitude is kEncodingBase^3 * kGridColumns^kGridCodeLength
static const size_t kGridLonPrecisionInverse = 8.192e6;
// Latitude bounds are -kLatMaxDegrees degrees and +kLatMaxDegrees degrees
// which we transpose to 0 and 180 degrees.
static const double kLatMaxDegrees = OLC_kLatMaxDegrees;
static const double kLatMaxDegreesT2 = 2 * OLC_kLatMaxDegrees;
// Longitude bounds are -kLonMaxDegrees degrees and +kLonMaxDegrees degrees
// which we transpose to 0 and 360 degrees.
static const double kLonMaxDegrees = OLC_kLonMaxDegrees;
static const double kLonMaxDegreesT2 = 2 * OLC_kLonMaxDegrees;
// Lookup table of the alphabet positions of characters 'C' through 'X',
// inclusive. A value of -1 means the character isn't part of the alphabet.
static const int kPositionLUT['X' - 'C' + 1] = {
8, -1, -1, 9, 10, 11, -1, 12, -1, -1, 13,
-1, -1, 14, 15, 16, -1, -1, -1, 17, 18, 19,
};
// Returns the position of a char in the encoding alphabet, or -1 if invalid.
static int get_alphabet_position(char c) {
char uc = toupper(c);
// We use a lookup table for performance reasons.
if (uc >= 'C' && uc <= 'X') return kPositionLUT[uc - 'C'];
if (uc >= 'c' && uc <= 'x') return kPositionLUT[uc - 'c'];
if (uc >= '2' && uc <= '9') return uc - '2';
return -1;
}

View File

@ -130,13 +130,16 @@ ARCH_FLAGS = -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu
DEVICE_FLAGS = -DUSE_HAL_DRIVER -DUSE_FULL_LL_DRIVER -DUSE_DMA_RAM -DMAX_MPU_REGIONS=16
# G4X3_TARGETS includes G47{3,4}{RE,CE,CEU}
# G47X_TARGETS includes G47{3,4}{RE,CE,CEU}
ifeq ($(TARGET),$(filter $(TARGET),$(G4X3_TARGETS)))
ifeq ($(TARGET),$(filter $(TARGET),$(G47X_TARGETS)))
DEVICE_FLAGS += -DSTM32G474xx
LD_SCRIPT = $(LINKER_DIR)/stm32_flash_g474.ld
STARTUP_SRC = startup_stm32g474xx.s
MCU_FLASH_SIZE = 512
# Override the OPTIMISE_SPEED compiler setting to save flash space on these 512KB targets.
# Performance is only slightly affected but around 50 kB of flash are saved.
OPTIMISE_SPEED = -O2
else
$(error Unknown MCU for G4 target)
endif

View File

@ -447,3 +447,12 @@ endif
# Search path and source files for the ST stdperiph library
VPATH := $(VPATH):$(STDPERIPH_DIR)/src
# Search path and source files for the Open Location Code library
OLC_DIR = $(ROOT)/lib/main/google/olc
ifneq ($(OLC_DIR),)
INCLUDE_DIRS += $(OLC_DIR)
SRC += $(OLC_DIR)/olc.c
SIZE_OPTIMISED_SRC += $(OLC_DIR)/olc.c
endif

View File

@ -18,7 +18,7 @@ endif
F4_TARGETS := $(F405_TARGETS) $(F411_TARGETS) $(F446_TARGETS)
F7_TARGETS := $(F7X2RE_TARGETS) $(F7X5XE_TARGETS) $(F7X5XG_TARGETS) $(F7X5XI_TARGETS) $(F7X6XG_TARGETS)
G4_TARGETS := $(G4X3_TARGETS)
G4_TARGETS := $(G47X_TARGETS)
H7_TARGETS := $(H743xI_TARGETS) $(H750xB_TARGETS) $(H7A3xI_TARGETS) $(H7A3xIQ_TARGETS) $(H723xG_TARGETS) $(H725xG_TARGETS)
ifeq ($(filter $(TARGET),$(VALID_TARGETS)),)
@ -26,7 +26,7 @@ $(error Target '$(TARGET)' is not valid, must be one of $(VALID_TARGETS). Have y
endif
ifeq ($(filter $(TARGET),$(F1_TARGETS) $(F3_TARGETS) $(F4_TARGETS) $(F7_TARGETS) $(G4_TARGETS) $(H7_TARGETS) $(SITL_TARGETS)),)
$(error Target '$(TARGET)' has not specified a valid STM group, must be one of F1, F3, F405, F411, F446, F7X2RE, F7X5XE, F7X5XG, F7X5XI, F7X6XG, G4X3 or H7X3XI. Have you prepared a valid target.mk?)
$(error Target '$(TARGET)' has not specified a valid STM group, must be one of F1, F3, F405, F411, F446, F7X2RE, F7X5XE, F7X5XG, F7X5XI, F7X6XG, G47X or H7X3XI. Have you prepared a valid target.mk?)
endif
ifeq ($(TARGET),$(filter $(TARGET),$(F3_TARGETS)))

View File

@ -82,7 +82,8 @@ UNSUPPORTED_TARGETS := \
UNIFIED_TARGETS := STM32F405 \
STM32F411 \
STM32F7X2 \
STM32F745
STM32F745 \
STM32G47X
# Legacy targets are targets that have been replaced by Unified Target configurations
LEGACY_TARGETS := MATEKF405 \

View File

@ -12,10 +12,12 @@
/* Specify the memory areas. */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 4K
FLASH1 (rx) : ORIGIN = 0x08001000, LENGTH = 492K
FLASH_CONFIG (r) : ORIGIN = 0x0807C000, LENGTH = 8K
FLASH_CUSTOM_DEFAULTS (r) : ORIGIN = 0x0807E000, LENGTH = 8K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 10K
FLASH_CUSTOM_DEFAULTS (r) : ORIGIN = 0x08002800, LENGTH = 6K
FLASH_CONFIG (r) : ORIGIN = 0x08004000, LENGTH = 8K
FLASH1 (rx) : ORIGIN = 0x08006000, LENGTH = DEFINED(USE_CUSTOM_DEFAULTS_EXTENDED) ? 480K : 488K
FLASH_CUSTOM_DEFAULTS_EXTENDED (r): ORIGIN = DEFINED(USE_CUSTOM_DEFAULTS_EXTENDED) ? 0x0807E000 : 0x08080000, LENGTH = DEFINED(USE_CUSTOM_DEFAULTS_EXTENDED) ? 8K : 0K
SYSTEM_MEMORY (r) : ORIGIN = 0x1FFF0000, LENGTH = 64K

View File

@ -1035,15 +1035,15 @@ static void writeGPSFrame(timeUs_t currentTimeUs)
}
blackboxWriteUnsignedVB(gpsSol.numSat);
blackboxWriteSignedVB(gpsSol.llh.lat - gpsHistory.GPS_home[LAT]);
blackboxWriteSignedVB(gpsSol.llh.lon - gpsHistory.GPS_home[LON]);
blackboxWriteSignedVB(gpsSol.llh.lat - gpsHistory.GPS_home[GPS_LATITUDE]);
blackboxWriteSignedVB(gpsSol.llh.lon - gpsHistory.GPS_home[GPS_LONGITUDE]);
blackboxWriteUnsignedVB(gpsSol.llh.altCm / 10); // was originally designed to transport meters in int16, but +-3276.7m is a good compromise
blackboxWriteUnsignedVB(gpsSol.groundSpeed);
blackboxWriteUnsignedVB(gpsSol.groundCourse);
gpsHistory.GPS_numSat = gpsSol.numSat;
gpsHistory.GPS_coord[LAT] = gpsSol.llh.lat;
gpsHistory.GPS_coord[LON] = gpsSol.llh.lon;
gpsHistory.GPS_coord[GPS_LATITUDE] = gpsSol.llh.lat;
gpsHistory.GPS_coord[GPS_LONGITUDE] = gpsSol.llh.lon;
}
#endif
@ -1461,8 +1461,6 @@ static bool blackboxWriteSysinfo(void)
BLACKBOX_PRINT_HEADER_LINE("rc_smoothing_cutoffs", "%d, %d", rcSmoothingData->inputCutoffSetting,
rcSmoothingData->derivativeCutoffSetting);
BLACKBOX_PRINT_HEADER_LINE("rc_smoothing_auto_factor", "%d", rcSmoothingData->autoSmoothnessFactor);
BLACKBOX_PRINT_HEADER_LINE("rc_smoothing_filter_type", "%d, %d", rcSmoothingData->inputFilterType,
rcSmoothingData->derivativeFilterType);
BLACKBOX_PRINT_HEADER_LINE("rc_smoothing_active_cutoffs", "%d, %d", rcSmoothingData->inputCutoffFrequency,
rcSmoothingData->derivativeCutoffFrequency);
BLACKBOX_PRINT_HEADER_LINE("rc_smoothing_rx_average", "%d", rcSmoothingData->averageFrameTimeUs);
@ -1639,8 +1637,8 @@ STATIC_UNIT_TESTED void blackboxLogIteration(timeUs_t currentTimeUs)
writeGPSHomeFrame();
writeGPSFrame(currentTimeUs);
} else if (gpsSol.numSat != gpsHistory.GPS_numSat
|| gpsSol.llh.lat != gpsHistory.GPS_coord[LAT]
|| gpsSol.llh.lon != gpsHistory.GPS_coord[LON]) {
|| gpsSol.llh.lat != gpsHistory.GPS_coord[GPS_LATITUDE]
|| gpsSol.llh.lon != gpsHistory.GPS_coord[GPS_LONGITUDE]) {
//We could check for velocity changes as well but I doubt it changes independent of position
writeGPSFrame(currentTimeUs);
}

View File

@ -4967,29 +4967,18 @@ static void cliRcSmoothing(const char *cmdName, char *cmdline)
cliPrintLinef("%d.%03dms", avgRxFrameUs / 1000, avgRxFrameUs % 1000);
}
}
cliPrintLinef("# Input filter type: %s", lookupTables[TABLE_RC_SMOOTHING_INPUT_TYPE].values[rcSmoothingData->inputFilterType]);
cliPrintf("# Active input cutoff: %dhz ", rcSmoothingData->inputCutoffFrequency);
if (rcSmoothingData->inputCutoffSetting == 0) {
cliPrintLine("(auto)");
} else {
cliPrintLine("(manual)");
}
cliPrintf("# Derivative filter type: %s", lookupTables[TABLE_RC_SMOOTHING_DERIVATIVE_TYPE].values[rcSmoothingData->derivativeFilterType]);
if (rcSmoothingData->derivativeFilterTypeSetting == RC_SMOOTHING_DERIVATIVE_AUTO) {
cliPrintLine(" (auto)");
} else {
cliPrintLinefeed();
}
cliPrintf("# Active derivative cutoff: %dhz (", rcSmoothingData->derivativeCutoffFrequency);
if (rcSmoothingData->derivativeFilterType == RC_SMOOTHING_DERIVATIVE_OFF) {
cliPrintLine("off)");
} else {
if (rcSmoothingData->derivativeCutoffSetting == 0) {
cliPrintLine("auto)");
} else {
cliPrintLine("manual)");
}
}
} else {
cliPrintLine("INTERPOLATION");
}

View File

@ -412,12 +412,6 @@ static const char * const lookupTableRcSmoothingType[] = {
static const char * const lookupTableRcSmoothingDebug[] = {
"ROLL", "PITCH", "YAW", "THROTTLE"
};
static const char * const lookupTableRcSmoothingInputType[] = {
"PT1", "BIQUAD"
};
static const char * const lookupTableRcSmoothingDerivativeType[] = {
"OFF", "PT1", "BIQUAD", "AUTO"
};
#endif // USE_RC_SMOOTHING_FILTER
#ifdef USE_VTX_COMMON
@ -605,8 +599,6 @@ const lookupTableEntry_t lookupTables[] = {
#ifdef USE_RC_SMOOTHING_FILTER
LOOKUP_TABLE_ENTRY(lookupTableRcSmoothingType),
LOOKUP_TABLE_ENTRY(lookupTableRcSmoothingDebug),
LOOKUP_TABLE_ENTRY(lookupTableRcSmoothingInputType),
LOOKUP_TABLE_ENTRY(lookupTableRcSmoothingDerivativeType),
#endif // USE_RC_SMOOTHING_FILTER
#ifdef USE_VTX_COMMON
LOOKUP_TABLE_ENTRY(lookupTableVtxLowPowerDisarm),
@ -750,8 +742,6 @@ const clivalue_t valueTable[] = {
{ "rc_smoothing_input_hz", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, UINT8_MAX }, PG_RX_CONFIG, offsetof(rxConfig_t, rc_smoothing_input_cutoff) },
{ "rc_smoothing_derivative_hz", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, UINT8_MAX }, PG_RX_CONFIG, offsetof(rxConfig_t, rc_smoothing_derivative_cutoff) },
{ "rc_smoothing_debug_axis", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_RC_SMOOTHING_DEBUG }, PG_RX_CONFIG, offsetof(rxConfig_t, rc_smoothing_debug_axis) },
{ "rc_smoothing_input_type", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_RC_SMOOTHING_INPUT_TYPE }, PG_RX_CONFIG, offsetof(rxConfig_t, rc_smoothing_input_type) },
{ "rc_smoothing_derivative_type",VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_RC_SMOOTHING_DERIVATIVE_TYPE }, PG_RX_CONFIG, offsetof(rxConfig_t, rc_smoothing_derivative_type) },
{ "rc_smoothing_auto_smoothness",VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { RC_SMOOTHING_AUTO_FACTOR_MIN, RC_SMOOTHING_AUTO_FACTOR_MAX }, PG_RX_CONFIG, offsetof(rxConfig_t, rc_smoothing_auto_factor) },
#endif // USE_RC_SMOOTHING_FILTER
@ -1174,6 +1164,7 @@ const clivalue_t valueTable[] = {
{ "ff_interpolate_sp", VAR_UINT8 | PROFILE_VALUE | MODE_LOOKUP, .config.lookup = {TABLE_INTERPOLATED_SP}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_interpolate_sp) },
{ "ff_max_rate_limit", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 150}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_max_rate_limit) },
{ "ff_smooth_factor", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 75}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_smooth_factor) },
{ "ff_jitter_reduction", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 20}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_jitter_factor) },
#endif
{ "ff_boost", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = { 0, 50 }, PG_PID_PROFILE, offsetof(pidProfile_t, ff_boost) },
@ -1326,7 +1317,7 @@ const clivalue_t valueTable[] = {
{ "osd_link_quality_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 100 }, PG_OSD_CONFIG, offsetof(osdConfig_t, link_quality_alarm) },
#endif
#ifdef USE_RX_RSSI_DBM
{ "osd_rssi_dbm_alarm", VAR_INT16 | MASTER_VALUE, .config.minmaxUnsigned = { CRSF_RSSI_MIN, CRSF_SNR_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, rssi_dbm_alarm) },
{ "osd_rssi_dbm_alarm", VAR_INT16 | MASTER_VALUE, .config.minmax = { CRSF_RSSI_MIN, CRSF_SNR_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, rssi_dbm_alarm) },
#endif
{ "osd_cap_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 20000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, cap_alarm) },
{ "osd_alt_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 10000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, alt_alarm) },
@ -1352,6 +1343,9 @@ const clivalue_t valueTable[] = {
#ifdef USE_RX_LINK_QUALITY_INFO
{ "osd_link_quality_pos", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, OSD_POSCFG_MAX }, PG_OSD_ELEMENT_CONFIG, offsetof(osdElementConfig_t, item_pos[OSD_LINK_QUALITY]) },
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
{ "osd_link_tx_power_pos", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, OSD_POSCFG_MAX }, PG_OSD_ELEMENT_CONFIG, offsetof(osdElementConfig_t, item_pos[OSD_TX_UPLINK_POWER]) },
#endif
#ifdef USE_RX_RSSI_DBM
{ "osd_rssi_dbm_pos", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, OSD_POSCFG_MAX }, PG_OSD_ELEMENT_CONFIG, offsetof(osdElementConfig_t, item_pos[OSD_RSSI_DBM_VALUE]) },
#endif

View File

@ -112,8 +112,6 @@ typedef enum {
#ifdef USE_RC_SMOOTHING_FILTER
TABLE_RC_SMOOTHING_TYPE,
TABLE_RC_SMOOTHING_DEBUG,
TABLE_RC_SMOOTHING_INPUT_TYPE,
TABLE_RC_SMOOTHING_DERIVATIVE_TYPE,
#endif // USE_RC_SMOOTHING_FILTER
#ifdef USE_VTX_COMMON
TABLE_VTX_LOW_POWER_DISARM,

View File

@ -519,6 +519,7 @@ static uint8_t cmsx_iterm_relax_cutoff;
#ifdef USE_INTERPOLATED_SP
static uint8_t cmsx_ff_interpolate_sp;
static uint8_t cmsx_ff_smooth_factor;
static uint8_t cmsx_ff_jitter_factor;
#endif
static const void *cmsx_profileOtherOnEnter(displayPort_t *pDisp)
@ -561,6 +562,7 @@ static const void *cmsx_profileOtherOnEnter(displayPort_t *pDisp)
#ifdef USE_INTERPOLATED_SP
cmsx_ff_interpolate_sp = pidProfile->ff_interpolate_sp;
cmsx_ff_smooth_factor = pidProfile->ff_smooth_factor;
cmsx_ff_jitter_factor = pidProfile->ff_jitter_factor;
#endif
#ifdef USE_BATTERY_VOLTAGE_SAG_COMPENSATION
@ -608,6 +610,7 @@ static const void *cmsx_profileOtherOnExit(displayPort_t *pDisp, const OSD_Entry
#ifdef USE_INTERPOLATED_SP
pidProfile->ff_interpolate_sp = cmsx_ff_interpolate_sp;
pidProfile->ff_smooth_factor = cmsx_ff_smooth_factor;
pidProfile->ff_jitter_factor = cmsx_ff_jitter_factor;
#endif
#ifdef USE_BATTERY_VOLTAGE_SAG_COMPENSATION
@ -625,6 +628,7 @@ static const OSD_Entry cmsx_menuProfileOtherEntries[] = {
#ifdef USE_INTERPOLATED_SP
{ "FF MODE", OME_TAB, NULL, &(OSD_TAB_t) { &cmsx_ff_interpolate_sp, 4, lookupTableInterpolatedSetpoint}, 0 },
{ "FF SMOOTHNESS", OME_UINT8, NULL, &(OSD_UINT8_t) { &cmsx_ff_smooth_factor, 0, 75, 1 } , 0 },
{ "FF JITTER", OME_UINT8, NULL, &(OSD_UINT8_t) { &cmsx_ff_jitter_factor, 0, 20, 1 } , 0 },
#endif
{ "FF BOOST", OME_UINT8, NULL, &(OSD_UINT8_t) { &cmsx_ff_boost, 0, 50, 1 } , 0 },
{ "ANGLE STR", OME_UINT8, NULL, &(OSD_UINT8_t) { &cmsx_angleStrength, 0, 200, 1 } , 0 },

View File

@ -22,6 +22,7 @@
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "platform.h"
@ -79,6 +80,20 @@ static const void *cmsx_menuRcConfirmBack(displayPort_t *pDisp, const OSD_Entry
}
}
static int16_t rcDataInt[AUX4 + 1];
static const void *cmsx_menuRcOnDisplayUpdate(displayPort_t *pDisp, const OSD_Entry *selected)
{
UNUSED(pDisp);
UNUSED(selected);
for (int i = 0; i <= AUX4; i++) {
rcDataInt[i] = lroundf(rcData[i]);
}
return NULL;
}
//
// RC preview
//
@ -86,15 +101,15 @@ static const OSD_Entry cmsx_menuRcEntries[] =
{
{ "-- RC PREV --", OME_Label, NULL, NULL, 0},
{ "ROLL", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[ROLL], 1, 2500, 0 }, DYNAMIC },
{ "PITCH", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[PITCH], 1, 2500, 0 }, DYNAMIC },
{ "THR", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[THROTTLE], 1, 2500, 0 }, DYNAMIC },
{ "YAW", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[YAW], 1, 2500, 0 }, DYNAMIC },
{ "ROLL", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[ROLL], 1, 2500, 0 }, DYNAMIC },
{ "PITCH", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[PITCH], 1, 2500, 0 }, DYNAMIC },
{ "THR", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[THROTTLE], 1, 2500, 0 }, DYNAMIC },
{ "YAW", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[YAW], 1, 2500, 0 }, DYNAMIC },
{ "AUX1", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[AUX1], 1, 2500, 0 }, DYNAMIC },
{ "AUX2", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[AUX2], 1, 2500, 0 }, DYNAMIC },
{ "AUX3", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[AUX3], 1, 2500, 0 }, DYNAMIC },
{ "AUX4", OME_INT16, NULL, &(OSD_INT16_t){ &rcData[AUX4], 1, 2500, 0 }, DYNAMIC },
{ "AUX1", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[AUX1], 1, 2500, 0 }, DYNAMIC },
{ "AUX2", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[AUX2], 1, 2500, 0 }, DYNAMIC },
{ "AUX3", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[AUX3], 1, 2500, 0 }, DYNAMIC },
{ "AUX4", OME_INT16, NULL, &(OSD_INT16_t){ &rcDataInt[AUX4], 1, 2500, 0 }, DYNAMIC },
{ "BACK", OME_Back, NULL, NULL, 0},
{NULL, OME_END, NULL, NULL, 0}
@ -107,7 +122,7 @@ CMS_Menu cmsx_menuRcPreview = {
#endif
.onEnter = cmsx_menuRcOnEnter,
.onExit = cmsx_menuRcConfirmBack,
.onDisplayUpdate = NULL,
.onDisplayUpdate = cmsx_menuRcOnDisplayUpdate,
.entries = cmsx_menuRcEntries
};

View File

@ -49,6 +49,8 @@
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "rx/crsf.h"
#include "sensors/battery.h"
#ifdef USE_EXTENDED_CMS_MENUS
@ -180,7 +182,7 @@ static CMS_Menu menuOsdActiveElems = {
static uint8_t osdConfig_rssi_alarm;
static uint16_t osdConfig_link_quality_alarm;
static uint8_t osdConfig_rssi_dbm_alarm;
static int16_t osdConfig_rssi_dbm_alarm;
static uint16_t osdConfig_cap_alarm;
static uint16_t osdConfig_alt_alarm;
static uint16_t osdConfig_distance_alarm;
@ -225,7 +227,7 @@ const OSD_Entry menuAlarmsEntries[] =
{"--- ALARMS ---", OME_Label, NULL, NULL, 0},
{"RSSI", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_alarm, 5, 90, 5}, 0},
{"LINK QUALITY", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_link_quality_alarm, 5, 300, 5}, 0},
{"RSSI DBM", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_dbm_alarm, 5, 130, 5}, 0},
{"RSSI DBM", OME_INT16, NULL, &(OSD_INT16_t){&osdConfig_rssi_dbm_alarm, CRSF_RSSI_MIN, CRSF_SNR_MAX, 5}, 0},
{"MAIN BAT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_cap_alarm, 50, 30000, 50}, 0},
{"MAX ALT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_alt_alarm, 1, 200, 1}, 0},
{"MAX DISTANCE", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_distance_alarm, 0, UINT16_MAX, 10}, 0},

View File

@ -65,6 +65,71 @@ FAST_CODE float pt1FilterApply(pt1Filter_t *filter, float input)
return filter->state;
}
// PT2 Low Pass filter
float pt2FilterGain(float f_cut, float dT)
{
const float order = 2.0f;
const float orderCutoffCorrection = (1 / sqrtf(powf(2, 1.0f / (order)) - 1));
float RC = 1 / ( 2 * orderCutoffCorrection * M_PIf * f_cut);
//float RC = 1 / ( 2 * 1.553773974f * M_PIf * f_cut);
// where 1.553773974 = 1 / sqrt( (2^(1 / order) - 1) ) and order is 2
return dT / (RC + dT);
}
void pt2FilterInit(pt2Filter_t *filter, float k)
{
filter->state = 0.0f;
filter->state1 = 0.0f;
filter->k = k;
}
void pt2FilterUpdateCutoff(pt2Filter_t *filter, float k)
{
filter->k = k;
}
FAST_CODE float pt2FilterApply(pt2Filter_t *filter, float input)
{
filter->state1 = filter->state1 + filter->k * (input - filter->state1);
filter->state = filter->state + filter->k * (filter->state1 - filter->state);
return filter->state;
}
// PT3 Low Pass filter
float pt3FilterGain(float f_cut, float dT)
{
const float order = 3.0f;
const float orderCutoffCorrection = (1 / sqrtf(powf(2, 1.0f / (order)) - 1));
float RC = 1 / ( 2 * orderCutoffCorrection * M_PIf * f_cut);
// float RC = 1 / ( 2 * 1.961459177f * M_PIf * f_cut);
// where 1.961459177 = 1 / sqrt( (2^(1 / order) - 1) ) and order is 3
return dT / (RC + dT);
}
void pt3FilterInit(pt3Filter_t *filter, float k)
{
filter->state = 0.0f;
filter->state1 = 0.0f;
filter->state2 = 0.0f;
filter->k = k;
}
void pt3FilterUpdateCutoff(pt3Filter_t *filter, float k)
{
filter->k = k;
}
FAST_CODE float pt3FilterApply(pt3Filter_t *filter, float input)
{
filter->state1 = filter->state1 + filter->k * (input - filter->state1);
filter->state2 = filter->state2 + filter->k * (filter->state1 - filter->state2);
filter->state = filter->state + filter->k * (filter->state2 - filter->state);
return filter->state;
}
// Slew filter with limit
void slewFilterInit(slewFilter_t *filter, float slewLimit, float threshold)

View File

@ -29,6 +29,19 @@ typedef struct pt1Filter_s {
float k;
} pt1Filter_t;
typedef struct pt2Filter_s {
float state;
float state1;
float k;
} pt2Filter_t;
typedef struct pt3Filter_s {
float state;
float state1;
float state2;
float k;
} pt3Filter_t;
typedef struct slewFilter_s {
float state;
float slewLimit;
@ -52,6 +65,8 @@ typedef struct laggedMovingAverage_s {
typedef enum {
FILTER_PT1 = 0,
FILTER_BIQUAD,
FILTER_PT2,
FILTER_PT3,
} lowpassFilterType_e;
typedef enum {
@ -81,5 +96,15 @@ void pt1FilterInit(pt1Filter_t *filter, float k);
void pt1FilterUpdateCutoff(pt1Filter_t *filter, float k);
float pt1FilterApply(pt1Filter_t *filter, float input);
float pt2FilterGain(float f_cut, float dT);
void pt2FilterInit(pt2Filter_t *filter, float k);
void pt2FilterUpdateCutoff(pt2Filter_t *filter, float k);
float pt2FilterApply(pt2Filter_t *filter, float input);
float pt3FilterGain(float f_cut, float dT);
void pt3FilterInit(pt3Filter_t *filter, float k);
void pt3FilterUpdateCutoff(pt3Filter_t *filter, float k);
float pt3FilterApply(pt3Filter_t *filter, float input);
void slewFilterInit(slewFilter_t *filter, float slewLimit, float threshold);
float slewFilterApply(slewFilter_t *filter, float input);

View File

@ -115,13 +115,6 @@ int gcd(int num, int denom)
return gcd(denom, num % denom);
}
float powerf(float base, int exp) {
float result = base;
for (int count = 1; count < exp; count++) result *= base;
return result;
}
int32_t applyDeadband(const int32_t value, const int32_t deadband)
{
if (ABS(value) < deadband) {

View File

@ -101,7 +101,6 @@ typedef struct fp_rotationMatrix_s {
} fp_rotationMatrix_t;
int gcd(int num, int denom);
float powerf(float base, int exp);
int32_t applyDeadband(int32_t value, int32_t deadband);
float fapplyDeadband(float value, float deadband);

View File

@ -38,7 +38,7 @@ static void applySqrt(const sdft_t *sdft, float *data);
void sdftInit(sdft_t *sdft, const uint8_t startBin, const uint8_t endBin, const uint8_t numBatches)
{
if (!isInitialized) {
rPowerN = powerf(SDFT_R, SDFT_SAMPLE_SIZE);
rPowerN = powf(SDFT_R, SDFT_SAMPLE_SIZE);
const float c = 2.0f * M_PIf / (float)SDFT_SAMPLE_SIZE;
float phi = 0.0f;
for (uint8_t i = 0; i < SDFT_BIN_COUNT; i++) {

View File

@ -183,7 +183,7 @@ typedef struct odrEntry_s {
uint8_t odr; // See GYRO_ODR in datasheet.
} odrEntry_t;
odrEntry_t khzToSupportedODRMap[] = {
static odrEntry_t icm42605PkhzToSupportedODRMap[] = {
{ 8, 3 },
{ 4, 4 },
{ 2, 5 },
@ -205,9 +205,9 @@ void icm42605GyroInit(gyroDev_t *gyro)
if (gyro->gyroRateKHz) {
uint8_t gyroSyncDenominator = gyro->mpuDividerDrops + 1; // rebuild it in here, see gyro_sync.c
uint8_t desiredODRKhz = 8 / gyroSyncDenominator;
for (uint32_t i = 0; i < ARRAYLEN(khzToSupportedODRMap); i++) {
if (khzToSupportedODRMap[i].khz == desiredODRKhz) {
outputDataRate = khzToSupportedODRMap[i].odr;
for (uint32_t i = 0; i < ARRAYLEN(icm42605PkhzToSupportedODRMap); i++) {
if (icm42605PkhzToSupportedODRMap[i].khz == desiredODRKhz) {
outputDataRate = icm42605PkhzToSupportedODRMap[i].odr;
supportedODRFound = true;
break;
}
@ -219,22 +219,14 @@ void icm42605GyroInit(gyroDev_t *gyro)
gyro->gyroRateKHz = GYRO_RATE_1_kHz;
}
uint8_t value;
STATIC_ASSERT(INV_FSR_2000DPS == 3, "INV_FSR_2000DPS must be 3 to generate correct value");
spiBusWriteRegister(&gyro->bus, ICM42605_RA_GYRO_CONFIG0, (3 - INV_FSR_2000DPS) << 5 | (outputDataRate & 0x0F));
delay(15);
value = spiBusReadRegister(&gyro->bus, ICM42605_RA_GYRO_CONFIG0);
debug[1] = value;
STATIC_ASSERT(INV_FSR_16G == 3, "INV_FSR_16G must be 3 to generate correct value");
spiBusWriteRegister(&gyro->bus, ICM42605_RA_ACCEL_CONFIG0, (3 - INV_FSR_16G) << 5 | (outputDataRate & 0x0F));
delay(15);
value = spiBusReadRegister(&gyro->bus, ICM42605_RA_ACCEL_CONFIG0);
debug[2] = value;
spiBusWriteRegister(&gyro->bus, ICM42605_RA_GYRO_ACCEL_CONFIG0, ICM42605_ACCEL_UI_FILT_BW_LOW_LATENCY | ICM42605_GYRO_UI_FILT_BW_LOW_LATENCY);
spiBusWriteRegister(&gyro->bus, ICM42605_RA_INT_CONFIG, ICM42605_INT1_MODE_PULSED | ICM42605_INT1_DRIVE_CIRCUIT_PP | ICM42605_INT1_POLARITY_ACTIVE_HIGH);

View File

@ -20,28 +20,26 @@
* BMP388 Driver author: Dominic Clifton
*/
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include "platform.h"
#include "build/build_config.h"
#if defined(USE_BARO) && (defined(USE_BARO_BMP388) || defined(USE_BARO_SPI_BMP388))
#include "build/build_config.h"
#include "build/debug.h"
#include "common/maths.h"
#include "barometer.h"
#include "drivers/barometer/barometer.h"
#include "drivers/bus.h"
#include "drivers/bus_i2c.h"
#include "drivers/bus_i2c_busdev.h"
#include "drivers/bus_spi.h"
#include "drivers/io.h"
#include "drivers/time.h"
#include "drivers/nvic.h"
#include "drivers/time.h"
#include "barometer_bmp388.h"
@ -303,8 +301,8 @@ bool bmp388Detect(const bmp388Config_t *config, baroDev_t *baro)
// See datasheet 3.9.2 "Measurement rate in forced mode and normal mode"
baro->up_delay = 234 +
(392 + (powerf(2, BMP388_PRESSURE_OSR + 1) * 2000)) +
(313 + (powerf(2, BMP388_TEMPERATURE_OSR + 1) * 2000));
(392 + (powf(2, BMP388_PRESSURE_OSR + 1) * 2000)) +
(313 + (powf(2, BMP388_TEMPERATURE_OSR + 1) * 2000));
baro->calculate = bmp388Calculate;

View File

@ -36,6 +36,8 @@
#include "nvic.h"
#include "rcc.h"
#define SPI_TIMEOUT_SYS_TICKS (SPI_TIMEOUT_US / 1000)
void spiInitDevice(SPIDevice device, bool leadingEdge)
{
spiDevice_t *spi = &(spiDevice[device]);
@ -93,8 +95,7 @@ void spiInitDevice(SPIDevice device, bool leadingEdge)
if (spi->leadingEdge) {
spi->hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
spi->hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
}
else {
} else {
spi->hspi.Init.CLKPolarity = SPI_POLARITY_HIGH;
spi->hspi.Init.CLKPhase = SPI_PHASE_2EDGE;
}
@ -118,28 +119,27 @@ uint8_t spiTransferByte(SPI_TypeDef *instance, uint8_t out)
bool spiIsBusBusy(SPI_TypeDef *instance)
{
SPIDevice device = spiDeviceByInstance(instance);
if(spiDevice[device].hspi.State == HAL_SPI_STATE_BUSY)
if (spiDevice[device].hspi.State == HAL_SPI_STATE_BUSY) {
return true;
else
} else {
return false;
}
}
bool spiTransfer(SPI_TypeDef *instance, const uint8_t *out, uint8_t *in, int len)
{
SPIDevice device = spiDeviceByInstance(instance);
HAL_StatusTypeDef status;
#define SPI_DEFAULT_TIMEOUT 10
if (!in) {
// Tx only
status = HAL_SPI_Transmit(&spiDevice[device].hspi, out, len, SPI_DEFAULT_TIMEOUT);
status = HAL_SPI_Transmit(&spiDevice[device].hspi, out, len, SPI_TIMEOUT_SYS_TICKS);
} else if (!out) {
// Rx only
status = HAL_SPI_Receive(&spiDevice[device].hspi, in, len, SPI_DEFAULT_TIMEOUT);
status = HAL_SPI_Receive(&spiDevice[device].hspi, in, len, SPI_TIMEOUT_SYS_TICKS);
} else {
// Tx and Rx
status = HAL_SPI_TransmitReceive(&spiDevice[device].hspi, out, in, len, SPI_DEFAULT_TIMEOUT);
status = HAL_SPI_TransmitReceive(&spiDevice[device].hspi, out, in, len, SPI_TIMEOUT_SYS_TICKS);
}
if (status != HAL_OK) {
@ -210,8 +210,9 @@ void SPI4_IRQHandler(void)
void dmaSPIIRQHandler(dmaChannelDescriptor_t* descriptor)
{
SPIDevice device = descriptor->userParam;
if (device != SPIINVALID)
if (device != SPIINVALID) {
HAL_DMA_IRQHandler(&spiDevice[device].hdma);
}
}
#endif // USE_DMA
#endif

View File

@ -20,6 +20,8 @@
#pragma once
#define SPI_TIMEOUT_US 10000
#if defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32G4)
#define MAX_SPI_PIN_SEL 2
#elif defined(STM32F7)

View File

@ -36,6 +36,7 @@
#include "drivers/io.h"
#include "drivers/nvic.h"
#include "drivers/rcc.h"
#include "drivers/time.h"
#ifndef SPI2_SCK_PIN
#define SPI2_NSS_PIN PB12
@ -71,8 +72,6 @@
#define SPI4_NSS_PIN NONE
#endif
#define SPI_DEFAULT_TIMEOUT 10
static LL_SPI_InitTypeDef defaultInit =
{
.TransferDirection = SPI_DIRECTION_2LINES,
@ -107,10 +106,12 @@ void spiInitDevice(SPIDevice device, bool leadingEdge)
IOInit(IOGetByTag(spi->miso), OWNER_SPI_MISO, RESOURCE_INDEX(device));
IOInit(IOGetByTag(spi->mosi), OWNER_SPI_MOSI, RESOURCE_INDEX(device));
if (spi->leadingEdge == true)
if (spi->leadingEdge == true) {
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG_LOW, spi->sckAF);
else
} else {
IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG_HIGH, spi->sckAF);
}
IOConfigGPIOAF(IOGetByTag(spi->miso), SPI_IO_AF_MISO_CFG, spi->misoAF);
IOConfigGPIOAF(IOGetByTag(spi->mosi), SPI_IO_AF_CFG, spi->mosiAF);
@ -136,18 +137,21 @@ void spiInitDevice(SPIDevice device, bool leadingEdge)
uint8_t spiTransferByte(SPI_TypeDef *instance, uint8_t txByte)
{
uint16_t spiTimeout = 1000;
timeUs_t timeoutStartUs = microsISR();
while (!LL_SPI_IsActiveFlag_TXE(instance))
if ((spiTimeout--) == 0)
while (!LL_SPI_IsActiveFlag_TXE(instance)) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
LL_SPI_TransmitData8(instance, txByte);
spiTimeout = 1000;
while (!LL_SPI_IsActiveFlag_RXNE(instance))
if ((spiTimeout--) == 0)
timeoutStartUs = microsISR();
while (!LL_SPI_IsActiveFlag_RXNE(instance)) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
return (uint8_t)LL_SPI_ReceiveData8(instance);
}
@ -163,12 +167,14 @@ bool spiIsBusBusy(SPI_TypeDef *instance)
bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, int len)
{
timeUs_t timeoutStartUs;
// set 16-bit transfer
CLEAR_BIT(instance->CR2, SPI_RXFIFO_THRESHOLD);
while (len > 1) {
int spiTimeout = 1000;
timeoutStartUs = microsISR();
while (!LL_SPI_IsActiveFlag_TXE(instance)) {
if ((spiTimeout--) == 0) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
@ -181,9 +187,9 @@ bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData,
}
LL_SPI_TransmitData16(instance, w);
spiTimeout = 1000;
timeoutStartUs = microsISR();
while (!LL_SPI_IsActiveFlag_RXNE(instance)) {
if ((spiTimeout--) == 0) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
@ -197,18 +203,18 @@ bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData,
// set 8-bit transfer
SET_BIT(instance->CR2, SPI_RXFIFO_THRESHOLD);
if (len) {
int spiTimeout = 1000;
timeoutStartUs = microsISR();
while (!LL_SPI_IsActiveFlag_TXE(instance)) {
if ((spiTimeout--) == 0) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
uint8_t b = txData ? *(txData++) : 0xFF;
LL_SPI_TransmitData8(instance, b);
spiTimeout = 1000;
timeoutStartUs = microsISR();
while (!LL_SPI_IsActiveFlag_RXNE(instance)) {
if ((spiTimeout--) == 0) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}

View File

@ -33,6 +33,7 @@
#include "drivers/exti.h"
#include "drivers/io.h"
#include "drivers/rcc.h"
#include "drivers/time.h"
static SPI_InitTypeDef defaultInit = {
.SPI_Mode = SPI_Mode_Master,
@ -104,23 +105,27 @@ void spiInitDevice(SPIDevice device, bool leadingEdge)
// return uint8_t value or -1 when failure
uint8_t spiTransferByte(SPI_TypeDef *instance, uint8_t txByte)
{
uint16_t spiTimeout = 1000;
timeUs_t timeoutStartUs = microsISR();
DISCARD(instance->DR);
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET)
if ((spiTimeout--) == 0)
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
#ifdef STM32F303xC
SPI_SendData8(instance, txByte);
#else
SPI_I2S_SendData(instance, txByte);
#endif
spiTimeout = 1000;
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_RXNE) == RESET)
if ((spiTimeout--) == 0)
timeoutStartUs = microsISR();
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_RXNE) == RESET) {
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
#ifdef STM32F303xC
return ((uint8_t)SPI_ReceiveData8(instance));
@ -144,36 +149,40 @@ bool spiIsBusBusy(SPI_TypeDef *instance)
bool spiTransfer(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, int len)
{
uint16_t spiTimeout = 1000;
timeUs_t timeoutStartUs;
uint8_t b;
DISCARD(instance->DR);
while (len--) {
b = txData ? *(txData++) : 0xFF;
timeoutStartUs = microsISR();
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET) {
if ((spiTimeout--) == 0)
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
#ifdef STM32F303xC
SPI_SendData8(instance, b);
#else
SPI_I2S_SendData(instance, b);
#endif
spiTimeout = 1000;
timeoutStartUs = microsISR();
while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_RXNE) == RESET) {
if ((spiTimeout--) == 0)
if (cmpTimeUs(microsISR(), timeoutStartUs) >= SPI_TIMEOUT_US) {
return spiTimeoutUserCallback(instance);
}
}
#ifdef STM32F303xC
b = SPI_ReceiveData8(instance);
#else
b = SPI_I2S_ReceiveData(instance);
#endif
if (rxData)
if (rxData) {
*(rxData++) = b;
}
}
return true;
}

View File

@ -89,7 +89,8 @@ struct {
{ 0xEF4016, 133, 50, 64, 256 },
// Winbond W25Q64
// Datasheet: https://www.winbond.com/resource-files/w25q64jv%20spi%20%20%20revc%2006032016%20kms.pdf
{ 0xEF4017, 133, 50, 128, 256 },
{ 0xEF4017, 133, 50, 128, 256 }, // W25Q64JV-IQ/JQ
{ 0xEF7017, 133, 50, 128, 256 }, // W25Q64JV-IM/JM*
// Winbond W25Q128
// Datasheet: https://www.winbond.com/resource-files/w25q128fv%20rev.l%2008242015.pdf
{ 0xEF4018, 104, 50, 256, 256 },

View File

@ -156,3 +156,8 @@
#define SYM_STICK_OVERLAY_CENTER 0x0B
#define SYM_STICK_OVERLAY_VERTICAL 0x16
#define SYM_STICK_OVERLAY_HORIZONTAL 0x17
// GPS degree/minute/second symbols
#define SYM_GPS_DEGREE SYM_STICK_OVERLAY_SPRITE_HIGH // kind of looks like the degree symbol
#define SYM_GPS_MINUTE 0x27 // '
#define SYM_GPS_SECOND 0x22 // "

View File

@ -47,16 +47,16 @@ void pgResetFn_controlRateProfiles(controlRateConfig_t *controlRateConfig)
.thrExpo8 = 0,
.dynThrPID = 65,
.tpa_breakpoint = 1350,
.rates_type = RATES_TYPE_BETAFLIGHT,
.rcRates[FD_ROLL] = 100,
.rcRates[FD_PITCH] = 100,
.rcRates[FD_YAW] = 100,
.rates_type = RATES_TYPE_ACTUAL,
.rcRates[FD_ROLL] = 7,
.rcRates[FD_PITCH] = 7,
.rcRates[FD_YAW] = 7,
.rcExpo[FD_ROLL] = 0,
.rcExpo[FD_PITCH] = 0,
.rcExpo[FD_YAW] = 0,
.rates[FD_ROLL] = 70,
.rates[FD_PITCH] = 70,
.rates[FD_YAW] = 70,
.rates[FD_ROLL] = 67,
.rates[FD_PITCH] = 67,
.rates[FD_YAW] = 67,
.throttle_limit_type = THROTTLE_LIMIT_TYPE_OFF,
.throttle_limit_percent = 100,
.rate_limit[FD_ROLL] = CONTROL_RATE_CONFIG_RATE_LIMIT_MAX,

View File

@ -101,11 +101,9 @@
#include "io/asyncfatfs/asyncfatfs.h"
#include "io/beeper.h"
#include "io/dashboard.h"
#include "io/displayport_crsf.h"
#include "io/displayport_frsky_osd.h"
#include "io/displayport_max7456.h"
#include "io/displayport_msp.h"
#include "io/displayport_srxl.h"
#include "io/flashfs.h"
#include "io/gimbal.h"
#include "io/gps.h"
@ -1011,15 +1009,6 @@ void init(void)
}
#endif
#if defined(USE_CMS) && defined(USE_SPEKTRUM_CMS_TELEMETRY) && defined(USE_TELEMETRY_SRXL)
// Register the srxl Textgen telemetry sensor as a displayport device
cmsDisplayPortRegister(displayPortSrxlInit());
#endif
#if defined(USE_CMS) && defined(USE_CRSF_CMS_TELEMETRY)
cmsDisplayPortRegister(displayPortCrsfInit());
#endif
setArmingDisabled(ARMING_DISABLED_BOOT_GRACE_TIME);
#ifdef USE_MOTOR

View File

@ -27,7 +27,6 @@
#include "build/debug.h"
#include "common/axis.h"
#include "common/maths.h"
#include "common/utils.h"
#include "config/config.h"
@ -61,7 +60,11 @@ typedef float (applyRatesFn)(const int axis, float rcCommandf, const float rcCom
#ifdef USE_INTERPOLATED_SP
// Setpoint in degrees/sec before RC-Smoothing is applied
static float rawSetpoint[XYZ_AXIS_COUNT];
static float oldRcCommand[XYZ_AXIS_COUNT];
static bool isDuplicate[XYZ_AXIS_COUNT];
float rcCommandDelta[XYZ_AXIS_COUNT];
#endif
static float setpointRate[3], rcDeflection[3], rcDeflectionAbs[3];
static float throttlePIDAttenuation;
static bool reverseMotors = false;
@ -82,7 +85,6 @@ enum {
};
#ifdef USE_RC_SMOOTHING_FILTER
#define RC_SMOOTHING_IDENTITY_FREQUENCY 80 // Used in the formula to convert a BIQUAD cutoff frequency to PT1
#define RC_SMOOTHING_FILTER_STARTUP_DELAY_MS 5000 // Time to wait after power to let the PID loop stabilize before starting average frame rate calculation
#define RC_SMOOTHING_FILTER_TRAINING_SAMPLES 50 // Number of rx frame rate samples to average during initial training
#define RC_SMOOTHING_FILTER_RETRAINING_SAMPLES 20 // Number of rx frame rate samples to average during frame rate changes
@ -91,7 +93,7 @@ enum {
#define RC_SMOOTHING_RX_RATE_CHANGE_PERCENT 20 // Look for samples varying this much from the current detected frame rate to initiate retraining
#define RC_SMOOTHING_RX_RATE_MIN_US 1000 // 1ms
#define RC_SMOOTHING_RX_RATE_MAX_US 50000 // 50ms or 20hz
#define RC_SMOOTHING_INTERPOLATED_FEEDFORWARD_DERIVATIVE_PT1_HZ 100 // The value to use for "auto" when interpolated feedforward is enabled
#define RC_SMOOTHING_INTERPOLATED_FEEDFORWARD_INITIAL_HZ 100 // Initial value for "auto" when interpolated feedforward is enabled
static FAST_DATA_ZERO_INIT rcSmoothingFilter_t rcSmoothingData;
#endif // USE_RC_SMOOTHING_FILTER
@ -133,6 +135,11 @@ float getRawSetpoint(int axis)
return rawSetpoint[axis];
}
float getRcCommandDelta(int axis)
{
return rcCommandDelta[axis];
}
#endif
#define THROTTLE_LOOKUP_LENGTH 12
@ -195,7 +202,7 @@ float applyKissRates(const int axis, float rcCommandf, const float rcCommandfAbs
float applyActualRates(const int axis, float rcCommandf, const float rcCommandfAbs)
{
float expof = currentControlRateProfile->rcExpo[axis] / 100.0f;
expof = rcCommandfAbs * (powerf(rcCommandf, 5) * expof + rcCommandf * (1 - expof));
expof = rcCommandfAbs * (powf(rcCommandf, 5) * expof + rcCommandf * (1 - expof));
const float centerSensitivity = currentControlRateProfile->rcRates[axis] * 10.0f;
const float stickMovement = MAX(0, currentControlRateProfile->rates[axis] * 10.0f - centerSensitivity);
@ -395,16 +402,12 @@ uint16_t getCurrentRxRefreshRate(void)
#ifdef USE_RC_SMOOTHING_FILTER
// Determine a cutoff frequency based on filter type and the calculated
// average rx frame time
FAST_CODE_NOINLINE int calcRcSmoothingCutoff(int avgRxFrameTimeUs, bool pt1, uint8_t autoSmoothnessFactor)
FAST_CODE_NOINLINE int calcRcSmoothingCutoff(int avgRxFrameTimeUs, uint8_t autoSmoothnessFactor)
{
if (avgRxFrameTimeUs > 0) {
const float cutoffFactor = (100 - autoSmoothnessFactor) / 100.0f;
float cutoff = (1 / (avgRxFrameTimeUs * 1e-6f)) / 2; // calculate the nyquist frequency
const float cutoffFactor = 1.5f / (1.0f + (autoSmoothnessFactor / 10.0f));
float cutoff = (1 / (avgRxFrameTimeUs * 1e-6f)); // link frequency
cutoff = cutoff * cutoffFactor;
if (pt1) {
cutoff = sq(cutoff) / RC_SMOOTHING_IDENTITY_FREQUENCY; // convert to a cutoff for pt1 that has similar characteristics
}
return lrintf(cutoff);
} else {
return 0;
@ -426,31 +429,17 @@ FAST_CODE_NOINLINE void rcSmoothingSetFilterCutoffs(rcSmoothingFilter_t *smoothi
uint16_t oldCutoff = smoothingData->inputCutoffFrequency;
if (smoothingData->inputCutoffSetting == 0) {
smoothingData->inputCutoffFrequency = calcRcSmoothingCutoff(smoothingData->averageFrameTimeUs, (smoothingData->inputFilterType == RC_SMOOTHING_INPUT_PT1), smoothingData->autoSmoothnessFactor);
smoothingData->inputCutoffFrequency = calcRcSmoothingCutoff(smoothingData->averageFrameTimeUs, smoothingData->autoSmoothnessFactor);
}
// initialize or update the input filter
if ((smoothingData->inputCutoffFrequency != oldCutoff) || !smoothingData->filterInitialized) {
for (int i = 0; i < PRIMARY_CHANNEL_COUNT; i++) {
if ((1 << i) & interpolationChannels) { // only update channels specified by rc_interp_ch
switch (smoothingData->inputFilterType) {
case RC_SMOOTHING_INPUT_PT1:
if (!smoothingData->filterInitialized) {
pt1FilterInit((pt1Filter_t*) &smoothingData->filter[i], pt1FilterGain(smoothingData->inputCutoffFrequency, dT));
pt3FilterInit((pt3Filter_t*) &smoothingData->filter[i], pt3FilterGain(smoothingData->inputCutoffFrequency, dT));
} else {
pt1FilterUpdateCutoff((pt1Filter_t*) &smoothingData->filter[i], pt1FilterGain(smoothingData->inputCutoffFrequency, dT));
}
break;
case RC_SMOOTHING_INPUT_BIQUAD:
default:
if (!smoothingData->filterInitialized) {
biquadFilterInitLPF((biquadFilter_t*) &smoothingData->filter[i], smoothingData->inputCutoffFrequency, targetPidLooptime);
} else {
biquadFilterUpdateLPF((biquadFilter_t*) &smoothingData->filter[i], smoothingData->inputCutoffFrequency, targetPidLooptime);
}
break;
pt3FilterUpdateCutoff((pt3Filter_t*) &smoothingData->filter[i], pt3FilterGain(smoothingData->inputCutoffFrequency, dT));
}
}
}
@ -458,15 +447,12 @@ FAST_CODE_NOINLINE void rcSmoothingSetFilterCutoffs(rcSmoothingFilter_t *smoothi
// update or initialize the derivative filter
oldCutoff = smoothingData->derivativeCutoffFrequency;
if ((rcSmoothingData.derivativeFilterType != RC_SMOOTHING_DERIVATIVE_OFF)
&& (currentPidProfile->ff_interpolate_sp == FF_INTERPOLATE_OFF)
&& (rcSmoothingData.derivativeCutoffSetting == 0)) {
smoothingData->derivativeCutoffFrequency = calcRcSmoothingCutoff(smoothingData->averageFrameTimeUs, (smoothingData->derivativeFilterType == RC_SMOOTHING_DERIVATIVE_PT1), smoothingData->autoSmoothnessFactor);
if ((currentPidProfile->ff_interpolate_sp != FF_INTERPOLATE_OFF) && (rcSmoothingData.derivativeCutoffSetting == 0)) {
smoothingData->derivativeCutoffFrequency = calcRcSmoothingCutoff(smoothingData->averageFrameTimeUs, smoothingData->autoSmoothnessFactor);
}
if (!smoothingData->filterInitialized) {
pidInitSetpointDerivativeLpf(smoothingData->derivativeCutoffFrequency, smoothingData->debugAxis, smoothingData->derivativeFilterType);
pidInitSetpointDerivativeLpf(smoothingData->derivativeCutoffFrequency, smoothingData->debugAxis);
} else if (smoothingData->derivativeCutoffFrequency != oldCutoff) {
pidUpdateSetpointDerivativeLpf(smoothingData->derivativeCutoffFrequency);
}
@ -505,14 +491,7 @@ static FAST_CODE bool rcSmoothingAccumulateSample(rcSmoothingFilter_t *smoothing
FAST_CODE_NOINLINE bool rcSmoothingAutoCalculate(void)
{
// if the input cutoff is 0 (auto) then we need to calculate cutoffs
if (rcSmoothingData.inputCutoffSetting == 0) {
return true;
}
// if the derivative type isn't OFF, and the cutoff is 0, and interpolated feedforward is not enabled then we need to calculate
if ((rcSmoothingData.derivativeFilterType != RC_SMOOTHING_DERIVATIVE_OFF)
&& (currentPidProfile->ff_interpolate_sp == FF_INTERPOLATE_OFF)
&& (rcSmoothingData.derivativeCutoffSetting == 0)) {
if ((rcSmoothingData.inputCutoffSetting == 0) || (rcSmoothingData.derivativeCutoffSetting == 0)) {
return true;
}
return false;
@ -533,40 +512,20 @@ static FAST_CODE uint8_t processRcSmoothingFilter(void)
rcSmoothingData.averageFrameTimeUs = 0;
rcSmoothingData.autoSmoothnessFactor = rxConfig()->rc_smoothing_auto_factor;
rcSmoothingData.debugAxis = rxConfig()->rc_smoothing_debug_axis;
rcSmoothingData.inputFilterType = rxConfig()->rc_smoothing_input_type;
rcSmoothingData.inputCutoffSetting = rxConfig()->rc_smoothing_input_cutoff;
rcSmoothingData.derivativeFilterTypeSetting = rxConfig()->rc_smoothing_derivative_type;
if (rxConfig()->rc_smoothing_derivative_type == RC_SMOOTHING_DERIVATIVE_AUTO) {
// for derivative filter type "AUTO" set to BIQUAD for classic FF and PT1 for interpolated FF
if (currentPidProfile->ff_interpolate_sp == FF_INTERPOLATE_OFF) {
rcSmoothingData.derivativeFilterType = RC_SMOOTHING_DERIVATIVE_BIQUAD;
} else {
rcSmoothingData.derivativeFilterType = RC_SMOOTHING_DERIVATIVE_PT1;
}
} else {
rcSmoothingData.derivativeFilterType = rxConfig()->rc_smoothing_derivative_type;
}
rcSmoothingData.derivativeCutoffSetting = rxConfig()->rc_smoothing_derivative_cutoff;
rcSmoothingResetAccumulation(&rcSmoothingData);
rcSmoothingData.inputCutoffFrequency = rcSmoothingData.inputCutoffSetting;
if (rcSmoothingData.derivativeFilterType != RC_SMOOTHING_DERIVATIVE_OFF) {
if ((currentPidProfile->ff_interpolate_sp != FF_INTERPOLATE_OFF) && (rcSmoothingData.derivativeCutoffSetting == 0)) {
// calculate the fixed derivative cutoff used for interpolated feedforward
const float cutoffFactor = (100 - rcSmoothingData.autoSmoothnessFactor) / 100.0f;
float derivativeCutoff = RC_SMOOTHING_INTERPOLATED_FEEDFORWARD_DERIVATIVE_PT1_HZ * cutoffFactor; // PT1 cutoff frequency
if (rcSmoothingData.derivativeFilterType == RC_SMOOTHING_DERIVATIVE_BIQUAD) {
// convert to an equivalent BIQUAD cutoff
derivativeCutoff = sqrt(derivativeCutoff * RC_SMOOTHING_IDENTITY_FREQUENCY);
}
// calculate the initial derivative cutoff used for interpolated feedforward until RC interval is known
const float cutoffFactor = 1.5f / (1.0f + (rcSmoothingData.autoSmoothnessFactor / 10.0f));
float derivativeCutoff = RC_SMOOTHING_INTERPOLATED_FEEDFORWARD_INITIAL_HZ * cutoffFactor; // PT1 cutoff frequency
rcSmoothingData.derivativeCutoffFrequency = lrintf(derivativeCutoff);
} else {
rcSmoothingData.derivativeCutoffFrequency = rcSmoothingData.derivativeCutoffSetting;
}
}
calculateCutoffs = rcSmoothingAutoCalculate();
@ -658,16 +617,7 @@ static FAST_CODE uint8_t processRcSmoothingFilter(void)
for (updatedChannel = 0; updatedChannel < PRIMARY_CHANNEL_COUNT; updatedChannel++) {
if ((1 << updatedChannel) & interpolationChannels) { // only smooth selected channels base on the rc_interp_ch value
if (rcSmoothingData.filterInitialized) {
switch (rcSmoothingData.inputFilterType) {
case RC_SMOOTHING_INPUT_PT1:
rcCommand[updatedChannel] = pt1FilterApply((pt1Filter_t*) &rcSmoothingData.filter[updatedChannel], lastRxData[updatedChannel]);
break;
case RC_SMOOTHING_INPUT_BIQUAD:
default:
rcCommand[updatedChannel] = biquadFilterApplyDF1((biquadFilter_t*) &rcSmoothingData.filter[updatedChannel], lastRxData[updatedChannel]);
break;
}
rcCommand[updatedChannel] = pt3FilterApply((pt3Filter_t*) &rcSmoothingData.filter[updatedChannel], lastRxData[updatedChannel]);
} else {
// If filter isn't initialized yet then use the actual unsmoothed rx channel data
rcCommand[updatedChannel] = lastRxData[updatedChannel];
@ -694,6 +644,9 @@ FAST_CODE void processRcCommand(void)
#ifdef USE_INTERPOLATED_SP
if (isRxDataNew) {
for (int i = FD_ROLL; i <= FD_YAW; i++) {
isDuplicate[i] = (oldRcCommand[i] == rcCommand[i]);
rcCommandDelta[i] = fabsf(rcCommand[i] - oldRcCommand[i]);
oldRcCommand[i] = rcCommand[i];
float rcCommandf;
if (i == FD_YAW) {
rcCommandf = rcCommand[i] / rcCommandYawDivider;
@ -763,7 +716,7 @@ FAST_CODE_NOINLINE void updateRcCommands(void)
for (int axis = 0; axis < 3; axis++) {
// non coupled PID reduction scaler used in PID controller 1 and PID controller 2.
int32_t tmp = MIN(ABS(rcData[axis] - rxConfig()->midrc), 500);
float tmp = MIN(ABS(rcData[axis] - rxConfig()->midrc), 500);
if (axis == ROLL || axis == PITCH) {
if (tmp > rcControlsConfig()->deadband) {
tmp -= rcControlsConfig()->deadband;

View File

@ -34,7 +34,7 @@ typedef enum {
#ifdef USE_RC_SMOOTHING_FILTER
#define RC_SMOOTHING_AUTO_FACTOR_MIN 0
#define RC_SMOOTHING_AUTO_FACTOR_MAX 50
#define RC_SMOOTHING_AUTO_FACTOR_MAX 250
#endif
void processRcCommand(void);
@ -51,6 +51,7 @@ rcSmoothingFilter_t *getRcSmoothingData(void);
bool rcSmoothingAutoCalculate(void);
bool rcSmoothingInitializationComplete(void);
float getRawSetpoint(int axis);
float getRcCommandDelta(int axis);
float applyCurve(int axis, float deflection);
bool getShouldUpdateFf();
void updateRcRefreshRate(timeUs_t currentTimeUs);

View File

@ -70,18 +70,6 @@ typedef enum {
RC_SMOOTHING_TYPE_FILTER
} rcSmoothingType_e;
typedef enum {
RC_SMOOTHING_INPUT_PT1,
RC_SMOOTHING_INPUT_BIQUAD
} rcSmoothingInputFilter_e;
typedef enum {
RC_SMOOTHING_DERIVATIVE_OFF,
RC_SMOOTHING_DERIVATIVE_PT1,
RC_SMOOTHING_DERIVATIVE_BIQUAD,
RC_SMOOTHING_DERIVATIVE_AUTO,
} rcSmoothingDerivativeFilter_e;
#define ROL_LO (1 << (2 * ROLL))
#define ROL_CE (3 << (2 * ROLL))
#define ROL_HI (2 << (2 * ROLL))
@ -116,19 +104,11 @@ typedef struct rcSmoothingFilterTraining_s {
uint16_t max;
} rcSmoothingFilterTraining_t;
typedef union rcSmoothingFilterTypes_u {
pt1Filter_t pt1Filter;
biquadFilter_t biquadFilter;
} rcSmoothingFilterTypes_t;
typedef struct rcSmoothingFilter_s {
bool filterInitialized;
rcSmoothingFilterTypes_t filter[4];
rcSmoothingInputFilter_e inputFilterType;
pt3Filter_t filter[4];
uint8_t inputCutoffSetting;
uint16_t inputCutoffFrequency;
rcSmoothingDerivativeFilter_e derivativeFilterTypeSetting;
rcSmoothingDerivativeFilter_e derivativeFilterType;
uint8_t derivativeCutoffSetting;
uint16_t derivativeCutoffFrequency;
int averageFrameTimeUs;

View File

@ -114,6 +114,7 @@ extern uint16_t flightModeFlags;
typedef enum {
GPS_FIX_HOME = (1 << 0),
GPS_FIX = (1 << 1),
GPS_FIX_EVER = (1 << 2),
} stateFlags_t;
#define DISABLE_STATE(mask) (stateFlags &= ~(mask))

View File

@ -179,6 +179,8 @@ static void taskUpdateRxMain(timeUs_t currentTimeUs)
case PROCESS:
ignoreTaskTime();
if (!processRx(currentTimeUs)) {
rxState = CHECK;
break;
}
rxState = MODES;

View File

@ -43,15 +43,12 @@ typedef struct laggedMovingAverageCombined_s {
laggedMovingAverageCombined_t setpointDeltaAvg[XYZ_AXIS_COUNT];
static float prevSetpointSpeed[XYZ_AXIS_COUNT];
static float prevAcceleration[XYZ_AXIS_COUNT];
static float prevRawSetpoint[XYZ_AXIS_COUNT];
//for smoothing
static float prevDeltaImpl[XYZ_AXIS_COUNT];
static float prevBoostAmount[XYZ_AXIS_COUNT];
static float prevSetpoint[XYZ_AXIS_COUNT]; // equals raw unless interpolated
static float prevSetpointSpeed[XYZ_AXIS_COUNT]; // equals raw unless interpolated
static float prevAcceleration[XYZ_AXIS_COUNT]; // for accurate duplicate interpolation
static float prevRcCommandDelta[XYZ_AXIS_COUNT]; // for accurate duplicate interpolation
static uint8_t ffStatus[XYZ_AXIS_COUNT];
static bool bigStep[XYZ_AXIS_COUNT];
static bool prevDuplicatePacket[XYZ_AXIS_COUNT]; // to identify multiple identical packets
static uint8_t averagingCount;
static float ffMaxRateLimit[XYZ_AXIS_COUNT];
@ -70,112 +67,86 @@ void interpolatedSpInit(const pidProfile_t *pidProfile) {
FAST_CODE_NOINLINE float interpolatedSpApply(int axis, bool newRcFrame, ffInterpolationType_t type) {
if (newRcFrame) {
float rawSetpoint = getRawSetpoint(axis);
float absRawSetpoint = fabsf(rawSetpoint);
float rcCommandDelta = getRcCommandDelta(axis);
float setpoint = getRawSetpoint(axis);
const float rxInterval = getCurrentRxRefreshRate() * 1e-6f;
const float rxRate = 1.0f / rxInterval;
float setpointSpeed = (rawSetpoint - prevRawSetpoint[axis]) * rxRate;
float absSetpointSpeed = fabsf(setpointSpeed);
float setpointSpeed = (setpoint - prevSetpoint[axis]) * rxRate;
float absPrevSetpointSpeed = fabsf(prevSetpointSpeed[axis]);
float setpointAccelerationModifier = 1.0f;
float setpointAcceleration = 0.0f;
const float ffSmoothFactor = pidGetFfSmoothFactor();
const float ffJitterFactor = pidGetFfJitterFactor();
if (setpointSpeed == 0 && absRawSetpoint < 0.98f * ffMaxRate[axis]) {
// no movement, or sticks at max; ffStatus set
// the max stick check is needed to prevent interpolation when arriving at max sticks
if (prevSetpointSpeed[axis] == 0) {
// no movement on two packets in a row
// do nothing now, but may use status = 3 to smooth following packet
ffStatus[axis] = 3;
// calculate an attenuator from average of two most recent rcCommand deltas vs jitter threshold
float ffAttenuator = 1.0f;
if (ffJitterFactor) {
if (rcCommandDelta < ffJitterFactor) {
ffAttenuator = MAX(1.0f - ((rcCommandDelta + prevRcCommandDelta[axis]) / 2.0f) / ffJitterFactor, 0.0f);
ffAttenuator = 1.0f - ffAttenuator * ffAttenuator;
}
}
// interpolate setpoint if necessary
if (rcCommandDelta == 0.0f) {
if (prevDuplicatePacket[axis] == false && fabsf(setpoint) < 0.98f * ffMaxRate[axis]) {
// first duplicate after movement
// interpolate rawSetpoint by adding (speed + acceleration) * attenuator to previous setpoint
setpoint = prevSetpoint[axis] + (prevSetpointSpeed[axis] + prevAcceleration[axis]) * ffAttenuator * rxInterval;
// recalculate setpointSpeed and (later) acceleration from this new setpoint value
setpointSpeed = (setpoint - prevSetpoint[axis]) * rxRate;
}
prevDuplicatePacket[axis] = true;
} else {
// there was movement on previous packet, now none
if (bigStep[axis] == true) {
// previous movement was big; likely an early FrSky packet
// don't project these forward or we get a sustained large spike
ffStatus[axis] = 2;
} else {
// likely a dropped packet
// interpolate forward using previous setpoint speed and acceleration
setpointSpeed = prevSetpointSpeed[axis] + prevAcceleration[axis];
// use status = 1 to halve the step for the next packet
ffStatus[axis] = 1;
// movement!
if (prevDuplicatePacket[axis] == true) {
// don't boost the packet after a duplicate, the FF alone is enough, usually
// in part because after a duplicate, the raw up-step is large, so the jitter attenuator is less active
ffAttenuator = 0.0f;
}
prevDuplicatePacket[axis] = false;
}
} else {
// we have movement; let's consider what happened on previous packets, using ffStatus
if (ffStatus[axis] != 0) {
if (ffStatus[axis] == 1) {
// was interpolated forward after previous dropped packet after small step
// this step is likely twice as tall as it should be
setpointSpeed = setpointSpeed / 2.0f;
} else if (ffStatus[axis] == 2) {
// we are doing nothing for these to avoid exaggerating the FrSky early packet problem
} else if (ffStatus[axis] == 3) {
// movement after nothing on previous two packets
// reduce boost when higher averaging is used to improve slow stick smoothness
setpointAccelerationModifier /= (averagingCount + 1);
}
ffStatus[axis] = 0;
// all is normal
}
prevSetpoint[axis] = setpoint;
if (axis == FD_ROLL) {
DEBUG_SET(DEBUG_FF_INTERPOLATED, 2, lrintf(setpoint)); // setpoint after interpolations
}
float setpointAcceleration = setpointSpeed - prevSetpointSpeed[axis];
float absSetpointSpeed = fabsf(setpointSpeed); // unsmoothed for kick prevention
// determine if this step was a relatively large one, to use when evaluating next packet
// calculate acceleration, smooth and attenuate it
setpointAcceleration = setpointSpeed - prevSetpointSpeed[axis];
setpointAcceleration = prevAcceleration[axis] + ffSmoothFactor * (setpointAcceleration - prevAcceleration[axis]);
setpointAcceleration *= ffAttenuator;
if (absSetpointSpeed > 1.5f * absPrevSetpointSpeed || absPrevSetpointSpeed > 1.5f * absSetpointSpeed){
bigStep[axis] = true;
} else {
bigStep[axis] = false;
}
// smooth deadband type suppression of FF jitter when sticks are at or returning to centre
// only when ff_averaging is 3 or more, for HD or cinematic flying
if (averagingCount > 2) {
const float rawSetpointCentred = absRawSetpoint / averagingCount;
if (rawSetpointCentred < 1.0f) {
setpointSpeed *= rawSetpointCentred;
setpointAcceleration *= rawSetpointCentred;
}
}
// smooth setpointSpeed but don't attenuate
setpointSpeed = prevSetpointSpeed[axis] + ffSmoothFactor * (setpointSpeed - prevSetpointSpeed[axis]);
prevSetpointSpeed[axis] = setpointSpeed;
prevAcceleration[axis] = setpointAcceleration;
prevRcCommandDelta[axis] = rcCommandDelta;
// all values afterwards are small numbers
setpointAcceleration *= pidGetDT();
setpointDeltaImpl[axis] = setpointSpeed * pidGetDT();
// calculate boost and prevent kick-back spike at max deflection
const float ffBoostFactor = pidGetFfBoostFactor();
float boostAmount = 0.0f;
if (ffBoostFactor != 0.0f) {
// calculate boost and prevent kick-back spike at max deflection
if (fabsf(rawSetpoint) < 0.95f * ffMaxRate[axis] || absSetpointSpeed > 3.0f * absPrevSetpointSpeed) {
boostAmount = ffBoostFactor * setpointAcceleration * setpointAccelerationModifier;
if (ffBoostFactor) {
if (fabsf(setpoint) < 0.95f * ffMaxRate[axis] || absSetpointSpeed > 3.0f * absPrevSetpointSpeed) {
boostAmount = ffBoostFactor * setpointAcceleration;
}
}
prevSetpointSpeed[axis] = setpointSpeed;
prevRawSetpoint[axis] = rawSetpoint;
if (axis == FD_ROLL) {
DEBUG_SET(DEBUG_FF_INTERPOLATED, 0, lrintf(setpointDeltaImpl[axis] * 100));
DEBUG_SET(DEBUG_FF_INTERPOLATED, 1, lrintf(setpointAcceleration * 100));
DEBUG_SET(DEBUG_FF_INTERPOLATED, 2, lrintf(((setpointDeltaImpl[axis] + boostAmount) * 100)));
DEBUG_SET(DEBUG_FF_INTERPOLATED, 3, ffStatus[axis]);
DEBUG_SET(DEBUG_FF_INTERPOLATED, 0, lrintf(setpointDeltaImpl[axis] * 100.0f)); // base FF
DEBUG_SET(DEBUG_FF_INTERPOLATED, 1, lrintf(boostAmount * 100.0f)); // boost amount
// debug 2 is interpolated setpoint, above
DEBUG_SET(DEBUG_FF_INTERPOLATED, 3, lrintf(rcCommandDelta * 100.0f)); // rcCommand packet difference
}
// first order smoothing of boost to reduce jitter
const float ffSmoothFactor = pidGetFfSmoothFactor();
boostAmount = prevBoostAmount[axis] + ffSmoothFactor * (boostAmount - prevBoostAmount[axis]);
prevBoostAmount[axis] = boostAmount;
// add boost to base feed forward
setpointDeltaImpl[axis] += boostAmount;
// first order smoothing of FF (second order boost filtering since boost filtered twice)
setpointDeltaImpl[axis] = prevDeltaImpl[axis] + ffSmoothFactor * (setpointDeltaImpl[axis] - prevDeltaImpl[axis]);
prevDeltaImpl[axis] = setpointDeltaImpl[axis];
// apply averaging
if (type == FF_INTERPOLATE_ON) {
setpointDelta[axis] = setpointDeltaImpl[axis];

View File

@ -30,7 +30,6 @@
#include "common/axis.h"
#include "common/filter.h"
#include "common/maths.h"
#include "config/config_reset.h"
#include "config/simplified_tuning.h"
@ -206,6 +205,7 @@ void resetPidProfile(pidProfile_t *pidProfile)
.ff_interpolate_sp = FF_INTERPOLATE_ON,
.ff_max_rate_limit = 100,
.ff_smooth_factor = 37,
.ff_jitter_factor = 7,
.ff_boost = 15,
.dyn_lpf_curve_expo = 5,
.level_race_mode = false,
@ -266,6 +266,11 @@ float pidGetFfSmoothFactor()
{
return pidRuntime.ffSmoothFactor;
}
float pidGetFfJitterFactor()
{
return pidRuntime.ffJitterFactor;
}
#endif
void pidResetIterm(void)
@ -315,7 +320,7 @@ float pidCompensateThrustLinearization(float throttle)
if (pidRuntime.thrustLinearization != 0.0f) {
// for whoops where a lot of TL is needed, allow more throttle boost
const float throttleReversed = (1.0f - throttle);
throttle /= 1.0f + pidRuntime.throttleCompensateAmount * powerf(throttleReversed, 2);
throttle /= 1.0f + pidRuntime.throttleCompensateAmount * powf(throttleReversed, 2);
}
return throttle;
}
@ -325,7 +330,7 @@ float pidApplyThrustLinearization(float motorOutput)
if (pidRuntime.thrustLinearization != 0.0f) {
if (motorOutput > 0.0f) {
const float motorOutputReversed = (1.0f - motorOutput);
motorOutput *= 1.0f + powerf(motorOutputReversed, 2) * pidRuntime.thrustLinearization;
motorOutput *= 1.0f + powf(motorOutputReversed, 2) * pidRuntime.thrustLinearization;
}
}
return motorOutput;
@ -638,14 +643,7 @@ float FAST_CODE applyRcSmoothingDerivativeFilter(int axis, float pidSetpointDelt
DEBUG_SET(DEBUG_RC_SMOOTHING, 1, lrintf(pidSetpointDelta * 100.0f));
}
if (pidRuntime.setpointDerivativeLpfInitialized) {
switch (pidRuntime.rcSmoothingFilterType) {
case RC_SMOOTHING_DERIVATIVE_PT1:
ret = pt1FilterApply(&pidRuntime.setpointDerivativePt1[axis], pidSetpointDelta);
break;
case RC_SMOOTHING_DERIVATIVE_BIQUAD:
ret = biquadFilterApplyDF1(&pidRuntime.setpointDerivativeBiquad[axis], pidSetpointDelta);
break;
}
ret = pt3FilterApply(&pidRuntime.setpointDerivativePt3[axis], pidSetpointDelta);
if (axis == pidRuntime.rcSmoothingDebugAxis) {
DEBUG_SET(DEBUG_RC_SMOOTHING, 2, lrintf(ret * 100.0f));
}

View File

@ -210,6 +210,7 @@ typedef struct pidProfile_s {
uint8_t ff_interpolate_sp; // Calculate FF from interpolated setpoint
uint8_t ff_max_rate_limit; // Maximum setpoint rate percentage for FF
uint8_t ff_smooth_factor; // Amount of smoothing for interpolated FF steps
uint8_t ff_jitter_factor; // Number of RC steps below which to attenuate FF
uint8_t dyn_lpf_curve_expo; // set the curve for dynamic dterm lowpass filter
uint8_t level_race_mode; // NFE race mode - when true pitch setpoint calcualtion is gyro based in level mode
uint8_t vbat_sag_compensation; // Reduce motor output by this percentage of the maximum compensation amount
@ -340,8 +341,7 @@ typedef struct pidRuntime_s {
#endif
#ifdef USE_RC_SMOOTHING_FILTER
pt1Filter_t setpointDerivativePt1[XYZ_AXIS_COUNT];
biquadFilter_t setpointDerivativeBiquad[XYZ_AXIS_COUNT];
pt3Filter_t setpointDerivativePt3[XYZ_AXIS_COUNT];
bool setpointDerivativeLpfInitialized;
uint8_t rcSmoothingDebugAxis;
uint8_t rcSmoothingFilterType;
@ -386,6 +386,7 @@ typedef struct pidRuntime_s {
#ifdef USE_INTERPOLATED_SP
ffInterpolationType_t ffFromInterpolatedSetpoint;
float ffSmoothFactor;
float ffJitterFactor;
#endif
} pidRuntime_t;
@ -440,4 +441,5 @@ float pidGetDT();
float pidGetPidFrequency();
float pidGetFfBoostFactor();
float pidGetFfSmoothFactor();
float pidGetFfJitterFactor();
float dynLpfCutoffFreq(float throttle, uint16_t dynLpfMin, uint16_t dynLpfMax, uint8_t expo);

View File

@ -30,7 +30,6 @@
#include "common/axis.h"
#include "common/filter.h"
#include "common/maths.h"
#include "drivers/dshot_command.h"
@ -222,37 +221,22 @@ void pidInit(const pidProfile_t *pidProfile)
}
#ifdef USE_RC_SMOOTHING_FILTER
void pidInitSetpointDerivativeLpf(uint16_t filterCutoff, uint8_t debugAxis, uint8_t filterType)
void pidInitSetpointDerivativeLpf(uint16_t filterCutoff, uint8_t debugAxis)
{
pidRuntime.rcSmoothingDebugAxis = debugAxis;
pidRuntime.rcSmoothingFilterType = filterType;
if ((filterCutoff > 0) && (pidRuntime.rcSmoothingFilterType != RC_SMOOTHING_DERIVATIVE_OFF)) {
if (filterCutoff > 0) {
pidRuntime.setpointDerivativeLpfInitialized = true;
for (int axis = FD_ROLL; axis <= FD_YAW; axis++) {
switch (pidRuntime.rcSmoothingFilterType) {
case RC_SMOOTHING_DERIVATIVE_PT1:
pt1FilterInit(&pidRuntime.setpointDerivativePt1[axis], pt1FilterGain(filterCutoff, pidRuntime.dT));
break;
case RC_SMOOTHING_DERIVATIVE_BIQUAD:
biquadFilterInitLPF(&pidRuntime.setpointDerivativeBiquad[axis], filterCutoff, targetPidLooptime);
break;
}
pt3FilterInit(&pidRuntime.setpointDerivativePt3[axis], pt3FilterGain(filterCutoff, pidRuntime.dT));
}
}
}
void pidUpdateSetpointDerivativeLpf(uint16_t filterCutoff)
{
if ((filterCutoff > 0) && (pidRuntime.rcSmoothingFilterType != RC_SMOOTHING_DERIVATIVE_OFF)) {
if (filterCutoff > 0) {
for (int axis = FD_ROLL; axis <= FD_YAW; axis++) {
switch (pidRuntime.rcSmoothingFilterType) {
case RC_SMOOTHING_DERIVATIVE_PT1:
pt1FilterUpdateCutoff(&pidRuntime.setpointDerivativePt1[axis], pt1FilterGain(filterCutoff, pidRuntime.dT));
break;
case RC_SMOOTHING_DERIVATIVE_BIQUAD:
biquadFilterUpdateLPF(&pidRuntime.setpointDerivativeBiquad[axis], filterCutoff, targetPidLooptime);
break;
}
pt3FilterUpdateCutoff(&pidRuntime.setpointDerivativePt3[axis], pt3FilterGain(filterCutoff, pidRuntime.dT));
}
}
}
@ -378,7 +362,7 @@ void pidInitConfig(const pidProfile_t *pidProfile)
#ifdef USE_THRUST_LINEARIZATION
pidRuntime.thrustLinearization = pidProfile->thrustLinearization / 100.0f;
pidRuntime.throttleCompensateAmount = pidRuntime.thrustLinearization - 0.5f * powerf(pidRuntime.thrustLinearization, 2);
pidRuntime.throttleCompensateAmount = pidRuntime.thrustLinearization - 0.5f * powf(pidRuntime.thrustLinearization, 2);
#endif
#if defined(USE_D_MIN)
@ -401,7 +385,13 @@ void pidInitConfig(const pidProfile_t *pidProfile)
#ifdef USE_INTERPOLATED_SP
pidRuntime.ffFromInterpolatedSetpoint = pidProfile->ff_interpolate_sp;
if (pidProfile->ff_smooth_factor) {
pidRuntime.ffSmoothFactor = 1.0f - ((float)pidProfile->ff_smooth_factor) / 100.0f;
} else {
// set automatically according to boost amount, limit to 0.5 for auto
pidRuntime.ffSmoothFactor = MAX(0.5f, 1.0f - ((float)pidProfile->ff_boost) * 2.0f / 100.0f);
}
pidRuntime.ffJitterFactor = pidProfile->ff_jitter_factor;
interpolatedSpInit(pidProfile);
#endif

View File

@ -24,6 +24,6 @@ void pidInit(const pidProfile_t *pidProfile);
void pidInitFilters(const pidProfile_t *pidProfile);
void pidInitConfig(const pidProfile_t *pidProfile);
void pidSetItermAccelerator(float newItermAccelerator);
void pidInitSetpointDerivativeLpf(uint16_t filterCutoff, uint8_t debugAxis, uint8_t filterType);
void pidInitSetpointDerivativeLpf(uint16_t filterCutoff, uint8_t debugAxis);
void pidUpdateSetpointDerivativeLpf(uint16_t filterCutoff);
void pidCopyProfile(uint8_t dstPidProfileIndex, uint8_t srcPidProfileIndex);

View File

@ -31,14 +31,10 @@
#include "common/printf.h"
#include "common/time.h"
#include "config/feature.h"
#include "drivers/display.h"
#include "drivers/time.h"
#include "io/displayport_crsf.h"
#include "rx/rx.h"
#include "displayport_crsf.h"
#define CRSF_DISPLAY_PORT_OPEN_DELAY_MS 400
#define CRSF_DISPLAY_PORT_CLEAR_DELAY_MS 45
@ -203,18 +199,16 @@ bool crsfDisplayPortIsReady(void)
return (bool)(delayExpired && cmsReady);
}
displayPort_t *displayPortCrsfInit()
static displayPort_t *displayPortCrsfInit()
{
if (featureIsEnabled(FEATURE_TELEMETRY)
&& featureIsEnabled(FEATURE_RX_SERIAL)
&& (rxConfig()->serialrx_provider == SERIALRX_CRSF)) {
crsfDisplayPortSetDimensions(CRSF_DISPLAY_PORT_ROWS_MAX, CRSF_DISPLAY_PORT_COLS_MAX);
displayInit(&crsfDisplayPort, &crsfDisplayPortVTable, DISPLAYPORT_DEVICE_TYPE_CRSF);
return &crsfDisplayPort;
} else {
return NULL;
}
}
void crsfDisplayportRegister(void)
{
cmsDisplayPortRegister(displayPortCrsfInit());
}
#endif

View File

@ -34,7 +34,7 @@ typedef struct crsfDisplayPortScreen_s {
bool reset;
} crsfDisplayPortScreen_t;
displayPort_t *displayPortCrsfInit(void);
void crsfDisplayportRegister(void);
crsfDisplayPortScreen_t *crsfDisplayPortScreen(void);
void crsfDisplayPortMenuOpen(void);
void crsfDisplayPortMenuExit(void);

View File

@ -128,7 +128,7 @@ static const displayPortVTable_t hottVTable = {
.layerCopy = NULL,
};
displayPort_t *displayPortHottInit()
static displayPort_t *displayPortHottInit()
{
hottDisplayPort.device = NULL;
displayInit(&hottDisplayPort, &hottVTable, DISPLAYPORT_DEVICE_TYPE_HOTT);

View File

@ -21,10 +21,6 @@
#pragma once
#include "drivers/display.h"
displayPort_t *displayPortHottInit();
extern displayPort_t hottDisplayPort;
void hottDisplayportRegister();
void hottCmsOpen();
void hottSetCmsKey(uint8_t hottKey, bool esc);

View File

@ -23,20 +23,19 @@
#include <string.h>
#include "platform.h"
#if defined (USE_SPEKTRUM_CMS_TELEMETRY) && defined (USE_CMS) && defined(USE_TELEMETRY_SRXL)
#if defined(USE_SPEKTRUM_CMS_TELEMETRY)
#include "cms/cms.h"
#include "common/utils.h"
#include "config/feature.h"
#include "drivers/display.h"
#include "rx/rx.h"
#include "telemetry/srxl.h"
#include "displayport_srxl.h"
displayPort_t srxlDisplayPort;
static int srxlDrawScreen(displayPort_t *displayPort)
@ -143,20 +142,18 @@ static const displayPortVTable_t srxlVTable = {
.layerCopy = NULL,
};
displayPort_t *displayPortSrxlInit()
static displayPort_t *displayPortSrxlInit()
{
if (featureIsEnabled(FEATURE_TELEMETRY)
&& featureIsEnabled(FEATURE_RX_SERIAL)
&& ((rxConfig()->serialrx_provider == SERIALRX_SRXL) || (rxConfig()->serialrx_provider == SERIALRX_SRXL2))) {
srxlDisplayPort.device = NULL;
displayInit(&srxlDisplayPort, &srxlVTable, DISPLAYPORT_DEVICE_TYPE_SRXL);
srxlDisplayPort.rows = SPEKTRUM_SRXL_TEXTGEN_BUFFER_ROWS;
srxlDisplayPort.cols = SPEKTRUM_SRXL_TEXTGEN_BUFFER_COLS;
return &srxlDisplayPort;
} else {
return NULL;
}
}
void srxlDisplayportRegister(void)
{
cmsDisplayPortRegister(displayPortSrxlInit());
}
#endif

View File

@ -20,6 +20,6 @@
#pragma once
displayPort_t *displayPortSrxlInit();
extern displayPort_t srxlDisplayPort;
void srxlDisplayportRegister(void);

View File

@ -968,11 +968,7 @@ static bool gpsNewFrameNMEA(char c)
gps_Msg.longitude *= -1;
break;
case 6:
if (string[0] > '0') {
ENABLE_STATE(GPS_FIX);
} else {
DISABLE_STATE(GPS_FIX);
}
gpsSetFixState(string[0] > '0');
break;
case 7:
gps_Msg.numSat = grab_fields(string, 0);
@ -1295,11 +1291,7 @@ static bool UBLOX_parse_gps(void)
gpsSol.llh.lon = _buffer.posllh.longitude;
gpsSol.llh.lat = _buffer.posllh.latitude;
gpsSol.llh.altCm = _buffer.posllh.altitudeMslMm / 10; //alt in cm
if (next_fix) {
ENABLE_STATE(GPS_FIX);
} else {
DISABLE_STATE(GPS_FIX);
}
gpsSetFixState(next_fix);
_new_position = true;
break;
case MSG_STATUS:
@ -1517,7 +1509,7 @@ static void GPS_calculateDistanceFlownVerticalSpeed(bool initialize)
if (speed > GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S) {
uint32_t dist;
int32_t dir;
GPS_distance_cm_bearing(&gpsSol.llh.lat, &gpsSol.llh.lon, &lastCoord[LAT], &lastCoord[LON], &dist, &dir);
GPS_distance_cm_bearing(&gpsSol.llh.lat, &gpsSol.llh.lon, &lastCoord[GPS_LATITUDE], &lastCoord[GPS_LONGITUDE], &dist, &dir);
if (gpsConfig()->gps_use_3d_speed) {
dist = sqrtf(powf(gpsSol.llh.altCm - lastAlt, 2.0f) + powf(dist, 2.0f));
}
@ -1527,8 +1519,8 @@ static void GPS_calculateDistanceFlownVerticalSpeed(bool initialize)
GPS_verticalSpeedInCmS = (gpsSol.llh.altCm - lastAlt) * 1000 / (currentMillis - lastMillis);
GPS_verticalSpeedInCmS = constrain(GPS_verticalSpeedInCmS, -1500, 1500);
}
lastCoord[LON] = gpsSol.llh.lon;
lastCoord[LAT] = gpsSol.llh.lat;
lastCoord[GPS_LONGITUDE] = gpsSol.llh.lon;
lastCoord[GPS_LATITUDE] = gpsSol.llh.lat;
lastAlt = gpsSol.llh.altCm;
lastMillis = currentMillis;
}
@ -1537,8 +1529,8 @@ void GPS_reset_home_position(void)
{
if (!STATE(GPS_FIX_HOME) || !gpsConfig()->gps_set_home_point_once) {
if (STATE(GPS_FIX) && gpsSol.numSat >= 5) {
GPS_home[LAT] = gpsSol.llh.lat;
GPS_home[LON] = gpsSol.llh.lon;
GPS_home[GPS_LATITUDE] = gpsSol.llh.lat;
GPS_home[GPS_LONGITUDE] = gpsSol.llh.lon;
GPS_calc_longitude_scaling(gpsSol.llh.lat); // need an initial value for distance and bearing calc
// Set ground altitude
ENABLE_STATE(GPS_FIX_HOME);
@ -1568,7 +1560,7 @@ void GPS_calculateDistanceAndDirectionToHome(void)
if (STATE(GPS_FIX_HOME)) { // If we don't have home set, do not display anything
uint32_t dist;
int32_t dir;
GPS_distance_cm_bearing(&gpsSol.llh.lat, &gpsSol.llh.lon, &GPS_home[LAT], &GPS_home[LON], &dist, &dir);
GPS_distance_cm_bearing(&gpsSol.llh.lat, &gpsSol.llh.lon, &GPS_home[GPS_LATITUDE], &GPS_home[GPS_LONGITUDE], &dist, &dir);
GPS_distanceToHome = dist / 100;
GPS_directionToHome = dir / 100;
} else {
@ -1603,4 +1595,13 @@ void onGpsNewData(void)
#endif
}
void gpsSetFixState(bool state)
{
if (state) {
ENABLE_STATE(GPS_FIX);
ENABLE_STATE(GPS_FIX_EVER);
} else {
DISABLE_STATE(GPS_FIX);
}
}
#endif

View File

@ -25,13 +25,15 @@
#include "pg/pg.h"
#define LAT 0
#define LON 1
#define GPS_DEGREES_DIVIDER 10000000L
#define GPS_X 1
#define GPS_Y 0
typedef enum {
GPS_LATITUDE,
GPS_LONGITUDE
} gpsCoordinateType_e;
typedef enum {
GPS_NMEA = 0,
GPS_UBLOX,
@ -188,4 +190,4 @@ void onGpsNewData(void);
void GPS_reset_home_position(void);
void GPS_calc_longitude_scaling(int32_t lat);
void GPS_distance_cm_bearing(int32_t *currentLat1, int32_t *currentLon1, int32_t *destinationLat2, int32_t *destinationLon2, uint32_t *dist, int32_t *bearing);
void gpsSetFixState(bool state);

View File

@ -1451,6 +1451,8 @@ static bool mspProcessOutCommand(int16_t cmdMSP, sbuf_t *dst)
sbufWriteU16(dst, gpsRescueConfig()->descendRate);
sbufWriteU8(dst, gpsRescueConfig()->allowArmingWithoutFix);
sbufWriteU8(dst, gpsRescueConfig()->altitudeMode);
// Added in API version 1.44
sbufWriteU16(dst, gpsRescueConfig()->minRescueDth);
break;
case MSP_GPS_RESCUE_PIDS:
@ -2451,6 +2453,10 @@ static mspResult_e mspProcessInCommand(mspDescriptor_t srcDesc, int16_t cmdMSP,
gpsRescueConfigMutable()->allowArmingWithoutFix = sbufReadU8(src);
gpsRescueConfigMutable()->altitudeMode = sbufReadU8(src);
}
if (sbufBytesRemaining(src) >= 2) {
// Added in API version 1.44
gpsRescueConfigMutable()->minRescueDth = sbufReadU16(src);
}
break;
case MSP_SET_GPS_RESCUE_PIDS:
@ -2660,7 +2666,7 @@ static mspResult_e mspProcessInCommand(mspDescriptor_t srcDesc, int16_t cmdMSP,
sbufReadU8(src);
#endif
}
if (sbufBytesRemaining(src) >= 1) {
if (sbufBytesRemaining(src) >= 2) {
#if defined(USE_GYRO_DATA_ANALYSE)
// Added in MSP API 1.43
gyroConfigMutable()->dyn_notch_max_hz = sbufReadU16(src);
@ -3175,11 +3181,7 @@ static mspResult_e mspProcessInCommand(mspDescriptor_t srcDesc, int16_t cmdMSP,
#ifdef USE_GPS
case MSP_SET_RAW_GPS:
if (sbufReadU8(src)) {
ENABLE_STATE(GPS_FIX);
} else {
DISABLE_STATE(GPS_FIX);
}
gpsSetFixState(sbufReadU8(src));
gpsSol.numSat = sbufReadU8(src);
gpsSol.llh.lat = sbufReadU32(src);
gpsSol.llh.lon = sbufReadU32(src);

View File

@ -160,6 +160,7 @@ typedef enum {
OSD_EFFICIENCY,
OSD_TOTAL_FLIGHTS,
OSD_UP_DOWN_REFERENCE,
OSD_TX_UPLINK_POWER,
OSD_ITEM_COUNT // MUST BE LAST
} osd_items_e;

View File

@ -59,6 +59,45 @@
CLI parameters should be added before line #endif // end of #ifdef USE_OSD
*/
/*
*********************
OSD element variants:
*********************
Each element can have up to 4 display variants. "Type 1" is always the default and every
every element has an implicit type 1 variant even if no additional options exist. The
purpose is to allow the user to choose a different element display or rendering style to
fit their needs. Like displaying GPS coordinates in a different format, displaying a voltage
with a different number of decimal places, etc. The purpose is NOT to display unrelated
information in different variants of the element. For example it would be inappropriate
to use variants to display RSSI for one type and link quality for another. In this case
they should be separate elements. Remember that element variants are mutually exclusive
and only one type can be displayed at a time. So they shouldn't be used in cases where
the user would want to display different types at the same time - like in the above example
where the user might want to display both RSSI and link quality at the same time.
As variants are added to the firmware, support must also be included in the Configurator.
The following lists the variants implemented so far (please update this as variants are added):
OSD_ALTITUDE
type 1: Altitude with one decimal place
type 2: Altitude with no decimal (whole number only)
OSD_GPS_LON
OSD_GPS_LAT
type 1: Decimal representation with 7 digits
type 2: Decimal representation with 4 digits
type 3: Degrees, minutes, seconds
type 4: Open location code (Google Plus Code)
OSD_MAIN_BATT_USAGE
type 1: Graphical bar showing remaining battery (shrinks as used)
type 2: Graphical bar showing battery used (grows as used)
type 3: Numeric % of remaining battery
type 4: Numeric % or used battery
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@ -122,7 +161,10 @@
#include "sensors/esc_sensor.h"
#include "sensors/sensors.h"
#include "common/maths.h"
#ifdef USE_GPS_PLUS_CODES
// located in lib/main/google/olc
#include "olc.h"
#endif
#define AH_SYMBOL_COUNT 9
#define AH_SIDEBAR_WIDTH_POS 7
@ -273,22 +315,71 @@ static void osdFormatAltitudeString(char * buff, int32_t altitudeCm, osdElementT
}
#ifdef USE_GPS
static void osdFormatCoordinate(char *buff, char sym, int32_t val)
static void osdFormatCoordinate(char *buff, gpsCoordinateType_e coordinateType, osdElementType_e variantType)
{
// latitude maximum integer width is 3 (-90).
// longitude maximum integer width is 4 (-180).
// We show 7 decimals, so we need to use 12 characters:
// eg: s-180.1234567z s=symbol, z=zero terminator, decimal separator between 0 and 1
int32_t gpsValue = 0;
const char leadingSymbol = (coordinateType == GPS_LONGITUDE) ? SYM_LON : SYM_LAT;
// NOTE: Don't use osdPrintFloat() for this. There are too many decimal places and float math doesn't have enough precision
int pos = 0;
buff[pos++] = sym;
if (val < 0) {
buff[pos++] = '-';
val = -val;
if (STATE(GPS_FIX_EVER)) { // don't display interim coordinates until we get the first position fix
gpsValue = (coordinateType == GPS_LONGITUDE) ? gpsSol.llh.lon : gpsSol.llh.lat;
}
const int degreesPart = ABS(gpsValue) / GPS_DEGREES_DIVIDER;
int fractionalPart = ABS(gpsValue) % GPS_DEGREES_DIVIDER;
switch (variantType) {
#ifdef USE_GPS_PLUS_CODES
#define PLUS_CODE_DIGITS 11
case OSD_ELEMENT_TYPE_4: // Open Location Code
{
*buff++ = SYM_SAT_L;
*buff++ = SYM_SAT_R;
if (STATE(GPS_FIX_EVER)) {
OLC_LatLon location;
location.lat = (double)gpsSol.llh.lat / GPS_DEGREES_DIVIDER;
location.lon = (double)gpsSol.llh.lon / GPS_DEGREES_DIVIDER;
OLC_Encode(&location, PLUS_CODE_DIGITS, buff, OSD_ELEMENT_BUFFER_LENGTH - 3);
} else {
memset(buff, SYM_HYPHEN, PLUS_CODE_DIGITS + 1);
buff[8] = '+';
buff[PLUS_CODE_DIGITS + 1] = '\0';
}
break;
}
#endif // USE_GPS_PLUS_CODES
case OSD_ELEMENT_TYPE_3: // degree, minutes, seconds style. ddd^mm'ss.00"W
{
char trailingSymbol;
*buff++ = leadingSymbol;
const int minutes = fractionalPart * 60 / GPS_DEGREES_DIVIDER;
const int fractionalMinutes = fractionalPart * 60 % GPS_DEGREES_DIVIDER;
const int seconds = fractionalMinutes * 60 / GPS_DEGREES_DIVIDER;
const int tenthSeconds = (fractionalMinutes * 60 % GPS_DEGREES_DIVIDER) * 10 / GPS_DEGREES_DIVIDER;
if (coordinateType == GPS_LONGITUDE) {
trailingSymbol = (gpsValue < 0) ? 'W' : 'E';
} else {
trailingSymbol = (gpsValue < 0) ? 'S' : 'N';
}
tfp_sprintf(buff, "%u%c%02u%c%02u.%u%c%c", degreesPart, SYM_GPS_DEGREE, minutes, SYM_GPS_MINUTE, seconds, tenthSeconds, SYM_GPS_SECOND, trailingSymbol);
break;
}
case OSD_ELEMENT_TYPE_2:
fractionalPart /= 1000;
FALLTHROUGH;
case OSD_ELEMENT_TYPE_1:
default:
*buff++ = leadingSymbol;
if (gpsValue < 0) {
*buff++ = SYM_HYPHEN;
}
tfp_sprintf(buff, (variantType == OSD_ELEMENT_TYPE_1 ? "%u.%07u" : "%u.%04u"), degreesPart, fractionalPart);
break;
}
tfp_sprintf(buff + pos, "%d.%07d", val / GPS_DEGREES_DIVIDER, val % GPS_DEGREES_DIVIDER);
}
#endif // USE_GPS
@ -953,14 +1044,15 @@ static void osdElementGpsHomeDistance(osdElementParms_t *element)
}
}
static void osdElementGpsLatitude(osdElementParms_t *element)
static void osdElementGpsCoordinate(osdElementParms_t *element)
{
osdFormatCoordinate(element->buff, SYM_LAT, gpsSol.llh.lat);
const gpsCoordinateType_e coordinateType = (element->item == OSD_GPS_LON) ? GPS_LONGITUDE : GPS_LATITUDE;
osdFormatCoordinate(element->buff, coordinateType, element->type);
if (STATE(GPS_FIX_EVER) && !STATE(GPS_FIX)) {
SET_BLINK(element->item); // blink if we had a fix but have since lost it
} else {
CLR_BLINK(element->item);
}
static void osdElementGpsLongitude(osdElementParms_t *element)
{
osdFormatCoordinate(element->buff, SYM_LON, gpsSol.llh.lon);
}
static void osdElementGpsSats(osdElementParms_t *element)
@ -1036,6 +1128,18 @@ static void osdElementLinkQuality(osdElementParms_t *element)
}
#endif // USE_RX_LINK_QUALITY_INFO
#ifdef USE_RX_LINK_UPLINK_POWER
static void osdElementTxUplinkPower(osdElementParms_t *element)
{
const uint16_t osdUplinkTxPowerMw = rxGetUplinkTxPwrMw();
if (osdUplinkTxPowerMw < 1000) {
tfp_sprintf(element->buff, "%c%3dMW", SYM_RSSI, osdUplinkTxPowerMw);
} else {
osdPrintFloat(element->buff, SYM_RSSI, osdUplinkTxPowerMw / 1000.0f, "", 1, false, 'W');
}
}
#endif // USE_RX_LINK_UPLINK_POWER
#ifdef USE_BLACKBOX
static void osdElementLogStatus(osdElementParms_t *element)
{
@ -1422,6 +1526,9 @@ static const uint8_t osdElementDisplayOrder[] = {
#ifdef USE_RX_LINK_QUALITY_INFO
OSD_LINK_QUALITY,
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
OSD_TX_UPLINK_POWER,
#endif
#ifdef USE_RX_RSSI_DBM
OSD_RSSI_DBM_VALUE,
#endif
@ -1478,8 +1585,8 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
[OSD_WARNINGS] = osdElementWarnings,
[OSD_AVG_CELL_VOLTAGE] = osdElementAverageCellVoltage,
#ifdef USE_GPS
[OSD_GPS_LON] = osdElementGpsLongitude,
[OSD_GPS_LAT] = osdElementGpsLatitude,
[OSD_GPS_LON] = osdElementGpsCoordinate,
[OSD_GPS_LAT] = osdElementGpsCoordinate,
#endif
[OSD_DEBUG] = osdElementDebug,
#ifdef USE_ACC
@ -1527,6 +1634,9 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
#ifdef USE_RX_LINK_QUALITY_INFO
[OSD_LINK_QUALITY] = osdElementLinkQuality,
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
[OSD_TX_UPLINK_POWER] = osdElementTxUplinkPower,
#endif
#ifdef USE_GPS
[OSD_FLIGHT_DIST] = osdElementGpsFlightDistance,
#endif

View File

@ -66,9 +66,7 @@ void pgResetFn_rxConfig(rxConfig_t *rxConfig)
.rc_smoothing_input_cutoff = 0, // automatically calculate the cutoff by default
.rc_smoothing_derivative_cutoff = 0, // automatically calculate the cutoff by default
.rc_smoothing_debug_axis = ROLL, // default to debug logging for the roll axis
.rc_smoothing_input_type = RC_SMOOTHING_INPUT_BIQUAD,
.rc_smoothing_derivative_type = RC_SMOOTHING_DERIVATIVE_AUTO, // automatically choose type based on feedforward method
.rc_smoothing_auto_factor = 10,
.rc_smoothing_auto_factor = 30,
.srxl2_unit_id = 1,
.srxl2_baud_fast = true,
.sbus_baud_fast = false,

View File

@ -45,7 +45,17 @@ void pgResetFn_sdcardConfig(sdcardConfig_t *config)
// On generic targets, SPI has precedence over SDIO; SDIO must be post-flash configured.
config->useDma = false;
config->device = SPI_DEV_TO_CFG(SPIINVALID);
#ifdef CONFIG_IN_SDCARD
// CONFIG_ID_SDDCARD requires a default mode.
#if defined(USE_SDCARD_SDIO)
config->mode = SDCARD_MODE_SDIO;
#elif defined(USE_SDCARD_SPI)
config->mode = SDCARD_MODE_SPI;
#endif
#else
config->mode = SDCARD_MODE_NONE;
#endif
#ifdef USE_SDCARD_SPI
// These settings do not work for Unified Targets

View File

@ -70,6 +70,12 @@ static uint8_t telemetryBufLen = 0;
static timeUs_t lastRcFrameTimeUs = 0;
#ifdef USE_RX_LINK_UPLINK_POWER
#define CRSF_UPLINK_POWER_LEVEL_MW_ITEMS_COUNT 8
// Uplink power levels by uplinkTXPower expressed in mW (250 mW is from ver >=4.00)
const uint16_t uplinkTXPowerStatesMw[CRSF_UPLINK_POWER_LEVEL_MW_ITEMS_COUNT] = {0, 10, 25, 100, 500, 1000, 2000, 250};
#endif
/*
* CRSF protocol
*
@ -130,7 +136,7 @@ typedef struct crsfPayloadRcChannelsPacked_s crsfPayloadRcChannelsPacked_t;
* int8_t Uplink SNR ( db )
* uint8_t Diversity active antenna ( enum ant. 1 = 0, ant. 2 )
* uint8_t RF Mode ( enum 4fps = 0 , 50fps, 150hz)
* uint8_t Uplink TX Power ( enum 0mW = 0, 10mW, 25 mW, 100 mW, 500 mW, 1000 mW, 2000mW )
* uint8_t Uplink TX Power ( enum 0mW = 0, 10mW, 25 mW, 100 mW, 500 mW, 1000 mW, 2000mW, 250mW )
* uint8_t Downlink RSSI ( dBm * -1 )
* uint8_t Downlink package success rate / Link quality ( % )
* int8_t Downlink SNR ( db )
@ -175,6 +181,11 @@ static void handleCrsfLinkStatisticsFrame(const crsfLinkStatistics_t* statsPtr,
}
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
const uint8_t crsfUplinkPowerStatesItemIndex = (stats.uplink_TX_Power < CRSF_UPLINK_POWER_LEVEL_MW_ITEMS_COUNT) ? stats.uplink_TX_Power : 0;
rxSetUplinkTxPwrMw(uplinkTXPowerStatesMw[crsfUplinkPowerStatesItemIndex]);
#endif
switch (debugMode) {
case DEBUG_CRSF_LINK_STATISTICS_UPLINK:
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 0, stats.uplink_RSSI_1);
@ -345,7 +356,7 @@ STATIC_UNIT_TESTED uint8_t crsfFrameStatus(rxRuntimeState_t *rxRuntimeState)
return RX_FRAME_PENDING;
}
STATIC_UNIT_TESTED uint16_t crsfReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
STATIC_UNIT_TESTED float crsfReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
UNUSED(rxRuntimeState);
/* conversion from RC value to PWM
@ -356,7 +367,7 @@ STATIC_UNIT_TESTED uint16_t crsfReadRawRC(const rxRuntimeState_t *rxRuntimeState
* scale factor = (2012-988) / (1811-172) = 0.62477120195241
* offset = 988 - 172 * 0.62477120195241 = 880.53935326418548
*/
return (0.62477120195241f * crsfChannelData[chan]) + 881;
return (0.62477120195241f * (float)crsfChannelData[chan]) + 881;
}
void crsfRxWriteTelemetryData(const void *data, int len)

View File

@ -289,7 +289,7 @@ static bool ghstProcessFrame(const rxRuntimeState_t *rxRuntimeState)
return true;
}
STATIC_UNIT_TESTED uint16_t ghstReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
STATIC_UNIT_TESTED float ghstReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
UNUSED(rxRuntimeState);
@ -302,7 +302,7 @@ STATIC_UNIT_TESTED uint16_t ghstReadRawRC(const rxRuntimeState_t *rxRuntimeState
// max 1024 1811 2012us
//
return (5 * (ghstChannelData[chan]+1) / 8) + 880;
return (5 * ((float)ghstChannelData[chan] + 1) / 8) + 880;
}
static timeUs_t ghstFrameTimeUs(void)

View File

@ -196,7 +196,7 @@ static uint8_t ibusFrameStatus(rxRuntimeState_t *rxRuntimeState)
}
static uint16_t ibusReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float ibusReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
UNUSED(rxRuntimeState);
return ibusChannelData[chan];

View File

@ -237,7 +237,7 @@ static uint8_t jetiExBusFrameStatus(rxRuntimeState_t *rxRuntimeState)
return frameStatus;
}
static uint16_t jetiExBusReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float jetiExBusReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
if (chan >= rxRuntimeState->channelCount)
return 0;

View File

@ -36,7 +36,7 @@
static uint16_t mspFrame[MAX_SUPPORTED_RC_CHANNEL_COUNT];
static bool rxMspFrameDone = false;
uint16_t rxMspReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
float rxMspReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
UNUSED(rxRuntimeState);
return mspFrame[chan];

View File

@ -22,6 +22,6 @@
struct rxConfig_s;
struct rxRuntimeState_s;
uint16_t rxMspReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan);
float rxMspReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan);
void rxMspInit(const struct rxConfig_s *rxConfig, struct rxRuntimeState_s *rxRuntimeState);
void rxMspFrameReceive(uint16_t *frame, int channelCount);

View File

@ -41,13 +41,13 @@
#include "rx/rx.h"
#include "rx/pwm.h"
static uint16_t pwmReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
static float pwmReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
{
UNUSED(rxRuntimeState);
return pwmRead(channel);
}
static uint16_t ppmReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
static float ppmReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
{
UNUSED(rxRuntimeState);
return ppmRead(channel);

View File

@ -85,6 +85,10 @@ static uint16_t linkQuality = 0;
static uint8_t rfMode = 0;
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
static uint16_t uplinkTxPwrMw = 0; //Uplink Tx power in mW
#endif
#define MSP_RSSI_TIMEOUT_US 1500000 // 1.5 sec
#define RSSI_ADC_DIVISOR (4096 / 1024)
@ -107,8 +111,8 @@ static uint32_t needRxSignalMaxDelayUs;
static uint32_t suspendRxSignalUntil = 0;
static uint8_t skipRxSamples = 0;
static int16_t rcRaw[MAX_SUPPORTED_RC_CHANNEL_COUNT]; // interval [1000;2000]
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT]; // interval [1000;2000]
static float rcRaw[MAX_SUPPORTED_RC_CHANNEL_COUNT]; // interval [1000;2000]
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT]; // interval [1000;2000]
uint32_t rcInvalidPulsPeriod[MAX_SUPPORTED_RC_CHANNEL_COUNT];
#define MAX_INVALID_PULS_TIME 300
@ -154,7 +158,7 @@ void resetAllRxChannelRangeConfigurations(rxChannelRangeConfig_t *rxChannelRange
}
}
static uint16_t nullReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
static float nullReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
{
UNUSED(rxRuntimeState);
UNUSED(channel);
@ -455,6 +459,13 @@ void setLinkQualityDirect(uint16_t linkqualityValue)
#endif
}
#ifdef USE_RX_LINK_UPLINK_POWER
void rxSetUplinkTxPwrMw(uint16_t uplinkTxPwrMwValue)
{
uplinkTxPwrMw = uplinkTxPwrMwValue;
}
#endif
bool rxUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs)
{
bool signalReceived = false;
@ -584,15 +595,15 @@ static uint16_t getRxfailValue(uint8_t channel)
}
}
STATIC_UNIT_TESTED uint16_t applyRxChannelRangeConfiguraton(int sample, const rxChannelRangeConfig_t *range)
STATIC_UNIT_TESTED float applyRxChannelRangeConfiguraton(float sample, const rxChannelRangeConfig_t *range)
{
// Avoid corruption of channel with a value of PPM_RCVR_TIMEOUT
if (sample == PPM_RCVR_TIMEOUT) {
return PPM_RCVR_TIMEOUT;
}
sample = scaleRange(sample, range->min, range->max, PWM_RANGE_MIN, PWM_RANGE_MAX);
sample = constrain(sample, PWM_PULSE_MIN, PWM_PULSE_MAX);
sample = scaleRangef(sample, range->min, range->max, PWM_RANGE_MIN, PWM_RANGE_MAX);
sample = constrainf(sample, PWM_PULSE_MIN, PWM_PULSE_MAX);
return sample;
}
@ -604,7 +615,7 @@ static void readRxChannelsApplyRanges(void)
const uint8_t rawChannel = channel < RX_MAPPABLE_CHANNEL_COUNT ? rxConfig()->rcmap[channel] : channel;
// sample the channel
uint16_t sample;
float sample;
#if defined(USE_RX_MSP_OVERRIDE)
if (rxConfig()->msp_override_channels_mask) {
sample = rxMspOverrideReadRawRc(&rxRuntimeState, rxConfig(), rawChannel);
@ -634,7 +645,7 @@ static void detectAndApplySignalLossBehaviour(void)
rxFlightChannelsValid = true;
for (int channel = 0; channel < rxChannelCount; channel++) {
uint16_t sample = rcRaw[channel];
float sample = rcRaw[channel];
const bool validPulse = useValueFromRx && isPulseValid(sample);
@ -881,6 +892,13 @@ uint16_t rxGetLinkQualityPercent(void)
}
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
uint16_t rxGetUplinkTxPwrMw(void)
{
return uplinkTxPwrMw;
}
#endif
uint16_t rxGetRefreshRate(void)
{
return rxRuntimeState.rxRefreshRate;

View File

@ -86,7 +86,7 @@ typedef enum {
extern const char rcChannelLetters[];
extern int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT]; // interval [1000;2000]
extern float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT]; // interval [1000;2000]
#define RSSI_SCALE_MIN 1
#define RSSI_SCALE_MAX 255
@ -124,7 +124,7 @@ typedef struct rxChannelRangeConfig_s {
PG_DECLARE_ARRAY(rxChannelRangeConfig_t, NON_AUX_CHANNEL_COUNT, rxChannelRangeConfigs);
struct rxRuntimeState_s;
typedef uint16_t (*rcReadRawDataFnPtr)(const struct rxRuntimeState_s *rxRuntimeState, uint8_t chan); // used by receiver driver to return channel data
typedef float (*rcReadRawDataFnPtr)(const struct rxRuntimeState_s *rxRuntimeState, uint8_t chan); // used by receiver driver to return channel data
typedef uint8_t (*rcFrameStatusFnPtr)(struct rxRuntimeState_s *rxRuntimeState);
typedef bool (*rcProcessFrameFnPtr)(const struct rxRuntimeState_s *rxRuntimeState);
typedef timeUs_t rcGetFrameTimeUsFn(void); // used to retrieve the timestamp in microseconds for the last channel data frame
@ -206,6 +206,9 @@ void setRssiDbmDirect(int16_t newRssiDbm, rssiSource_e source);
void rxSetRfMode(uint8_t rfModeValue);
uint8_t rxGetRfMode(void);
void rxSetUplinkTxPwrMw(uint16_t uplinkTxPwrMwValue);
uint16_t rxGetUplinkTxPwrMw(void);
void resetAllRxChannelRangeConfigurations(rxChannelRangeConfig_t *rxChannelRangeConfig);
void suspendRxPwmPpmSignal(void);

View File

@ -67,7 +67,7 @@ static protocolDataReceivedFnPtr protocolDataReceived;
static protocolProcessFrameFnPtr protocolProcessFrame;
static protocolSetRcDataFromPayloadFnPtr protocolSetRcDataFromPayload;
STATIC_UNIT_TESTED uint16_t rxSpiReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
STATIC_UNIT_TESTED float rxSpiReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
{
STATIC_ASSERT(NRF24L01_MAX_PAYLOAD_SIZE <= RX_SPI_MAX_PAYLOAD_SIZE, NRF24L01_MAX_PAYLOAD_SIZE_larger_than_RX_SPI_MAX_PAYLOAD_SIZE);

View File

@ -87,11 +87,11 @@ uint8_t sbusChannelsDecode(rxRuntimeState_t *rxRuntimeState, const sbusChannels_
return RX_FRAME_COMPLETE;
}
static uint16_t sbusChannelsReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float sbusChannelsReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
// Linear fitting values read from OpenTX-ppmus and comparing with values received by X4R
// http://www.wolframalpha.com/input/?i=linear+fit+%7B173%2C+988%7D%2C+%7B1812%2C+2012%7D%2C+%7B993%2C+1500%7D
return (5 * rxRuntimeState->channelData[chan] / 8) + 880;
return (5 * (float)rxRuntimeState->channelData[chan] / 8) + 880;
}
void sbusChannelsInit(const rxConfig_t *rxConfig, rxRuntimeState_t *rxRuntimeState)

View File

@ -174,18 +174,19 @@ static uint8_t spektrumFrameStatus(rxRuntimeState_t *rxRuntimeState)
return result;
}
static uint16_t spektrumReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float spektrumReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
uint16_t data;
float data;
if (chan >= rxRuntimeState->channelCount) {
return 0;
}
if (spekHiRes)
data = 988 + (spekChannelData[chan] >> 1); // 2048 mode
else
data = 988 + spekChannelData[chan]; // 1024 mode
if (spekHiRes) {
data = 0.5f * (float)spekChannelData[chan] + 988; // 2048 mode
} else {
data = spekChannelData[chan] + 988; // 1024 mode
}
return data;
}

View File

@ -55,7 +55,7 @@
#define SRXL2_MAX_CHANNELS 32
#define SRXL2_FRAME_PERIOD_US 11000 // 5500 for DSMR
#define SRXL2_CHANNEL_SHIFT 5
#define SRXL2_CHANNEL_SHIFT 2
#define SRXL2_CHANNEL_CENTER 0x8000
#define SRXL2_PORT_BAUDRATE_DEFAULT 115200
@ -458,13 +458,13 @@ static bool srxl2ProcessFrame(const rxRuntimeState_t *rxRuntimeState)
return true;
}
static uint16_t srxl2ReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channelIdx)
static float srxl2ReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channelIdx)
{
if (channelIdx >= rxRuntimeState->channelCount) {
return 0;
}
return SPEKTRUM_PULSE_OFFSET + ((rxRuntimeState->channelData[channelIdx] >> SRXL2_CHANNEL_SHIFT) >> 1);
return ((float)(rxRuntimeState->channelData[channelIdx] >> SRXL2_CHANNEL_SHIFT) / 16) + SPEKTRUM_PULSE_OFFSET;
}
void srxl2RxWriteData(const void *data, int len)

View File

@ -162,10 +162,10 @@ static uint8_t sumdFrameStatus(rxRuntimeState_t *rxRuntimeState)
return frameStatus;
}
static uint16_t sumdReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float sumdReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
UNUSED(rxRuntimeState);
return sumdChannels[chan] / 8;
return (float)sumdChannels[chan] / 8;
}
static timeUs_t sumdFrameTimeUsFn(void)

View File

@ -108,7 +108,7 @@ static uint8_t sumhFrameStatus(rxRuntimeState_t *rxRuntimeState)
return RX_FRAME_COMPLETE;
}
static uint16_t sumhReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float sumhReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
UNUSED(rxRuntimeState);

View File

@ -248,7 +248,7 @@ static uint8_t xBusFrameStatus(rxRuntimeState_t *rxRuntimeState)
return RX_FRAME_COMPLETE;
}
static uint16_t xBusReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float xBusReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
uint16_t data;

View File

@ -191,7 +191,7 @@ void currentMeterVirtualRefresh(int32_t lastUpdateAt, bool armed, bool throttleL
throttleOffset = 0;
}
int throttleFactor = throttleOffset + (throttleOffset * throttleOffset / 50); // FIXME magic number 50, 50hz?
int throttleFactor = throttleOffset + (throttleOffset * throttleOffset / 50); // FIXME magic number 50. Possibly use thrustLinearization if configured.
currentMeterVirtualState.amperage += throttleFactor * (int32_t)currentSensorVirtualConfig()->scale / 1000;
}
updateCurrentmAhDrawnState(&currentMeterVirtualState.mahDrawnState, currentMeterVirtualState.amperage, lastUpdateAt);

View File

@ -82,12 +82,12 @@ PG_DECLARE(currentSensorADCConfig_t, currentSensorADCConfig);
typedef struct currentMeterVirtualState_s {
currentMeterMAhDrawnState_t mahDrawnState;
int32_t amperage; // current read by current sensor in centiampere (1/100th A)
int32_t amperage; // current read by current sensor in centiamperes (1/100th A)
} currentSensorVirtualState_t;
typedef struct currentSensorVirtualConfig_s {
int16_t scale; // scale the current sensor output voltage to milliamps. Value in 1/10th mV/A
uint16_t offset; // offset of the current sensor in millivolt steps
int16_t scale; // scale the throttle to centiamperes, using a hardcoded thrust linearization function (see Battery.md)
uint16_t offset; // offset of the current sensor in centiamperes (1/100th A)
} currentSensorVirtualConfig_t;
PG_DECLARE(currentSensorVirtualConfig_t, currentSensorVirtualConfig);

View File

@ -505,7 +505,7 @@ static bool gyroDetectSensor(gyroSensor_t *gyroSensor, const gyroDeviceConfig_t
{
#if defined(USE_GYRO_MPU6050) || defined(USE_GYRO_MPU3050) || defined(USE_GYRO_MPU6500) || defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU6000) \
|| defined(USE_ACC_MPU6050) || defined(USE_GYRO_SPI_MPU9250) || defined(USE_GYRO_SPI_ICM20601) || defined(USE_GYRO_SPI_ICM20649) \
|| defined(USE_GYRO_SPI_ICM20689) || defined(USE_GYRO_L3GD20) || defined(USE_ACCGYRO_BMI160) || defined(USE_ACCGYRO_BMI270) || defined(USE_ACCGYRO_LSM6DSO)
|| defined(USE_GYRO_SPI_ICM20689) || defined(USE_GYRO_L3GD20) || defined(USE_ACCGYRO_BMI160) || defined(USE_ACCGYRO_BMI270) || defined(USE_ACCGYRO_LSM6DSO) || defined(USE_GYRO_SPI_ICM42605)
bool gyroFound = mpuDetect(&gyroSensor->gyroDev, config);

View File

@ -201,7 +201,7 @@ static uint8_t frameStatus(rxRuntimeState_t *rxRuntimeState)
return RX_FRAME_COMPLETE;
}
static uint16_t readRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
static float readRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t chan)
{
if (chan >= rxRuntimeState->channelCount) {
return 0;

View File

@ -139,6 +139,42 @@
#define TARGET_IO_PORTD 0xffff
#define TARGET_IO_PORTE 0xffff
#define TARGET_IO_PORTF 0xffff
#elif defined(STM32G47X)
#define TARGET_BOARD_IDENTIFIER "SG47"
#define USBD_PRODUCT_STRING "Betaflight STM32G47x"
#define USE_I2C_DEVICE_1
#define USE_I2C_DEVICE_2
#define USE_I2C_DEVICE_3
#define USE_I2C_DEVICE_4
#define USE_UART1
#define USE_UART2
#define USE_UART3
#define USE_UART4
#define USE_UART5
#define USE_UART9
#define SERIAL_PORT_COUNT (UNIFIED_SERIAL_PORT_COUNT + 6)
#define USE_SPI_DEVICE_1
#define USE_SPI_DEVICE_2
#define USE_SPI_DEVICE_3
#define USE_SPI_DEVICE_4
#define TARGET_IO_PORTA 0xffff
#define TARGET_IO_PORTB 0xffff
#define TARGET_IO_PORTC 0xffff
#define TARGET_IO_PORTD 0xffff
#define TARGET_IO_PORTE 0xffff
#define TARGET_IO_PORTF 0xffff
#elif !defined(UNIT_TEST)
#error "No resources defined for this Unified Target."
#endif
// Treat the target as unified, and expect manufacturer id / board name
@ -190,9 +226,12 @@
#define USE_BARO_DPS310
#define USE_BARO_SPI_DPS310
#if !defined(STM32G4)
// G4 support needs fixing
#define USE_SDCARD
#define USE_SDCARD_SPI
#define USE_SDCARD_SDIO
#endif
#define USE_FLASHFS
#define USE_FLASH_TOOLS
@ -207,11 +246,14 @@
#define USE_VTX_RTC6705
#define USE_VTX_RTC6705_SOFTSPI
#if !defined(STM32G4)
// To make it fit RAM
#define USE_TRANSPONDER
#define USE_RANGEFINDER
#define USE_RANGEFINDER_HCSR04
#define USE_RANGEFINDER_TF
#endif
#define USE_SPI
#define SPI_FULL_RECONFIGURABILITY

View File

@ -9,12 +9,17 @@ else
ifeq ($(TARGET), STM32F7X2)
F7X2RE_TARGETS += $(TARGET)
else
ifeq ($(TARGET), STM32G47X)
G47X_TARGETS += $(TARGET)
else # STM32F745
F7X5XG_TARGETS += $(TARGET)
endif
endif
endif
endif
ifeq ($(TARGET), $(filter $(TARGET), STM32F405 STM32F745))
# Use a full block (16 kB) of flash for custom defaults - with 1 MB flash we have more than we know how to use anyway
@ -22,8 +27,11 @@ ifeq ($(TARGET), $(filter $(TARGET), STM32F405 STM32F745))
CUSTOM_DEFAULTS_EXTENDED = yes
endif
ifeq ($(TARGET), STM32G47X)
FEATURES += VCP ONBOARDFLASH
else
FEATURES += VCP SDCARD_SPI SDCARD_SDIO ONBOARDFLASH
endif
TARGET_SRC = \
$(addprefix drivers/accgyro/,$(notdir $(wildcard $(SRC_DIR)/drivers/accgyro/*.c))) \

View File

@ -90,7 +90,6 @@
#endif
#if !defined(USE_TELEMETRY)
#undef USE_CRSF_CMS_TELEMETRY
#undef USE_TELEMETRY_CRSF
#undef USE_TELEMETRY_GHST
#undef USE_TELEMETRY_FRSKY_HUB
@ -136,10 +135,13 @@
#undef USE_SPEKTRUM_RSSI_PERCENT_CONVERSION
#undef USE_SPEKTRUM_VTX_CONTROL
#undef USE_SPEKTRUM_VTX_TELEMETRY
#undef USE_SPEKTRUM_CMS_TELEMETRY
#undef USE_TELEMETRY_SRXL
#endif
#if !defined(USE_CMS) || !defined(USE_TELEMETRY_SRXL)
#undef USE_SPEKTRUM_CMS_TELEMETRY
#endif
#if defined(USE_SERIALRX_SBUS) || defined(USE_SERIALRX_FPORT)
#define USE_SBUS_CHANNELS
#endif
@ -229,6 +231,7 @@
#undef USE_RX_LINK_QUALITY_INFO
#undef USE_OSD_PROFILES
#undef USE_OSD_STICK_OVERLAY
#undef USE_RX_LINK_UPLINK_POWER
#endif
#if defined(USE_GPS_RESCUE)
@ -240,7 +243,7 @@
#define USE_I2C_GYRO
#endif
#if defined(USE_GYRO_SPI_ICM20689) || defined(USE_GYRO_SPI_MPU6000) || defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250) || defined(USE_GYRO_L3GD20)
#if defined(USE_GYRO_SPI_ICM20689) || defined(USE_GYRO_SPI_MPU6000) || defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250) || defined(USE_GYRO_L3GD20) || defined(USE_GYRO_SPI_ICM42605)
#define USE_SPI_GYRO
#endif
@ -406,3 +409,7 @@ extern uint8_t __config_end;
#if defined(USE_RX_SPI) || defined (USE_SERIALRX_SRXL2)
#define USE_RX_BIND
#endif
#ifndef USE_GPS
#undef USE_GPS_PLUS_CODES
#endif

View File

@ -397,4 +397,6 @@
#define USE_BATTERY_VOLTAGE_SAG_COMPENSATION
#define USE_RX_MSP_OVERRIDE
#define USE_SIMPLIFIED_TUNING
#define USE_RX_LINK_UPLINK_POWER
#define USE_GPS_PLUS_CODES
#endif

View File

@ -30,21 +30,19 @@
#include "build/build_config.h"
#include "build/version.h"
#include "config/feature.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "cms/cms.h"
#include "config/feature.h"
#include "config/config.h"
#include "common/crc.h"
#include "common/maths.h"
#include "common/printf.h"
#include "common/streambuf.h"
#include "common/utils.h"
#include "cms/cms.h"
#include "drivers/nvic.h"
#include "config/config.h"
#include "fc/rc_modes.h"
#include "fc/runtime_config.h"
@ -55,6 +53,9 @@
#include "io/gps.h"
#include "io/serial.h"
#include "pg/pg.h"
#include "pg/pg_ids.h"
#include "rx/crsf.h"
#include "rx/crsf_protocol.h"
@ -64,7 +65,7 @@
#include "telemetry/telemetry.h"
#include "telemetry/msp_shared.h"
#include "telemetry/crsf.h"
#include "crsf.h"
#define CRSF_CYCLETIME_US 100000 // 100ms, 10 Hz
@ -525,6 +526,10 @@ void initCrsfTelemetry(void)
}
#endif
crsfScheduleCount = (uint8_t)index;
#if defined(USE_CRSF_CMS_TELEMETRY)
crsfDisplayportRegister();
#endif
}
bool checkCrsfTelemetryState(void)

View File

@ -257,15 +257,15 @@ static void GPStoDDDMM_MMMM(int32_t mwiigps, gpsCoordinateDDDMMmmmm_t *result)
static void sendLatLong(int32_t coord[2])
{
gpsCoordinateDDDMMmmmm_t coordinate;
GPStoDDDMM_MMMM(coord[LAT], &coordinate);
GPStoDDDMM_MMMM(coord[GPS_LATITUDE], &coordinate);
frSkyHubWriteFrame(ID_LATITUDE_BP, coordinate.dddmm);
frSkyHubWriteFrame(ID_LATITUDE_AP, coordinate.mmmm);
frSkyHubWriteFrame(ID_N_S, coord[LAT] < 0 ? 'S' : 'N');
frSkyHubWriteFrame(ID_N_S, coord[GPS_LATITUDE] < 0 ? 'S' : 'N');
GPStoDDDMM_MMMM(coord[LON], &coordinate);
GPStoDDDMM_MMMM(coord[GPS_LONGITUDE], &coordinate);
frSkyHubWriteFrame(ID_LONGITUDE_BP, coordinate.dddmm);
frSkyHubWriteFrame(ID_LONGITUDE_AP, coordinate.mmmm);
frSkyHubWriteFrame(ID_E_W, coord[LON] < 0 ? 'W' : 'E');
frSkyHubWriteFrame(ID_E_W, coord[GPS_LONGITUDE] < 0 ? 'W' : 'E');
}
#if defined(USE_GPS)
@ -316,8 +316,8 @@ static void sendFakeLatLong(void)
// Heading is only displayed on OpenTX if non-zero lat/long is also sent
int32_t coord[2] = {0,0};
coord[LAT] = ((0.01f * telemetryConfig()->gpsNoFixLatitude) * GPS_DEGREES_DIVIDER);
coord[LON] = ((0.01f * telemetryConfig()->gpsNoFixLongitude) * GPS_DEGREES_DIVIDER);
coord[GPS_LATITUDE] = ((0.01f * telemetryConfig()->gpsNoFixLatitude) * GPS_DEGREES_DIVIDER);
coord[GPS_LONGITUDE] = ((0.01f * telemetryConfig()->gpsNoFixLongitude) * GPS_DEGREES_DIVIDER);
sendLatLong(coord);
}
@ -330,8 +330,8 @@ static void sendGPSLatLong(void)
if (STATE(GPS_FIX) || gpsFixOccured == 1) {
// If we have ever had a fix, send the last known lat/long
gpsFixOccured = 1;
coord[LAT] = gpsSol.llh.lat;
coord[LON] = gpsSol.llh.lon;
coord[GPS_LATITUDE] = gpsSol.llh.lat;
coord[GPS_LONGITUDE] = gpsSol.llh.lon;
sendLatLong(coord);
} else {
// otherwise send fake lat/long in order to display compass value

View File

@ -213,8 +213,8 @@ static void ltm_oframe(void)
{
ltm_initialise_packet('O');
#if defined(USE_GPS)
ltm_serialise_32(GPS_home[LAT]);
ltm_serialise_32(GPS_home[LON]);
ltm_serialise_32(GPS_home[GPS_LATITUDE]);
ltm_serialise_32(GPS_home[GPS_LONGITUDE]);
#else
ltm_serialise_32(0);
ltm_serialise_32(0);

View File

@ -367,9 +367,9 @@ void mavlinkSendPosition(void)
mavlink_msg_gps_global_origin_pack(0, 200, &mavMsg,
// latitude Latitude (WGS84), expressed as * 1E7
GPS_home[LAT],
GPS_home[GPS_LATITUDE],
// longitude Longitude (WGS84), expressed as * 1E7
GPS_home[LON],
GPS_home[GPS_LONGITUDE],
// altitude Altitude(WGS84), expressed as * 1000
0);
msgLength = mavlink_msg_to_send_buffer(mavBuffer, &mavMsg);

View File

@ -29,46 +29,44 @@
#include "build/version.h"
#include "cms/cms.h"
#include "io/displayport_srxl.h"
#include "common/crc.h"
#include "common/streambuf.h"
#include "common/utils.h"
#include "config/config.h"
#include "config/feature.h"
#include "io/gps.h"
#include "io/serial.h"
#include "drivers/dshot.h"
#include "drivers/vtx_common.h"
#include "config/config.h"
#include "fc/rc_controls.h"
#include "fc/runtime_config.h"
#include "flight/imu.h"
#include "flight/mixer.h"
#include "io/displayport_srxl.h"
#include "io/gps.h"
#include "io/serial.h"
#include "io/vtx_smartaudio.h"
#include "io/vtx_tramp.h"
#include "pg/rx.h"
#include "pg/motor.h"
#include "rx/rx.h"
#include "rx/spektrum.h"
#include "rx/srxl2.h"
#include "io/spektrum_vtx_control.h"
#include "rx/srxl2.h"
#include "sensors/battery.h"
#include "sensors/adcinternal.h"
#include "sensors/battery.h"
#include "sensors/esc_sensor.h"
#include "telemetry/telemetry.h"
#include "telemetry/srxl.h"
#include "drivers/vtx_common.h"
#include "drivers/dshot.h"
#include "io/vtx_tramp.h"
#include "io/vtx_smartaudio.h"
#include "srxl.h"
#define SRXL_ADDRESS_FIRST 0xA5
#define SRXL_ADDRESS_SECOND 0x80
@ -786,6 +784,12 @@ void initSrxlTelemetry(void)
srxlTelemetryEnabled = false;
srxl2 = false;
}
#if defined(USE_SPEKTRUM_CMS_TELEMETRY)
if (srxlTelemetryEnabled) {
srxlDisplayportRegister();
}
#endif
}
bool checkSrxlTelemetryState(void)

View File

@ -59,7 +59,7 @@ extern "C" {
PG_REGISTER(motorConfig_t, motorConfig, PG_MOTOR_CONFIG, 0);
float rcCommand[4];
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
uint16_t averageSystemLoadPercent = 0;
uint8_t cliMode = 0;
uint8_t debugMode = 0;

View File

@ -543,7 +543,7 @@ TEST(FlightFailsafeTest, TestFailsafeNotActivatedWhenDisarmedAndRXLossIsDetected
// STUBS
extern "C" {
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcCommand[4];
int16_t debug[DEBUG16_VALUE_COUNT];
bool isUsingSticksToArm = true;

View File

@ -204,7 +204,7 @@ TEST(FlightImuTest, TestSmallAngle)
extern "C" {
boxBitmask_t rcModeActivationMask;
float rcCommand[4];
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
gyro_t gyro;
acc_t acc;

View File

@ -299,7 +299,7 @@ uint8_t armingFlags = 0;
uint8_t stateFlags = 0;
uint16_t flightModeFlags = 0;
float rcCommand[4];
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
boxBitmask_t rcModeActivationMask;
gpsSolutionData_t gpsSol;

View File

@ -69,7 +69,7 @@ extern "C" {
float rMat[3][3];
pidProfile_t *currentPidProfile;
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
uint8_t GPS_numSat;
uint16_t GPS_distanceToHome;
int16_t GPS_directionToHome;

View File

@ -73,7 +73,7 @@ extern "C" {
pidProfile_t *currentPidProfile;
int16_t debug[DEBUG16_VALUE_COUNT];
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
uint8_t GPS_numSat;
uint16_t GPS_distanceToHome;
int16_t GPS_directionToHome;

View File

@ -640,7 +640,7 @@ uint8_t armingFlags = 0;
uint16_t flightModeFlags = 0;
int16_t heading;
uint8_t stateFlags = 0;
int16_t rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
float rcData[MAX_SUPPORTED_RC_CHANNEL_COUNT];
pidProfile_t *currentPidProfile;
rxRuntimeState_t rxRuntimeState;
PG_REGISTER(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 0);

Some files were not shown because too many files have changed in this diff Show More