Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.altnautica.com/llms.txt

Use this file to discover all available pages before exploring further.

Mission planning is an extension point the GCS exposes to plugins. This page covers how to add pattern generators, read and write missions, and write to the geofence + rally surfaces. Higher-level flight behaviors (Follow-Me, Orbit, thermal overlay, gimbal control, etc.) are scoped per-drone and ship through the extensions registry. See Multi-component plugins for the per-drone tab pattern those use.

Capabilities

CapabilityLets the plugin…
mission.readRead the active mission and its items.
mission.writeAppend, replace, or modify mission items.
mission.pattern.registerRegister a custom pattern generator.
mission.write is high risk; the operator sees an orange badge. mission.pattern.register is medium risk because it affects what gets uploaded to the FC.

Pattern generators

Mission Control ships built-in patterns (grid survey, spiral search, waypoint chain). A plugin adds new ones.
import { definePlugin, type PatternGenerator } from "@altnautica/plugin-sdk";

const concentricRings: PatternGenerator = {
  id: "com.example.concentric-rings",
  name: "Concentric rings",
  description: "Inspection orbit at increasing radii.",
  fields: [
    { id: "center", kind: "latlng", label: "Center" },
    { id: "minRadius", kind: "number", label: "Min radius (m)", default: 5 },
    { id: "maxRadius", kind: "number", label: "Max radius (m)", default: 30 },
    { id: "step", kind: "number", label: "Step (m)", default: 5 },
    { id: "altitude", kind: "number", label: "Altitude (m AGL)", default: 20 },
  ],
  generate(input) {
    const items = [];
    for (let r = input.minRadius; r <= input.maxRadius; r += input.step) {
      for (let theta = 0; theta < 360; theta += 30) {
        items.push({
          kind: "waypoint",
          lat: offsetLat(input.center.lat, r, theta),
          lng: offsetLng(input.center.lng, r, theta),
          alt_agl_m: input.altitude,
        });
      }
    }
    return items;
  },
};

definePlugin({
  id: "com.example.inspection",
  version: "0.1.0",
  mount(ctx) {
    ctx.mission.registerPattern(concentricRings);
    return null;
  },
});
The host renders the form from fields[], calls generate(input) on submit, and offers the result as a preview the operator can edit before upload. fields[] field kinds:
KindRenders as
textText input
numberNumber input
booleanToggle
selectDropdown with options[]
latlngMap-pinned point picker
polygonMap polygon drawing tool
polylineMap polyline drawing tool
altitudeAltitude picker (AGL / MSL toggle)
The host validates the form and rejects out-of-range numbers before calling generate.

Reading and writing missions

// read
const mission = await ctx.mission.read("active");
console.log(mission.items.length);

// modify
const next = {
  ...mission,
  items: [
    ...mission.items,
    { kind: "waypoint", lat: 12.97, lng: 77.59, alt_agl_m: 30 },
  ],
};
await ctx.mission.write("active", next);
The host validates the mission against the FC’s accepted item types and rejects writes with schema_invalid if anything is malformed. The validation rules are firmware-specific (ArduPilot accepts different MAV_CMDs than Betaflight) and the host applies the right rule set automatically.

Geofence and rally points

Plugins extend missions; they do not extend geofence directly. Use the canonical mission.write with the geofence-specific mission items:
await ctx.mission.write("geofence", {
  items: [
    { kind: "fence_polygon_inclusion", points: [...] },
    { kind: "fence_circle_exclusion", center: {...}, radius_m: 50 },
  ],
});
The host enforces a fixed schema for fence items and rejects unknown kinds.

Pattern preview

Patterns show a live preview on the map. The host calls generate (or preview if your generator defines one) every time the form changes. Patterns must therefore be cheap; spending more than 100 ms per generate is a UX bug. For expensive generation, ship a preview that returns a coarse shape and a generate that does the full work:
{
  preview(input) { return cheapPolygon(input); },
  generate(input) { return expensiveWaypointList(input); },
}

Persisting custom mission types

If your generator needs to round-trip through the GCS’s mission library, add a kind discriminator to your items and register a serializer:
ctx.mission.registerItemKind("com.example.soil-sampling", {
  serialize(item) { return { ...item, version: 1 }; },
  deserialize(blob) { return blob; },
});
The host stores serialized items in the mission row in Convex and restores them on load. The agent half ignores unknown kinds when uploading to the FC unless a handler is registered for them.

Testing

The host ships a mock mission store the SDK exposes for tests:
import { createMockMission } from "@altnautica/plugin-sdk/testing";

const mission = createMockMission();
await mission.write("active", { items: [...] });
const items = await mission.read("active");
expect(items.length).toBe(3);

See also