Skip to content

RFC: addressing for the MAVLink-M messages that direct a specific system - #10

Open
TSC21 wants to merge 2 commits into
Dronecode:mainfrom
TSC21:rfc/sensor-tasking-addressing
Open

RFC: addressing for the MAVLink-M messages that direct a specific system#10
TSC21 wants to merge 2 commits into
Dronecode:mainfrom
TSC21:rfc/sensor-tasking-addressing

Conversation

@TSC21

@TSC21 TSC21 commented Jul 31, 2026

Copy link
Copy Markdown
Member

RIIS

Contributed on behalf of RIIS, LLC.



Opening this as an RFC. If the answer is that routing belongs outside the dialect I am happy to close it, but I think a message whose definition singles out one recipient should be able to name it, the same way core MAVLink already does.

What I ran into

While integrating the dialect into QGC, which consumes the situational-awareness messages and originates sensor tasking and nothing else, I checked the dialect for addressing and there is none. All 26 messages are broadcast, none of them carries target_system or target_component.

For the messages that describe the world that looks right and I am reading it as intended. A contact is a contact, and anything listening should be able to draw it.

For the messages whose definition directs a single system it is a spec gap. SENSOR_TASKING tasks one sensor, yet it cannot say which one, so the tasking goes out and every system reading the dialect sees it. The same shape repeats across the dialect's other directive messages. On one aircraft with one payload that works by luck. With two aircraft on a shared bearer, or one aircraft carrying two payloads that both speak the dialect, nothing in the message separates them.

What we do today in QGC is pick the link: we send sensor tasking only on the selected vehicle's link. That works because we own the link. It does nothing for anyone routing this over a shared bus, a mesh or a router, and it does not separate two payloads on the same aircraft.

What this PR does

Adds target_system and target_component to the eleven messages whose definitions direct a specific system or hand something to a named one: FIRES, SPLASH_CORRECTION, TARGET_HANDOVER, ESAD_ARMING, ESAD_CONFIG, SENSOR_TASKING, CAS_9LINE, TERMINAL_CONTROL, ENGAGEMENT_DIRECTIVE, CALL_FOR_FIRE, LOITER_MUNITION_CONTROL.

The messages that describe the world are left alone.

On scope, to be explicit: this adds no capability and no semantics, it only lets a directive the dialect already defines name its recipient, using the same two reserved field names core MAVLink uses for the purpose. The integration behind this RFC exercises SENSOR_TASKING and nothing else; the rest of the list is swept because a definitions change should treat every directive in the dialect consistently, not because I implement them or plan to.

The fields are part of the message, not an extension. That changes the wire format and the CRC. The dialect is still under development and the messages can be replaced, so this is the moment to put addressing in the message body rather than bolt it on the end later.

Why the field names matter

This is the part I would have got wrong if I had named the fields something of my own.

A router does not read the XML. It reads the message-entry table mavgen generates, and mavgen fills the target offsets in that table only for fields called exactly target_system and target_component. With the change, SENSOR_TASKING generates as:

{53050, 107, 26, 26, 3, 22, 23}

where 3 is the have-target flag and 22 and 23 are the byte offsets of the two fields in the payload. That is what lets a router find the target at a fixed offset without knowing anything else about the message, look up which endpoint that system was last heard on, and send it there instead of to everyone.

A picture message stays as it is:

{53000, 21, 141, 141, 0, 0, 0}

Flag 0, no offsets, so a router floods it to every endpoint, which is what you want for a contact.

Worth saying out loud: the router still has to have this dialect compiled in. A router that does not know 53050 cannot read its target fields and will broadcast it, so addressing in the message helps the routers that carry the dialect and changes nothing for the ones that do not.

Questions

  1. Is broadcast-only deliberate for the whole dialect, or has addressing simply not come up yet?
  2. Is my split right? I put the line at "does this message tell a system to do something". TARGET_CUE was the one I went back and forth on, since its description says it is relayed to one or more consumers, and I left it broadcast.
  3. MAVLINK_M_ACK already has ack_sysid, so I left it alone. Would you rather it used the reserved names too, so a router can place an acknowledgement back to the asker?

Happy to extend or narrow this whichever way you prefer.

Every message in the dialect is broadcast: none carries
target_system or target_component, so a message that orders one
system to do something cannot say which system it is for. A station
tasking a sensor on one aircraft, or ordering an effector to engage,
puts that message where every system reading the dialect sees it.
With two aircraft on a shared bearer, or one aircraft carrying two
payloads that both speak the dialect, nothing in the message
separates them.

The eleven messages that command a system, or that hand something
to a named one, carry the two fields: FIRES, SPLASH_CORRECTION,
TARGET_HANDOVER, ESAD_ARMING, ESAD_CONFIG, SENSOR_TASKING,
CAS_9LINE, TERMINAL_CONTROL, ENGAGEMENT_DIRECTIVE, CALL_FOR_FIRE and
LOITER_MUNITION_CONTROL. The messages that describe the world keep
broadcasting, because a contact is a contact and anything listening
should draw it.

