cu-flight-controller 1.2.0

This is a basic quadcopter Flight Controller implemented end to end using Copper components
import "../../support/just/plan.just"

ROOT := shell('cd "$1/../.." && pwd', justfile_directory())

# Run the Bevy + Copper simulation loop.
default: sim

# Compile both independent multi-Copper subsystem targets.
subsystems-check: end2end-check

# Validate every feature-selected MCU, compute, and deployment graph.
config-matrix:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo test -p cu-flight-controller --no-default-features --features logreader --test config_matrix

# Compile the ZED-enabled compute graph and the independent MCU firmware graph.
end2end-check:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo check -p cu-flight-controller --no-default-features --features end2end --bin quad-compute
  cargo check --config examples/cu_flight_controller/.cargo/config-firmware.toml --no-default-features --features firmware -p cu-flight-controller --bin quad

# Run the onboard-compute graph with the ZED camera source enabled.
end2end:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run -p cu-flight-controller --no-default-features --features end2end --bin quad-compute

# Render the simulated default mission across the complete two-subsystem deployment.
dag config="flight_controller.ron" mission="default" features="sim":
  #!/usr/bin/env bash
  set -euo pipefail

  invocation_dir="{{invocation_directory()}}"
  cfg_path="${invocation_dir}/{{config}}"
  if [[ ! -f "$cfg_path" ]]; then
    echo "No Copper config found at ${cfg_path}" >&2
    exit 1
  fi

  cd "{{ROOT}}"
  mission_value="{{mission}}"
  features_value="{{features}}"
  args=()
  if [[ -n "$mission_value" ]]; then
    args+=(--mission "$mission_value")
  fi
  if [[ -n "$features_value" ]]; then
    args+=(--features "$features_value")
  fi
  cargo run -p cu29-runtime --bin cu29-rendercfg -- "${args[@]}" --open "$cfg_path"

# A quick test to see if the sim can pick up your RC.
rc:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run -p cu-flight-controller --no-default-features --features sim --bin rc-tester

# Run the Bevy + Copper simulation loop.
sim:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo --config examples/cu_flight_controller/.cargo/config.toml run -r -p cu-flight-controller --no-default-features --features sim,forest-world --bin quad-sim

# Run a self-starting autonomous demo and exit cleanly so logs are finalized.
demo:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo --config examples/cu_flight_controller/.cargo/config.toml run -r -p cu-flight-controller --no-default-features --features sim,forest-world,autonomous-demo --bin quad-sim

# Run the Bevy + Copper simulation loop in the urban world.
sim-urban:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo --config examples/cu_flight_controller/.cargo/config.toml run -r -p cu-flight-controller --no-default-features --features sim,urban-world --bin quad-sim

# Run the simulator with ViTFly inference on NVIDIA CUDA.
sim-cuda:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run --release -p cu-flight-controller --no-default-features --features vitfly-cuda,forest-world --bin quad-sim

# Run the replay-backed remote debug server for the flight controller log.
resim-mcu-debug debug_base="copper/examples/cu_flight_controller/debug/v1" log="logs/flight_controller_sim.copper" replay_log="logs/flight_controller_resim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  replay_log="{{replay_log}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  if [[ "$replay_log" != /* ]]; then
    replay_log="${example_dir}/${replay_log}"
  fi
  cd "{{ROOT}}"
  cargo --config examples/cu_flight_controller/.cargo/config.toml run --profile debug-optimized -p cu-flight-controller --no-default-features --features sim-debug --bin quad-resim -- \
    --debug-base "{{debug_base}}" \
    --log-base "$log" \
    --replay-log-base "$replay_log"

# Run the replay-backed debugger for the compute log, including the depth
# images that were actually presented to ViTFly.
resim-compute-debug debug_base="copper/examples/cu_flight_controller/compute/debug/v1" log="logs/flight_compute_sim.copper" replay_log="logs/flight_compute_resim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  replay_log="{{replay_log}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  if [[ "$replay_log" != /* ]]; then
    replay_log="${example_dir}/${replay_log}"
  fi
  cd "{{ROOT}}"
  cargo --config examples/cu_flight_controller/.cargo/config.toml run --profile debug-optimized -p cu-flight-controller --no-default-features --features sim-debug --bin quad-compute-resim -- \
    --debug-base "{{debug_base}}" \
    --log-base "$log" \
    --replay-log-base "$replay_log"

# Run the Bevy + Copper simulation loop with cu_bevymon embedded in the window.
bevy:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run -p cu-flight-controller --no-default-features --features bevymon,forest-world --bin quad-bevymon

# Rebuild the local prototype-world GLB, editable Blender scene, and spawn-point preview.
forest-assets:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{invocation_directory()}}"
  blender --background --python tools/build_forest.py

# Download the local scene assets needed by the browser BevyMon app.
prepare-assets:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{invocation_directory()}}"
  mkdir -p assets
  if [[ ! -f "assets/quadcopter.glb" ]]; then
    echo "Downloading quadcopter.glb..."
    curl --fail --location --silent --show-error --output "assets/quadcopter.glb" "https://cdn.copper-robotics.com/quadcopter.glb"
  fi
  if [[ ! -f "assets/forest.glb" ]]; then
    echo "Downloading forest.glb..."
    curl --fail --location --silent --show-error --output "assets/forest.glb" "https://cdn.copper-robotics.com/forest.glb"
  fi
  if [[ ! -f "assets/skybox.ktx2" ]]; then
    echo "Downloading skybox.ktx2..."
    curl --fail --location --silent --show-error --output "assets/skybox.ktx2" "https://cdn.copper-robotics.com/skybox.ktx2"
  fi
  if [[ ! -f "assets/specular_map.ktx2" ]]; then
    echo "Downloading specular_map.ktx2..."
    curl --fail --location --silent --show-error --output "assets/specular_map.ktx2" "https://cdn.copper-robotics.com/specular_map.ktx2"
  fi

# Serve the BevyMon flight controller sim in the browser.
web:
  #!/usr/bin/env bash
  set -euo pipefail
  if ! command -v trunk >/dev/null 2>&1; then
    echo "Missing trunk. Install with: cargo install --locked trunk" >&2
    exit 1
  fi
  cd "{{invocation_directory()}}"
  just prepare-assets
  trunk serve --open

# Build a static browser bundle into dist/flight-controller with hashed asset filenames.
web-dist:
  #!/usr/bin/env bash
  set -euo pipefail
  if ! command -v trunk >/dev/null 2>&1; then
    echo "Missing trunk. Install with: cargo install --locked trunk" >&2
    exit 1
  fi
  cd "{{invocation_directory()}}"
  just prepare-assets
  rm -rf dist/flight-controller
  trunk build --release --public-url ./ --dist dist/flight-controller
  echo "Static web bundle written to ${PWD}/dist/flight-controller"

# Extract CopperLists from a log file (JSON export).
logreader log="logs/flight_controller_sim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  cd "{{ROOT}}"
  RUST_BACKTRACE=1 cargo run -p cu-flight-controller --no-default-features --features logreader --bin quad-logreader -- "$log" extract-copperlists --export-format json

# Extract compute CopperLists, including ViTFly predictions (JSON export).
compute-logreader log="logs/flight_compute_sim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  log="{{log}}"
  if [[ "$log" != /* ]]; then
    log="{{justfile_directory()}}/${log}"
  fi
  cd "{{ROOT}}"
  RUST_BACKTRACE=1 cargo run -p cu-flight-controller --no-default-features --features compute-logreader --bin quad-compute-logreader -- "$log" extract-copperlists --export-format json

# Run the compute-subsystem log reader fsck.
compute-fsck log="logs/flight_compute_sim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  log="{{log}}"
  if [[ "$log" != /* ]]; then
    log="{{justfile_directory()}}/${log}"
  fi
  cd "{{ROOT}}"
  RUST_BACKTRACE=1 cargo run -p cu-flight-controller --no-default-features --features compute-logreader --bin quad-compute-logreader -- "$log" fsck

# Build the cu_flight_controller Python extension module.
py-build:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo rustc -p cu-flight-controller --no-default-features --features python-bindings --lib --crate-type cdylib

# Python example reading the log.
py log="logs/flight_controller_sim.copper": py-build
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  cd "{{ROOT}}"
  cargo rustc -p cu-flight-controller --no-default-features --features python-bindings --lib --crate-type cdylib
  python3 "${example_dir}/python/print_gnss_from_log.py" "$log"

# Print GNSS coordinates from a Copper log via the Python extension.
py-gnss log="logs/flight_controller_sim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  cd "{{ROOT}}"
  cargo rustc -p cu-flight-controller --no-default-features --features python-bindings --lib --crate-type cdylib
  python3 "${example_dir}/python/print_gnss_from_log.py" "$log"
# Run log reader fsck against a log file.
fsck log="logs/flight_controller_sim.copper":
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  cd "{{ROOT}}"
  RUST_BACKTRACE=1 cargo run -p cu-flight-controller --no-default-features --features logreader --bin quad-logreader -- "$log" fsck

# Extract text logs with a specified log index directory.
textlogs log="logs/embedded.copper" index="../../target/debug/cu29_log_index":
  #!/usr/bin/env bash
  set -euo pipefail
  example_dir="{{justfile_directory()}}"
  log="{{log}}"
  index="{{index}}"
  if [[ "$log" != /* ]]; then
    log="${example_dir}/${log}"
  fi
  if [[ "$index" != /* ]]; then
    index="${example_dir}/${index}"
  fi
  [[ -e "$index" ]] || { echo "log index not found: $index" >&2; exit 1; }
  cd "{{ROOT}}"
  RUST_BACKTRACE=1 cargo run -p cu-flight-controller --no-default-features --features logreader --bin quad-logreader -- "$log" extract-text-log "$index"

# Build and run firmware with textlogs enabled (release profile).
fw:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run --release --config examples/cu_flight_controller/.cargo/config-firmware.toml --no-default-features --features firmware,textlogs -p cu-flight-controller --bin quad

# Release release. With no textlogs.
fwr:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run --profile screaming --config examples/cu_flight_controller/.cargo/config-firmware.toml --no-default-features --features firmware -p cu-flight-controller --bin quad

# Debug (it won't be fast enough to run be at least you van debug the bringup).
fwd:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  cargo run --config examples/cu_flight_controller/.cargo/config-firmware.toml --no-default-features --features firmware,log-level-debug -p cu-flight-controller --bin quad

# Attach to the already-flashed target without rebuilding or flashing.
attach:
  #!/usr/bin/env bash
  set -euo pipefail
  cd "{{ROOT}}"
  probe-rs attach --chip STM32H743VITx --protocol swd target/thumbv7em-none-eabihf/release/quad