Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GPS Lap Timer #11856

Merged
merged 15 commits into from
May 25, 2023
Merged

Conversation

SpencerGraffunder
Copy link
Contributor

@SpencerGraffunder SpencerGraffunder commented Sep 26, 2022

Resubmitting #10968. See discussion there.
Feature described here: https://youtu.be/TA5cWwFafY4

Test with:
make XYZ EXTRA_FLAGS="-DUSE_GPS -DUSE_GPS_LAP_TIMER"

@SpencerGraffunder SpencerGraffunder changed the title Add gps lap timer and supporting type changes Add GPS Lap Timer Sep 26, 2022
@github-actions

This comment has been minimized.

@@ -185,8 +187,8 @@ typedef enum {
#define VALUE_MODE_MASK (0xE0)

typedef struct cliMinMaxConfig_s {
const int16_t min;
const int16_t max;
const int32_t min;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will double size of cliValueConfig_t, considerably increasing flash size.
Maybe just assume that int32 has symmetric limits and use d32Max. But it will need some more general six in future


typedef struct gpsLapTimerData_s {
gpsLocation_t gateLocation;
uint32_t previousLaps[3];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use #define for lap history. It bill make rest of code slightly cleaner (for instead of inlining 3 readings)

@@ -683,7 +685,17 @@ void gpsInitUblox(void)
}
break;
case 12:
ubloxSetNavRate(0x64, 1, 1); // set rate to 10Hz (measurement period: 100ms, navigation rate: 1 cycle) (for 5Hz use 0xC8)
switch (gpsConfig()->gps_update_rate) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using array mapping index to {0xc8,0x64,0x35} and maybe #defining it's context near GPS_UPDATE_RATE_xHZ will be cleaner,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I want to just use an int to store the update rate.
The easiest way to do that would be to store it as a period in milliseconds, because that can be passed straight to the existing ubloxSetNavRate function.
The other way, which would be easier for the user, would be to store it as a rate in Hz and have ubloxSetNavRate convert the Hz to a period. The problem here is that the rates may not be integers. The max rate I found to work on my units was 53ms, or 18.86Hz. So then we would set the max allowed value to 19Hz, but clamp the calculated period to 53ms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO using enum is a bit better here. It will prevent users using values that don't work. Without error message, this can be support nightmare.

GPS_UPDATE_RATE_10HZ,
GPS_UPDATE_RATE_19HZ
} gpsUpdateRate_e;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#define GPS_UPDATE_RATE__MAP {0xc8,0x64,0x35} or similar, see above

Comment on lines 879 to 888
uint32_t lapTimeSeconds;
uint32_t lapTimeDecimals;
if (gpsLapTimerData.best3Consec != 0) {
lapTimeSeconds = gpsLapTimerData.best3Consec / 1000;
lapTimeDecimals = (gpsLapTimerData.best3Consec % 1000) / 10;
tfp_sprintf(buff, "%3u.%02u", lapTimeSeconds, lapTimeDecimals);
} else {
tfp_sprintf(buff, " -.--");
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be replaced with function (maybe including optional prefix character/string /empty value), removing lot of code duplication

Comment on lines 896 to 897
lapTimeSeconds = gpsLapTimerData.bestLapTime / 1000;
lapTimeDecimals = (gpsLapTimerData.bestLapTime % 1000) / 10;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused code

@@ -1014,8 +1015,18 @@ static void osdElementGpsHomeDirection(osdElementParms_t *element)
{
if (STATE(GPS_FIX) && STATE(GPS_FIX_HOME)) {
if (GPS_distanceToHome > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(technically, gpsLapTimerData.timerRunning will be ignored with GPS_distanceToHome == 0)

Also, does code below handle correctly case when distance to gate is zero?

Comment on lines 1018 to 1050
#ifdef USE_GPS_LAP_TIMER
// Change the "home" point to the start/finish location if the lap timer is running
if (gpsLapTimerData.timerRunning) {
const int h = gpsLapTimerData.dirToPoint/100 - DECIDEGREES_TO_DEGREES(attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
} else {
#endif
const int h = DECIDEGREES_TO_DEGREES(GPS_directionToHome - attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
#ifdef USE_GPS_LAP_TIMER
}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#ifdef USE_GPS_LAP_TIMER
// Change the "home" point to the start/finish location if the lap timer is running
if (gpsLapTimerData.timerRunning) {
const int h = gpsLapTimerData.dirToPoint/100 - DECIDEGREES_TO_DEGREES(attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
} else {
#endif
const int h = DECIDEGREES_TO_DEGREES(GPS_directionToHome - attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
#ifdef USE_GPS_LAP_TIMER
}
#endif
do {
#ifdef USE_GPS_LAP_TIMER
// Override the "home" point to the start/finish location if the lap timer is running
if (gpsLapTimerData.timerRunning) {
const int h = gpsLapTimerData.dirToPoint/100 - DECIDEGREES_TO_DEGREES(attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
break;
}
#endif
const int h = DECIDEGREES_TO_DEGREES(GPS_directionToHome - attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
} while(0);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely need to fix this section. Is there any reason not to do it like this? It uses a non-const int, but I assume that doesn't make it to the machine code anyways, right?

Suggested change
#ifdef USE_GPS_LAP_TIMER
// Change the "home" point to the start/finish location if the lap timer is running
if (gpsLapTimerData.timerRunning) {
const int h = gpsLapTimerData.dirToPoint/100 - DECIDEGREES_TO_DEGREES(attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
} else {
#endif
const int h = DECIDEGREES_TO_DEGREES(GPS_directionToHome - attitude.values.yaw);
element->buff[0] = osdGetDirectionSymbolFromHeading(h);
#ifdef USE_GPS_LAP_TIMER
}
#endif
int direction = GPS_directionToHome
#ifdef USE_GPS_LAP_TIMER
// Override the "home" point to the start/finish location if the lap timer is running
if (gpsLapTimerData.timerRunning) {
direction = gpsLapTimerData.dirToPoint/10; // dirToPoint is centidegrees
}
#endif
element->buff[0] = osdGetDirectionSymbolFromHeading(DECIDEGREES_TO_DEGREES(direction - attitude.values.yaw));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, overriding direction is fine.
My comment was mainly about two #ifdef mixed into C block, it is too easy to break with future changes.

#ifdef USE_GPS_LAP_TIMER
static void osdElementGpsLapTimeCurrent(osdElementParms_t *element)
{
uint32_t lapTimeSeconds = gpsLapTimerData.currentLapTime / 1000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(see above; prefix variant)

Also, isn't -:-- possible here too ?

@@ -373,7 +373,7 @@ extern uint8_t _dmaram_end__;
#define USE_CANVAS
#define USE_FRSKYOSD
#define USE_GPS
#define USE_GPS_NMEA
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like quite intrusive change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. This was done mostly just let me test. We will have to discuss feature priority. I understand keeping NMEA for whacky receivers that can't do UBLOX. How do we decide which features go away when making room for new ones?

@blckmn
Copy link
Member

blckmn commented Sep 26, 2022

AUTOMERGE: (FAIL)

  • github identifies PR as mergeable -> PASS
  • assigned to a milestone -> PASS
  • cooling off period lapsed -> PASS
  • commit count less or equal to three -> FAIL
  • Don't merge label NOT found -> PASS
  • at least one RN: label found -> PASS
  • Tested label found -> FAIL
  • assigned to an approver -> FAIL
  • approver count at least three -> PASS

@SpencerGraffunder
Copy link
Contributor Author

SpencerGraffunder commented Sep 26, 2022

Thanks for the through review, @ledvinap! Just what I've been waiting for.
I'll have to take some time to work through your comments.

@ledvinap
Copy link
Contributor

BTW: What about using home position as a gate? Gate positioning code would apply to home (which may be useful for other purposes), only problem I see is unintended flyaway when someone saves different site as home (I heard of $20k drone flying back to Africa on first test)

@SpencerGraffunder
Copy link
Contributor Author

BTW: What about using home position as a gate? Gate positioning code would apply to home (which may be useful for other purposes)

Can you explain more? I don't see how it would be helpful.

@ledvinap
Copy link
Contributor

Current code uses concept of 'home' location. It is initialized on arming and used for HOME info on OSD and RTH code. But it is quite similar to your gate location.
If user can specify Home location (using mechanisms from this PR), it can be used as Gate. And it will allow specifying RTH/OSD home that is different from arming location (possibly useful if you want clear place for RTH / OSD landing). Eventually, it can be extended to support geofencing etc.

@github-actions

This comment has been minimized.

@SpencerGraffunder
Copy link
Contributor Author

Current code uses concept of 'home' location. It is initialized on arming and used for HOME info on OSD and RTH code. But it is quite similar to your gate location. If user can specify Home location (using mechanisms from this PR), it can be used as Gate. And it will allow specifying RTH/OSD home that is different from arming location (possibly useful if you want clear place for RTH / OSD landing). Eventually, it can be extended to support geofencing etc.

I see what you're talking about now. I think a non-volatile RTH position wouldn't be desirable because of flyaways. I could see it being cool to have a completely separate "POI" with a distance and pointer that you could have set at your vacation home or an airport, but I don't feel like betaflight is the right place for a secondary airfield feature.
If we don't like the idea of hijacking the home OSD elements, I could create a second set that's just for the lap timer.

@ctzsnooze
Copy link
Member

Very cool!

Thanks @ledvinap for your assistance.

Can I clarify that GPS Rescue can still be activated via switch or RC loss, while this function is active? I can imagine this opening up the possibility of relatively long-distance races where a functional GPS Rescue could be required, even if the user wants to use GPS to time their laps.

Also, GPS Rescue now does good landings. I can imagine that if the user chose the 'set home point once' rescue option, and arms once at the landing point, then they could go racing with this, do their laps, and at the end just hit GPS Rescue switch, and it would bring back and land at the correct spot. Hence I think that keeping the GPS Rescue capability and its home point separate, and both functional, would be good.

How much extra flash space is required?

@SpencerGraffunder
Copy link
Contributor Author

I haven't tested GPS Rescue while the timer is running, but I can't imagine any way it would interfere. I'm not planning to overwrite the home point at all other than hijacking the OSD element. Even that isn't necessary if we want to make two new elements for distance and direction to the gate. I'm not controlling flight or injecting positioning data to existing structs.

Not sure on flash space. After implementing some of the suggestions here, I was able to build for all targets within the flash limits without removing any other features.

Unfortunately I am too busy to work on this any more this month. The code still needs some changes done, so this isn't ready for testing yet.

@ctzsnooze
Copy link
Member

ctzsnooze commented Oct 13, 2022

OK thanks for the heads up.
Temporary hijacking of home point is OK to me.
Later on we need to test and confirm that GPS Rescue still works while this is active.
We will put this PR 'on ice' until you get back to us.

src/main/cms/cms_menu_gps_lap_timer.c Show resolved Hide resolved
src/main/fc/gps_lap_timer.c Show resolved Hide resolved
Comment on lines 94 to 96
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

These macros are defined in common/maths.h, so we can remove them here.

@KarateBrot
Copy link
Member

KarateBrot commented Oct 17, 2022

Later on we need to test and confirm that GPS Rescue still works while this is active.

Why would GPS Rescue not work with this active? This is the normal acro mode we are used to, just with OSD elements for lap times that is using the GPS data to get times. This is not some fancy flight mode that could potentially block other flight modes.

@howels
Copy link
Contributor

howels commented Nov 2, 2022

@SpencerGraffunder can you update this so it's ready for 4.5? Looks like some changes were requested but the feature freeze was missed. However it's a great looking feature so please update as I'm sure this will be popular.

@KarateBrot
Copy link
Member

@SpencerGraffunder can you update this so it's ready for 4.5? Looks like some changes were requested but the feature freeze was missed. However it's a great looking feature so please update as I'm sure this will be popular.

Feature freeze wasn't missed. This PR was opened well before the feature freeze so no problem 👍

@howels
Copy link
Contributor

howels commented Nov 2, 2022

@SpencerGraffunder can you update this so it's ready for 4.5? Looks like some changes were requested but the feature freeze was missed. However it's a great looking feature so please update as I'm sure this will be popular.

Feature freeze wasn't missed. This PR was opened well before the feature freeze so no problem +1

thanks for the correction, was worried this wasn't making the cut. Even better news!

SpencerGraffunder and others added 2 commits May 19, 2023 14:03
Co-authored-by: Jan Post <Rm2k-Freak@web.de>
@github-actions

This comment has been minimized.

@haslinghuis
Copy link
Member

@SteveCEvans are the requested changes resolved?

} else {
// Just left the circle, so record the lap time
if (wasInCircle) {
// Not the first time through the gate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with this PR, but as a possible future enhancement would we be able to get better timing accuracy with a smaller circle size qualified with an exit direction? Setting this direction would be a potential challenge, perhaps requiring training on a practice lap?

Copy link
Member

@KarateBrot KarateBrot May 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a compass you could dial in the heading of the gate directly through the CMS menu or CLI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SteveCEvans IIRC gate time is taken at closest point to gate (minimum distance is tracked, with timestamp). So actual gate size should not matter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An exit direction could help by letting us make the circle bigger and not be caught by other parts of the track. Since it's using the closest point to the gate waypoint, I think that's the most accurate we can be without correcting the gate waypoint. I'm sure there is a lot more we could do with this in the future. Split times and auto-correcting the start/finish point are two things I've thought of.

@github-actions

This comment has been minimized.

@SpencerGraffunder
Copy link
Contributor Author

I think I've resolved everything. Does the INT32 stuff still need to be changed, @ledvinap?

src/main/cms/cms_menu_gps_lap_timer.c Outdated Show resolved Hide resolved
src/main/cms/cms_menu_gps_lap_timer.h Outdated Show resolved Hide resolved
@haslinghuis haslinghuis self-requested a review May 23, 2023 09:50
@github-actions

This comment has been minimized.

Co-authored-by: Jan Post <Rm2k-Freak@web.de>
@github-actions
Copy link

Do you want to test this code? Here you have an automated build:
Assets

  • See: https://www.youtube.com/watch?v=I1uN9CN30gw for instructions about installing this unified targets!
  • An easier way to test this, using the Configurator cloud build, is simply put #11856 (this pull request number) in the Select commit field of the Configurator firmware flasher tab (you need to Enable expert mode, Show release candidates and Development).

WARNING: It may be unstable. Use only for testing!

@haslinghuis haslinghuis merged commit aad197f into betaflight:master May 25, 2023
@Easy4Racing
Copy link

Hi , I want to run a test for this new feature but failed.

  1. I flashed the new Hex file cloud built by Configurator(version 10.10.0-debug-e6f3b5b).
  2. Selected sensor input 'GPS' in Ports tab, enabled GPS & OSD in Configuration tab, enabled 'Unknown 1.2.3' in OSD tab. Nothing of GPS_lap_timer displayed but the craft_name and on time is ok.

What should I do next ?
My board is OMNIBUSF4

@SpencerGraffunder
Copy link
Contributor Author

What should I do next ? My board is OMNIBUSF4

Did you add USE_GPS_LAP_TIMER in custom defines?

image

Your GPS also needs a lock before the elements will show.

@Easy4Racing
Copy link

What should I do next ? My board is OMNIBUSF4

Did you add USE_GPS_LAP_TIMER in custom defines?

image

Your GPS also needs a lock before the elements will show.

Thx for reply.
I set the custom defines but have not connect the GPS module. So, this is the reason.
I thought here may be an initialization value, even if I don't pick up GPS, or the GPS module work improperly .

W1V2Y3P1NKSTQ5E4`Z{IVJP

firmware

@haslinghuis
Copy link
Member

Don't use USE in Custom Defines: GPS_LAP_TIMER

AkankshaJjw pushed a commit to AkankshaJjw/betaflight that referenced this pull request May 29, 2023
* Add gps lap timer

* change timing to GPS time instead of local time

* rebase and minor changes

* implement KarateBrot's suggestions

* follow ledvinap's suggestions, some OSD symbol changes

* move platform.h include to the top

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

* fix osd elements not showing, remove useless block

* cleanup, move pg stuff to pg folder

* cleanup from review

* minor mods to gps lap timer update, add number of laps tracked

* rename time variable

* add const to timeMs

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

* Update licenses, add is_sys_element macro

* update licenses

* round to nearest centisecond

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

---------

Co-authored-by: Jan Post <Rm2k-Freak@web.de>
davidbitton pushed a commit to davidbitton/betaflight that referenced this pull request Feb 5, 2024
* Add gps lap timer

* change timing to GPS time instead of local time

* rebase and minor changes

* implement KarateBrot's suggestions

* follow ledvinap's suggestions, some OSD symbol changes

* move platform.h include to the top

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

* fix osd elements not showing, remove useless block

* cleanup, move pg stuff to pg folder

* cleanup from review

* minor mods to gps lap timer update, add number of laps tracked

* rename time variable

* add const to timeMs

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

* Update licenses, add is_sys_element macro

* update licenses

* round to nearest centisecond

Co-authored-by: Jan Post <Rm2k-Freak@web.de>

---------

Co-authored-by: Jan Post <Rm2k-Freak@web.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: COMPLETED
Development

Successfully merging this pull request may close these issues.