Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 91 additions & 1 deletion gcs/data/default_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,95 @@
}
]
}
},
"Developer": {
"MAV_DATA_STREAM_RAW_SENSORS": {
"default": 1,
"type": "number",
"range": [
0,
15
],
"display": "RAW_SENSORS",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_EXTENDED_STATUS": {
"default": 1,
"type": "number",
"range": [
0,
15
],
"display": "EXTENDED_STATUS",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_RC_CHANNELS": {
"default": 1,
"type": "number",
"range": [
0,
15
],
"display": "RC_CHANNELS",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_RAW_CONTROLLER": {
"default": 1,
"type": "number",
"range": [
0,
15
],
"display": "RAW_CONTROLLER",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_POSITION": {
"default": 1,
"type": "number",
"range": [
0,
15
],
"display": "POSITION",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_EXTRA1": {
"default": 4,
"type": "number",
"range": [
0,
15
],
"display": "EXTRA1",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_EXTRA2": {
"default": 3,
"type": "number",
"range": [
0,
15
],
"display": "EXTRA2",
"suffix": "Hz",
"group": "Data stream rates"
},
"MAV_DATA_STREAM_EXTRA3": {
"default": 1,
"type": "number",
"range": [
0,
15
],
"display": "EXTRA3",
"suffix": "Hz",
"group": "Data stream rates"
}
}
}
}
46 changes: 46 additions & 0 deletions gcs/src/components/settingsModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +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 { emitSetStreamRates } from "../redux/slices/droneConnectionSlice"

const octokit = new Octokit({})

Expand Down Expand Up @@ -197,6 +200,37 @@ 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)) {
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 }))
}
}

return (
<div className="mt-0! px-10">
<p className="text-xs text-gray-400 mb-2">
Note: Data stream rates here apply to the dashboard only.
</p>
<div className="flex justify-end">
<Button size="compact-xs" color="blue" onClick={onClick}>
Set rates
</Button>
</div>
</div>
)
}

function OptionSetting({ settingName, options }) {
const { getSetting, setSetting } = useSettings()
return (
Expand Down Expand Up @@ -741,6 +775,13 @@ function SettingsModal() {
>
<Tabs.List>
{settingTabs.map((t) => {
// Only show developer tag when developer features are on
if (
!getSetting("General.experimentalDeveloperFeatures") &&
t === "Developer"
) {
return <></>
}
return (
<Tabs.Tab key={t} value={t}>
{t}
Expand Down Expand Up @@ -797,6 +838,11 @@ function SettingsModal() {
<ReleaseCheckRow />
</div>
)}
{tab === "Developer" && (
<>
<SetRatesRow />
</>
)}
</Tabs.Panel>
)
})}
Expand Down
12 changes: 12 additions & 0 deletions gcs/src/helpers/mavlinkConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,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,
}
11 changes: 8 additions & 3 deletions gcs/src/helpers/settingsProvider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
9 changes: 9 additions & 0 deletions gcs/src/redux/middleware/emitters.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
emitSetCurrentFlightMode,
emitSetLoiterRadius,
emitSetState,
emitSetStreamRates,
emitStartForwarding,
emitStopForwarding,
emitTakeoff,
Expand Down Expand Up @@ -226,6 +227,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,
Expand Down
2 changes: 2 additions & 0 deletions gcs/src/redux/slices/droneConnectionSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ const droneConnectionSlice = createSlice({
emitTakeoff: () => {},
emitLand: () => {},
emitSetCurrentFlightMode: () => {},
emitSetStreamRates: () => {},
},
selectors: {
selectConnecting: (state) => state.connecting,
Expand Down Expand Up @@ -285,6 +286,7 @@ export const {
emitTakeoff,
emitLand,
emitSetCurrentFlightMode,
emitSetStreamRates,
} = droneConnectionSlice.actions

export const {
Expand Down
23 changes: 21 additions & 2 deletions radio/app/drone.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand All @@ -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."""
Expand Down
Loading
Loading