Skip to content

Commit a74a130

Browse files
997 feature choose message stream rates (#1133)
* - Added developer tab in settings that only appears when developer settings are turned on. - Populated developer tab with data stream rates fields * Added set rates row * - 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 * Looks nicer * Format * - Moved map to MAVlink constants - Changed max data stream rate to 15 - Included logging statements and stream rate validation in backend - Formatted * Remove stream all * Max as 15 * Fix bug with datastreams not getting set, fix bug with heartbeat thread not sending at 1hz * Add note to stream rates setting --------- Co-authored-by: Kush Makkapati <kush.makkapati@icloud.com>
1 parent 821ad9e commit a74a130

8 files changed

Lines changed: 255 additions & 15 deletions

File tree

gcs/data/default_settings.json

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,95 @@
112112
}
113113
]
114114
}
115+
},
116+
"Developer": {
117+
"MAV_DATA_STREAM_RAW_SENSORS": {
118+
"default": 1,
119+
"type": "number",
120+
"range": [
121+
0,
122+
15
123+
],
124+
"display": "RAW_SENSORS",
125+
"suffix": "Hz",
126+
"group": "Data stream rates"
127+
},
128+
"MAV_DATA_STREAM_EXTENDED_STATUS": {
129+
"default": 1,
130+
"type": "number",
131+
"range": [
132+
0,
133+
15
134+
],
135+
"display": "EXTENDED_STATUS",
136+
"suffix": "Hz",
137+
"group": "Data stream rates"
138+
},
139+
"MAV_DATA_STREAM_RC_CHANNELS": {
140+
"default": 1,
141+
"type": "number",
142+
"range": [
143+
0,
144+
15
145+
],
146+
"display": "RC_CHANNELS",
147+
"suffix": "Hz",
148+
"group": "Data stream rates"
149+
},
150+
"MAV_DATA_STREAM_RAW_CONTROLLER": {
151+
"default": 1,
152+
"type": "number",
153+
"range": [
154+
0,
155+
15
156+
],
157+
"display": "RAW_CONTROLLER",
158+
"suffix": "Hz",
159+
"group": "Data stream rates"
160+
},
161+
"MAV_DATA_STREAM_POSITION": {
162+
"default": 1,
163+
"type": "number",
164+
"range": [
165+
0,
166+
15
167+
],
168+
"display": "POSITION",
169+
"suffix": "Hz",
170+
"group": "Data stream rates"
171+
},
172+
"MAV_DATA_STREAM_EXTRA1": {
173+
"default": 4,
174+
"type": "number",
175+
"range": [
176+
0,
177+
15
178+
],
179+
"display": "EXTRA1",
180+
"suffix": "Hz",
181+
"group": "Data stream rates"
182+
},
183+
"MAV_DATA_STREAM_EXTRA2": {
184+
"default": 3,
185+
"type": "number",
186+
"range": [
187+
0,
188+
15
189+
],
190+
"display": "EXTRA2",
191+
"suffix": "Hz",
192+
"group": "Data stream rates"
193+
},
194+
"MAV_DATA_STREAM_EXTRA3": {
195+
"default": 1,
196+
"type": "number",
197+
"range": [
198+
0,
199+
15
200+
],
201+
"display": "EXTRA3",
202+
"suffix": "Hz",
203+
"group": "Data stream rates"
204+
}
115205
}
116-
}
206+
}

gcs/src/components/settingsModal.jsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ import {
2424
} from "@tabler/icons-react"
2525
import { Octokit } from "octokit"
2626
import { memo, useEffect, useState } from "react"
27+
import { useDispatch } from "react-redux"
2728
import semverGt from "semver/functions/gt"
2829
import DefaultSettings from "../../data/default_settings.json"
30+
import { DATA_STREAM_MAP } from "../helpers/mavlinkConstants"
2931
import {
3032
closeLoadingNotification,
3133
redColor,
3234
showLoadingNotification,
3335
} from "../helpers/notification"
36+
import { emitSetStreamRates } from "../redux/slices/droneConnectionSlice"
3437

