From 44398fed5bfc0bd48287513132d20687226ae91a Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Wed, 18 Mar 2026 16:49:30 +0000 Subject: [PATCH 01/10] - Added developer tab in settings that only appears when developer settings are turned on. - Populated developer tab with data stream rates fields --- gcs/data/default_settings.json | 101 +++++++++++++++++++++++++++ gcs/src/components/settingsModal.jsx | 4 ++ 2 files changed, 105 insertions(+) diff --git a/gcs/data/default_settings.json b/gcs/data/default_settings.json index fbc68b971..a40dbcec4 100644 --- a/gcs/data/default_settings.json +++ b/gcs/data/default_settings.json @@ -112,5 +112,106 @@ } ] } + }, + "Developer": { + "MAV_DATA_STREAM_ALL": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_ALL", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_RAW_SENSORS": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_RAW_SENSORS", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_EXTENDED_STATUS": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_EXTENDED_STATUS", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_RC_CHANNELS": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_RC_CHANNELS", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_RAW_CONTROLLER": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_RAW_CONTROLLER", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_POSITION": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_POSITION", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_EXTRA1": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_EXTRA1", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_EXTRA2": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_EXTRA2", + "suffix": "Hz", + "group": "Data stream rates" + }, + "MAV_DATA_STREAM_EXTRA3": { + "default": 5, + "type": "number", + "range": [ + 0, + 100000 + ], + "display": "MAV_DATA_STREAM_EXTRA3", + "suffix": "Hz", + "group": "Data stream rates" + } } } diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index d80c0c342..1deeb9424 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -741,6 +741,10 @@ function SettingsModal() { > {settingTabs.map((t) => { + // Only show developer tag when developer features are on + if (!getSetting("General.experimentalDeveloperFeatures") && t === "Developer") { + return <> + } return ( {t} From 4b4e2d8fb4c64d057f020dbc37baf30b99cddcb1 Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Thu, 19 Mar 2026 10:31:42 +0000 Subject: [PATCH 02/10] Added set rates row --- gcs/src/components/settingsModal.jsx | 51 ++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index 1deeb9424..76879da46 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -197,6 +197,37 @@ function ReleaseCheckRow() { ) } +function SetRatesRow() { + const { getSetting } = useSettings() + + + const onClick = () => { + const MAV_DATA_STREAM_ALL = getSetting("Developer.MAV_DATA_STREAM_ALL") + const MAV_DATA_STREAM_RAW_SENSORS = getSetting("Developer.MAV_DATA_STREAM_RAW_SENSORS") + const MAV_DATA_STREAM_EXTENDED_STATUS = getSetting("Developer.MAV_DATA_STREAM_EXTENDED_STATUS") + const MAV_DATA_STREAM_RC_CHANNELS = getSetting("Developer.MAV_DATA_STREAM_RC_CHANNELS") + const MAV_DATA_STREAM_RAW_CONTROLLER = getSetting("Developer.MAV_DATA_STREAM_RAW_CONTROLLER") + const MAV_DATA_STREAM_POSITION = getSetting("Developer.MAV_DATA_STREAM_POSITION") + const MAV_DATA_STREAM_EXTRA1 = getSetting("Developer.MAV_DATA_STREAM_EXTRA1") + const MAV_DATA_STREAM_EXTRA2 = getSetting("Developer.MAV_DATA_STREAM_EXTRA2") + const MAV_DATA_STREAM_EXTRA3 = getSetting("Developer.MAV_DATA_STREAM_EXTRA3") + + console.log(MAV_DATA_STREAM_ALL) + } + + return ( +
+
+
+
+ +
+
+ ) +} + function OptionSetting({ settingName, options }) { const { getSetting, setSetting } = useSettings() return ( @@ -299,12 +330,12 @@ function ExtendableTextSetting({ settingName, df }) { const [items, setItems] = useState( getSetting(settingName).length > 0 ? getSetting(settingName).map((item) => { - const newItem = { id: generateId() } - df.fields.forEach((field) => { - newItem[field.key] = item[field.key] || "" - }) - return newItem + const newItem = { id: generateId() } + df.fields.forEach((field) => { + newItem[field.key] = item[field.key] || "" }) + return newItem + }) : [], ) @@ -569,7 +600,7 @@ function Setting({ settingName, df, initialValue }) { setChangedFromDefault( !["[]", '""'].includes(JSON.stringify(df.default)) && - JSON.stringify(getSetting(settingName)) != JSON.stringify(df.default), + JSON.stringify(getSetting(settingName)) != JSON.stringify(df.default), ) }, [getSetting(settingName)]) @@ -801,6 +832,12 @@ function SettingsModal() { )} + {tab === "Developer" && ( + <> + + + ) + } ) })} @@ -824,7 +861,7 @@ function SettingsModal() { setting .split(".") .reduce((title, value) => title[value], DefaultSettings)[ - "display" + "display" ] }