The names are the ones MAVLink reserves. mavgen fills the target
offsets in the message-entry table only for fields called
target_system and target_component, and that table is what a router
reads to place a message: FIRES becomes {53020, 124, 64, 64, 3, 40,
41}, where 3 is the have-target flag and 40 and 41 are the byte
offsets. A field named anything else leaves that row at zero and the
message keeps flooding every endpoint.

The fields are part of the message rather than an extension, so the
wire format and the CRC change. This dialect is under development
and its messages can still be replaced, which is the moment to put
addressing in the message body where it belongs.
@TSC21
TSC21 force-pushed the rfc/sensor-tasking-addressing branch from 51e2e3f to 00bad7c Compare July 31, 2026 22:28
@TSC21 TSC21 changed the title RFC: optional target_system and target_component on SENSOR_TASKING RFC: address the MAVLink-M messages that command a specific system Jul 31, 2026
@TSC21
TSC21 requested a review from mrpollo July 31, 2026 22:34
@TSC21 TSC21 changed the title RFC: address the MAVLink-M messages that command a specific system RFC: addressing for the MAVLink-M messages that direct a specific system Aug 1, 2026
@ryanjAA

ryanjAA commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Q1 - not an oversight. The design assumes addressing.

The MAVLink-M swarm dataflow concept specifies "Addressed TARGET_HANDOVER per target to the chosen shooter's sysid", and lists "addressed handoff" among the things the dialect already provides.

It explicitly rules out the alternative: "What is explicitly not supported is a third, unsafe pattern - broadcasting one target to everyone and letting whoever arrives strike it. That produces convergence: multiple munitions wasted on one target, and multiple armed aircraft meeting at one point in space."
So addressed delivery is a design requirement with a stated safety rationale. The gap is that it was left to the frame's source sysid - which isn't something a router can act on. That's exactly what this PR fixes.
Addressing already exists, just below system level.

store_id, effector_id and esad_id all carry "0 = all" semantics.
The dialect addresses sub-components in-band, but not the system that owns them.
Related gap, probably a separate PR: several of the same command messages can't say what they're about.

No track_uid on ESAD_ARMING, ESAD_CONFIG, SENSOR_TASKING, TERMINAL_CONTROL, CAS_9LINE.
Sharpest case: ESAD_STATE reports track_uid ("armed against track X"), but ESAD_ARMING cannot specify it - the binding is set out-of-band and then assumed to hold.

Q2 - agree with the split; ESAD_ARMING especially.

Q3 - yes, use the reserved names on MAVLINK_M_ACK.

@TSC21

TSC21 commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

Thanks @ryanjAA, that answers all three.

Q1: that settles it. Addressing was always the design, it just was not in a place a router can act on. Good point on store_id, effector_id and esad_id as well. Those pick the device inside an aircraft, these new fields pick the aircraft, and 0 still means all at both levels.

Q2: good, the split stays as posted and TARGET_CUE stays broadcast.

Q3: I will add it to this PR. Concretely, origin_sysid becomes target_system, since it already names the system the ack has to get back to, target_component goes next to it, and ack_sysid goes away because the frame header already says who is sending. That leaves MAVLINK_M_ACK looking like COMMAND_ACK in common. Tell me if ack_sysid is there for a reason I am missing, otherwise I will push that here.

On track_uid, agreed, and I think it belongs in its own PR because it answers a different question: these fields say who has to act, track_uid says what the message is about. Your ESAD_ARMING against ESAD_STATE example is the clearest one. It hits SENSOR_TASKING too, where you cannot tell a sensor which track to watch, so a ground station ends up sending coordinates and the task cannot follow a moving target. Happy to write that one up if nobody else is on it.

So: the ack change lands here, this PR covers who has to act, and what the message is about comes separately. If that works for you, an approval once the ack commit is up and this can stop being an RFC.

MAVLINK_M_ACK closes the loop on a message one system sent to
another, so it has exactly one correct destination: the originator of
the acknowledged message. A field of its own naming that originator
stays invisible to a router, which places messages from the
message-entry table mavgen generates and finds a zeroed target row
there, so an acknowledgment reaches every endpoint on the bearer.

The reserved names carry the destination. target_system names the
originator the acknowledgment belongs to, and target_component names
the consumer inside it, which an aircraft carrying two systems that
speak the dialect needs in order to place the answer. The
acknowledging system is the source of the MAVLink frame, so the frame
header identifies it and the message body leaves that to the header.

The dialect generates MAVLINK_M_ACK as {53004, 224, 69, 69, 3, 16,
17}: 3 is the have-target flag and 16 and 17 are the byte offsets of
the two fields. The payload holds at 69 bytes, the CRC changes as a
field change requires, and the other twenty-five messages generate
byte-identical entry rows.
@TSC21

TSC21 commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

@ryanjAA pushed as c55620e.

MAVLINK_M_ACK carries target_system and target_component, and ack_sysid is gone because the frame header already says who is sending. It generates as {53004, 224, 69, 69, 3, 16, 17}, so a router can place it, and the payload stays at 69 bytes since two fields go in and two come out. Every other message in the dialect generates a byte-identical entry row.

I left origin_sysid alone on TRACK_IDENTITY, TARGET_CUE, PARTICIPANT_POSITION and ENGAGEMENT_DIRECTIVE. Those say who a report is about, not where it goes, so they are not the same field wearing a different name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants