From e320791f90b369d1e7e1f2c381e4a0b84995fa61 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Wed, 24 Dec 2025 22:29:05 +0100 Subject: [PATCH 1/8] Add configurable OSD element refresh rate --- src/main/fc/settings.yaml | 7 +++++++ src/main/io/osd.c | 36 +++++++++++++++++++++++++++++++++++- src/main/io/osd.h | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 1e1932531e5..2e9a10e3e6a 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -3329,6 +3329,13 @@ groups: max: 600 type: int16_t field: msp_displayport_fullframe_interval + - name: osd_framerate_hz + description: "OSD element refresh rate in Hz. Controls how often OSD elements are updated (except artificial horizon and telemetry which are always updated). Higher values provide smoother updates but increase CPU load. Set to -1 for legacy mode (one element per frame). Default: -1" + default_value: -1 + min: -1 + max: 60 + type: int8_t + field: osd_framerate_hz - name: osd_units description: "IMPERIAL, METRIC, UK" default_value: "METRIC" diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 6ad55632c17..c2e7aef0fb6 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -4242,6 +4242,18 @@ uint8_t osdIncElementIndex(uint8_t elementIndex) return elementIndex; } +static void osdDrawAllElements(void) +{ + for (uint8_t element = 0; element < OSD_ITEM_COUNT; element++) { + osdDrawSingleElement(element); + } + + osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); + if (osdConfig()->telemetry>0){ + osdDisplayTelemetry(); + } +} + void osdDrawNextElement(void) { static uint8_t elementIndex = 0; @@ -4304,6 +4316,7 @@ PG_RESET_TEMPLATE(osdConfig_t, osdConfig, .video_system = SETTING_OSD_VIDEO_SYSTEM_DEFAULT, .row_shiftdown = SETTING_OSD_ROW_SHIFTDOWN_DEFAULT, .msp_displayport_fullframe_interval = SETTING_OSD_MSP_DISPLAYPORT_FULLFRAME_INTERVAL_DEFAULT, + .framerate_hz = SETTING_OSD_FRAMERATE_HZ_DEFAULT, .ahi_reverse_roll = SETTING_OSD_AHI_REVERSE_ROLL_DEFAULT, .ahi_max_pitch = SETTING_OSD_AHI_MAX_PITCH_DEFAULT, @@ -5976,7 +5989,28 @@ static void osdRefresh(timeUs_t currentTimeUs) displayClearScreen(osdDisplayPort); fullRedraw = false; } - osdDrawNextElement(); + + if (osdConfig()->osd_framerate_hz == -1) { + osdDrawNextElement(); + } else { + static uint32_t lastDrawAllTimeUs = 0; + const int8_t hz = osdConfig()->osd_framerate_hz; + const uint32_t drawAllIntervalUs = (hz > 0) ? (1000000 / hz) : 0; + + const bool forceDraw = (drawAllIntervalUs == 0); + const bool intervalExceeded = (currentTimeUs - lastDrawAllTimeUs) >= drawAllIntervalUs; + + if (forceDraw || intervalExceeded) { + osdDrawAllElements(); + lastDrawAllTimeUs = currentTimeUs; + } + + osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); + if (osdConfig()->telemetry>0){ + osdDisplayTelemetry(); + } + } + displayHeartbeat(osdDisplayPort); displayCommitTransaction(osdDisplayPort); #ifdef OSD_CALLS_CMS diff --git a/src/main/io/osd.h b/src/main/io/osd.h index bbaa68f862d..d644d841a01 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -451,6 +451,7 @@ typedef struct osdConfig_s { videoSystem_e video_system; uint8_t row_shiftdown; int16_t msp_displayport_fullframe_interval; + int8_t osd_framerate_hz; // Preferences uint8_t main_voltage_decimals; From 67dab356b986f0211ebfcb6abfb996a28a4ae1d3 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Thu, 25 Dec 2025 17:33:03 +0100 Subject: [PATCH 2/8] iterate over OSD elements using osdIncElementIndex func --- src/main/io/osd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/io/osd.c b/src/main/io/osd.c index c2e7aef0fb6..3beefefb859 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -4244,9 +4244,11 @@ uint8_t osdIncElementIndex(uint8_t elementIndex) static void osdDrawAllElements(void) { - for (uint8_t element = 0; element < OSD_ITEM_COUNT; element++) { + uint8_t element = 0; + do { osdDrawSingleElement(element); - } + element = osdIncElementIndex(element); + } while (element != 0); osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); if (osdConfig()->telemetry>0){ @@ -4316,7 +4318,7 @@ PG_RESET_TEMPLATE(osdConfig_t, osdConfig, .video_system = SETTING_OSD_VIDEO_SYSTEM_DEFAULT, .row_shiftdown = SETTING_OSD_ROW_SHIFTDOWN_DEFAULT, .msp_displayport_fullframe_interval = SETTING_OSD_MSP_DISPLAYPORT_FULLFRAME_INTERVAL_DEFAULT, - .framerate_hz = SETTING_OSD_FRAMERATE_HZ_DEFAULT, + .osd_framerate_hz = SETTING_OSD_FRAMERATE_HZ_DEFAULT, .ahi_reverse_roll = SETTING_OSD_AHI_REVERSE_ROLL_DEFAULT, .ahi_max_pitch = SETTING_OSD_AHI_MAX_PITCH_DEFAULT, From b93e2932c85eb8cfa4976e54e6bd06f0d99552d0 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Thu, 25 Dec 2025 23:24:17 +0100 Subject: [PATCH 3/8] time budget based element rendering. should be faster --- src/main/fc/settings.yaml | 7 ----- src/main/io/osd.c | 57 ++++++++++++++++++++++----------------- src/main/io/osd.h | 1 - 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 2e9a10e3e6a..1e1932531e5 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -3329,13 +3329,6 @@ groups: max: 600 type: int16_t field: msp_displayport_fullframe_interval - - name: osd_framerate_hz - description: "OSD element refresh rate in Hz. Controls how often OSD elements are updated (except artificial horizon and telemetry which are always updated). Higher values provide smoother updates but increase CPU load. Set to -1 for legacy mode (one element per frame). Default: -1" - default_value: -1 - min: -1 - max: 60 - type: int8_t - field: osd_framerate_hz - name: osd_units description: "IMPERIAL, METRIC, UK" default_value: "METRIC" diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 3beefefb859..f3107bdc1c4 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -4242,18 +4242,19 @@ uint8_t osdIncElementIndex(uint8_t elementIndex) return elementIndex; } -static void osdDrawAllElements(void) +#define OSD_TIME_BUDGET_PERCENT 70 + +static uint32_t osdCalculateSafeTimeBudget(void) { - uint8_t element = 0; - do { - osdDrawSingleElement(element); - element = osdIncElementIndex(element); - } while (element != 0); + uint32_t pidLooptimeUs = getLooptime(); + uint32_t gyroLooptimeUs = getGyroLooptime(); + uint32_t criticalLooptimeUs = MIN(pidLooptimeUs, gyroLooptimeUs); + uint32_t safeBudgetUs = (criticalLooptimeUs * OSD_TIME_BUDGET_PERCENT) / 100; - osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); - if (osdConfig()->telemetry>0){ - osdDisplayTelemetry(); - } + if (safeBudgetUs < 100) safeBudgetUs = 100; + if (safeBudgetUs > 2000) safeBudgetUs = 2000; + + return safeBudgetUs; } void osdDrawNextElement(void) @@ -5992,25 +5993,31 @@ static void osdRefresh(timeUs_t currentTimeUs) fullRedraw = false; } - if (osdConfig()->osd_framerate_hz == -1) { - osdDrawNextElement(); - } else { - static uint32_t lastDrawAllTimeUs = 0; - const int8_t hz = osdConfig()->osd_framerate_hz; - const uint32_t drawAllIntervalUs = (hz > 0) ? (1000000 / hz) : 0; + // Draw elements until time budget + static uint8_t elementIndex = 0; + const uint32_t timeBudgetUs = osdCalculateSafeTimeBudget(); + const uint32_t startUs = micros(); + const uint8_t startElement = elementIndex; + uint8_t elementsDrawn = 0; - const bool forceDraw = (drawAllIntervalUs == 0); - const bool intervalExceeded = (currentTimeUs - lastDrawAllTimeUs) >= drawAllIntervalUs; + // Draw elements in round-robin fashion until time budget expires + do { + elementIndex = osdIncElementIndex(elementIndex); + osdDrawSingleElement(elementIndex); + elementsDrawn++; - if (forceDraw || intervalExceeded) { - osdDrawAllElements(); - lastDrawAllTimeUs = currentTimeUs; - } + const bool timeBudgetExceeded = (micros() - startUs) >= timeBudgetUs; + const bool completedFullCycle = (elementIndex == startElement); - osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); - if (osdConfig()->telemetry>0){ - osdDisplayTelemetry(); + if (timeBudgetExceeded || completedFullCycle) { + break; } + + } while (true); + + osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); + if (osdConfig()->telemetry > 0) { + osdDisplayTelemetry(); } displayHeartbeat(osdDisplayPort); diff --git a/src/main/io/osd.h b/src/main/io/osd.h index d644d841a01..bbaa68f862d 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -451,7 +451,6 @@ typedef struct osdConfig_s { videoSystem_e video_system; uint8_t row_shiftdown; int16_t msp_displayport_fullframe_interval; - int8_t osd_framerate_hz; // Preferences uint8_t main_voltage_decimals; From 95e532750b299db05d842c3529fbcbbb458cf003 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Thu, 25 Dec 2025 23:25:18 +0100 Subject: [PATCH 4/8] debugging time budget --- src/main/build/debug.h | 1 + src/main/fc/cli.c | 3 ++- src/main/io/osd.c | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/build/debug.h b/src/main/build/debug.h index 0bb74bac1ac..b33868af8b2 100644 --- a/src/main/build/debug.h +++ b/src/main/build/debug.h @@ -79,6 +79,7 @@ typedef enum { DEBUG_GPS, DEBUG_LULU, DEBUG_SBUS2, + DEBUG_OSD_REFRESH, DEBUG_COUNT // also update debugModeNames in cli.c } debugType_e; diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index 6c60f08c6ed..4546a6abf27 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -219,7 +219,8 @@ static const char *debugModeNames[DEBUG_COUNT] = { "HEADTRACKER", "GPS", "LULU", - "SBUS2" + "SBUS2", + "OSD_REFRESH" }; /* Sensor names (used in lookup tables for *_hardware settings and in status diff --git a/src/main/io/osd.c b/src/main/io/osd.c index f3107bdc1c4..1baf71e4ae2 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -6015,6 +6015,12 @@ static void osdRefresh(timeUs_t currentTimeUs) } while (true); + const uint32_t actualTimeUs = micros() - startUs; + DEBUG_SET(DEBUG_OSD_REFRESH, 0, elementsDrawn); + DEBUG_SET(DEBUG_OSD_REFRESH, 1, actualTimeUs); + DEBUG_SET(DEBUG_OSD_REFRESH, 2, timeBudgetUs); + DEBUG_SET(DEBUG_OSD_REFRESH, 3, actualTimeUs >= timeBudgetUs); + osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); if (osdConfig()->telemetry > 0) { osdDisplayTelemetry(); From 842a7a8f37070389079d78b5519637510a0264a5 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:02:34 +0300 Subject: [PATCH 5/8] add target osd_framerate_hz to control how many OSD elements are drawn per cycle --- src/main/fc/settings.yaml | 7 ++++ src/main/io/osd.c | 88 +++++++++++++++------------------------ src/main/io/osd.h | 1 + 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 1e1932531e5..f6dc2c1d75f 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -3329,6 +3329,13 @@ groups: max: 600 type: int16_t field: msp_displayport_fullframe_interval + - name: osd_framerate_hz + description: "Target refresh rate for OSD elements in Hz. Each element is redrawn at approximately this rate. Values above 12-15 Hz provide no visible improvement for typical flight data. Artificial horizon and telemetry are always updated every cycle regardless of this setting. Set to -1 for legacy behavior (one element per frame)." + default_value: -1 + min: -1 + max: 15 + type: int8_t + field: osd_framerate_hz - name: osd_units description: "IMPERIAL, METRIC, UK" default_value: "METRIC" diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 1baf71e4ae2..5d87b32a60c 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -4242,33 +4242,46 @@ uint8_t osdIncElementIndex(uint8_t elementIndex) return elementIndex; } -#define OSD_TIME_BUDGET_PERCENT 70 - -static uint32_t osdCalculateSafeTimeBudget(void) +void osdDrawNextElement(void) { - uint32_t pidLooptimeUs = getLooptime(); - uint32_t gyroLooptimeUs = getGyroLooptime(); - uint32_t criticalLooptimeUs = MIN(pidLooptimeUs, gyroLooptimeUs); - uint32_t safeBudgetUs = (criticalLooptimeUs * OSD_TIME_BUDGET_PERCENT) / 100; + static uint8_t elementIndex = 0; + static uint8_t activeElements = 0; + static unsigned lastLayout = UINT_MAX; + + // Recount visible elements on layout change + if (currentLayout != lastLayout) { + lastLayout = currentLayout; + activeElements = 0; + uint8_t idx = 0; + do { + idx = osdIncElementIndex(idx); + if (OSD_VISIBLE(osdLayoutsConfig()->item_pos[currentLayout][idx])) { + activeElements++; + } + } while (idx > 0); + } - if (safeBudgetUs < 100) safeBudgetUs = 100; - if (safeBudgetUs > 2000) safeBudgetUs = 2000; + int8_t framerate_hz = osdConfig()->osd_framerate_hz; - return safeBudgetUs; -} + uint8_t elementsPerCycle; + if (framerate_hz <= 0 || activeElements == 0) { + elementsPerCycle = 1; // legacy: one element per cycle + } else { + elementsPerCycle = ((uint16_t)activeElements * framerate_hz * 2 + 124) / 125; + if (elementsPerCycle < 1) elementsPerCycle = 1; + if (elementsPerCycle > activeElements) elementsPerCycle = activeElements; + } -void osdDrawNextElement(void) -{ - static uint8_t elementIndex = 0; - // Flag for end of loop, also prevents infinite loop when no elements are enabled - uint8_t index = elementIndex; - do { - elementIndex = osdIncElementIndex(elementIndex); - } while (!osdDrawSingleElement(elementIndex) && index != elementIndex); + for (uint8_t i = 0; i < elementsPerCycle; i++) { + uint8_t index = elementIndex; + do { + elementIndex = osdIncElementIndex(elementIndex); + } while (!osdDrawSingleElement(elementIndex) && index != elementIndex); + } // Draw artificial horizon + tracking telemetry last osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); - if (osdConfig()->telemetry>0){ + if (osdConfig()->telemetry > 0) { osdDisplayTelemetry(); } } @@ -5992,40 +6005,7 @@ static void osdRefresh(timeUs_t currentTimeUs) displayClearScreen(osdDisplayPort); fullRedraw = false; } - - // Draw elements until time budget - static uint8_t elementIndex = 0; - const uint32_t timeBudgetUs = osdCalculateSafeTimeBudget(); - const uint32_t startUs = micros(); - const uint8_t startElement = elementIndex; - uint8_t elementsDrawn = 0; - - // Draw elements in round-robin fashion until time budget expires - do { - elementIndex = osdIncElementIndex(elementIndex); - osdDrawSingleElement(elementIndex); - elementsDrawn++; - - const bool timeBudgetExceeded = (micros() - startUs) >= timeBudgetUs; - const bool completedFullCycle = (elementIndex == startElement); - - if (timeBudgetExceeded || completedFullCycle) { - break; - } - - } while (true); - - const uint32_t actualTimeUs = micros() - startUs; - DEBUG_SET(DEBUG_OSD_REFRESH, 0, elementsDrawn); - DEBUG_SET(DEBUG_OSD_REFRESH, 1, actualTimeUs); - DEBUG_SET(DEBUG_OSD_REFRESH, 2, timeBudgetUs); - DEBUG_SET(DEBUG_OSD_REFRESH, 3, actualTimeUs >= timeBudgetUs); - - osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); - if (osdConfig()->telemetry > 0) { - osdDisplayTelemetry(); - } - + osdDrawNextElement(); displayHeartbeat(osdDisplayPort); displayCommitTransaction(osdDisplayPort); #ifdef OSD_CALLS_CMS diff --git a/src/main/io/osd.h b/src/main/io/osd.h index bbaa68f862d..d644d841a01 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -451,6 +451,7 @@ typedef struct osdConfig_s { videoSystem_e video_system; uint8_t row_shiftdown; int16_t msp_displayport_fullframe_interval; + int8_t osd_framerate_hz; // Preferences uint8_t main_voltage_decimals; From d1aafac902c13548b5d6e503816b02c40b469b49 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Thu, 12 Mar 2026 23:08:12 +0100 Subject: [PATCH 6/8] debug osd_framerate_hz --- src/main/fc/settings.yaml | 2 +- src/main/io/osd.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index f6dc2c1d75f..24f4d93bfe1 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -84,7 +84,7 @@ tables: "VIBE", "CRUISE", "REM_FLIGHT_TIME", "SMARTAUDIO", "ACC", "NAV_YAW", "PCF8574", "DYN_GYRO_LPF", "AUTOLEVEL", "ALTITUDE", "AUTOTRIM", "AUTOTUNE", "RATE_DYNAMICS", "LANDING", "POS_EST", - "ADAPTIVE_FILTER", "HEADTRACKER", "GPS", "LULU", "SBUS2"] + "ADAPTIVE_FILTER", "HEADTRACKER", "GPS", "LULU", "SBUS2", "OSD_REFRESH"] - name: aux_operator values: ["OR", "AND"] enum: modeActivationOperator_e diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 5d87b32a60c..ee7a3e2023b 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "platform.h" @@ -4272,6 +4273,10 @@ void osdDrawNextElement(void) if (elementsPerCycle > activeElements) elementsPerCycle = activeElements; } + DEBUG_SET(DEBUG_OSD_REFRESH, 0, elementsPerCycle); + DEBUG_SET(DEBUG_OSD_REFRESH, 1, activeElements); + DEBUG_SET(DEBUG_OSD_REFRESH, 2, framerate_hz); + for (uint8_t i = 0; i < elementsPerCycle; i++) { uint8_t index = elementIndex; do { From be3545ca174e756954c7bc484b1c3237acbd4501 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Fri, 13 Mar 2026 01:46:42 +0100 Subject: [PATCH 7/8] cli command description + element idx debug --- src/main/fc/settings.yaml | 2 +- src/main/io/osd.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 24f4d93bfe1..ff510762bff 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -3330,7 +3330,7 @@ groups: type: int16_t field: msp_displayport_fullframe_interval - name: osd_framerate_hz - description: "Target refresh rate for OSD elements in Hz. Each element is redrawn at approximately this rate. Values above 12-15 Hz provide no visible improvement for typical flight data. Artificial horizon and telemetry are always updated every cycle regardless of this setting. Set to -1 for legacy behavior (one element per frame)." + description: "Target refresh rate for OSD elements in Hz. Each element is redrawn at approximately this rate. Values above 10 Hz provide no visible improvement for typical flight data but increase CPU load. Artificial horizon and telemetry are always updated every cycle regardless of this setting. Set to -1 for legacy behavior (one element per frame)." default_value: -1 min: -1 max: 15 diff --git a/src/main/io/osd.c b/src/main/io/osd.c index ee7a3e2023b..f138be9a523 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -4275,7 +4275,8 @@ void osdDrawNextElement(void) DEBUG_SET(DEBUG_OSD_REFRESH, 0, elementsPerCycle); DEBUG_SET(DEBUG_OSD_REFRESH, 1, activeElements); - DEBUG_SET(DEBUG_OSD_REFRESH, 2, framerate_hz); + DEBUG_SET(DEBUG_OSD_REFRESH, 2, elementIndex); + DEBUG_SET(DEBUG_OSD_REFRESH, 3, framerate_hz); for (uint8_t i = 0; i < elementsPerCycle; i++) { uint8_t index = elementIndex; From 3732e51cc84b6b6a838389d9ed4b629e3a63ef46 Mon Sep 17 00:00:00 2001 From: hntirgeam <56179857+hntirgeam@users.noreply.github.com> Date: Fri, 13 Mar 2026 04:37:30 +0100 Subject: [PATCH 8/8] updated settings.md --- docs/Settings.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/Settings.md b/docs/Settings.md index 4e577bbedd5..95c9e80446a 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -4842,6 +4842,16 @@ Force OSD to work in grid mode even if the OSD device supports pixel level acces --- +### osd_framerate_hz + +Target refresh rate for OSD elements in Hz. Each element is redrawn at approximately this rate. Values above 10 Hz provide no visible improvement for typical flight data but increase CPU load. Artificial horizon and telemetry are always updated every cycle regardless of this setting. Set to -1 for legacy behavior (one element per frame). + +| Default | Min | Max | +| --- | --- | --- | +| -1 | -1 | 15 | + +--- + ### osd_gforce_alarm Value above which the OSD g force indicator will blink (g)