Welcome! This guide explains how to fork, customize, and submit your custom software pilot.
# 1. Fork the repo on GitHub
# Using the GitHub CLI:
gh repo fork --default-branch-only DroneResponse/sade-software-pilot --clone=true
# OR:
# 1. Navigate to https://github.com/DroneResponse/sade-software-pilot and click "Fork"
# 2. Clone your fork
# git clone https://github.com/YOUR_USERNAME/sade-software-pilot.git
# cd sade-software-pilot
# 3. Setup development environment
# just dev-setup
# 4. Customize your mission
# edit the source files under src/software_pilot/
# 5. Test locally
just hooks
just security
just test
# 6. Push and open a PR
git push origin feature/my-mission
# Then open a PR at: https://github.com/DroneResponse/sade-software-pilotEdit src/software_pilot/mission.py to implement your mission logic:
async def run_example_mission(config: PilotConfig, drone: ResilientDrone) -> None:
"""Your custom mission implementation."""
# Fetch drone position
lat, lon, alt = await drone.fetch_drone_position()
home = Lla(lat=lat, lon=lon, altitude=alt)
# Create mission waypoints
mission = [
MissionStep(
short_name="takeoff",
description="Take off",
ned=NED(north=0, east=0, down=-50), # 50m altitude
home_alt=home.altitude,
speed=20.0, # m/s
home=home,
),
# Add more waypoints...
]
# Execute mission
await drone.execute_mission(mission)
# Request SADE zone access if needed
lease = request_sade_zone_entry(drone, emulate_wait=False)
if lease:
print(f"Zone access granted until {lease.expiration_time}")
# Land
await drone.action_land()Add tests for your mission in tests/:
import pytest
from software_pilot.mission import create_example_mission
from droneresponse_mathtools import Lla
@pytest.mark.asyncio
async def test_mission_creation():
home = Lla(lat=41.6, lon=-86.3, altitude=229)
mission = await create_example_mission(home)
assert len(mission) > 0
assert mission[0].short_name == "takeoff"Run tests:
just test
# you can also pass any pytest arguments:
just test -v --capture=no -k test_zone
just test --cov=src --cov-fail-under=70
just test --help
# Run security checks
just securityThese checks will standardize code style, and catch common bugs and potential problems before running a simulation.
This saves everyone's time in the review process, saves your time when running simulations, and prevents security issues.
# Run linter, formatter, type checking, etc on all files
just hooks
# or pass options
just hooks -d src/software_pilot
just hooks --last-commit --show-diff-on-failure
# useful, as sometimes these will catch bugs before running the code
# Note sometimes hooks will re-format files and apply automated fixes. You just need to
# stage the modified files (git add ...) and try committing them again (git commit -m ...)
# If you really need to skip a check, you can pass `--no-verify` to git commit, but the
# issue will arise again when the PR is open, so we recommend fixing it when you have
# the chance. Alternatively, you can add in-line ignores depending on what kind of
# problem it is.
# Run security checks
just securitygit add src/ tests/
# example with commit message
git commit -m "Add search-pattern mission for autonomous grid search"
git push origin feature/your-mission-nameOn GitHub:
- Navigate to your fork
- Click Pull Requests → New Pull Request
- Select:
- Base:
main(SADE original repository) - Compare:
feature/your-mission-name(your fork)
- Base:
- Fill out the PR template.
A SADE team member will:
- Request an automated review from an AI agent, highlight points that need fixing
- Review code quality and security manually
- Request changes, ask questions, or suggest improvements (marked with
[nit]) - Approve once everything looks good
- Merge to a custom branch:
contrib/{username}/{mission-name}
What we look for:
- ✅ No security issues: safe file I/O, no shell invocation, no obfuscated code, limited remote calls, etc.
- ✅ Comprehensive tests: this will shorten the feedback loop for you, as it is faster to run tests than to submit a simulation.
Once approved and merged, the simulation config will look like this:
Payload subject to change.
{
"pilot": {
"repo_url": "https://github.com/YOUR_USERNAME/sade-software-pilot",
"repo_branch": "contrib/YOUR_USERNAME/mission-name",
"custom_settings": {
"grid_size_m": 100,
"search_altitude_m": 150
}
},
"drones": [ ... ],
"environment": [ ... ]
}Main interface to drone operations.
Methods:
| Method | Description |
|---|---|
await drone.connect() |
Connects to autopilot |
await drone.fetch_drone_position() |
Gets lat/lon/alt |
await drone.execute_mission(mission_steps) |
Uploads and executes waypoints |
await drone.action_arm() |
Arms the drone |
await drone.action_takeoff() |
Takes off |
await drone.action_land() |
Lands |
await drone.telemetry_position() |
Streams live position |
await drone.telemetry_health() |
Streams health status |
Represents a single waypoint in a mission.
Constructor:
MissionStep(
short_name="waypoint_1", # brief identifier
description="fly north 100m", # human-readable description
ned=NED(north=100, east=0, down=-50), # coordinates relative to home
home_alt=229.0, # sea-level altitude of home
speed=20.0, # m/s
home=home_lla, # Lla object for home position
)North-East-Down coordinate system (relative to home position).
NED(
north=100, # meters north of home
east=50, # meters east of home
down=-100, # meters below home (negative = up)
)Request airspace access for a specific zone.
Returns: SadeZoneLease if approved, None if denied
Example:
lease = request_sade_zone_entry(drone, emulate_wait=False)
if lease:
print(f"Access granted until {lease.expiration_time}")
# Now it's safe to enter the zone
else:
print("Access denied, returning home")
await drone.action_land()Configuration passed to your pilot at startup.
Attributes:
drone_id: int- Unique drone identifiermavsdk_port: int- gRPC autopilot portmavlink_port: int- MAVLink UDP portmqtt_broker_address: str- MQTT broker for telemetrysade_zone_config_path: Path | None- Zone configuration filecustom_settings: dict[str, Any]- User-provided settings
Example:
async def run_mission(config: PilotConfig):
print(f"Flying drone {config.drone_id}")
print(f"Custom settings: {config.custom_settings}")
speed = config.custom_settings.get("speed_mps", 15.0)Problem: uv sync fails with dependency resolution error
Solution:
- Check that dependencies don't conflict with SADE core
- See
pyproject.tomlfor current versions - Propose version updates in your PR
Problem: ImportError: cannot import name 'ResilientDrone'
Solution:
# Ensure package is installed in editable mode
uv sync
# Then run with uv
uv run python src/software_pilot/mission.pyProblem: ConnectionError: Could not connect to drone
Solution:
- Verify MAVSDK port matches simulator configuration
- Check Firmware is running and listening
- Monitor logs:
docker compose logs sade-sim
Problem: ModuleNotFoundError during pytest
Solution:
# Ensure tests can import your package
cd sade-software-pilot
uv run pytest tests/ -v- API Questions: See API.md
- SADE Concepts: See QUICKSTART.md
- Examples: Browse examples/
- Issues: Check GitHub Issues
- Questions: Start a GitHub Discussion
Be respectful, inclusive, and professional. We appreciate all contributions!
Happy flying! 🚁