From f92232e9e2eabc2a9590aeaa7179ea20cdb2fe81 Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Thu, 19 Mar 2026 15:06:47 +0000 Subject: [PATCH 03/10] - Created set_stream_rate endpoint - Added emitter to drone connection slice - Added emitter implementation in emitter.js - Added button to developer tab that calls emitter --- gcs/data/default_settings.json | 36 ++++++++++---------- gcs/src/components/settingsModal.jsx | 22 ++++++++++-- gcs/src/redux/middleware/emitters.js | 9 +++++ gcs/src/redux/slices/droneConnectionSlice.js | 2 ++ radio/app/endpoints/states.py | 14 ++++++++ 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/gcs/data/default_settings.json b/gcs/data/default_settings.json index a40dbcec4..b87196956 100644 --- a/gcs/data/default_settings.json +++ b/gcs/data/default_settings.json @@ -115,99 +115,99 @@ }, "Developer": { "MAV_DATA_STREAM_ALL": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_ALL", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_RAW_SENSORS": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_RAW_SENSORS", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_EXTENDED_STATUS": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_EXTENDED_STATUS", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_RC_CHANNELS": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_RC_CHANNELS", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_RAW_CONTROLLER": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_RAW_CONTROLLER", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_POSITION": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_POSITION", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_EXTRA1": { - "default": 5, + "default": 4, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_EXTRA1", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_EXTRA2": { - "default": 5, + "default": 3, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_EXTRA2", "suffix": "Hz", "group": "Data stream rates" }, "MAV_DATA_STREAM_EXTRA3": { - "default": 5, + "default": 1, "type": "number", "range": [ 0, - 100000 + 150 ], "display": "MAV_DATA_STREAM_EXTRA3", "suffix": "Hz", diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index 76879da46..f3af624ac 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -31,9 +31,23 @@ import { redColor, showLoadingNotification, } from "../helpers/notification" +import { useDispatch } from "react-redux" +import { emitSetStreamRates } from "../redux/slices/droneConnectionSlice" const octokit = new Octokit({}) +const STREAM_MAP = { + MAV_DATA_STREAM_ALL: 0, + MAV_DATA_STREAM_RAW_SENSORS: 1, + MAV_DATA_STREAM_EXTENDED_STATUS: 2, + MAV_DATA_STREAM_RC_CHANNELS: 3, + MAV_DATA_STREAM_RAW_CONTROLLER: 4 , + MAV_DATA_STREAM_POSITION: 6, + MAV_DATA_STREAM_EXTRA1: 10, + MAV_DATA_STREAM_EXTRA2: 11, + MAV_DATA_STREAM_EXTRA3: 12 +} + const isValidNumber = (num, range) => { return ( num && @@ -199,7 +213,7 @@ function ReleaseCheckRow() { function SetRatesRow() { const { getSetting } = useSettings() - + const dispatch = useDispatch() const onClick = () => { const MAV_DATA_STREAM_ALL = getSetting("Developer.MAV_DATA_STREAM_ALL") @@ -212,7 +226,11 @@ function SetRatesRow() { const MAV_DATA_STREAM_EXTRA2 = getSetting("Developer.MAV_DATA_STREAM_EXTRA2") const MAV_DATA_STREAM_EXTRA3 = getSetting("Developer.MAV_DATA_STREAM_EXTRA3") - console.log(MAV_DATA_STREAM_ALL) + + for (const [name, value] of Object.entries(STREAM_MAP)) { + let rate = getSetting(`Developer.${name}`) + dispatch(emitSetStreamRates({stream: value, rate: rate})) + } } return ( diff --git a/gcs/src/redux/middleware/emitters.js b/gcs/src/redux/middleware/emitters.js index 634dfeb30..695685f5e 100644 --- a/gcs/src/redux/middleware/emitters.js +++ b/gcs/src/redux/middleware/emitters.js @@ -37,6 +37,7 @@ import { emitSetCurrentFlightMode, emitSetLoiterRadius, emitSetState, + emitSetStreamRates, emitStartForwarding, emitStopForwarding, emitTakeoff, @@ -210,6 +211,14 @@ export function handleEmitters(socket, store, action) { newFlightMode: action.payload.newFlightMode, }), }, + { + emitter: emitSetStreamRates, + callback: () => + socket.socket.emit("set_stream_rate", { + stream: action.payload.stream, + rate: action.payload.rate + }) + }, { emitter: emitStartSimulation, diff --git a/gcs/src/redux/slices/droneConnectionSlice.js b/gcs/src/redux/slices/droneConnectionSlice.js index 85b5c5405..a388a9f62 100644 --- a/gcs/src/redux/slices/droneConnectionSlice.js +++ b/gcs/src/redux/slices/droneConnectionSlice.js @@ -188,6 +188,7 @@ const droneConnectionSlice = createSlice({ emitTakeoff: () => {}, emitLand: () => {}, emitSetCurrentFlightMode: () => {}, + emitSetStreamRates: () => {} }, selectors: { selectConnecting: (state) => state.connecting, @@ -263,6 +264,7 @@ export const { emitTakeoff, emitLand, emitSetCurrentFlightMode, + emitSetStreamRates } = droneConnectionSlice.actions export const { selectConnecting, diff --git a/radio/app/endpoints/states.py b/radio/app/endpoints/states.py index 9b38a4de8..f898ce3d4 100644 --- a/radio/app/endpoints/states.py +++ b/radio/app/endpoints/states.py @@ -13,6 +13,11 @@ class SetStateType(TypedDict): state: str +class SetStreamRateType(TypedDict): + stream: int + rate: int + + GLOBAL_MESSAGE_LISTENERS = ["HEARTBEAT", "STATUSTEXT", "GLOBAL_POSITION_INT", "VFR_HUD"] STATES_MESSAGE_LISTENERS = { @@ -113,3 +118,12 @@ def set_state(data: SetStateType) -> None: for message in STATES_MESSAGE_LISTENERS["config.servo"]: droneStatus.drone.addMessageListener(message, sendMessage) + + +@socketio.on("set_stream_rate") +def set_stream_rate(data: SetStreamRateType): + if not droneStatus.drone: + return + + print("Setting data stream rate") + droneStatus.drone.sendDataStreamRequestMessage(data["stream"], data["rate"]) From b15667830f55091b101750eb2f45712ba17a51fd Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Thu, 26 Mar 2026 21:15:40 +0000 Subject: [PATCH 04/10] Looks nicer --- gcs/data/default_settings.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gcs/data/default_settings.json b/gcs/data/default_settings.json index b87196956..31097ef37 100644 --- a/gcs/data/default_settings.json +++ b/gcs/data/default_settings.json @@ -121,7 +121,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_ALL", + "display": "STREAM_ALL", "suffix": "Hz", "group": "Data stream rates" }, @@ -132,7 +132,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_RAW_SENSORS", + "display": "RAW_SENSORS", "suffix": "Hz", "group": "Data stream rates" }, @@ -143,7 +143,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_EXTENDED_STATUS", + "display": "EXTENDED_STATUS", "suffix": "Hz", "group": "Data stream rates" }, @@ -154,7 +154,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_RC_CHANNELS", + "display": "RC_CHANNELS", "suffix": "Hz", "group": "Data stream rates" }, @@ -165,7 +165,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_RAW_CONTROLLER", + "display": "RAW_CONTROLLER", "suffix": "Hz", "group": "Data stream rates" }, @@ -176,7 +176,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_POSITION", + "display": "POSITION", "suffix": "Hz", "group": "Data stream rates" }, @@ -187,7 +187,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_EXTRA1", + "display": "EXTRA1", "suffix": "Hz", "group": "Data stream rates" }, @@ -198,7 +198,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_EXTRA2", + "display": "EXTRA2", "suffix": "Hz", "group": "Data stream rates" }, @@ -209,7 +209,7 @@ 0, 150 ], - "display": "MAV_DATA_STREAM_EXTRA3", + "display": "EXTRA3", "suffix": "Hz", "group": "Data stream rates" } From 30b6e67db142c1cad53bbe0c4d286fa13639be19 Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Thu, 26 Mar 2026 21:26:06 +0000 Subject: [PATCH 05/10] Format --- gcs/src/components/settingsModal.jsx | 51 ++++++++------------ gcs/src/redux/middleware/emitters.js | 4 +- gcs/src/redux/slices/droneConnectionSlice.js | 4 +- radio/app/endpoints/params.py | 4 +- 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index f3af624ac..29c1d0cbf 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -41,11 +41,11 @@ const STREAM_MAP = { MAV_DATA_STREAM_RAW_SENSORS: 1, MAV_DATA_STREAM_EXTENDED_STATUS: 2, MAV_DATA_STREAM_RC_CHANNELS: 3, - MAV_DATA_STREAM_RAW_CONTROLLER: 4 , + MAV_DATA_STREAM_RAW_CONTROLLER: 4, MAV_DATA_STREAM_POSITION: 6, MAV_DATA_STREAM_EXTRA1: 10, MAV_DATA_STREAM_EXTRA2: 11, - MAV_DATA_STREAM_EXTRA3: 12 + MAV_DATA_STREAM_EXTRA3: 12, } const isValidNumber = (num, range) => { @@ -216,32 +216,17 @@ function SetRatesRow() { const dispatch = useDispatch() const onClick = () => { - const MAV_DATA_STREAM_ALL = getSetting("Developer.MAV_DATA_STREAM_ALL") - const MAV_DATA_STREAM_RAW_SENSORS = getSetting("Developer.MAV_DATA_STREAM_RAW_SENSORS") - const MAV_DATA_STREAM_EXTENDED_STATUS = getSetting("Developer.MAV_DATA_STREAM_EXTENDED_STATUS") - const MAV_DATA_STREAM_RC_CHANNELS = getSetting("Developer.MAV_DATA_STREAM_RC_CHANNELS") - const MAV_DATA_STREAM_RAW_CONTROLLER = getSetting("Developer.MAV_DATA_STREAM_RAW_CONTROLLER") - const MAV_DATA_STREAM_POSITION = getSetting("Developer.MAV_DATA_STREAM_POSITION") - const MAV_DATA_STREAM_EXTRA1 = getSetting("Developer.MAV_DATA_STREAM_EXTRA1") - const MAV_DATA_STREAM_EXTRA2 = getSetting("Developer.MAV_DATA_STREAM_EXTRA2") - const MAV_DATA_STREAM_EXTRA3 = getSetting("Developer.MAV_DATA_STREAM_EXTRA3") - - for (const [name, value] of Object.entries(STREAM_MAP)) { let rate = getSetting(`Developer.${name}`) - dispatch(emitSetStreamRates({stream: value, rate: rate})) + dispatch(emitSetStreamRates({ stream: value, rate: rate })) } } return ( -
-
-
-
- -
+
+
) } @@ -348,12 +333,12 @@ function ExtendableTextSetting({ settingName, df }) { const [items, setItems] = useState( getSetting(settingName).length > 0 ? getSetting(settingName).map((item) => { - const newItem = { id: generateId() } - df.fields.forEach((field) => { - newItem[field.key] = item[field.key] || "" + const newItem = { id: generateId() } + df.fields.forEach((field) => { + newItem[field.key] = item[field.key] || "" + }) + return newItem }) - return newItem - }) : [], ) @@ -618,7 +603,7 @@ function Setting({ settingName, df, initialValue }) { setChangedFromDefault( !["[]", '""'].includes(JSON.stringify(df.default)) && - JSON.stringify(getSetting(settingName)) != JSON.stringify(df.default), + JSON.stringify(getSetting(settingName)) != JSON.stringify(df.default), ) }, [getSetting(settingName)]) @@ -791,7 +776,10 @@ function SettingsModal() { {settingTabs.map((t) => { // Only show developer tag when developer features are on - if (!getSetting("General.experimentalDeveloperFeatures") && t === "Developer") { + if ( + !getSetting("General.experimentalDeveloperFeatures") && + t === "Developer" + ) { return <> } return ( @@ -854,8 +842,7 @@ function SettingsModal() { <> - ) - } + )} ) })} @@ -879,7 +866,7 @@ function SettingsModal() { setting .split(".") .reduce((title, value) => title[value], DefaultSettings)[ - "display" + "display" ] }

diff --git a/gcs/src/redux/middleware/emitters.js b/gcs/src/redux/middleware/emitters.js index 695685f5e..a45368840 100644 --- a/gcs/src/redux/middleware/emitters.js +++ b/gcs/src/redux/middleware/emitters.js @@ -216,8 +216,8 @@ export function handleEmitters(socket, store, action) { callback: () => socket.socket.emit("set_stream_rate", { stream: action.payload.stream, - rate: action.payload.rate - }) + rate: action.payload.rate, + }), }, { diff --git a/gcs/src/redux/slices/droneConnectionSlice.js b/gcs/src/redux/slices/droneConnectionSlice.js index a388a9f62..68cd51f89 100644 --- a/gcs/src/redux/slices/droneConnectionSlice.js +++ b/gcs/src/redux/slices/droneConnectionSlice.js @@ -188,7 +188,7 @@ const droneConnectionSlice = createSlice({ emitTakeoff: () => {}, emitLand: () => {}, emitSetCurrentFlightMode: () => {}, - emitSetStreamRates: () => {} + emitSetStreamRates: () => {}, }, selectors: { selectConnecting: (state) => state.connecting, @@ -264,7 +264,7 @@ export const { emitTakeoff, emitLand, emitSetCurrentFlightMode, - emitSetStreamRates + emitSetStreamRates, } = droneConnectionSlice.actions export const { selectConnecting, diff --git a/radio/app/endpoints/params.py b/radio/app/endpoints/params.py index b9510188a..ce2ce738a 100644 --- a/radio/app/endpoints/params.py +++ b/radio/app/endpoints/params.py @@ -39,9 +39,7 @@ def set_multiple_params(params_list: List[Any]) -> None: if droneStatus.state != "params": socketio.emit( "params_error", - { - "message": "You must be on the params screen to save parameters." - }, + {"message": "You must be on the params screen to save parameters."}, ) logger.debug(f"Current state: {droneStatus.state}") return From 75dd0106a617f8f5bebc2059c35192e3ceed7c71 Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Fri, 27 Mar 2026 10:58:12 +0000 Subject: [PATCH 06/10] - Moved map to MAVlink constants - Changed max data stream rate to 15 - Included logging statements and stream rate validation in backend - Formatted --- gcs/src/components/settingsModal.jsx | 15 ++------------- gcs/src/helpers/mavlinkConstants.js | 12 ++++++++++++ radio/app/endpoints/states.py | 11 +++++++++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index 29c1d0cbf..9baca44b9 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -33,21 +33,10 @@ import { } from "../helpers/notification" import { useDispatch } from "react-redux" import { emitSetStreamRates } from "../redux/slices/droneConnectionSlice" +import { DATA_STREAM_MAP } from "../helpers/mavlinkConstants" const octokit = new Octokit({}) -const STREAM_MAP = { - MAV_DATA_STREAM_ALL: 0, - MAV_DATA_STREAM_RAW_SENSORS: 1, - MAV_DATA_STREAM_EXTENDED_STATUS: 2, - MAV_DATA_STREAM_RC_CHANNELS: 3, - MAV_DATA_STREAM_RAW_CONTROLLER: 4, - MAV_DATA_STREAM_POSITION: 6, - MAV_DATA_STREAM_EXTRA1: 10, - MAV_DATA_STREAM_EXTRA2: 11, - MAV_DATA_STREAM_EXTRA3: 12, -} - const isValidNumber = (num, range) => { return ( num && @@ -216,7 +205,7 @@ function SetRatesRow() { const dispatch = useDispatch() const onClick = () => { - for (const [name, value] of Object.entries(STREAM_MAP)) { + for (const [name, value] of Object.entries(DATA_STREAM_MAP)) { let rate = getSetting(`Developer.${name}`) dispatch(emitSetStreamRates({ stream: value, rate: rate })) } diff --git a/gcs/src/helpers/mavlinkConstants.js b/gcs/src/helpers/mavlinkConstants.js index e2a64fbe5..d76bc494b 100644 --- a/gcs/src/helpers/mavlinkConstants.js +++ b/gcs/src/helpers/mavlinkConstants.js @@ -536,3 +536,15 @@ export const EXCLUDE_PARAMS_LOAD = [ "STAT_RESET", "STAT_RUNTIME", ] + +export const DATA_STREAM_MAP = { + MAV_DATA_STREAM_ALL: 0, + MAV_DATA_STREAM_RAW_SENSORS: 1, + MAV_DATA_STREAM_EXTENDED_STATUS: 2, + MAV_DATA_STREAM_RC_CHANNELS: 3, + MAV_DATA_STREAM_RAW_CONTROLLER: 4, + MAV_DATA_STREAM_POSITION: 6, + MAV_DATA_STREAM_EXTRA1: 10, + MAV_DATA_STREAM_EXTRA2: 11, + MAV_DATA_STREAM_EXTRA3: 12, +} diff --git a/radio/app/endpoints/states.py b/radio/app/endpoints/states.py index f898ce3d4..cde0854e4 100644 --- a/radio/app/endpoints/states.py +++ b/radio/app/endpoints/states.py @@ -125,5 +125,12 @@ def set_stream_rate(data: SetStreamRateType): if not droneStatus.drone: return - print("Setting data stream rate") - droneStatus.drone.sendDataStreamRequestMessage(data["stream"], data["rate"]) + if (rate := data.get("rate", None)) is None: + return missingParameterError("set_state", "state") + + if rate > 15 or rate < 0: + logger.error("Cannot set data stream rate outside of range [0, 15]") + return + + logger.info(f"Setting data stream rate {data['rate']}") + droneStatus.drone.sendDataStreamRequestMessage(data["stream"], rate) From 8069753beec53621ce1425d12468cfdfb3c37b38 Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Sun, 29 Mar 2026 15:03:14 +0100 Subject: [PATCH 07/10] Remove stream all --- gcs/data/default_settings.json | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/gcs/data/default_settings.json b/gcs/data/default_settings.json index 31097ef37..100f3e154 100644 --- a/gcs/data/default_settings.json +++ b/gcs/data/default_settings.json @@ -114,17 +114,6 @@ } }, "Developer": { - "MAV_DATA_STREAM_ALL": { - "default": 1, - "type": "number", - "range": [ - 0, - 150 - ], - "display": "STREAM_ALL", - "suffix": "Hz", - "group": "Data stream rates" - }, "MAV_DATA_STREAM_RAW_SENSORS": { "default": 1, "type": "number", @@ -214,4 +203,4 @@ "group": "Data stream rates" } } -} +} \ No newline at end of file From fba7c4a6706e973ea974b063f999d862de37cb6e Mon Sep 17 00:00:00 2001 From: ProgramPhantom Date: Sun, 29 Mar 2026 15:03:50 +0100 Subject: [PATCH 08/10] Max as 15 --- gcs/data/default_settings.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gcs/data/default_settings.json b/gcs/data/default_settings.json index 100f3e154..5b052fc0f 100644 --- a/gcs/data/default_settings.json +++ b/gcs/data/default_settings.json @@ -119,7 +119,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "RAW_SENSORS", "suffix": "Hz", @@ -130,7 +130,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "EXTENDED_STATUS", "suffix": "Hz", @@ -141,7 +141,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "RC_CHANNELS", "suffix": "Hz", @@ -152,7 +152,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "RAW_CONTROLLER", "suffix": "Hz", @@ -163,7 +163,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "POSITION", "suffix": "Hz", @@ -174,7 +174,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "EXTRA1", "suffix": "Hz", @@ -185,7 +185,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "EXTRA2", "suffix": "Hz", @@ -196,7 +196,7 @@ "type": "number", "range": [ 0, - 150 + 15 ], "display": "EXTRA3", "suffix": "Hz", From 9b1c279e6a8c5837a6780b4f41aab34ec31cce12 Mon Sep 17 00:00:00 2001 From: Kush Makkapati Date: Sun, 29 Mar 2026 18:59:10 +0100 Subject: [PATCH 09/10] Fix bug with datastreams not getting set, fix bug with heartbeat thread not sending at 1hz --- gcs/src/components/settingsModal.jsx | 12 ++++-- gcs/src/helpers/settingsProvider.jsx | 11 +++-- radio/app/drone.py | 23 ++++++++++- radio/app/endpoints/states.py | 60 ++++++++++++++++++++++------ 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index 9baca44b9..4a879c237 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -24,16 +24,16 @@ import { } from "@tabler/icons-react" import { Octokit } from "octokit" import { memo, useEffect, useState } from "react" +import { useDispatch } from "react-redux" import semverGt from "semver/functions/gt" import DefaultSettings from "../../data/default_settings.json" +import { DATA_STREAM_MAP } from "../helpers/mavlinkConstants" import { closeLoadingNotification, redColor, showLoadingNotification, } from "../helpers/notification" -import { useDispatch } from "react-redux" import { emitSetStreamRates } from "../redux/slices/droneConnectionSlice" -import { DATA_STREAM_MAP } from "../helpers/mavlinkConstants" const octokit = new Octokit({}) @@ -203,10 +203,16 @@ function ReleaseCheckRow() { function SetRatesRow() { const { getSetting } = useSettings() const dispatch = useDispatch() + const developerDefaults = DefaultSettings?.Developer ?? {} const onClick = () => { for (const [name, value] of Object.entries(DATA_STREAM_MAP)) { - let rate = getSetting(`Developer.${name}`) + if (!Object.hasOwn(developerDefaults, name)) continue + + const rateSetting = getSetting(`Developer.${name}`) + const rate = Number(rateSetting) + if (!Number.isFinite(rate)) continue + if (rate < 0 || rate > 15) continue dispatch(emitSetStreamRates({ stream: value, rate: rate })) } } diff --git a/gcs/src/helpers/settingsProvider.jsx b/gcs/src/helpers/settingsProvider.jsx index c0bf3b35a..b32226e77 100644 --- a/gcs/src/helpers/settingsProvider.jsx +++ b/gcs/src/helpers/settingsProvider.jsx @@ -56,10 +56,15 @@ export const SettingsProvider = ({ children }) => { const getSetting = (setting) => { const userSetting = getSettingFromSettings(setting, settings.settings) + const defaultSetting = getSettingFromSettings(setting, DefaultSettings) - return userSetting === null - ? getSettingFromSettings(setting, DefaultSettings).default - : userSetting + if (userSetting !== null) return userSetting + + if (defaultSetting === null || defaultSetting === undefined) { + return null + } + + return defaultSetting.default } return ( diff --git a/radio/app/drone.py b/radio/app/drone.py index 2a75a7bbd..5a8573a41 100644 --- a/radio/app/drone.py +++ b/radio/app/drone.py @@ -46,6 +46,7 @@ mavutil.mavlink.MAV_DATA_STREAM_RAW_SENSORS: 1, mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS: 1, mavutil.mavlink.MAV_DATA_STREAM_RC_CHANNELS: 1, + mavutil.mavlink.MAV_DATA_STREAM_RAW_CONTROLLER: 1, mavutil.mavlink.MAV_DATA_STREAM_POSITION: 1, mavutil.mavlink.MAV_DATA_STREAM_EXTRA1: 4, mavutil.mavlink.MAV_DATA_STREAM_EXTRA2: 3, @@ -520,7 +521,13 @@ def setupSingleDataStream(self, stream: int) -> None: Args: stream (int): The data stream to set up """ - self.sendDataStreamRequestMessage(stream, DATASTREAM_RATES[stream]) + rate = DATASTREAM_RATES.get(stream) + if rate is None: + self.logger.warning( + f"No configured rate for stream {stream}; skipping setup request" + ) + return + self.sendDataStreamRequestMessage(stream, rate) @sendingCommandLock def sendDataStreamRequestMessage(self, stream: int, rate: int) -> None: @@ -888,7 +895,15 @@ def getLinkDebugData(self) -> None: def sendHeartbeatMessage(self) -> None: """Sends a heartbeat message to the drone every second.""" + heartbeat_interval_secs = 1.0 + next_heartbeat_time = time.monotonic() + while self.is_active.is_set(): + now = time.monotonic() + sleep_time = next_heartbeat_time - now + if sleep_time > 0: + time.sleep(sleep_time) + try: self.master.mav.heartbeat_send( mavutil.mavlink.MAV_TYPE_GCS, @@ -899,7 +914,11 @@ def sendHeartbeatMessage(self) -> None: ) except Exception as e: self.logger.error(f"Failed to send heartbeat: {e}", exc_info=True) - time.sleep(1) + + # Keep a stable 1Hz cadence and recover if we fall behind. + next_heartbeat_time += heartbeat_interval_secs + if next_heartbeat_time < time.monotonic(): + next_heartbeat_time = time.monotonic() + heartbeat_interval_secs def startThread(self) -> None: """Starts the listener and sender threads.""" diff --git a/radio/app/endpoints/states.py b/radio/app/endpoints/states.py index 56f5ca893..7aa0b5bd5 100644 --- a/radio/app/endpoints/states.py +++ b/radio/app/endpoints/states.py @@ -1,8 +1,11 @@ +import copy + from pymavlink import mavutil from typing_extensions import TypedDict import app.droneStatus as droneStatus from app import logger, socketio +from app.drone import DATASTREAM_RATES from app.utils import ( missingParameterError, sendMessage, @@ -48,6 +51,17 @@ class SetStreamRateType(TypedDict): } +DASHBOARD_STREAM_RATES = copy.deepcopy(DATASTREAM_RATES) + + +def apply_dashboard_stream_rates() -> None: + if not droneStatus.drone: + return + + for stream, rate in DASHBOARD_STREAM_RATES.items(): + droneStatus.drone.sendDataStreamRequestMessage(stream, rate) + + @socketio.on("set_state") def set_state(data: SetStateType) -> None: """ @@ -73,29 +87,37 @@ def set_state(data: SetStateType) -> None: # Remove all existing message listeners droneStatus.drone.clearAllMessageListeners() - # Always setup position stream to get GLOBAL_POSITION_INT messages - droneStatus.drone.setupSingleDataStream(mavutil.mavlink.MAV_DATA_STREAM_POSITION) + # Always setup position stream to get GLOBAL_POSITION_INT messages on + # non-dashboard pages + if droneStatus.state != "dashboard": + droneStatus.drone.sendDataStreamRequestMessage( + mavutil.mavlink.MAV_DATA_STREAM_POSITION, 1 + ) for message in GLOBAL_MESSAGE_LISTENERS: droneStatus.drone.addMessageListener(message, sendMessage) if droneStatus.state == "dashboard": - droneStatus.drone.setupDataStreams() + apply_dashboard_stream_rates() for message in STATES_MESSAGE_LISTENERS["dashboard"]: droneStatus.drone.addMessageListener(message, sendMessage) elif droneStatus.state == "missions": - droneStatus.drone.setupSingleDataStream( - mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS + droneStatus.drone.sendDataStreamRequestMessage( + mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS, 1 ) for message in STATES_MESSAGE_LISTENERS["missions"]: droneStatus.drone.addMessageListener(message, sendMessage) elif droneStatus.state == "graphs": - droneStatus.drone.setupSingleDataStream( - mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS + droneStatus.drone.sendDataStreamRequestMessage( + mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS, 1 + ) + droneStatus.drone.sendDataStreamRequestMessage( + mavutil.mavlink.MAV_DATA_STREAM_EXTRA1, 4 + ) + droneStatus.drone.sendDataStreamRequestMessage( + mavutil.mavlink.MAV_DATA_STREAM_EXTRA2, 3 ) - droneStatus.drone.setupSingleDataStream(mavutil.mavlink.MAV_DATA_STREAM_EXTRA1) - droneStatus.drone.setupSingleDataStream(mavutil.mavlink.MAV_DATA_STREAM_EXTRA2) for message in STATES_MESSAGE_LISTENERS["graphs"]: droneStatus.drone.addMessageListener(message, sendMessage) @@ -128,11 +150,25 @@ def set_stream_rate(data: SetStreamRateType): return if (rate := data.get("rate", None)) is None: - return missingParameterError("set_state", "state") + return missingParameterError("set_stream_rate", "rate") + + if (stream := data.get("stream", None)) is None: + return missingParameterError("set_stream_rate", "stream") + + try: + rate = int(rate) + stream = int(stream) + except (ValueError, TypeError): + logger.error("Invalid set_stream_rate payload types") + return if rate > 15 or rate < 0: logger.error("Cannot set data stream rate outside of range [0, 15]") return - logger.info(f"Setting data stream rate {data['rate']}") - droneStatus.drone.sendDataStreamRequestMessage(data["stream"], rate) + DASHBOARD_STREAM_RATES[stream] = rate + + # Dashboard-only behavior: only apply immediately while dashboard is active. + if droneStatus.state == "dashboard": + logger.info(f"Setting dashboard data stream {stream} rate to {rate}") + droneStatus.drone.sendDataStreamRequestMessage(stream, rate) From 84be4964a4c3e1f7a275f8e35d97c026610bbeef Mon Sep 17 00:00:00 2001 From: Kush Makkapati Date: Sun, 29 Mar 2026 20:02:12 +0100 Subject: [PATCH 10/10] Add note to stream rates setting --- gcs/src/components/settingsModal.jsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/gcs/src/components/settingsModal.jsx b/gcs/src/components/settingsModal.jsx index 4a879c237..5bdea40fd 100644 --- a/gcs/src/components/settingsModal.jsx +++ b/gcs/src/components/settingsModal.jsx @@ -218,10 +218,15 @@ function SetRatesRow() { } return ( -
- +
+

+ Note: Data stream rates here apply to the dashboard only. +

+
+ +
) }