Architecture
MavNet is intentionally small. Each layer depends only on layers below it.
MavNet.Core
MavNet.Protocol
MavNet.Protocol.Generated
MavNet.Transport.Udp
MavNet.PX4
MavNet.Probe
Layers
MavNet.Core: shared primitives such asMavId,StateRate, andStateSubscription.MavNet.Protocol: MAVLink v2 frame decode/encode primitives, X.25 CRC, message contracts, and the generated message registry.MavNet.Protocol.Generated: generated message structs, enums, and command structs.MavNet.Transport.Udp: one UDP socket, one receive loop, typed message events, and outbound GCS heartbeat.MavNet.PX4: vehicle-level state and command helpers.Droneis the current high-level entry point.MavNet.Probe: console sample for SITL/manual smoke testing.
Frame Rules
MavlinkFrame.TryDecode accepts MAVLink v2 frames only. It drops frames with:
- MAVLink v1 magic
- signed frames
- unknown incompatibility bits
- unknown message ids
- payload lengths outside the generated min/max bounds
- CRC mismatch
Important
Unknown incompatibility bits are dropped on purpose. MAVLink requires this for forward compatibility; accepting them can make future frames unsafe to forward or decode.
Generated Code
Do not hand-edit generated files:
src/MavNet.Protocol.Generated/src/MavNet.Protocol/Generated/MessageRegistry.cs
Regenerate with:
dotnet run --project tools/MavNet.CodeGen
Adding a message means:
- Add it to
tools/MavNet.CodeGen/allowlist.txt. - Regenerate.
- Add a typed event and dispatch case in
MavlinkConnection. - Surface it in
Vehicle/Droneonly if it belongs in the high-level API.
Runtime Model
- Receive events fire synchronously on the UDP receive thread.
- Subscriber exceptions are swallowed so one bad handler does not kill the receive loop.
Send<T>uses generated static message metadata and strips MAVLink v2 trailing zero payload bytes.VehiclecorrelatesCOMMAND_LONGwithCOMMAND_ACK; some commands also wait for a confirming heartbeat state change.
Keep event handlers short. UI apps should dispatch from handlers into their UI synchronization context.