3538
const octokit = new Octokit({})
3639

@@ -197,6 +200,37 @@ function ReleaseCheckRow() {
197200
)
198201
}
199202

203+
function SetRatesRow() {
204+
const { getSetting } = useSettings()
205+
const dispatch = useDispatch()
206+
const developerDefaults = DefaultSettings?.Developer ?? {}
207+
208+
const onClick = () => {
209+
for (const [name, value] of Object.entries(DATA_STREAM_MAP)) {
210+
if (!Object.hasOwn(developerDefaults, name)) continue
211+
212+
const rateSetting = getSetting(`Developer.${name}`)
213+
const rate = Number(rateSetting)
214+
if (!Number.isFinite(rate)) continue
215+
if (rate < 0 || rate > 15) continue
216+
dispatch(emitSetStreamRates({ stream: value, rate: rate }))
217+
}
218+
}
219+
220+
return (
221+
<div className="mt-0! px-10">
222+
<p className="text-xs text-gray-400 mb-2">
223+
Note: Data stream rates here apply to the dashboard only.
224+
</p>
225+
<div className="flex justify-end">
226+
<Button size="compact-xs" color="blue" onClick={onClick}>
227+
Set rates
228+
</Button>
229+
</div>
230+
</div>
231+
)
232+
}
233+
200234
function OptionSetting({ settingName, options }) {
201235
const { getSetting, setSetting } = useSettings()
202236
return (
@@ -741,6 +775,13 @@ function SettingsModal() {
741775
>
742776
<Tabs.List>
743777
{settingTabs.map((t) => {
778+
// Only show developer tag when developer features are on
779+
if (
780+
!getSetting("General.experimentalDeveloperFeatures") &&
781+
t === "Developer"
782+
) {
783+
return <></>
784+
}
744785
return (
745786
<Tabs.Tab key={t} value={t}>
746787
{t}
@@ -797,6 +838,11 @@ function SettingsModal() {
797838
<ReleaseCheckRow />
798839
</div>
799840
)}
841+
{tab === "Developer" && (
842+
<>
843+
<SetRatesRow />
844+
</>
845+
)}
800846
</Tabs.Panel>
801847
)
802848
})}

gcs/src/helpers/mavlinkConstants.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,15 @@ export const EXCLUDE_PARAMS_LOAD = [
557557
"STAT_RESET",
558558
"STAT_RUNTIME",
559559
]
560+
561+
export const DATA_STREAM_MAP = {
562+
MAV_DATA_STREAM_ALL: 0,
563+
MAV_DATA_STREAM_RAW_SENSORS: 1,
564+
MAV_DATA_STREAM_EXTENDED_STATUS: 2,
565+
MAV_DATA_STREAM_RC_CHANNELS: 3,
566+
MAV_DATA_STREAM_RAW_CONTROLLER: 4,
567+
MAV_DATA_STREAM_POSITION: 6,
568+
MAV_DATA_STREAM_EXTRA1: 10,
569+
MAV_DATA_STREAM_EXTRA2: 11,
570+
MAV_DATA_STREAM_EXTRA3: 12,
571+
}

gcs/src/helpers/settingsProvider.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ export const SettingsProvider = ({ children }) => {
5656

5757
const getSetting = (setting) => {
5858
const userSetting = getSettingFromSettings(setting, settings.settings)
59+
const defaultSetting = getSettingFromSettings(setting, DefaultSettings)
5960

60-
return userSetting === null
61-
? getSettingFromSettings(setting, DefaultSettings).default
62-
: userSetting
61+
if (userSetting !== null) return userSetting
62+
63+
if (defaultSetting === null || defaultSetting === undefined) {
64+
return null
65+
}
66+
67+
return defaultSetting.default
6368
}
6469

6570
return (

gcs/src/redux/middleware/emitters.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
emitSetCurrentFlightMode,
4040
emitSetLoiterRadius,
4141
emitSetState,
42+
emitSetStreamRates,
4243
emitStartForwarding,
4344
emitStopForwarding,
4445
emitTakeoff,
@@ -226,6 +227,14 @@ export function handleEmitters(socket, store, action) {
226227
newFlightMode: action.payload.newFlightMode,
227228
}),
228229
},
230+
{
231+
emitter: emitSetStreamRates,
232+
callback: () =>
233+
socket.socket.emit("set_stream_rate", {
234+
stream: action.payload.stream,
235+
rate: action.payload.rate,
236+
}),
237+
},
229238

230239
{
231240
emitter: emitStartSimulation,

gcs/src/redux/slices/droneConnectionSlice.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ const droneConnectionSlice = createSlice({
205205
emitTakeoff: () => {},
206206
emitLand: () => {},
207207
emitSetCurrentFlightMode: () => {},
208+
emitSetStreamRates: () => {},
208209
},
209210
selectors: {
210211
selectConnecting: (state) => state.connecting,
@@ -285,6 +286,7 @@ export const {
285286
emitTakeoff,
286287
emitLand,
287288
emitSetCurrentFlightMode,
289+
emitSetStreamRates,
288290
} = droneConnectionSlice.actions
289291

290292
export const {

radio/app/drone.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
mavutil.mavlink.MAV_DATA_STREAM_RAW_SENSORS: 1,
4747
mavutil.mavlink.MAV_DATA_STREAM_EXTENDED_STATUS: 1,
4848
mavutil.mavlink.MAV_DATA_STREAM_RC_CHANNELS: 1,
49+
mavutil.mavlink.MAV_DATA_STREAM_RAW_CONTROLLER: 1,
4950
mavutil.mavlink.MAV_DATA_STREAM_POSITION: 1,
5051
mavutil.mavlink.MAV_DATA_STREAM_EXTRA1: 4,
5152
mavutil.mavlink.MAV_DATA_STREAM_EXTRA2: 3,
@@ -520,7 +521,13 @@ def setupSingleDataStream(self, stream: int) -> None:
520521
Args:
521522
stream (int): The data stream to set up
522523
"""
523-
self.sendDataStreamRequestMessage(stream, DATASTREAM_RATES[stream])
524+
rate = DATASTREAM_RATES.get(stream)
525+
if rate is None:
526+
self.logger.warning(
527+
f"No configured rate for stream {stream}; skipping setup request"
528+
)
529+
return
530+
self.sendDataStreamRequestMessage(stream, rate)
524531

525532
@sendingCommandLock
526533
def sendDataStreamRequestMessage(self, stream: int, rate: int) -> None:
@@ -888,7 +895,15 @@ def getLinkDebugData(self) -> None:
888895

889896
def sendHeartbeatMessage(self) -> None:
890897
"""Sends a heartbeat message to the drone every second."""
898+
heartbeat_interval_secs = 1.0
899+
next_heartbeat_time = time.monotonic()
900+
891901
while self.is_active.is_set():
902+
now = time.monotonic()
903+
sleep_time = next_heartbeat_time - now
904+
if sleep_time > 0:
905+
time.sleep(sleep_time)
906+
892907
try:
893908
self.master.mav.heartbeat_send(
894909
mavutil.mavlink.MAV_TYPE_GCS,
@@ -899,7 +914,11 @@ def sendHeartbeatMessage(self) -> None:
899914
)
900915
except Exception as e:
901916
self.logger.error(f"Failed to send heartbeat: {e}", exc_info=True)
902-
time.sleep(1)
917+
918+
# Keep a stable 1Hz cadence and recover if we fall behind.
919+
next_heartbeat_time += heartbeat_interval_secs
920+
if next_heartbeat_time < time.monotonic():
921+
next_heartbeat_time = time.monotonic() + heartbeat_interval_secs
903922

904923
def startThread(self) -> None:
905924
"""Starts the listener and sender threads."""

0 commit comments

Comments
 (0)