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.
+
+
+
+
)
}