Compare commits

..

5 Commits

Author SHA1 Message Date
Jacob Dahl d4bf6216e3 fix(rc): warn and deny invalid Spektrum bind sub-type
Previously, an unrecognized param2 sub-type would silently leave
dsm_bind_pulses at 0 and return the generic UNSUPPORTED ACK. Add an
explicit else-branch that logs a PX4_WARN and returns DENIED so users
get clear feedback in QGC.
2026-03-19 16:56:36 -08:00
Jacob Dahl c47fcf7390 fix(rc): check write return value in BindCRSF, guard Spektrum bind against invalid sub-type 2026-03-17 20:06:53 -08:00
Jacob Dahl 3d92ca5edf style(crsf_rc): zero-init vcmd, remove noisy comments, drop unused enum value 2026-03-17 19:16:57 -08:00
Jacob Dahl 0478395ada style(crsf_rc): use C++ style comment 2026-03-17 19:15:02 -08:00
Jacob Dahl 2bf50badf4 feat(crsf_rc): add CRSF receiver bind command
Add ability to initiate CRSF receiver binding from QGroundControl or
the NSH console. When MAV_CMD_START_RX_PAIR is received with
RC_TYPE_CRSF, the driver sends the CRSF bind command frame over UART.

Binding is rejected when armed or on singlewire configurations.

Also adds RC_TYPE and RC_SUB_TYPE constants to VehicleCommand.msg and
replaces magic numbers in DsmRc and RCInput drivers.

Based on PX4/PX4-Autopilot#23294.
2026-03-17 19:11:33 -08:00
1244 changed files with 32023 additions and 36775 deletions
-28
View File
@@ -1,28 +0,0 @@
---
name: commit
description: Create a conventional commit for PX4 changes
disable-model-invocation: true
argument-hint: "[optional: description of changes]"
allowed-tools: Bash, Read, Glob, Grep
---
# PX4 Conventional Commit
Create a git commit: `type(scope): description`
**NEVER add Co-Authored-By lines. No Claude attribution in commits.**
Follow [CONTRIBUTING.md](../../CONTRIBUTING.md) for full project conventions.
## Steps
1. **Read [CONTRIBUTING.md](../../CONTRIBUTING.md)** for commit message format, types, scopes, and conventions.
2. Check branch (`git branch --show-current`). If on `main`, create a feature branch. Use `<username>/<description>` format where `<username>` comes from `gh api user --jq .login`. If unavailable, just use `<description>`.
3. Run `git status` and `git diff --staged`. If nothing staged, ask what to stage.
4. Follow the commit message convention from CONTRIBUTING.md: pick the correct **type** and **scope**, write a concise imperative description.
5. Body (if needed): explain **why**, not what.
6. Run `make format` or `./Tools/astyle/fix_code_style.sh <file>` on changed C/C++ files before committing.
7. Check if GPG signing is available: `git config --get user.signingkey`. If set, use `git commit -S -s`. Otherwise, use `git commit -s`.
8. Stage and commit. No `Co-Authored-By`.
If the user provided arguments, use them as context: $ARGUMENTS
-24
View File
@@ -1,24 +0,0 @@
---
name: pr
description: Create a pull request with conventional commit title and description
disable-model-invocation: true
argument-hint: "[optional: target branch or description]"
allowed-tools: Bash, Read, Glob, Grep
---
# PX4 Pull Request
**No Claude attribution anywhere (no Co-Authored-By, no "Generated with Claude").**
Follow [CONTRIBUTING.md](../../CONTRIBUTING.md) for full project conventions.
## Steps
1. Check branch. If on `main`, create a feature branch. Use `<username>/<description>` format where `<username>` comes from `gh api user --jq .login`. If unavailable, just use `<description>`.
2. Gather context: `git status`, `git log --oneline main..HEAD`, `git diff main...HEAD --stat`, check if remote tracking branch exists.
3. PR **title**: `type(scope): description` — under 72 chars, describes the overall change across all commits. This becomes the squash-merge commit message.
4. PR **body**: brief summary + bullet points for key changes. No filler.
5. Push with `-u` if needed, then `gh pr create`. Default base is `main` unless user says otherwise.
6. Return the PR URL.
If the user provided arguments, use them as context: $ARGUMENTS
-73
View File
@@ -1,73 +0,0 @@
---
name: rebase-onto-main
description: Rebase a branch onto main, handling squash-merged parent branches cleanly
argument-hint: "[optional: branch name, defaults to current branch]"
allowed-tools: Bash, Read, Glob, Grep, Agent
---
# Rebase Branch onto Main
Rebase the current (or specified) branch onto `main`, correctly handling the case where the branch was built on top of another branch that has since been squash-merged into `main`.
## Background
When a parent branch is squash-merged, its individual commits become a single new commit on `main` with a different hash. A normal `git rebase main` will try to replay the parent's original commits, causing messy conflicts. The fix is to **cherry-pick only the commits unique to this branch** onto a fresh branch from `main`.
## Steps
1. **Identify the branch.** Use `$ARGUMENTS` if provided, otherwise use the current branch.
2. **Fetch and update main:**
```
git fetch origin main:main
```
3. **Find the merge base** between the branch and `main`:
```
git merge-base <branch> main
```
4. **List all commits** on the branch since the merge base:
```
git log --oneline <merge-base>..<branch>
```
5. **Identify which commits are unique to this branch** vs. inherited from a parent branch. Look for:
- Squash-merged commits on `main` that correspond to a group of commits at the bottom of the branch's history (check PR titles, commit message keywords).
- The boundary commit: the first commit that belongs to *this* branch's work, not the parent's.
- If ALL commits are unique (no parent branch), just do a normal `git rebase main` and skip the rest.
6. **Create a fresh branch from `main`:**
```
git checkout -b <branch>-rebase main
```
7. **Cherry-pick only the unique commits** (oldest first):
```
git cherry-pick <first-unique-commit>^..<branch>
```
The `A^..B` range means "from the parent of A through B inclusive."
8. **Handle conflicts** if any arise during cherry-pick. Resolve and `git cherry-pick --continue`.
9. **Replace the old branch:**
```
git branch -m <branch> <branch>-old
git branch -m <branch>-rebase <branch>
```
10. **Verify** the result:
```
git log --oneline main..<branch>
```
Confirm only the expected commits are present.
11. **Ask the user** before force-pushing. When approved:
```
git push origin <branch> --force-with-lease
```
12. **Clean up** the old branch:
```
git branch -D <branch>-old
```
-207
View File
@@ -1,207 +0,0 @@
---
name: review-pr
description: Review a pull request with structured, domain-aware feedback
argument-hint: "<PR number or URL>"
allowed-tools: Bash, Read, Glob, Grep, Agent
---
# PX4 Pull Request Review
Review a pull request with domain-aware checks based on which files are changed.
**No Claude attribution anywhere.**
## Steps
1. **Fetch PR context.** Run these in parallel:
- `gh pr view <PR> --json number,title,body,baseRefName,headRefName,files,commits,reviewRequests,reviews,author`
- `gh pr checks <PR>` (exit code 8 means some checks are pending, this is normal, not an error)
- `gh pr diff <PR>` -- if this fails with HTTP 406 (300+ files), do NOT retry. Instead use `gh api repos/OWNER/REPO/pulls/NUMBER/files --paginate` to get the full file list in one call, then fetch patches for key infrastructure files individually and sample representative changes from each domain touched.
- `gh api repos/OWNER/REPO/pulls/NUMBER/comments --paginate --jq '.[] | {user: .user.login, body: .body, path: .path, created_at: .created_at}'` to get inline review comments
- `gh api repos/OWNER/REPO/issues/NUMBER/comments --paginate --jq '.[] | {user: .user.login, body: .body, created_at: .created_at}'` to get PR conversation comments
From the PR metadata, note:
- **Assigned reviewers**: who has been requested to review (from `reviewRequests`)
- **Existing reviews**: who has already reviewed and their verdict (from `reviews` -- approved, changes_requested, commented, dismissed)
- **PR comments and inline comments**: read all existing feedback to avoid duplicating points already raised by other reviewers, and to build on their discussion rather than ignoring it
2. **Check CI status.** From the `gh pr checks` output in step 1, summarize pass/fail/pending. If there are failures, fetch logs with `gh run view <run-id> --log-failed`. Include CI status in the output.
3. **Recommend merge strategy.** Analyze the commit history and recommend squash or rebase merge. This decision informs all subsequent commit hygiene feedback.
**Recommend rebase merge** when:
- Commits are atomic, each builds/works independently
- Each commit has a proper `type(scope): description` message
- The PR intentionally separates logical changes (e.g., refactor + feature, or one commit per module)
- The commit history tells a useful story that would be lost by squashing
**Recommend squash merge** when:
- There are WIP, fixup, or review-response commits
- Commit messages are messy or inconsistent
- The PR is a single logical change spread across multiple commits
- There are "oops" or "make format" commits mixed in
Include the recommendation in the output. If recommending rebase, flag any commits that break atomicity or have bad messages. If recommending squash, don't bother flagging individual commit messages (they'll be discarded) but ensure the PR title is correct since it becomes the squash commit message.
4. **Check conventional commit title.** Verify the PR title follows `type(scope): description` per CONTRIBUTING.md. The PR title becomes the commit message on squash-merge, so it must be accurate and descriptive. Verify the scope matches the primary area of changed files. If the PR introduces breaking changes, the title must include `!` before the colon. If rebase merge was recommended in step 3, also scan individual commit messages for anti-patterns: vague messages ("fix", "update"), missing type prefix, review-response noise ("apply suggestions from code review", "do make format"), or WIP markers. Flag these for rewording.
5. **Identify domains touched.** Classify changed files into domains based on paths (a PR may touch multiple):
- **Estimation**: `src/modules/ekf2/`, `src/lib/wind_estimator/`, `src/lib/world_magnetic_model/`
- **Control**: `src/modules/mc_*control*/`, `src/modules/fw_*control*/`, `src/modules/flight_mode_manager/`, `src/lib/rate_control/`, `src/lib/npfg/`, `src/modules/vtol_att_control/`
- **Drivers/CAN**: `src/drivers/`, `src/modules/cyphal/`, `src/drivers/uavcan*/`
- **Simulation**: `src/modules/simulation/`, `Tools/simulation/`
- **System**: `src/modules/commander/`, `src/modules/logger/`, `src/systemcmds/`, `platforms/`, `src/modules/dataman/`
- **Board Addition**: `boards/{manufacturer}/{board}/` (new directories only, not modifications to existing boards)
- **CI/Build**: `.github/`, `CMakeLists.txt`, `Makefile`, `cmake/`, `Tools/`, `Kconfig`
- **Messages/Protocol**: `msg/`, `src/modules/mavlink/`, `src/modules/uxrce_dds_client/`
6. **Apply core checks** (always):
- **Correctness**: logic errors, off-by-ones, unhandled edge cases
- **Type safety**: int16 overflow, float/double promotion, unsigned subtraction, use `uint64_t` for absolute time
- **Initialization**: uninitialized variables, missing default construction
- **Buffer safety**: unchecked array access, stack allocation of large buffers, snprintf bounds
- **Magic numbers**: every numeric literal needs a named constant or justification
- **Framework reuse**: use PX4_ERR/WARN/INFO, existing libraries (AlphaFilter, SlewRate, RateControl), MAVLink constants from the library
- **Naming**: accurate, no unjustified abbreviations, current terminology (GPS -> GNSS for new code)
- **Unnecessary complexity**: can code be removed instead of added? Is there a simpler pattern?
- **Test coverage**: new features should include unit or integration tests; bug fixes should include regression tests where practical. When automated testing is infeasible (hardware-specific), require a flight log link from https://logs.px4.io or bench test evidence.
- **PR hygiene**: focused scope, no unrelated formatting, no stale submodule changes. Commits should be atomic and independently revertable. Multiple WIP or review-response commits should be squashed. Clean, logical commits will be preserved individually on main via rebase merge. **Do NOT assume PRs are squash-merged. Both squash and rebase merge are enabled; merge commits are disabled.** Verify the PR targets `main` unless it is a backport or release-specific fix.
- **Formatting**: `make format` / `make check_format` (astyle) for C/C++ files; `clang-tidy` clean. Python files checked with `mypy` and `flake8`. PRs failing CI format or lint checks will not be merged.
- **Coding style**: C/C++ must follow the [PX4 coding style](https://docs.px4.io/main/en/contribute/code.html)
- **Necessity**: challenge every addition with "Why?" Is this actually needed or just copied? Can we change a default instead of adding runtime detection?
- **Root cause vs symptom**: is this fixing the real problem or masking it?
- **Ecosystem impact**: what does this change mean for QGC users, log analysis tools, and third-party integrations?
- **Sustainability**: who will maintain this? Does it create long-term burden?
- **Architecture fit**: does the code live in the module that naturally owns the data? Are there unnecessary cross-module dependencies?
- **End user impact**: will parameters confuse less-technical users? Are error messages actionable in QGC?
7. **Apply domain checks** based on step 5:
**Estimation:**
- Singularities in aerospace math (euler angles near gimbal lock, sideslip at low airspeed)
- Aliasing from downsampling sensor data without filtering
- Kalman filter correctness (Joseph form, innovation variance, covariance symmetry)
- CPU cost on embedded targets (avoid unnecessary sqrt, limit fusion rate)
- Frame/coordinate system correctness (FRD vs NED, body vs earth)
**Control:**
- Phase margin: output filters consume margin for no benefit; prefer adjusting gyro/d-gyro cutoffs
- Circular dependencies: sensor data feeding back into its own control loop (e.g., throttle-based airspeed in TECS)
- NaN propagation in flight-critical math; check `PX4_ISFINITE` before magnitude checks
- Setpoint generation vs output-stage hacks: prefer proper setpoint smoothing over controller output filtering
- Yaw control edge cases: heading lock, drift, setpoint propagation
- Flight task inheritance chain: correct base class for the desired behavior
- Control allocation: actuator function ordering, motor index mapping
**Drivers/CAN:**
- CAN bus devices behave differently from serial/SPI; check driver assumptions
- ESC index mapping: telemetry index != channel when motors are disabled
- ESC hardware quirks: 4-in-1 ESCs may report current on only one channel
- device_id correctness and I2CSPIDriver patterns
- Time representation: prefer `hrt_abstime` over iteration counts
**Simulation:**
- Physics fidelity: noise models should match reality (GPS noise is not Gaussian)
- Keep gz_bridge generic; vehicle-specific logic belongs in plugins
- Prefer gz-transport over ROS2 dependencies when possible
- Wrench commands for physics correctness vs kinematic constraints
- Library generic/specific boundary: only base classes in common libs
**System:**
- Race conditions and concurrency: no partial fixes, demand complete solutions
- Semaphore/scheduling edge cases; understand RTOS guarantees
- State machine sequential-logic bugs (consecutive RTL, armed/disarmed alternation)
- uORB-driven scheduling (`SubscriptionCallback`), not extra threads
- param_set triggers auto-save; no redundant param_save_default
- Flash/memory efficiency: avoid `std::string` on embedded, minimize SubscriptionData usage
- Constructor initialization order matters
**CI/Build:**
- Pipeline race conditions (tag + branch push double-trigger, git describe correctness)
- Container image size (check layer bloat)
- Ubuntu LTS support policy (latest + one prior only)
- Build time impact
- CMake preferred over Makefiles
**Messages/Protocol:**
- Backwards compatibility: will this break QGC, post-flight tools, or uLog parsers?
- uORB: `timestamp` for publication metadata, `timestamp_sample` close to physical sample, include `device_id`
- Don't version messages unless strictly needed
- Parameter UX: will this confuse users in a GCS? Every new param is a configuration burden
- MAVLink: use library constants, don't implement custom stream rates
**Board Addition:**
- **Flight logs**: require a link to https://logs.px4.io demonstrating basic operation for the vehicle type (hover for multicopters, stable flight for fixed-wing, driving for rovers, etc.); short bench-only logs are insufficient
- **Documentation**: require a docs page in `docs/en/flight_controller/` with pinout, where-to-buy, connector types, version badge, and manufacturer-supported notice block
- **USB VID/PID**: must not reuse another manufacturer's Vendor ID; manufacturer must use their own
- **Board naming**: directory is `boards/{manufacturer}/{board}/`, both lowercase, hyphens for board name
- **Unique board_id**: registered in `boards/boards.json`, no collisions
- **Copied code cleanup**: check for leftover files, configs, or comments from the template board; "Is this real or leftover?"
- **RC configuration**: prefer `CONFIG_DRIVERS_COMMON_RC` over legacy `CONFIG_DRIVERS_RC_INPUT`
- **No board-specific custom modules**: reject copy-pasted drivers (e.g., custom heater) when existing infrastructure works
- **Bootloader**: expect a bootloader defconfig (`nuttx-config/bootloader/defconfig`) or explanation of shared bootloader
- **CI integration**: board must be added to CI compile workflows so it builds on every PR
- **Flash constraints**: verify enabled modules fit in flash; we are running low across all board targets
- **Port labels**: serial port labels must match what is physically printed on the board
- **Hardware availability**: for unknown manufacturers, verify the product exists and is purchasable (no vaporware)
8. **Format output** as:
- **CI status**: pass/fail summary, link to failed runs if any
- **Merge strategy**: recommend squash or rebase merge with reasoning
- **Title check**: pass/fail with suggestion
- **Review status**: list assigned reviewers and any existing reviews (who approved, who requested changes, key points already raised). Note if your review would duplicate feedback already given.
- **Domains detected**: list which domain checks were applied
- **Summary**: one paragraph on what the PR does and whether the approach is sound
- **Issues**: numbered list, each with file:line, severity (blocker/warning/nit), and explanation. Skip issues already raised by other reviewers unless you have something to add.
- **Verdict**: approve, request changes, or needs discussion
After the structured output, also display a **draft PR comment** formatted using the PR comment formatting rules from step 9. This gives the user a preview of what would be posted.
9. **Interactive dialog.** After displaying the review, present the user with these options:
Present options based on the verdict:
If verdict is **approve**:
```
What would you like to do?
1. Chat about this PR (ask questions, explore code) [default]
2. Approve this PR and post the review comment
3. Adjust the review or draft (tell me what to change)
4. Done for now
```
If verdict is **request changes**:
```
What would you like to do?
1. Chat about this PR (ask questions, explore code) [default]
2. Request changes on this PR and post the review comment
3. Adjust the review or draft (tell me what to change)
4. Done for now
```
If verdict is **needs discussion**:
```
What would you like to do?
1. Chat about this PR (ask questions, explore code) [default]
2. Post the review as a comment (no approval or rejection)
3. Adjust the review or draft (tell me what to change)
4. Done for now
```
Wait for the user to choose before proceeding. If they pick:
- **1 (chat)**: enter a free-form conversation about the PR. The user can ask about specific files, code paths, or decisions. When done, loop back to the options. This is the default if the user just presses enter.
- **2 (submit)**: use the draft PR comment already shown. Before posting, check if you have review permissions: run `gh api repos/OWNER/REPO/collaborators/$(gh api user --jq .login)/permission --jq .permission` -- if `admin` or `write`, submit as a formal review with `gh pr review <PR> --approve --body "..."` or `gh pr review <PR> --request-changes --body "..."` based on the verdict. If no write access, fall back to `gh pr comment <PR> --body "..."`. Always confirm with the user before posting.
- **3 (adjust)**: ask what to change, update the review and draft, then loop back to the options.
- **4 (done)**: stop.
**PR comment formatting rules** (for the draft):
When writing the GitHub comment, rewrite the review to sound like a human reviewer, not a structured report. Do NOT include the full skill output. Instead:
- Drop most meta-sections (CI status, title check, domains detected, severity labels) but keep the merge strategy recommendation (e.g., "I'd suggest a rebase merge here since the commits are clean and atomic" or "This should be squash-merged, the commit history is messy")
- Write conversationally: "Nice work on this. A few things I noticed:" not "Issues: 1. file:line (warning):"
- Lead with a brief take on the overall change (1-2 sentences)
- List only actionable feedback as natural review comments, not numbered checklists
- Skip nits unless they are particularly useful
- End with a clear stance: looks good to merge, needs a few changes, or let's discuss X
- Post with `gh pr comment <PR> --body "$(cat <<'EOF' ... EOF)"`. Do not post without explicit confirmation.
If the user provided arguments, use them as context: $ARGUMENTS
-1
View File
@@ -1 +0,0 @@
build/
-115
View File
@@ -1,115 +0,0 @@
name: Build PX4 .deb Package
description: Build PX4 SITL, run cpack, validate the .deb, and upload artifact
inputs:
target:
description: 'Build target: default or sih'
required: true
artifact-name:
description: Name for the uploaded artifact
required: true
ccache-key-prefix:
description: Prefix for ccache cache keys
default: deb-ccache
ccache-max-size:
description: Maximum ccache size
default: 400M
runs:
using: composite
steps:
- name: Restore ccache
id: ccache-restore
uses: actions/cache/restore@v4
with:
path: ~/.ccache
key: ${{ inputs.ccache-key-prefix }}-${{ github.ref_name }}-${{ github.sha }}
restore-keys: |
${{ inputs.ccache-key-prefix }}-${{ github.ref_name }}-
${{ inputs.ccache-key-prefix }}-${{ github.base_ref || 'main' }}-
${{ inputs.ccache-key-prefix }}-
- name: Configure ccache
shell: bash
run: |
mkdir -p ~/.ccache
echo "base_dir = ${GITHUB_WORKSPACE}" > ~/.ccache/ccache.conf
echo "compression = true" >> ~/.ccache/ccache.conf
echo "compression_level = 6" >> ~/.ccache/ccache.conf
echo "max_size = ${{ inputs.ccache-max-size }}" >> ~/.ccache/ccache.conf
echo "hash_dir = false" >> ~/.ccache/ccache.conf
echo "compiler_check = content" >> ~/.ccache/ccache.conf
ccache -s
ccache -z
- name: Build PX4 SITL
shell: bash
run: make px4_sitl_${{ inputs.target }}
- name: ccache stats
if: always()
shell: bash
run: ccache -s
- name: Save ccache
uses: actions/cache/save@v4
if: always()
with:
path: ~/.ccache
key: ${{ inputs.ccache-key-prefix }}-${{ github.ref_name }}-${{ github.sha }}
- name: Build .deb package
shell: bash
run: |
cd build/px4_sitl_${{ inputs.target }}
cpack -G DEB
- name: Print package info and contents
shell: bash
run: |
cd build/px4_sitl_${{ inputs.target }}
echo "--- Package info ---"
dpkg-deb -I *.deb
echo "--- Package contents ---"
dpkg-deb -c *.deb
- name: Validate sih package
if: inputs.target == 'sih'
shell: bash
run: |
cd build/px4_sitl_sih
echo "--- Verify NO Gazebo resources ---"
! dpkg-deb -c px4_*.deb | grep share/gz > /dev/null && echo "PASS: no Gazebo" || { echo "FAIL: Gazebo found"; exit 1; }
echo "--- Install test ---"
dpkg -i px4_*.deb
test -x /opt/px4/bin/px4 || { echo "FAIL: px4 binary not found"; exit 1; }
test -L /usr/bin/px4 || { echo "FAIL: symlink not created"; exit 1; }
test ! -d /opt/px4/share/gz || { echo "FAIL: Gazebo dir should not exist"; exit 1; }
echo "--- Smoke test ---"
/opt/px4/bin/px4 -h
echo "PASS: sih package validation successful"
- name: Validate gazebo package
if: inputs.target == 'default'
shell: bash
run: |
cd build/px4_sitl_default
echo "--- Verify Gazebo resources in package ---"
dpkg-deb -c px4-gazebo_*.deb | grep share/gz/models > /dev/null || { echo "FAIL: models missing"; exit 1; }
dpkg-deb -c px4-gazebo_*.deb | grep share/gz/worlds > /dev/null || { echo "FAIL: worlds missing"; exit 1; }
echo "--- Install test ---"
dpkg -i px4-gazebo_*.deb
test -x /opt/px4-gazebo/bin/px4 || { echo "FAIL: px4 binary not found"; exit 1; }
test -x /opt/px4-gazebo/bin/px4-gazebo || { echo "FAIL: wrapper not found"; exit 1; }
test -L /usr/bin/px4-gazebo || { echo "FAIL: symlink not created"; exit 1; }
test -d /opt/px4-gazebo/share/gz/models || { echo "FAIL: Gazebo models not installed"; exit 1; }
echo "--- Smoke test ---"
/opt/px4-gazebo/bin/px4 -h
echo "PASS: gazebo package validation successful"
- name: Upload .deb artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.artifact-name }}
path: build/px4_sitl_${{ inputs.target }}/*.deb
if-no-files-found: error
@@ -1,21 +0,0 @@
name: Build Gazebo Classic SITL
description: Build PX4 firmware and Gazebo Classic plugins with ccache stats
runs:
using: composite
steps:
- name: Build - PX4 Firmware (SITL)
shell: bash
run: make px4_sitl_default
- name: Cache - Stats after PX4 Firmware
shell: bash
run: ccache -s
- name: Build - Gazebo Classic Plugins
shell: bash
run: make px4_sitl_default sitl_gazebo-classic
- name: Cache - Stats after Gazebo Plugins
shell: bash
run: ccache -s
-22
View File
@@ -1,22 +0,0 @@
name: Save ccache
description: Print ccache stats and save to cache
inputs:
cache-primary-key:
description: Primary cache key from setup-ccache output
required: true
runs:
using: composite
steps:
- name: Cache - Stats
if: always()
shell: bash
run: ccache -s
- name: Cache - Save ccache
if: always()
uses: actions/cache/save@v4
with:
path: ~/.ccache
key: ${{ inputs.cache-primary-key }}
-56
View File
@@ -1,56 +0,0 @@
name: Setup ccache
description: Restore ccache from cache and configure ccache.conf
inputs:
cache-key-prefix:
description: Cache key prefix (e.g. ccache-sitl)
required: true
max-size:
description: Max ccache size (e.g. 300M)
required: false
default: '300M'
base-dir:
description: ccache base_dir value
required: false
default: '${GITHUB_WORKSPACE}'
install-ccache:
description: Install ccache via apt before configuring
required: false
default: 'false'
outputs:
cache-primary-key:
description: Primary cache key (pass to save-ccache)
value: ${{ steps.restore.outputs.cache-primary-key }}
runs:
using: composite
steps:
- name: Cache - Install ccache
if: inputs.install-ccache == 'true'
shell: bash
run: apt-get update && apt-get install -y ccache
- name: Cache - Restore ccache
id: restore
uses: actions/cache/restore@v4
with:
path: ~/.ccache
key: ${{ inputs.cache-key-prefix }}-${{ github.ref_name }}-${{ github.sha }}
restore-keys: |
${{ inputs.cache-key-prefix }}-${{ github.ref_name }}-
${{ inputs.cache-key-prefix }}-${{ github.base_ref || 'main' }}-
${{ inputs.cache-key-prefix }}-
- name: Cache - Configure ccache
shell: bash
run: |
mkdir -p ~/.ccache
echo "base_dir = ${{ inputs.base-dir }}" > ~/.ccache/ccache.conf
echo "compression = true" >> ~/.ccache/ccache.conf
echo "compression_level = 6" >> ~/.ccache/ccache.conf
echo "max_size = ${{ inputs.max-size }}" >> ~/.ccache/ccache.conf
echo "hash_dir = false" >> ~/.ccache/ccache.conf
echo "compiler_check = content" >> ~/.ccache/ccache.conf
ccache -s
ccache -z
@@ -1,21 +0,0 @@
---
applyTo: "boards/**"
---
# Board Addition Review Guidelines
In addition to the core code review guidelines, when reviewing new board additions:
- **Flight logs**: require a link to https://logs.px4.io demonstrating basic operation for the vehicle type (hover for multicopters, stable flight for fixed-wing, driving for rovers, etc.); short bench-only logs are insufficient
- **Documentation**: require a docs page in `docs/en/flight_controller/` with pinout, where-to-buy, connector types, version badge, and manufacturer-supported notice block
- **USB VID/PID**: must not reuse another manufacturer's Vendor ID; manufacturer must use their own
- **Board naming**: directory is `boards/{manufacturer}/{board}/`, both lowercase, hyphens for board name
- **Unique board_id**: registered in `boards/boards.json`, no collisions
- **Copied code cleanup**: check for leftover files, configs, or comments from the template board. Ask "Is this real or leftover?"
- **RC configuration**: prefer `CONFIG_DRIVERS_COMMON_RC` over legacy `CONFIG_DRIVERS_RC_INPUT`
- **No board-specific custom modules**: reject copy-pasted drivers (e.g., custom heater) when existing infrastructure works
- **Bootloader**: expect a bootloader defconfig (`nuttx-config/bootloader/defconfig`) or explanation of shared bootloader
- **CI integration**: board must be added to CI compile workflows so it builds on every PR
- **Flash constraints**: verify enabled modules fit in flash; we are running low across all board targets
- **Port labels**: serial port labels must match what is physically printed on the board
- **Hardware availability**: for unknown manufacturers, verify the product exists and is purchasable (no vaporware)
@@ -1,13 +0,0 @@
---
applyTo: ".github/**,cmake/**,Makefile,CMakeLists.txt,Tools/**,**/Kconfig"
---
# CI/Build Review Guidelines
In addition to the core code review guidelines:
- Check for pipeline race conditions (tag + branch push double-trigger, git describe correctness)
- Container image size: check for layer bloat
- Ubuntu LTS support policy: only latest + one prior LTS version
- Consider build time impact of changes
- Prefer CMake over Makefiles
@@ -1,32 +0,0 @@
---
applyTo: "src/**,boards/**,platforms/**,msg/**,cmake/**,Makefile,CMakeLists.txt,Tools/**,.github/**"
---
# PX4 Code Review Guidelines
## Conventions
- PR titles must follow conventional commits: `type(scope): description` (see CONTRIBUTING.md)
- Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
- Scope should match the primary area of changed files
- Append `!` before the colon for breaking changes
- Both squash merge and rebase merge are enabled; merge commits are disabled
- Commits should be atomic and independently revertable
- WIP or review-response commits should be squashed before merge
## Core Checks (always apply)
- **Correctness**: logic errors, off-by-ones, unhandled edge cases
- **Type safety**: int16 overflow, float/double promotion, unsigned subtraction, use `uint64_t` for absolute time
- **Initialization**: uninitialized variables, missing default construction
- **Buffer safety**: unchecked array access, stack allocation of large buffers, snprintf bounds
- **Magic numbers**: every numeric literal needs a named constant or justification
- **Framework reuse**: use PX4_ERR/WARN/INFO, existing libraries (AlphaFilter, SlewRate, RateControl), MAVLink constants from the library
- **Naming**: accurate, no unjustified abbreviations, current terminology (GPS -> GNSS for new code)
- **Unnecessary complexity**: can code be removed instead of added? Is there a simpler pattern?
- **Test coverage**: new features should include unit or integration tests; bug fixes should include regression tests where practical
- **Formatting**: `make format` / `make check_format` (astyle) for C/C++ files; `clang-tidy` clean
- **Coding style**: C/C++ must follow the PX4 coding style (https://docs.px4.io/main/en/contribute/code.html)
- **Necessity**: challenge every addition. Is this actually needed or just copied?
- **Architecture fit**: does the code live in the module that naturally owns the data? No unnecessary cross-module dependencies
- **Ecosystem impact**: consider QGC users, log analysis tools, and third-party integrations
@@ -1,15 +0,0 @@
---
applyTo: "src/modules/mc_*control*/**,src/modules/fw_*control*/**,src/modules/flight_mode_manager/**,src/lib/rate_control/**,src/lib/npfg/**,src/modules/vtol_att_control/**"
---
# Control Review Guidelines
In addition to the core code review guidelines:
- Phase margin: output filters consume margin for no benefit; prefer adjusting gyro/d-gyro cutoffs
- Check for circular dependencies: sensor data feeding back into its own control loop (e.g., throttle-based airspeed in TECS)
- NaN propagation in flight-critical math; check `PX4_ISFINITE` before magnitude checks
- Prefer proper setpoint smoothing over controller output filtering (setpoint generation vs output-stage hacks)
- Check yaw control edge cases: heading lock, drift, setpoint propagation
- Verify flight task inheritance chain uses the correct base class for desired behavior
- Control allocation: verify actuator function ordering and motor index mapping
@@ -1,13 +0,0 @@
---
applyTo: "src/drivers/**,src/modules/cyphal/**"
---
# Drivers/CAN Review Guidelines
In addition to the core code review guidelines:
- CAN bus devices behave differently from serial/SPI; check driver assumptions
- ESC index mapping: telemetry index != channel when motors are disabled
- ESC hardware quirks: 4-in-1 ESCs may report current on only one channel
- Verify device_id correctness and I2CSPIDriver patterns
- Time representation: prefer `hrt_abstime` over iteration counts
@@ -1,13 +0,0 @@
---
applyTo: "src/modules/ekf2/**,src/lib/wind_estimator/**,src/lib/world_magnetic_model/**"
---
# Estimation Review Guidelines
In addition to the core code review guidelines:
- Check for singularities in aerospace math (euler angles near gimbal lock, sideslip at low airspeed)
- Flag aliasing from downsampling sensor data without proper filtering
- Verify Kalman filter correctness (Joseph form, innovation variance, covariance symmetry)
- Consider CPU cost on embedded targets (avoid unnecessary sqrt, limit fusion rate)
- Verify frame/coordinate system correctness (FRD vs NED, body vs earth frame)
@@ -1,13 +0,0 @@
---
applyTo: "msg/**,src/modules/mavlink/**,src/modules/uxrce_dds_client/**"
---
# Messages/Protocol Review Guidelines
In addition to the core code review guidelines:
- Backwards compatibility: will this break QGC, post-flight tools, or uLog parsers?
- uORB: `timestamp` for publication metadata, `timestamp_sample` close to physical sample, include `device_id`
- Don't version messages unless strictly needed
- Parameter UX: will this confuse users in a GCS? Every new param is a configuration burden
- MAVLink: use library constants, don't implement custom stream rates
@@ -1,13 +0,0 @@
---
applyTo: "src/modules/simulation/**,Tools/simulation/**"
---
# Simulation Review Guidelines
In addition to the core code review guidelines:
- Physics fidelity: noise models should match reality (GPS noise is not Gaussian)
- Keep gz_bridge generic; vehicle-specific logic belongs in plugins
- Prefer gz-transport over ROS2 dependencies when possible
- Use wrench commands for physics correctness vs kinematic constraints
- Library generic/specific boundary: only base classes in common libs
@@ -1,15 +0,0 @@
---
applyTo: "src/modules/commander/**,src/modules/logger/**,src/systemcmds/**,platforms/**,src/modules/dataman/**"
---
# System Review Guidelines
In addition to the core code review guidelines:
- Race conditions and concurrency: no partial fixes, demand complete solutions
- Semaphore/scheduling edge cases; understand RTOS guarantees
- State machine sequential-logic bugs (consecutive RTL, armed/disarmed alternation)
- Use uORB-driven scheduling (`SubscriptionCallback`), not extra threads
- `param_set` triggers auto-save; no redundant `param_save_default`
- Flash/memory efficiency: avoid `std::string` on embedded, minimize SubscriptionData usage
- Constructor initialization order matters
+1 -4
View File
@@ -265,8 +265,5 @@ jobs:
with:
draft: true
prerelease: ${{ steps.upload-location.outputs.is_prerelease == 'true' }}
files: |
artifacts/*.px4
artifacts/*.deb
artifacts/**/*.sbom.spdx.json
files: artifacts/*.px4
name: ${{ steps.upload-location.outputs.uploadlocation }}
-220
View File
@@ -1,220 +0,0 @@
name: SITL Packages and Containers
on:
push:
tags: ['v*']
pull_request:
paths:
- 'cmake/package.cmake'
- 'platforms/posix/CMakeLists.txt'
- 'Tools/packaging/**'
- 'boards/px4/sitl/sih.px4board'
- '.github/workflows/build_deb_package.yml'
- '.github/actions/build-deb/**'
workflow_dispatch:
inputs:
deploy_containers:
description: 'Push container images to registry'
required: false
type: boolean
default: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: write
jobs:
# ---------------------------------------------------------------------------
# Setup: extract version and determine whether to push containers
# ---------------------------------------------------------------------------
setup:
name: Setup
runs-on: [runs-on,"runner=1cpu-linux-x64","image=ubuntu24-full-x64","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
outputs:
px4_version: ${{ steps.version.outputs.px4_version }}
should_push: ${{ steps.push.outputs.should_push }}
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v4
with:
fetch-tags: true
submodules: false
fetch-depth: 0
- name: Set PX4 version
id: version
run: echo "px4_version=$(git describe --tags --match 'v[0-9]*')" >> $GITHUB_OUTPUT
- name: Check if we should push containers
id: push
run: |
if [[ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]] || \
[[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.deploy_containers }}" == "true" ]]; then
echo "should_push=true" >> $GITHUB_OUTPUT
else
echo "should_push=false" >> $GITHUB_OUTPUT
fi
# ---------------------------------------------------------------------------
# Build .deb packages (all distros, arches, targets)
# ---------------------------------------------------------------------------
build-deb:
name: "Build .deb (${{ matrix.target }}/${{ matrix.codename }}/${{ matrix.arch }})"
needs: setup
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
container:
image: ${{ matrix.container }}
volumes:
- /github/workspace:/github/workspace
strategy:
fail-fast: false
matrix:
include:
- { codename: noble, arch: amd64, runner: x64, container: "ubuntu:24.04", target: default, setup_flags: "" }
- { codename: noble, arch: arm64, runner: arm64, container: "ubuntu:24.04", target: default, setup_flags: "" }
- { codename: jammy, arch: amd64, runner: x64, container: "ubuntu:22.04", target: default, setup_flags: "" }
- { codename: jammy, arch: arm64, runner: arm64, container: "ubuntu:22.04", target: default, setup_flags: "" }
- { codename: noble, arch: amd64, runner: x64, container: "ubuntu:24.04", target: sih, setup_flags: "--no-sim-tools" }
- { codename: noble, arch: arm64, runner: arm64, container: "ubuntu:24.04", target: sih, setup_flags: "--no-sim-tools" }
- { codename: jammy, arch: amd64, runner: x64, container: "ubuntu:22.04", target: sih, setup_flags: "--no-sim-tools" }
- { codename: jammy, arch: arm64, runner: arm64, container: "ubuntu:22.04", target: sih, setup_flags: "--no-sim-tools" }
env:
RUNS_IN_DOCKER: true
steps:
- uses: runs-on/action@v2
- name: Fix git in container
run: |
apt-get update && apt-get install -y git
git config --global --add safe.directory $(realpath .)
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Use AWS regional apt mirror
if: startsWith(runner.name, 'runs-on--')
run: ./Tools/ci/use_aws_apt_mirror.sh
- name: Cache apt packages
uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}-${{ hashFiles('Tools/setup/ubuntu.sh') }}
restore-keys: apt-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}-
- name: Install dependencies
run: ./Tools/setup/ubuntu.sh --no-nuttx ${{ matrix.setup_flags }}
- name: Build and package .deb
uses: ./.github/actions/build-deb
with:
target: ${{ matrix.target }}
artifact-name: px4-sitl-debs-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}
ccache-key-prefix: deb-ccache-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}
# ---------------------------------------------------------------------------
# Build Docker images from Noble .debs
# ---------------------------------------------------------------------------
build-docker:
name: "Build Image (${{ matrix.image }}/${{ matrix.arch }})"
needs: [setup, build-deb]
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
strategy:
fail-fast: false
matrix:
include:
- { image: sih, repo: px4-sitl, target: sih, arch: amd64, runner: x64, platform: "linux/amd64", dockerfile: Dockerfile.sih }
- { image: sih, repo: px4-sitl, target: sih, arch: arm64, runner: arm64, platform: "linux/arm64", dockerfile: Dockerfile.sih }
- { image: gazebo, repo: px4-sitl-gazebo, target: default, arch: amd64, runner: x64, platform: "linux/amd64", dockerfile: Dockerfile.gazebo }
- { image: gazebo, repo: px4-sitl-gazebo, target: default, arch: arm64, runner: arm64, platform: "linux/arm64", dockerfile: Dockerfile.gazebo }
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v4
with:
submodules: false
fetch-depth: 1
- name: Download Noble .deb artifact
uses: actions/download-artifact@v4
with:
name: px4-sitl-debs-${{ matrix.target }}-noble-${{ matrix.arch }}
path: docker-context
- name: Prepare build context
run: cp Tools/packaging/px4-entrypoint.sh docker-context/
- name: Login to registries
if: needs.setup.outputs.should_push == 'true'
run: |
echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
platforms: ${{ matrix.platform }}
- name: Build and push container image
uses: docker/build-push-action@v6
with:
context: docker-context
file: Tools/packaging/${{ matrix.dockerfile }}
tags: |
px4io/${{ matrix.repo }}:${{ needs.setup.outputs.px4_version }}-${{ matrix.arch }}
px4io/${{ matrix.repo }}:latest-${{ matrix.arch }}
ghcr.io/px4/${{ matrix.repo }}:${{ needs.setup.outputs.px4_version }}-${{ matrix.arch }}
ghcr.io/px4/${{ matrix.repo }}:latest-${{ matrix.arch }}
platforms: ${{ matrix.platform }}
load: false
push: ${{ needs.setup.outputs.should_push == 'true' }}
provenance: false
cache-from: type=gha,scope=sitl-${{ matrix.image }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=sitl-${{ matrix.image }}-${{ matrix.arch }}
# ---------------------------------------------------------------------------
# Deploy: create multi-arch manifests and push to registries
# ---------------------------------------------------------------------------
deploy:
name: "Deploy (${{ matrix.image }})"
needs: [setup, build-docker]
if: needs.setup.outputs.should_push == 'true'
runs-on: [runs-on,"runner=1cpu-linux-x64","image=ubuntu24-full-x64","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
strategy:
matrix:
include:
- { image: sih, repo: px4-sitl }
- { image: gazebo, repo: px4-sitl-gazebo }
steps:
- uses: runs-on/action@v2
- name: Login to registries
run: |
echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Create and push multi-arch manifests
run: |
VERSION="${{ needs.setup.outputs.px4_version }}"
for REGISTRY in px4io ghcr.io/px4; do
IMAGE="${REGISTRY}/${{ matrix.repo }}"
for TAG in ${VERSION} latest; do
docker manifest create ${IMAGE}:${TAG} \
--amend ${IMAGE}:${TAG}-arm64 \
--amend ${IMAGE}:${TAG}-amd64
docker manifest annotate ${IMAGE}:${TAG} ${IMAGE}:${TAG}-arm64 --arch arm64
docker manifest annotate ${IMAGE}:${TAG} ${IMAGE}:${TAG}-amd64 --arch amd64
docker manifest push ${IMAGE}:${TAG}
done
done
-2
View File
@@ -46,8 +46,6 @@ jobs:
fetch-depth: 0
- name: Building [${{ matrix.check }}]
env:
PX4_SBOM_DISABLE: 1
run: |
cd "$GITHUB_WORKSPACE"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
-4
View File
@@ -49,10 +49,6 @@ jobs:
- uses: actions/checkout@v4
- name: Use AWS regional apt mirror
if: startsWith(runner.name, 'runs-on--')
run: ./Tools/ci/use_aws_apt_mirror.sh
- name: Install Deps, Build, and Make Quick Check
run: |
# we need to install dependencies and build on the same step
+19 -30
View File
@@ -24,11 +24,6 @@ on:
description: 'Container tag (e.g. v1.16.0)'
required: true
type: string
build_ref:
description: 'Git ref to build from (branch, tag, or SHA). Leave empty to build from the dispatch ref.'
required: false
type: string
default: ''
deploy_to_registry:
description: 'Whether to push built images to the registry'
required: false
@@ -50,10 +45,9 @@ jobs:
meta_tags: ${{ steps.meta.outputs.tags }}
meta_labels: ${{ steps.meta.outputs.labels }}
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v5
- uses: runs-on/action@v1
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.build_ref || github.ref }}
fetch-tags: true
submodules: false
fetch-depth: 0
@@ -70,7 +64,7 @@ jobs:
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/PX4/px4-dev
@@ -95,23 +89,22 @@ jobs:
runner: x64
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v5
- uses: runs-on/action@v1
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.build_ref || github.ref }}
fetch-tags: true
submodules: false
fetch-depth: 0
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
uses: docker/login-action@v3
if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
with:
registry: ghcr.io
@@ -119,13 +112,13 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
platforms: ${{ matrix.platform }}
- name: Build and Load Container Image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
id: docker
with:
context: Tools/setup
@@ -138,7 +131,7 @@ jobs:
push: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
provenance: false
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }},ignore-error=true
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
deploy:
name: Deploy To Registry
@@ -147,27 +140,23 @@ jobs:
packages: write
runs-on: [runs-on,"runner=4cpu-linux-x64","image=ubuntu24-full-x64","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
needs: [build, setup]
if: |
!cancelled() &&
needs.setup.result == 'success' &&
(startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry == 'true'))
if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry) }}
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v5
- uses: runs-on/action@v1
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.build_ref || github.ref }}
fetch-tags: true
submodules: false
fetch-depth: 0
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -175,10 +164,10 @@ jobs:
- name: Verify Images Exist Before Creating Manifest
run: |
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64 || echo "⚠️ Warning: No ARM64 image found!"
docker manifest inspect px4io/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64 || echo "⚠️ Warning: No AMD64 image found!"
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-arm64 || echo "⚠️ Warning: No ARM64 image found!"
docker manifest inspect ghcr.io/px4/px4-dev:${{ needs.setup.outputs.px4_version }}-amd64 || echo "⚠️ Warning: No AMD64 image found!"
- name: Create and Push Multi-Arch Manifest for Docker Hub
run: |
+2 -2
View File
@@ -36,8 +36,8 @@ jobs:
px4io/px4-dev-ros-melodic:2021-09-08 \
bash -c '
git config --global --add safe.directory /workspace
PX4_SBOM_DISABLE=1 make px4_sitl_default
PX4_SBOM_DISABLE=1 make px4_sitl_default sitl_gazebo-classic
make px4_sitl_default
make px4_sitl_default sitl_gazebo-classic
./test/rostest_px4_run.sh \
mavros_posix_test_mission.test \
mission:=MC_mission_box \
+2 -2
View File
@@ -36,8 +36,8 @@ jobs:
px4io/px4-dev-ros-melodic:2021-09-08 \
bash -c '
git config --global --add safe.directory /workspace
PX4_SBOM_DISABLE=1 make px4_sitl_default
PX4_SBOM_DISABLE=1 make px4_sitl_default sitl_gazebo-classic
make px4_sitl_default
make px4_sitl_default sitl_gazebo-classic
./test/rostest_px4_run.sh \
mavros_posix_tests_offboard_posctl.test \
vehicle:=iris
+2 -14
View File
@@ -89,17 +89,7 @@ jobs:
. /opt/ros/galactic/setup.bash
mkdir -p /opt/px4_ws/src
cd /opt/px4_ws/src
# On a PR, target the branch we're merging into (main or release/X.Y).
# On a direct push, fall back to the branch we're running on.
BRANCH="${GITHUB_BASE_REF:-$GITHUB_REF_NAME}"
REPO_URL="https://github.com/Auterion/px4-ros2-interface-lib.git"
if git ls-remote --heads "$REPO_URL" "$BRANCH" | grep -q "$BRANCH"; then
echo "Cloning px4-ros2-interface-lib with matching branch: $BRANCH"
git clone --recursive --branch "$BRANCH" "$REPO_URL"
else
echo "Branch '$BRANCH' not found in px4-ros2-interface-lib, using default (main)"
git clone --recursive "$REPO_URL"
fi
git clone --recursive https://github.com/Auterion/px4-ros2-interface-lib.git
# Ignore python packages due to compilation issue (can be enabled when updating ROS)
touch px4-ros2-interface-lib/px4_ros2_py/COLCON_IGNORE || true
touch px4-ros2-interface-lib/examples/python/COLCON_IGNORE || true
@@ -112,8 +102,6 @@ jobs:
run: ccache -s
- name: Build PX4
env:
PX4_SBOM_DISABLE: 1
run: make px4_sitl_default
- name: ccache post-run px4/firmware
run: ccache -s
@@ -132,7 +120,7 @@ jobs:
run: |
. /opt/px4_ws/install/setup.bash
/opt/Micro-XRCE-DDS-Agent/build/MicroXRCEAgent udp4 localhost -p 8888 -v 0 &
test/ros_test_runner.py --verbose --model iris --force-color
test/ros_test_runner.py --verbose --model iris --upload --force-color
timeout-minutes: 45
- name: Upload failed logs
-42
View File
@@ -1,42 +0,0 @@
name: SBOM License Check
on:
push:
branches:
- 'main'
- 'release/**'
- 'stable'
paths:
- '.gitmodules'
- 'Tools/ci/license-overrides.yaml'
- 'Tools/ci/generate_sbom.py'
pull_request:
branches:
- '**'
paths:
- '.gitmodules'
- 'Tools/ci/license-overrides.yaml'
- 'Tools/ci/generate_sbom.py'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
verify-licenses:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: false
- name: Install PyYAML
run: pip install pyyaml --break-system-packages
- name: Verify submodule licenses
run: python3 Tools/ci/generate_sbom.py --verify-licenses --source-dir .
-132
View File
@@ -1,132 +0,0 @@
name: SBOM Monthly Audit
on:
schedule:
# First Monday of each month at 09:00 UTC
- cron: '0 9 1-7 * 1'
workflow_dispatch:
inputs:
branch:
description: 'Branch to audit (leave empty for current)'
required: false
type: string
permissions:
contents: read
issues: write
jobs:
audit:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch || github.ref }}
fetch-depth: 1
submodules: recursive
- name: Install PyYAML
run: pip install pyyaml --break-system-packages
- name: Run license verification
id: verify
continue-on-error: true
run: |
python3 Tools/ci/generate_sbom.py --verify-licenses --source-dir . 2>&1 | tee /tmp/sbom-verify.txt
echo "exit_code=$?" >> "$GITHUB_OUTPUT"
- name: Check for issues
id: check
run: |
if grep -q "<-- UNRESOLVED" /tmp/sbom-verify.txt; then
echo "has_issues=true" >> "$GITHUB_OUTPUT"
# Extract only genuinely unresolved license lines
grep "<-- UNRESOLVED" /tmp/sbom-verify.txt > /tmp/sbom-issues.txt || true
# Extract copyleft lines
sed -n '/Copyleft licenses detected/,/^$/p' /tmp/sbom-verify.txt > /tmp/sbom-copyleft.txt || true
else
echo "has_issues=false" >> "$GITHUB_OUTPUT"
fi
- name: Create issue if problems found
if: steps.check.outputs.has_issues == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const fullOutput = fs.readFileSync('/tmp/sbom-verify.txt', 'utf8');
let issueLines = '';
try {
issueLines = fs.readFileSync('/tmp/sbom-issues.txt', 'utf8');
} catch (e) {
issueLines = 'No specific NOASSERTION lines captured.';
}
let copyleftLines = '';
try {
copyleftLines = fs.readFileSync('/tmp/sbom-copyleft.txt', 'utf8');
} catch (e) {
copyleftLines = '';
}
const date = new Date().toISOString().split('T')[0];
const branch = '${{ inputs.branch || github.ref_name }}';
// Check for existing open issue to avoid duplicates
const existing = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: 'sbom-audit',
state: 'open',
});
if (existing.data.length > 0) {
// Update existing issue with new findings
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.data[0].number,
body: `## Monthly audit update (${date})\n\nIssues still present:\n\n\`\`\`\n${issueLines}\n\`\`\`\n${copyleftLines ? `\n### Copyleft warnings\n\`\`\`\n${copyleftLines}\n\`\`\`` : ''}`,
});
return;
}
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `chore(sbom): license audit found NOASSERTION entries on ${branch} (${date})`,
labels: ['sbom-audit'],
assignees: ['mrpollo'],
body: [
`## SBOM Monthly Audit -- ${branch} -- ${date}`,
'',
'The automated SBOM license audit found submodules with unresolved licenses.',
'',
'### NOASSERTION entries',
'',
'```',
issueLines,
'```',
'',
copyleftLines ? `### Copyleft warnings\n\n\`\`\`\n${copyleftLines}\n\`\`\`\n` : '',
'### How to fix',
'',
'1. Check the submodule repo for a LICENSE file',
'2. Add an override to `Tools/ci/license-overrides.yaml`',
'3. Run `python3 Tools/ci/generate_sbom.py --verify-licenses --source-dir .` to confirm',
'',
'### Full output',
'',
'<details>',
'<summary>Click to expand</summary>',
'',
'```',
fullOutput,
'```',
'',
'</details>',
'',
'cc @mrpollo',
].join('\n'),
});
-1
View File
@@ -71,7 +71,6 @@ jobs:
- name: Build PX4
env:
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
PX4_SBOM_DISABLE: 1
run: make px4_sitl_default
- name: Cache Post-Run [px4_sitl_default]
@@ -1,43 +0,0 @@
name: Sync release branch to px4-ros2-interface-lib
on:
create:
workflow_dispatch:
inputs:
branch:
description: 'Release branch name (e.g. release/1.18)'
required: true
type: string
permissions: {}
jobs:
notify-interface-lib:
if: >-
github.repository == 'PX4/PX4-Autopilot' &&
(
(github.event_name == 'create' && github.ref_type == 'branch' && startsWith(github.ref_name, 'release/')) ||
github.event_name == 'workflow_dispatch'
)
runs-on: ubuntu-latest
steps:
- name: Determine branch name
id: params
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
BRANCH="${{ inputs.branch }}"
else
BRANCH="${{ github.ref_name }}"
fi
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
echo "Dispatching for branch: $BRANCH"
- name: Dispatch release branch creation
run: |
BRANCH="${{ steps.params.outputs.branch }}"
curl -s -f -X POST \
-H "Authorization: token ${{ secrets.PX4BUILTBOT_PERSONAL_ACCESS_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/Auterion/px4-ros2-interface-lib/dispatches \
-d "{\"event_type\":\"px4_release_branch\",\"client_payload\":{\"branch\":\"$BRANCH\"}}"
echo "Dispatched px4_release_branch event for $BRANCH"
@@ -1,135 +0,0 @@
name: Tag px4_msgs from PX4 release tags
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag_name:
description: 'PX4 tag to propagate (example: v1.17.0)'
required: true
type: string
permissions:
contents: read
jobs:
tag_px4_msgs:
if: github.repository == 'PX4/PX4-Autopilot'
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu22-full-x64,"run-id=${{ github.run_id }}",spot=false]
env:
TAG_NAME: ${{ github.event_name == 'workflow_dispatch' && inputs.tag_name || github.ref_name }}
steps:
- name: Checkout PX4 repo
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Setup git credentials
run: |
git config --global user.name "${{ secrets.PX4BUILDBOT_USER }}"
git config --global user.email "${{ secrets.PX4BUILDBOT_EMAIL }}"
- name: Resolve release branch from tag
id: tag_info
shell: bash
run: |
set -euo pipefail
if [[ ! "${TAG_NAME}" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
echo "Tag format is not stable vX.Y.Z, skipping: ${TAG_NAME}"
echo "should_run=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "should_run=true" >> "$GITHUB_OUTPUT"
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
release_branch="release/${major}.${minor}"
git show-ref --verify --quiet "refs/heads/${release_branch}" || {
echo "PX4 branch ${release_branch} not found"
exit 1
}
tag_date="$(git for-each-ref --format='%(creatordate:iso8601)' "refs/tags/${TAG_NAME}")"
if [[ -z "${tag_date}" ]]; then
echo "Unable to resolve tag date for ${TAG_NAME}"
exit 1
fi
echo "release_branch=${release_branch}" >> "$GITHUB_OUTPUT"
echo "tag_date=${tag_date}" >> "$GITHUB_OUTPUT"
- name: Clone px4_msgs repo
if: steps.tag_info.outputs.should_run == 'true'
run: |
git clone https://${{ secrets.PX4BUILTBOT_PERSONAL_ACCESS_TOKEN }}@github.com/PX4/px4_msgs.git
- name: Checkout matching px4_msgs release branch
if: steps.tag_info.outputs.should_run == 'true'
shell: bash
run: |
set -euo pipefail
cd px4_msgs
release_branch="${{ steps.tag_info.outputs.release_branch }}"
if git show-ref --verify --quiet "refs/remotes/origin/${release_branch}"; then
git checkout -B "${release_branch}" "origin/${release_branch}"
else
echo "px4_msgs branch ${release_branch} does not exist"
exit 1
fi
- name: Verify msg and srv trees are identical
if: steps.tag_info.outputs.should_run == 'true'
shell: bash
run: |
set -euo pipefail
release_branch="${{ steps.tag_info.outputs.release_branch }}"
git checkout "${release_branch}"
# Use the same synchronization logic as sync_to_px4_msgs.yml,
# then verify there are no changes in px4_msgs.
rm -f px4_msgs/msg/*.msg
rm -f px4_msgs/msg/versioned/*.msg
rm -f px4_msgs/srv/*.srv
rm -f px4_msgs/srv/versioned/*.srv
cp msg/*.msg px4_msgs/msg/
cp msg/versioned/*.msg px4_msgs/msg/ || true
cp srv/*.srv px4_msgs/srv/
cp srv/versioned/*.srv px4_msgs/srv/ || true
if ! git -C px4_msgs diff --exit-code -- msg srv; then
echo "Message/service definitions differ between PX4 ${release_branch} and px4_msgs ${release_branch}"
exit 1
fi
- name: Create and push tag in px4_msgs
if: steps.tag_info.outputs.should_run == 'true'
shell: bash
run: |
set -euo pipefail
cd px4_msgs
target="$(git rev-parse HEAD)"
existing_target="$(git rev-parse "refs/tags/${TAG_NAME}^{}" 2>/dev/null || true)"
if [[ -n "${existing_target}" ]]; then
if [[ "${existing_target}" == "${target}" ]]; then
echo "Tag ${TAG_NAME} already exists on ${target}; nothing to do"
exit 0
fi
echo "Tag ${TAG_NAME} already exists on ${existing_target}, expected ${target}"
exit 1
fi
GIT_COMMITTER_DATE="${{ steps.tag_info.outputs.tag_date }}" \
git tag -a "${TAG_NAME}" "${target}" \
-m "PX4 msgs and srvs definitions matching PX4 stable release ${TAG_NAME#v}"
git push origin "refs/tags/${TAG_NAME}"
-3
View File
@@ -112,6 +112,3 @@ keys/
# metadata
_emscripten_sdk/
# virtual Python environment
.venv
+3 -33
View File
@@ -173,35 +173,13 @@ set(config_module_list)
set(config_kernel_list)
# Find Python
#
# Force FindPython3 to prefer the interpreter on PATH (and any activated
# virtualenv/conda/pyenv environment) so downstream tooling runs under
# the same Python the developer invoked the build with. On Linux these
# are already the CMake defaults; the settings exist to normalize
# behavior on macOS and Windows:
#
# - FIND_FRAMEWORK=LAST prevents FindPython3 from preferring a macOS
# Python.framework install (e.g. Homebrew's
# /opt/homebrew/Frameworks/Python.framework/...) over a pyenv shim
# or venv on PATH, which would otherwise pick an interpreter that
# lacks PX4's Python deps (kconfiglib, empy, jinja2, etc.).
# - FIND_REGISTRY=NEVER skips the Windows registry lookup for the
# same reason.
# - FIND_STRATEGY and FIND_VIRTUALENV are set explicitly to make the
# intent obvious and insulate us from future default changes.
set(Python3_FIND_STRATEGY LOCATION)
set(Python3_FIND_VIRTUALENV FIRST)
set(Python3_FIND_FRAMEWORK LAST)
set(Python3_FIND_REGISTRY NEVER)
find_package(Python3 COMPONENTS Interpreter)
find_package(PythonInterp 3)
# We have a custom error message to tell users how to install python3.
if(NOT Python3_Interpreter_FOUND)
if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "Python 3 not found. Please install Python 3:\n"
" Ubuntu: sudo apt install python3 python3-dev python3-pip\n"
" macOS: brew install python")
endif()
# Alias to the legacy variable so downstream CMakeLists keep working.
set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
option(PYTHON_COVERAGE "Python code coverage" OFF)
if(PYTHON_COVERAGE)
@@ -262,15 +240,8 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE ${PX4_BUILD_TYPE} CACHE STRING "Build type" FORCE)
endif()
if(CONFIG_BOARD_SUPPORT_FORTIFIED_TOOLCHAIN)
set(PX4_DEBUG_OPT_LEVEL -Og)
message(STATUS "fortified toolchain support enabled: PX4_DEBUG_OPT_LEVEL=${PX4_DEBUG_OPT_LEVEL}")
else()
set(PX4_DEBUG_OPT_LEVEL -O0)
endif()
if((CMAKE_BUILD_TYPE STREQUAL "Debug") OR (CMAKE_BUILD_TYPE STREQUAL "Coverage"))
set(MAX_CUSTOM_OPT_LEVEL ${PX4_DEBUG_OPT_LEVEL})
set(MAX_CUSTOM_OPT_LEVEL -O0)
elseif(CMAKE_BUILD_TYPE MATCHES "Sanitizer")
set(MAX_CUSTOM_OPT_LEVEL -O1)
elseif(CMAKE_BUILD_TYPE MATCHES "Release")
@@ -513,7 +484,6 @@ include(bloaty)
include(metadata)
include(package)
include(sbom)
# install python requirements using configured python
add_custom_target(install_python_requirements
+20 -57
View File
@@ -2,82 +2,45 @@
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
Examples of behavior that contributes to creating a positive environment include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior include:
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
## Our Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at coc@dronecode.org. All complaints will be reviewed and investigated promptly and fairly.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at lorenz@px4.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
-14
View File
@@ -67,16 +67,6 @@ menu "Toolchain"
help
Enables Cmake Release for -O3 optimization
config BOARD_SUPPORT_FORTIFIED_TOOLCHAIN
bool "Fortified toolchain support"
default n
help
Enable compatibility with toolchains that define
_FORTIFY_SOURCE.
This switches PX4_DEBUG_OPT_LEVEL from -O0 to -Og. Keep this
disabled unless the fortified toolchain requires optimization.
config BOARD_ROMFSROOT
string "ROMFSROOT"
default "px4fmu_common"
@@ -220,10 +210,6 @@ menu "examples"
source "src/examples/Kconfig"
endmenu
menu "templates"
source "src/templates/Kconfig"
endmenu
menu "platforms"
depends on PLATFORM_QURT || PLATFORM_POSIX || PLATFORM_NUTTX
source "platforms/Kconfig"
+2 -13
View File
@@ -1,11 +1,9 @@
Maintainers
===========
PX4 is maintained by a group of contributors trusted to steward the project. All maintainers listed below are members of the @PX4/dev-team, have write access, and participate in maintainer decisions. We recognize two types: **Code Owners**, responsible for specific components, and **Reviewers**, who help across the project without a fixed component.
See [the documentation on Maintainers](https://docs.px4.io/main/en/contribute/maintainers.html) to learn about the role of the maintainers and the process to become one.
See [the documentation on Maintainers](https://docs.px4.io/main/en/contribute/maintainers) to learn about the role of the maintainers and the process to become one.
**Code Owners**
**Active Maintainers**
| Name | Sector | GitHub | Chat | email
|-------------------------|--------|--------|------|----------------
@@ -25,15 +23,6 @@ See [the documentation on Maintainers](https://docs.px4.io/main/en/contribute/ma
| Jacob Dahl | Simulation | [@dakejahl](https://github.com/dakejahl) | dakejahl | <dahl.jakejacob@gmail.com>
**Reviewers**
Reviewers help maintain PX4 across the project without ownership of a specific component.
| Name | GitHub | Chat | email
|------|--------|------|----------------------
| _No reviewers yet._ | | |
**Documentation Maintainers**
| Name | GitHub | Chat | email
+1 -21
View File
@@ -162,12 +162,6 @@ else
endif
# Prefer the interpreter from an active Python virtual environment.
# Otherwise leave PYTHON_EXECUTABLE unset and let CMake resolve Python.
ifneq ($(strip $(VIRTUAL_ENV)),)
PYTHON_EXECUTABLE ?= $(VIRTUAL_ENV)/bin/python
endif
# Pick up specific Python path if set
ifdef PYTHON_EXECUTABLE
override CMAKE_ARGS += -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}
@@ -232,22 +226,9 @@ CONFIG_TARGETS_DEFAULT := $(patsubst %_default,%,$(filter %_default,$(ALL_CONFIG
$(CONFIG_TARGETS_DEFAULT):
@$(call cmake-build,$@_default$(BUILD_DIR_SUFFIX))
# Multi-processor boards: build all processor targets together
# VOXL2 apps processor (default) depends on SLPI DSP being built first
modalai_voxl2_default: modalai_voxl2_slpi
modalai_voxl2: modalai_voxl2_slpi
modalai_voxl2_deb: modalai_voxl2_slpi
all_config_targets: $(ALL_CONFIG_TARGETS)
all_default_targets: $(CONFIG_TARGETS_DEFAULT)
# DEB package targets: builds _default config, then runs cpack.
# Multi-processor boards (e.g. VOXL2) chain companion builds automatically
# via existing cmake prerequisites.
%_deb:
@$(call cmake-build,$(subst _deb,_default,$@)$(BUILD_DIR_SUFFIX))
@cd "$(SRC_DIR)/build/$(subst _deb,_default,$@)" && cpack -G DEB
updateconfig:
@./Tools/kconfig/updateconfig.py
@@ -554,8 +535,7 @@ validate_module_configs:
-not -path "$(SRC_DIR)/src/modules/zenoh/zenoh-pico/*" \
-not -path "$(SRC_DIR)/src/lib/events/libevents/*" \
-not -path "$(SRC_DIR)/src/lib/cdrstream/*" \
-not -path "$(SRC_DIR)/src/lib/crypto/libtommath/*" \
-not -path "$(SRC_DIR)/src/lib/tensorflow_lite_micro/*" -print0 | \
-not -path "$(SRC_DIR)/src/lib/crypto/libtommath/*" -print0 | \
xargs -0 "$(SRC_DIR)"/Tools/validate_yaml.py --schema-file "$(SRC_DIR)"/validation/module_schema.yaml
# Cleanup
+4 -25
View File
@@ -9,16 +9,11 @@
</p>
<p align="center">
<a href="https://github.com/PX4/PX4-Autopilot/releases"><img src="https://img.shields.io/github/release/PX4/PX4-Autopilot.svg" alt="Release"></a>
<a href="https://zenodo.org/badge/latestdoi/22634/PX4/PX4-Autopilot"><img src="https://zenodo.org/badge/22634/PX4/PX4-Autopilot.svg" alt="DOI"></a>
<a href="https://discord.gg/dronecode"><img src="https://img.shields.io/discord/1022170275984457759?label=discord&logo=discord&logoColor=white&color=5865F2" alt="Discord"></a>
</p>
<p align="center">
<a href="https://github.com/PX4/PX4-Autopilot/releases"><img src="https://img.shields.io/github/release/PX4/PX4-Autopilot.svg" alt="Releases"></a>
<a href="https://www.bestpractices.dev/projects/6520"><img src="https://www.bestpractices.dev/projects/6520/badge" alt="OpenSSF Best Practices"></a>
<a href="https://insights.linuxfoundation.org/project/px4"><img src="https://insights.linuxfoundation.org/api/badge/health-score?project=px4" alt="LFX Health Score"></a>
<a href="https://insights.linuxfoundation.org/project/px4"><img src="https://insights.linuxfoundation.org/api/badge/contributors?project=px4" alt="LFX Contributors"></a>
<a href="https://insights.linuxfoundation.org/project/px4"><img src="https://insights.linuxfoundation.org/api/badge/active-contributors?project=px4" alt="LFX Active Contributors"></a>
<a href="https://zenodo.org/badge/latestdoi/22634/PX4/PX4-Autopilot"><img src="https://zenodo.org/badge/22634/PX4/PX4-Autopilot.svg" alt="DOI"></a>
<a href="https://github.com/PX4/PX4-Autopilot/actions/workflows/build_all_targets.yml"><img src="https://github.com/PX4/PX4-Autopilot/actions/workflows/build_all_targets.yml/badge.svg?branch=main" alt="Build Targets"></a>
<a href="https://discord.gg/dronecode"><img src="https://discordapp.com/api/guilds/1022170275984457759/widget.png?style=shield" alt="Discord"></a>
</p>
---
@@ -104,22 +99,6 @@ make px4_sitl
We welcome contributions of all kinds — bug reports, documentation, new features, and code reviews. Please read the [Contribution Guide](https://docs.px4.io/main/en/contribute/) to get started.
## Citation
If you use PX4 in academic work, please cite it. BibTeX:
```bibtex
@software{px4_autopilot,
author = {Meier, Lorenz and {The PX4 Contributors}},
title = {{PX4 Autopilot}},
publisher = {Zenodo},
doi = {10.5281/zenodo.595432},
url = {https://px4.io}
}
```
The DOI above is a Zenodo concept DOI that always resolves to the latest release. For a version-pinned citation, see the [Zenodo record](https://doi.org/10.5281/zenodo.595432) or our [`CITATION.cff`](CITATION.cff).
## Governance
The PX4 Autopilot project is hosted by the [Dronecode Foundation](https://www.dronecode.org/), a [Linux Foundation](https://www.linuxfoundation.org/) Collaborative Project. Dronecode holds all PX4 trademarks and serves as the project's legal guardian, ensuring vendor-neutral stewardship — no single company owns the name or controls the roadmap. The source code is licensed under the [BSD 3-Clause](LICENSE) license, so you are free to use, modify, and distribute it in your own projects.
@@ -18,7 +18,6 @@ param set-default SENS_EN_BAROSIM 1
param set-default SENS_EN_MAGSIM 1
param set SIH_VEHICLE_TYPE 4
param set-default MAV_TYPE 13
# Symmetric hexacopter X clockwise motor numbering
param set-default CA_ROTOR_COUNT 6
+3 -3
View File
@@ -119,11 +119,11 @@ else
param set SYS_AUTOCONFIG 1
fi
# To trigger a parameter reset during boot SYS_AUTOCONFIG was set to 1 before
# To trigger a parameter reset during boot SYS_AUTCONFIG was set to 1 before
if param greater SYS_AUTOCONFIG 0
then
# Reset parameters except airframe, parameter version, sensor calibration, total flight time, flight UUID
param reset_all SYS_AUTOSTART SYS_PARAM_VER CAL_* LND_FLIGHT* TC_* COM_FLIGHT*
# Reset parameters except airframe, parameter version, RC calibration, sensor calibration, total flight time, flight UUID
param reset_all SYS_AUTOSTART SYS_PARAM_VER RC* CAL_* LND_FLIGHT* TC_* COM_FLIGHT*
set AUTOCNF yes
fi
@@ -14,7 +14,6 @@
param set UAVCAN_ENABLE 0
param set-default CA_AIRFRAME 1
param set-default CA_ROTOR_COUNT 1
param set-default CA_ROTOR0_PX 0.3
@@ -39,6 +38,7 @@ param set-default SYS_HITL 2
# - without real battery
param set-default CBRK_SUPPLY_CHK 894281
param set SIH_T_MAX 6
param set SIH_MASS 0.3
param set SIH_IXX 0.00402
param set SIH_IYY 0.0144
@@ -48,21 +48,3 @@ param set SIH_KDV 0.2
param set SIH_VEHICLE_TYPE 1 # sih as fixed wing
param set RWTO_TKOFF 1 # enable takeoff from runway (as opposed to launched)
# pusher propeller model with advance ratio, model from UIUC APC 8x6"
param set SIH_F_T_MAX 6
param set SIH_F_Q_MAX 0.03
# if SIH_F_CT0 > 0, SIH_F_T_MAX and SIH_F_Q_MAX will be overridden
param set SIH_F_CT0 0.131
param set SIH_F_CT1 0.004
param set SIH_F_CT2 -0.146
param set SIH_F_CP0 0.0777
param set SIH_F_CP1 0.0498
param set SIH_F_CP2 -0.11
param set SIH_F_DIA_INCH 8
param set SIH_F_RPM_MAX 9000
param set-default FW_AIRSPD_MIN 7
param set-default FW_AIRSPD_TRIM 10
param set-default FW_AIRSPD_MAX 12
param set-default FW_PSP_OFF 0.5
@@ -28,7 +28,6 @@ param set-default VT_FW_DIFTHR_EN 1
param set-default VT_FW_DIFTHR_S_Y 0.3
param set-default MPC_MAN_Y_MAX 60
param set-default MC_PITCH_P 5
param set-default FW_PSP_OFF 5
param set-default CA_AIRFRAME 4
param set-default CA_ROTOR_COUNT 2
@@ -57,6 +56,7 @@ param set-default HIL_ACT_REV 32
param set-default MAV_TYPE 19
# set SYS_HITL to 2 to start the SIH and avoid sensors startup
param set-default SYS_HITL 2
@@ -66,9 +66,8 @@ param set-default CBRK_SUPPLY_CHK 894281
param set-default SENS_DPRES_OFF 0.001
# tailsitter is equipped with two forward propellers
param set SIH_F_T_MAX 2
param set SIH_F_Q_MAX 0.0165
param set SIH_T_MAX 2.0
param set SIH_Q_MAX 0.0165
param set SIH_MASS 0.2
# IXX and IZZ are inverted from the thesis as the body frame is pitched by 90 deg
param set SIH_IXX 0.00354
@@ -78,19 +77,6 @@ param set SIH_IXZ 0
param set SIH_KDV 0.2
param set SIH_L_ROLL 0.145
# propeller diameter, rpm, and coeffs coming from the thesis
# Modeling and control of a flying wing tailsitter unmanned aerial vehicle."
# Chiappinelli, Romain, supervised by Nahon, Meyer, McGill University, Masters thesis, 2018.
# if SIH_F_CT0 > 0, SIH_F_T_MAX and SIH_F_Q_MAX will be overridden
param set SIH_F_CT0 0.1342
param set SIH_F_CT1 -0.1196
param set SIH_F_CT2 -0.1281
param set SIH_F_CP0 0.0522
param set SIH_F_CP1 -0.0146
param set SIH_F_CP2 -0.0602
param set SIH_F_DIA_INCH 5
param set SIH_F_RPM_MAX 14000
# sih as tailsitter
param set SIH_VEHICLE_TYPE 2
@@ -56,7 +56,6 @@ param set-default CA_SV_CS2_TYPE 4 # rudder
param set-default FW_AIRSPD_MIN 7
param set-default FW_AIRSPD_TRIM 10
param set-default FW_AIRSPD_MAX 12
param set-default VT_FWD_THRUST_EN 1
param set-default HIL_ACT_FUNC1 101
param set-default HIL_ACT_FUNC2 102
@@ -78,7 +77,6 @@ param set-default CBRK_SUPPLY_CHK 894281
param set-default SENS_DPRES_OFF 0.001
# quadrotor propellers
param set SIH_T_MAX 2.0
param set SIH_Q_MAX 0.0165
param set SIH_MASS 0.2
@@ -90,18 +88,5 @@ param set SIH_IXZ 0
param set SIH_KDV 0.2
param set SIH_L_ROLL 0.2
# pusher propeller model with advance ratio, model from UIUC APC 8x6"
param set SIH_F_T_MAX 6
param set SIH_F_Q_MAX 0.03
# if SIH_F_CT0 > 0, SIH_F_T_MAX and SIH_F_Q_MAX will be overridden
param set SIH_F_CT0 0.131
param set SIH_F_CT1 0.004
param set SIH_F_CT2 -0.146
param set SIH_F_CP0 0.0777
param set SIH_F_CP1 0.0498
param set SIH_F_CP2 -0.11
param set SIH_F_DIA_INCH 8
param set SIH_F_RPM_MAX 9000
# sih as standard vtol
param set SIH_VEHICLE_TYPE 3
@@ -1,49 +0,0 @@
#!/bin/sh
#
# @name SIH Hexacopter X
#
# @type Simulation
# @class Copter
#
# @maintainer Romain Chiappinelli <romain.chiap@gmail.com>
#
# @board px4_fmu-v2 exclude
#
. ${R}etc/init.d/rc.mc_defaults
param set UAVCAN_ENABLE 0
# set SYS_HITL to 2 to start the SIH and avoid sensors startup
param set SYS_HITL 2
# disable some checks to allow to fly:
# - without real battery
param set-default CBRK_SUPPLY_CHK 894281
param set SIH_VEHICLE_TYPE 4
# Symmetric hexacopter X clockwise motor numbering
param set-default CA_ROTOR_COUNT 6
param set-default CA_ROTOR0_PX 0.866
param set-default CA_ROTOR0_PY 0.5
param set-default CA_ROTOR1_PX 0
param set-default CA_ROTOR1_PY 1
param set-default CA_ROTOR1_KM -0.05
param set-default CA_ROTOR2_PX -0.866
param set-default CA_ROTOR2_PY 0.5
param set-default CA_ROTOR3_PX -0.866
param set-default CA_ROTOR3_PY -0.5
param set-default CA_ROTOR3_KM -0.05
param set-default CA_ROTOR4_PX 0
param set-default CA_ROTOR4_PY -1
param set-default CA_ROTOR5_PX 0.866
param set-default CA_ROTOR5_PY -0.5
param set-default CA_ROTOR5_KM -0.05
param set-default HIL_ACT_FUNC1 101
param set-default HIL_ACT_FUNC2 102
param set-default HIL_ACT_FUNC3 103
param set-default HIL_ACT_FUNC4 104
param set-default HIL_ACT_FUNC5 105
param set-default HIL_ACT_FUNC6 106
@@ -49,7 +49,6 @@ if(CONFIG_MODULES_SIMULATION_PWM_OUT_SIM)
1101_rc_plane_sih.hil
1102_tailsitter_duo_sih.hil
1103_standard_vtol_sih.hil
1105_rc_hexa_x_sih.hil
)
if(CONFIG_MODULES_ROVER_ACKERMANN)
px4_add_romfs_files(
+1 -1
View File
@@ -238,7 +238,7 @@ then
fi
# Start TMP102 temperature sensor
if param compare -s SENS_EN_TMP102 1
if param compare SENS_EN_TMP102 1
then
tmp102 start -X
fi
+7 -10
View File
@@ -188,11 +188,11 @@ else
netman update -i eth0
fi
# To trigger a parameter reset during boot SYS_AUTOCONFIG was set to 1 before
# To trigger a parameter reset during boot SYS_AUTCONFIG was set to 1 before
if param greater SYS_AUTOCONFIG 0
then
# Reset parameters except airframe, parameter version, sensor calibration, total flight time, flight UUID
param reset_all SYS_AUTOSTART SYS_PARAM_VER CAL_* LND_FLIGHT* TC_* COM_FLIGHT*
# Reset parameters except airframe, parameter version, RC calibration, sensor calibration, total flight time, flight UUID
param reset_all SYS_AUTOSTART SYS_PARAM_VER RC* CAL_* LND_FLIGHT* TC_* COM_FLIGHT*
fi
#
@@ -633,15 +633,12 @@ else
#
# Start the VTX services.
#
if ! param compare -s VTX_SER_CFG 0
set RC_VTXTABLE ${R}etc/init.d/rc.vtxtable
if [ -f ${RC_VTXTABLE} ]
then
set RC_VTXTABLE ${R}etc/init.d/rc.vtxtable
if [ -f ${RC_VTXTABLE} ]
then
. ${RC_VTXTABLE}
fi
unset RC_VTXTABLE
. ${RC_VTXTABLE}
fi
unset RC_VTXTABLE
#
# Set additional parameters and env variables for selected AUTOSTART.
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#! /bin/bash
# exit when any command fails
set -e
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
if [[ $# -eq 0 ]] ; then
exit 0
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# Flash PX4 to a device running AuterionOS in the local network
if [ "$1" == "-h" ] || [ "$1" == "--help" ] || [ $# -lt 2 ]; then
echo "Usage: $0 -f <firmware.px4|.elf> [-c <configuration_dir>] -d <IP/Device> [-u <user>] [-p <ssh_port>] [--revert]"
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# This script is meant to be used by the build_all.yml workflow in a github runner
# Please only modify if you know what you are doing
set -e
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#! /bin/bash
# Copy a git diff between two commits if msg versioning is added
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-59
View File
@@ -189,65 +189,6 @@ for manufacturer in sorted(os.scandir(os.path.join(source_dir, '../boards')), ke
if target is not None:
build_configs.append(target)
# Remove companion targets from CI groups (parent target builds them via Make prerequisite)
for manufacturer in sorted(os.scandir(os.path.join(source_dir, '../boards')), key=lambda e: e.name):
if not manufacturer.is_dir():
continue
for board in sorted(os.scandir(manufacturer.path), key=lambda e: e.name):
if not board.is_dir():
continue
companion_file = os.path.join(board.path, 'companion_targets')
if os.path.exists(companion_file):
with open(companion_file) as f:
companions = {l.strip() for l in f if l.strip() and not l.startswith('#')}
for arch in grouped_targets:
for man in grouped_targets[arch]['manufacturers']:
grouped_targets[arch]['manufacturers'][man] = [
t for t in grouped_targets[arch]['manufacturers'][man]
if t not in companions
]
# Append _deb targets for boards that have cmake/package.cmake
for manufacturer in sorted(os.scandir(os.path.join(source_dir, '../boards')), key=lambda e: e.name):
if not manufacturer.is_dir():
continue
if manufacturer.name in excluded_manufacturers:
continue
for board in sorted(os.scandir(manufacturer.path), key=lambda e: e.name):
if not board.is_dir():
continue
board_name = manufacturer.name + '_' + board.name
if board_name in excluded_boards:
continue
package_cmake = os.path.join(board.path, 'cmake', 'package.cmake')
if os.path.exists(package_cmake):
deb_target = board_name + '_deb'
if target_filter and not any(deb_target.startswith(f) for f in target_filter):
continue
# Determine the container and group for this board
container = default_container
if board_name in board_container_overrides:
container = board_container_overrides[board_name]
target_entry = {'target': deb_target, 'container': container}
if args.group:
# Find the group where this board's _default target already lives
default_target = board_name + '_default'
group = None
for g in grouped_targets:
targets_in_group = grouped_targets[g].get('manufacturers', {}).get(manufacturer.name, [])
if default_target in targets_in_group:
group = g
break
if group is None:
group = 'base'
target_entry['arch'] = group
if group not in grouped_targets:
grouped_targets[group] = {'container': container, 'manufacturers': {}}
if manufacturer.name not in grouped_targets[group]['manufacturers']:
grouped_targets[group]['manufacturers'][manufacturer.name] = []
grouped_targets[group]['manufacturers'][manufacturer.name].append(deb_target)
build_configs.append(target_entry)
if(verbose):
import pprint
print("============================")
-607
View File
@@ -1,607 +0,0 @@
#!/usr/bin/env python3
"""Generate SPDX 2.3 JSON SBOM for a PX4 firmware build.
Produces one SBOM per board target containing:
- PX4 firmware as the primary package
- Git submodules as CONTAINS dependencies
- Python build requirements as BUILD_DEPENDENCY_OF packages
- Board-specific modules as CONTAINS packages
Requires PyYAML (pyyaml) for loading license overrides.
"""
import argparse
import configparser
import json
import re
import subprocess
import uuid
from datetime import datetime, timezone
from pathlib import Path
import yaml
# Ordered most-specific first: all keywords must appear for a match.
LICENSE_PATTERNS = [
# Copyleft licenses first (more specific keywords prevent false matches)
("GPL-3.0-only", ["GNU GENERAL PUBLIC LICENSE", "Version 3"]),
("GPL-2.0-only", ["GNU GENERAL PUBLIC LICENSE", "Version 2"]),
("LGPL-3.0-only", ["GNU LESSER GENERAL PUBLIC LICENSE", "Version 3"]),
("LGPL-2.1-only", ["GNU Lesser General Public License", "Version 2.1"]),
("AGPL-3.0-only", ["GNU AFFERO GENERAL PUBLIC LICENSE", "Version 3"]),
# Permissive licenses
("Apache-2.0", ["Apache License", "Version 2.0"]),
("MIT", ["Permission is hereby granted"]),
("BSD-3-Clause", ["Redistribution and use", "Neither the name"]),
("BSD-2-Clause", ["Redistribution and use", "THIS SOFTWARE IS PROVIDED"]),
("ISC", ["Permission to use, copy, modify, and/or distribute"]),
("EPL-2.0", ["Eclipse Public License", "2.0"]),
("Unlicense", ["The Unlicense", "unlicense.org"]),
]
COPYLEFT_LICENSES = {
"GPL-2.0-only", "GPL-3.0-only",
"LGPL-2.1-only", "LGPL-3.0-only",
"AGPL-3.0-only",
}
def load_license_overrides(source_dir):
"""Load license overrides and comments from YAML config file.
Returns (overrides, comments) dicts mapping submodule path to values.
Falls back to empty dicts if the file is missing.
"""
yaml_path = source_dir / "Tools" / "ci" / "license-overrides.yaml"
if not yaml_path.exists():
return {}, {}
with open(yaml_path) as f:
data = yaml.safe_load(f)
overrides = {}
comments = {}
for path, entry in (data.get("overrides") or {}).items():
overrides[path] = entry["license"]
if "comment" in entry:
comments[path] = entry["comment"]
return overrides, comments
LICENSE_FILENAMES = ["LICENSE", "LICENSE.md", "LICENSE.txt", "LICENCE", "LICENCE.md", "COPYING", "COPYING.md"]
def detect_license(submodule_dir):
"""Auto-detect SPDX license ID from LICENSE/COPYING file in a directory.
Reads the first 100 lines of the first license file found and matches
keywords against LICENSE_PATTERNS. Returns 'NOASSERTION' if no file
is found or no pattern matches.
"""
for fname in LICENSE_FILENAMES:
license_file = submodule_dir / fname
if license_file.is_file():
try:
lines = license_file.read_text(errors="replace").splitlines()[:100]
text = "\n".join(lines)
except OSError:
continue
text_upper = text.upper()
for spdx_id_val, keywords in LICENSE_PATTERNS:
if all(kw.upper() in text_upper for kw in keywords):
return spdx_id_val
return "NOASSERTION"
return "NOASSERTION"
def get_submodule_license(source_dir, sub_path, license_overrides):
"""Return the SPDX license for a submodule: override > auto-detect."""
if sub_path in license_overrides:
return license_overrides[sub_path]
return detect_license(source_dir / sub_path)
def spdx_id(name: str) -> str:
"""Convert a name to a valid SPDX identifier (letters, digits, dots, hyphens)."""
return re.sub(r"[^a-zA-Z0-9.\-]", "-", name)
def parse_gitmodules(source_dir):
"""Parse .gitmodules and return list of {name, path, url}."""
gitmodules_path = source_dir / ".gitmodules"
if not gitmodules_path.exists():
return []
config = configparser.ConfigParser()
config.read(str(gitmodules_path))
submodules = []
for section in config.sections():
if section.startswith("submodule "):
name = section.split('"')[1] if '"' in section else section.split(" ", 1)[1]
path = config.get(section, "path", fallback="")
url = config.get(section, "url", fallback="")
submodules.append({"name": name, "path": path, "url": url})
return submodules
def get_submodule_commits(source_dir):
"""Get commit hashes for all submodules via git ls-tree -r (works without init)."""
try:
result = subprocess.run(
["git", "ls-tree", "-r", "HEAD"],
cwd=str(source_dir),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True,
)
except (subprocess.CalledProcessError, FileNotFoundError):
return {}
commits = {}
for line in result.stdout.splitlines():
parts = line.split()
if len(parts) >= 4 and parts[1] == "commit":
commits[parts[3]] = parts[2]
return commits
def get_git_info(source_dir: Path) -> dict:
"""Get PX4 git version and hash."""
info = {"version": "unknown", "hash": "unknown"}
try:
result = subprocess.run(
["git", "describe", "--always", "--tags", "--dirty"],
cwd=str(source_dir),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True,
)
info["version"] = result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
pass
try:
result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=str(source_dir),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True,
)
info["hash"] = result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError):
pass
return info
def parse_requirements(requirements_path):
"""Parse pip requirements.txt into list of {name, version_spec}."""
if not requirements_path.exists():
return []
deps = []
for line in requirements_path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or line.startswith("-"):
continue
# Split on version specifiers
match = re.match(r"^([a-zA-Z0-9_\-]+)(.*)?$", line)
if match:
deps.append({
"name": match.group(1),
"version_spec": match.group(2).strip() if match.group(2) else "",
})
return deps
def read_module_list(modules_file, source_dir):
"""Read board-specific module list from file.
Paths may be absolute; they are converted to relative paths under src/.
Duplicates are removed while preserving order.
"""
if not modules_file or not modules_file.exists():
return []
seen = set()
modules = []
source_str = str(source_dir.resolve()) + "/"
for line in modules_file.read_text().splitlines():
path = line.strip()
if not path or path.startswith("#"):
continue
# Convert absolute path to relative
if path.startswith(source_str):
path = path[len(source_str):]
if path not in seen:
seen.add(path)
modules.append(path)
return modules
def make_purl(pkg_type: str, namespace: str, name: str, version: str = "") -> str:
"""Construct a Package URL (purl)."""
purl = f"pkg:{pkg_type}/{namespace}/{name}"
if version:
purl += f"@{version}"
return purl
def extract_git_host_org_repo(url):
"""Extract host type, org, and repo from a git URL.
Returns (host, org, repo) where host is 'github', 'gitlab', or ''.
"""
match = re.search(r"github\.com[:/]([^/]+)/([^/]+?)(?:\.git)?$", url)
if match:
return "github", match.group(1), match.group(2)
match = re.search(r"gitlab\.com[:/](.+?)/([^/]+?)(?:\.git)?$", url)
if match:
return "gitlab", match.group(1), match.group(2)
return "", "", ""
def generate_sbom(source_dir, board, modules_file, compiler, platform=""):
"""Generate a complete SPDX 2.3 JSON document."""
license_overrides, license_comments = load_license_overrides(source_dir)
git_info = get_git_info(source_dir)
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
# Deterministic namespace using UUID5 from git hash + board
ns_seed = f"{git_info['hash']}:{board}"
doc_namespace = f"https://spdx.org/spdxdocs/{board}-{uuid.uuid5(uuid.NAMESPACE_URL, ns_seed)}"
doc = {
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": f"PX4 Firmware SBOM for {board}",
"documentNamespace": doc_namespace,
"creationInfo": {
"created": timestamp,
"creators": [
"Tool: px4-generate-sbom",
"Organization: Dronecode Foundation",
],
"licenseListVersion": "3.22",
},
"packages": [],
"relationships": [],
}
# Primary package: PX4 firmware
primary_spdx_id = f"SPDXRef-PX4-{spdx_id(board)}"
doc["packages"].append({
"SPDXID": primary_spdx_id,
"name": board,
"versionInfo": git_info["version"],
"packageFileName": f"{board}.px4",
"supplier": "Organization: Dronecode Foundation",
"downloadLocation": "https://github.com/PX4/PX4-Autopilot",
"filesAnalyzed": False,
"primaryPackagePurpose": "FIRMWARE",
"licenseConcluded": "BSD-3-Clause",
"licenseDeclared": "BSD-3-Clause",
"copyrightText": "Copyright (c) PX4 Development Team",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": make_purl(
"github", "PX4", "PX4-Autopilot", git_info["version"]
),
}
],
})
doc["relationships"].append({
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": primary_spdx_id,
})
# Git submodules (filtered to those relevant to this board's modules)
submodules = parse_gitmodules(source_dir)
submodule_commits = get_submodule_commits(source_dir)
modules = read_module_list(modules_file, source_dir)
def submodule_is_relevant(sub_path):
"""A submodule is relevant if any board module path overlaps with it."""
# NuttX platform submodules are only relevant for NuttX builds
if sub_path.startswith("platforms/nuttx/"):
return platform in ("nuttx", "")
if not modules:
return True # no module list means include all
# Other platform submodules are always relevant
if sub_path.startswith("platforms/"):
return True
for mod in modules:
# Module is under this submodule, or submodule is under a module
if mod.startswith(sub_path + "/") or sub_path.startswith(mod + "/"):
return True
return False
for sub in submodules:
if not submodule_is_relevant(sub["path"]):
continue
sub_path = sub["path"]
sub_path_id = sub_path.replace("/", "-")
sub_spdx_id = f"SPDXRef-Submodule-{spdx_id(sub_path_id)}"
commit = submodule_commits.get(sub_path, "unknown")
license_id = get_submodule_license(source_dir, sub_path, license_overrides)
host, org, repo = extract_git_host_org_repo(sub["url"])
download = sub["url"] if sub["url"] else "NOASSERTION"
# Use repo name from URL for human-readable name, fall back to last path component
display_name = repo if repo else sub_path.rsplit("/", 1)[-1]
pkg = {
"SPDXID": sub_spdx_id,
"name": display_name,
"versionInfo": commit,
"supplier": f"Organization: {org}" if org else "NOASSERTION",
"downloadLocation": download,
"filesAnalyzed": False,
"licenseConcluded": license_id,
"licenseDeclared": license_id,
"copyrightText": "NOASSERTION",
}
comment = license_comments.get(sub_path)
if comment:
pkg["licenseComments"] = comment
if host and org and repo:
pkg["externalRefs"] = [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": make_purl(host, org, repo, commit),
}
]
doc["packages"].append(pkg)
doc["relationships"].append({
"spdxElementId": primary_spdx_id,
"relationshipType": "CONTAINS",
"relatedSpdxElement": sub_spdx_id,
})
# Python build dependencies
requirements_path = source_dir / "Tools" / "setup" / "requirements.txt"
py_deps = parse_requirements(requirements_path)
for dep in py_deps:
dep_name = dep["name"]
dep_spdx_id = f"SPDXRef-PyDep-{spdx_id(dep_name)}"
version_str = dep["version_spec"] if dep["version_spec"] else "NOASSERTION"
doc["packages"].append({
"SPDXID": dep_spdx_id,
"name": dep_name,
"versionInfo": version_str,
"supplier": "NOASSERTION",
"downloadLocation": f"https://pypi.org/project/{dep_name}/",
"filesAnalyzed": False,
"primaryPackagePurpose": "APPLICATION",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": f"pkg:pypi/{dep_name}",
}
],
})
doc["relationships"].append({
"spdxElementId": dep_spdx_id,
"relationshipType": "BUILD_DEPENDENCY_OF",
"relatedSpdxElement": primary_spdx_id,
})
# Board-specific modules (already read above for submodule filtering)
for mod in modules:
mod_path_id = mod.replace("/", "-")
mod_spdx_id = f"SPDXRef-Module-{spdx_id(mod_path_id)}"
# Derive short name: strip leading src/ for readability
display_name = mod
if display_name.startswith("src/"):
display_name = display_name[4:]
doc["packages"].append({
"SPDXID": mod_spdx_id,
"name": display_name,
"versionInfo": git_info["version"],
"supplier": "Organization: Dronecode Foundation",
"downloadLocation": "https://github.com/PX4/PX4-Autopilot",
"filesAnalyzed": False,
"licenseConcluded": "BSD-3-Clause",
"licenseDeclared": "BSD-3-Clause",
"copyrightText": "NOASSERTION",
})
doc["relationships"].append({
"spdxElementId": primary_spdx_id,
"relationshipType": "CONTAINS",
"relatedSpdxElement": mod_spdx_id,
})
# Compiler as a build tool
if compiler:
compiler_spdx_id = f"SPDXRef-Compiler-{spdx_id(compiler)}"
doc["packages"].append({
"SPDXID": compiler_spdx_id,
"name": compiler,
"versionInfo": "NOASSERTION",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": False,
"primaryPackagePurpose": "APPLICATION",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
})
doc["relationships"].append({
"spdxElementId": compiler_spdx_id,
"relationshipType": "BUILD_TOOL_OF",
"relatedSpdxElement": primary_spdx_id,
})
return doc
def verify_licenses(source_dir):
"""Verify license detection for all submodules. Returns exit code."""
license_overrides, _ = load_license_overrides(source_dir)
submodules = parse_gitmodules(source_dir)
if not submodules:
print("No submodules found in .gitmodules")
return 1
has_noassertion = False
print(f"{'Submodule Path':<65} {'Detected':<16} {'Override':<16} {'Final'}")
print("-" * 115)
for sub in submodules:
sub_path = sub["path"]
sub_dir = source_dir / sub_path
checked_out = sub_dir.is_dir() and any(sub_dir.iterdir())
has_explicit_override = sub_path in license_overrides
if not checked_out:
detected = "(not checked out)"
override = license_overrides.get(sub_path, "")
final = override if override else "NOASSERTION"
else:
detected = detect_license(sub_dir)
override = license_overrides.get(sub_path, "")
final = override if override else detected
if final == "NOASSERTION" and has_explicit_override:
# Explicitly acknowledged in overrides file — not a failure
marker = " (acknowledged)"
elif final == "NOASSERTION" and checked_out:
has_noassertion = True
marker = " <-- UNRESOLVED"
elif final == "NOASSERTION" and not checked_out:
marker = " (skipped)"
else:
marker = ""
print(f"{sub_path:<65} {str(detected):<16} {str(override) if override else '':<16} {final}{marker}")
# Copyleft warning (informational, not a failure)
copyleft_found = []
for sub in submodules:
sub_path = sub["path"]
sub_dir = source_dir / sub_path
checked_out = sub_dir.is_dir() and any(sub_dir.iterdir())
override = license_overrides.get(sub_path, "")
if checked_out:
final_lic = override if override else detect_license(sub_dir)
else:
final_lic = override if override else "NOASSERTION"
for cl in COPYLEFT_LICENSES:
if cl in final_lic:
copyleft_found.append((sub_path, final_lic))
break
print()
if copyleft_found:
print("Copyleft licenses detected (informational):")
for path, lic in copyleft_found:
print(f" {path}: {lic}")
print()
if has_noassertion:
print("FAIL: Some submodules have unresolved licenses. "
"Add an entry to Tools/ci/license-overrides.yaml or check the LICENSE file.")
return 1
print("OK: All submodules have a resolved license.")
return 0
def main():
parser = argparse.ArgumentParser(
description="Generate SPDX 2.3 JSON SBOM for PX4 firmware"
)
parser.add_argument(
"--source-dir",
type=Path,
default=Path.cwd(),
help="PX4 source directory (default: cwd)",
)
parser.add_argument(
"--verify-licenses",
action="store_true",
help="Verify license detection for all submodules and exit",
)
parser.add_argument(
"--board",
default=None,
help="Board target name (e.g. px4_fmu-v5x_default)",
)
parser.add_argument(
"--modules-file",
type=Path,
default=None,
help="Path to config_module_list.txt",
)
parser.add_argument(
"--compiler",
default="",
help="Compiler identifier (e.g. arm-none-eabi-gcc)",
)
parser.add_argument(
"--platform",
default="",
help="PX4 platform (nuttx, posix, qurt). Filters platform-specific submodules.",
)
parser.add_argument(
"--output",
type=Path,
default=None,
help="Output SBOM file path",
)
args = parser.parse_args()
if args.verify_licenses:
raise SystemExit(verify_licenses(args.source_dir))
if not args.board:
parser.error("--board is required when not using --verify-licenses")
if not args.output:
parser.error("--output is required when not using --verify-licenses")
sbom = generate_sbom(
source_dir=args.source_dir,
board=args.board,
modules_file=args.modules_file,
compiler=args.compiler,
platform=args.platform,
)
args.output.parent.mkdir(parents=True, exist_ok=True)
with open(args.output, "w") as f:
json.dump(sbom, f, indent=2)
f.write("\n")
pkg_count = len(sbom["packages"])
print(f"SBOM generated: {args.output} ({pkg_count} packages)")
if __name__ == "__main__":
main()
-163
View File
@@ -1,163 +0,0 @@
#!/usr/bin/env python3
"""Inspect a PX4 SPDX SBOM file.
Usage:
inspect_sbom.py <sbom.spdx.json> # full summary
inspect_sbom.py <sbom.spdx.json> search <term> # search packages by name
inspect_sbom.py <sbom.spdx.json> ntia # NTIA minimum elements check
inspect_sbom.py <sbom.spdx.json> licenses # license summary
inspect_sbom.py <sbom.spdx.json> list <type> # list packages (Submodule|PyDep|Module|all)
"""
import json
import sys
from collections import Counter
from pathlib import Path
def load(path):
return json.loads(Path(path).read_text())
def pkg_type(pkg):
spdx_id = pkg["SPDXID"]
for prefix in ("Submodule", "PyDep", "Module", "Compiler", "PX4"):
if f"-{prefix}-" in spdx_id or spdx_id.startswith(f"SPDXRef-{prefix}"):
return prefix
return "Other"
def summary(doc):
print(f"spdxVersion: {doc['spdxVersion']}")
print(f"name: {doc['name']}")
print(f"namespace: {doc['documentNamespace']}")
print(f"created: {doc['creationInfo']['created']}")
print(f"creators: {', '.join(doc['creationInfo']['creators'])}")
print()
types = Counter(pkg_type(p) for p in doc["packages"])
print(f"Packages: {len(doc['packages'])}")
for t, c in types.most_common():
print(f" {t}: {c}")
print()
rc = Counter(r["relationshipType"] for r in doc["relationships"])
print(f"Relationships: {len(doc['relationships'])}")
for t, n in rc.most_common():
print(f" {t}: {n}")
print()
primary = doc["packages"][0]
print(f"Primary package:")
print(f" name: {primary['name']}")
print(f" version: {primary['versionInfo']}")
print(f" purpose: {primary.get('primaryPackagePurpose', 'N/A')}")
print(f" license: {primary['licenseDeclared']}")
print()
noassert = [
p["name"]
for p in doc["packages"]
if pkg_type(p) == "Submodule" and p["licenseDeclared"] == "NOASSERTION"
]
if noassert:
print(f"WARNING: {len(noassert)} submodules with NOASSERTION license:")
for n in noassert:
print(f" - {n}")
else:
print("All submodule licenses mapped")
print(f"\nFile size: {Path(sys.argv[1]).stat().st_size // 1024}KB")
def search(doc, term):
term = term.lower()
found = [p for p in doc["packages"] if term in p["name"].lower()]
if not found:
print(f"No packages matching '{term}'")
return
print(f"Found {len(found)} packages matching '{term}':\n")
for p in found:
print(json.dumps(p, indent=2))
print()
def ntia_check(doc):
required = ["SPDXID", "name", "versionInfo", "supplier", "downloadLocation"]
missing = []
for p in doc["packages"]:
for f in required:
if f not in p or p[f] in ("", None):
missing.append((p["name"], f))
if missing:
print(f"FAIL: {len(missing)} missing fields:")
for name, field in missing:
print(f" {name}: missing {field}")
else:
print(f"PASS: All {len(doc['packages'])} packages have required fields")
print(f"\nCreators: {doc['creationInfo']['creators']}")
print(f"Timestamp: {doc['creationInfo']['created']}")
rels = [r for r in doc["relationships"] if r["relationshipType"] == "DESCRIBES"]
print(f"DESCRIBES relationships: {len(rels)}")
return len(missing) == 0
def licenses(doc):
by_license = {}
for p in doc["packages"]:
lic = p.get("licenseDeclared", "NOASSERTION")
by_license.setdefault(lic, []).append(p["name"])
for lic in sorted(by_license.keys()):
names = by_license[lic]
print(f"\n{lic} ({len(names)}):")
for n in sorted(names):
print(f" {n}")
def list_packages(doc, filter_type):
filter_type = filter_type.lower()
for p in sorted(doc["packages"], key=lambda x: x["name"]):
t = pkg_type(p)
if filter_type != "all" and t.lower() != filter_type:
continue
lic = p.get("licenseDeclared", "?")
ver = p["versionInfo"][:20] if len(p["versionInfo"]) > 20 else p["versionInfo"]
print(f" {t:10s} {p['name']:50s} {ver:20s} {lic}")
def main():
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
doc = load(sys.argv[1])
cmd = sys.argv[2] if len(sys.argv) > 2 else "summary"
if cmd == "summary":
summary(doc)
elif cmd == "search":
if len(sys.argv) < 4:
print("Usage: inspect_sbom.py <file> search <term>")
sys.exit(1)
search(doc, sys.argv[3])
elif cmd == "ntia":
if not ntia_check(doc):
sys.exit(1)
elif cmd == "licenses":
licenses(doc)
elif cmd == "list":
filter_type = sys.argv[3] if len(sys.argv) > 3 else "all"
list_packages(doc, filter_type)
else:
print(f"Unknown command: {cmd}")
print(__doc__)
sys.exit(1)
if __name__ == "__main__":
main()
-62
View File
@@ -1,62 +0,0 @@
# SPDX license overrides for submodules where auto-detection fails or is wrong.
# Each entry maps a submodule path to its SPDX license identifier and an
# optional comment explaining why the override exists.
#
# Run `python3 Tools/ci/generate_sbom.py --verify-licenses` to validate.
overrides:
src/modules/mavlink/mavlink:
license: "LGPL-3.0-only AND MIT"
comment: "Generator is LGPL-3.0; PX4 ships only MIT-licensed generated headers."
Tools/simulation/gazebo-classic/sitl_gazebo-classic:
license: "BSD-3-Clause"
comment: >-
PX4 org project. No LICENSE file in repo; source files carry
BSD-3-Clause headers consistent with the PX4 project license.
src/lib/cdrstream/cyclonedds:
license: "EPL-2.0 OR BSD-3-Clause"
comment: >-
Dual-licensed. PX4 elects BSD-3-Clause.
No board currently enables CONFIG_LIB_CDRSTREAM.
src/lib/cdrstream/rosidl:
license: "Apache-2.0"
src/lib/crypto/monocypher:
license: "BSD-2-Clause OR CC0-1.0"
comment: >-
Dual-licensed. LICENCE.md offers BSD-2-Clause with CC0-1.0 as
public domain fallback.
src/lib/crypto/libtomcrypt:
license: "Unlicense"
comment: "Public domain dedication. Functionally equivalent to Unlicense."
src/lib/crypto/libtommath:
license: "Unlicense"
comment: "Public domain dedication. Functionally equivalent to Unlicense."
platforms/nuttx/NuttX/nuttx:
license: "Apache-2.0"
comment: >-
Composite LICENSE (6652 lines) includes BSD/MIT/ISC sub-components.
Primary license is Apache-2.0. NOTICE file contains FAT LFN patent warnings.
platforms/nuttx/NuttX/apps:
license: "Apache-2.0"
boards/modalai/voxl2/libfc-sensor-api:
license: "NOASSERTION"
comment: >-
No LICENSE file in repo. README describes it as public interface
for proprietary sensor library.
boards/modalai/voxl2/src/lib/mpa/libmodal-json:
license: "LGPL-3.0-only"
comment: "LGPL-3.0 weak copyleft. Used via header includes in VOXL2 mpa library."
boards/modalai/voxl2/src/lib/mpa/libmodal-pipe:
license: "LGPL-3.0-only"
comment: "LGPL-3.0 weak copyleft. Used via header includes in VOXL2 mpa library."
+1 -7
View File
@@ -1,9 +1,8 @@
#!/usr/bin/env bash
#!/bin/bash
mkdir artifacts
cp **/**/*.px4 artifacts/ 2>/dev/null || true
cp **/**/*.elf artifacts/ 2>/dev/null || true
cp **/**/*.deb artifacts/ 2>/dev/null || true
for build_dir_path in build/*/ ; do
build_dir_path=${build_dir_path::${#build_dir_path}-1}
build_dir=${build_dir_path#*/}
@@ -29,11 +28,6 @@ for build_dir_path in build/*/ ; do
# Events
mkdir -p artifacts/$build_dir/events/
cp $build_dir_path/events/all_events.json.xz artifacts/$build_dir/events/ 2>/dev/null || true
# Also copy to top level: firmware advertises the metadata URI without the events/ subdirectory
# (see src/lib/component_information/CMakeLists.txt comp_metadata_events_uri_board)
cp $build_dir_path/events/all_events.json.xz artifacts/$build_dir/ 2>/dev/null || true
# SBOM
cp $build_dir_path/*.sbom.spdx.json artifacts/$build_dir/ 2>/dev/null || true
ls -la artifacts/$build_dir
echo "----------"
done
-147
View File
@@ -1,147 +0,0 @@
#!/usr/bin/env python3
"""
Run clang-tidy incrementally on files changed in a PR.
Usage: run-clang-tidy-pr.py <base-ref>
base-ref: e.g. origin/main
Computes the set of translation units (TUs) affected by the PR diff,
then invokes Tools/run-clang-tidy.py on that subset only.
Exits 0 silently when no C++ files were changed.
"""
import argparse
import json
import os
import subprocess
import sys
EXTENSIONS_CPP = {'.cpp', '.c'}
EXTENSIONS_HDR = {'.hpp', '.h'}
# Manual exclusions from Makefile:508
EXCLUDE_EXTRA = '|'.join([
'src/systemcmds/tests',
'src/examples',
'src/modules/gyro_fft/CMSIS_5',
'src/lib/drivers/smbus',
'src/drivers/gpio',
r'src/modules/commander/failsafe/emscripten',
r'failsafe_test\.dir',
])
def repo_root():
try:
return subprocess.check_output(
['git', 'rev-parse', '--show-toplevel'], text=True).strip()
except subprocess.CalledProcessError:
print('error: not inside a git repository', file=sys.stderr)
sys.exit(1)
def changed_files(base_ref, root):
try:
out = subprocess.check_output(
['git', 'diff', '--name-only', f'{base_ref}...HEAD',
'--', '*.cpp', '*.hpp', '*.h', '*.c'],
text=True, cwd=root).strip()
return out.splitlines() if out else []
except subprocess.CalledProcessError:
print(f'error: could not diff against "{base_ref}"'
'is the ref valid and fetched?', file=sys.stderr)
sys.exit(1)
def submodule_paths(root):
# Returns [] if .gitmodules is absent or has no paths — both valid
try:
out = subprocess.check_output(
['git', 'config', '--file', '.gitmodules',
'--get-regexp', 'path'],
text=True, cwd=root).strip()
return [line.split()[1] for line in out.splitlines()]
except subprocess.CalledProcessError:
return []
def build_exclude(root):
submodules = '|'.join(submodule_paths(root))
return f'{submodules}|{EXCLUDE_EXTRA}' if submodules else EXCLUDE_EXTRA
def load_db(build_dir):
db_path = os.path.join(build_dir, 'compile_commands.json')
if not os.path.isfile(db_path):
print(f'error: {db_path} not found', file=sys.stderr)
print('Run "make px4_sitl_default-clang" first to generate '
'the compilation database', file=sys.stderr)
sys.exit(1)
try:
with open(db_path) as f:
return json.load(f)
except json.JSONDecodeError as e:
print(f'error: compile_commands.json is malformed: {e}', file=sys.stderr)
sys.exit(1)
def find_tus(changed, db, root):
db_files = {e['file'] for e in db}
result = set()
for f in changed:
abs_path = os.path.join(root, f)
ext = os.path.splitext(f)[1]
if ext in EXTENSIONS_CPP:
if abs_path in db_files:
result.add(abs_path)
elif ext in EXTENSIONS_HDR:
hdr = os.path.basename(f)
for e in db:
try:
if hdr in open(e['file']).read():
result.add(e['file'])
except OSError:
pass # file deleted in PR — skip
return sorted(result)
def main():
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('base_ref',
help='Git ref to diff against, e.g. origin/main')
args = parser.parse_args()
root = repo_root()
build_dir = os.path.join(root, 'build', 'px4_sitl_default-clang')
run_tidy = os.path.join(root, 'Tools', 'run-clang-tidy.py')
if not os.path.isfile(run_tidy):
print(f'error: {run_tidy} not found', file=sys.stderr)
sys.exit(1)
changed = changed_files(args.base_ref, root)
if not changed:
print('No C++ files changed — skipping clang-tidy')
sys.exit(0)
db = load_db(build_dir)
tus = find_tus(changed, db, root)
if not tus:
print('No matching TUs in compile_commands.json — skipping clang-tidy')
sys.exit(0)
print(f'Running clang-tidy on {len(tus)} translation unit(s)')
result = subprocess.run(
[sys.executable, run_tidy,
'-header-filter=.*\\.hpp',
'-j0',
f'-exclude={build_exclude(root)}',
'-p', build_dir] + tus
)
sys.exit(result.returncode)
if __name__ == '__main__':
main()
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# This script runs the fuzz tests from a given binary for a certain amount of time
set -e
-42
View File
@@ -1,42 +0,0 @@
#!/bin/sh
# Rewrite the container's apt sources to point at the AWS regional Ubuntu
# mirror that is local to the runs-on instance.
#
# The default archive.ubuntu.com round-robin sometimes serves out-of-sync
# index files mid-sync, breaking apt-get update with errors like:
# File has unexpected size (25378 != 25381). Mirror sync in progress?
# The Canonical-operated EC2 mirrors are region-local and sync aggressively,
# eliminating that failure mode.
#
# This script is a no-op outside runs-on, so it is safe to call from any CI
# job (forks, self-hosted runners, local docker runs, etc.) without changing
# behavior there.
#
# Usage (from a workflow step running inside the container):
# ./Tools/ci/use_aws_apt_mirror.sh
set -e
if [ -z "$RUNS_ON_AWS_REGION" ]; then
echo "use_aws_apt_mirror: not running on runs-on (RUNS_ON_AWS_REGION unset), skipping"
exit 0
fi
MIRROR="http://${RUNS_ON_AWS_REGION}.ec2.archive.ubuntu.com/ubuntu"
echo "use_aws_apt_mirror: rewriting apt sources to ${MIRROR}"
# Noble (24.04+) uses the deb822 format at /etc/apt/sources.list.d/ubuntu.sources
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
sed -i \
-e "s|http://archive.ubuntu.com/ubuntu|${MIRROR}|g" \
-e "s|http://security.ubuntu.com/ubuntu|${MIRROR}|g" \
/etc/apt/sources.list.d/ubuntu.sources
fi
# Jammy (22.04) and earlier use the legacy /etc/apt/sources.list
if [ -f /etc/apt/sources.list ]; then
sed -i \
-e "s|http://archive.ubuntu.com/ubuntu|${MIRROR}|g" \
-e "s|http://security.ubuntu.com/ubuntu|${MIRROR}|g" \
/etc/apt/sources.list
fi
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#! /bin/bash
# Copy msgs and the message translation node into a ROS workspace directory
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#! /bin/bash
if [ -z ${PX4_DOCKER_REPO+x} ]; then
PX4_DOCKER_REPO="px4io/px4-dev:v1.17.0-beta1"
+3 -13
View File
@@ -128,9 +128,6 @@ class SourceParser:
# start waiting for the next part - long comment.
if state == "wait-short-end":
state = "wait-long"
elif state == "wait-long-end":
# Preserve paragraph breaks in long description
long_desc += "\n"
else:
m = self.re_comment_tag.match(comment_content)
if m:
@@ -211,7 +208,8 @@ class SourceParser:
raise Exception('short description too long (150 max, is {:}, parameter: {:})'.format(len(short_desc), name))
param.fields["short_desc"] = self.re_remove_dots.sub('', short_desc)
if long_desc is not None:
param.fields["long_desc"] = long_desc.rstrip('\n')
long_desc = self.re_remove_carriage_return.sub(' ', long_desc)
param.fields["long_desc"] = long_desc
for tag in tags:
if tag == "group":
group = tags[tag]
@@ -409,15 +407,7 @@ def generate_yaml(filename: str, groups: list[ParameterGroup]) -> str:
g["definitions"][parameter.name] = p
data["parameters"].append(g)
# Use block scalar style for multi-line strings
class BlockStyleDumper(yaml.SafeDumper):
pass
def str_representer(dumper, data):
if '\n' in data:
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
return dumper.represent_scalar('tag:yaml.org,2002:str', data)
BlockStyleDumper.add_representer(str, str_representer)
return yaml.dump(data, Dumper=BlockStyleDumper, sort_keys=False)
return yaml.dump(data, sort_keys=False)
def main():
+1 -4
View File
@@ -108,7 +108,7 @@ def parse_yaml_parameters_config(yaml_config, ethernet_supported):
tags = '@group {:}'.format(param_group)
if param['type'] == 'enum':
param_type = 'INT32'
for key in sorted(param['values'], key=float):
for key in param['values']:
tags += '\n * @value {:} {:}'.format(key, param['values'][key])
elif param['type'] == 'bitmask':
param_type = 'INT32'
@@ -124,9 +124,6 @@ def parse_yaml_parameters_config(yaml_config, ethernet_supported):
param_type = 'INT32'
elif param['type'] == 'float':
param_type = 'FLOAT'
if 'values' in param:
for key in sorted(param['values'], key=float):
tags += '\n * @value {:} {:}'.format(key, param['values'][key])
else:
raise Exception("unknown param type {:}".format(param['type']))
+1 -3
View File
@@ -316,9 +316,7 @@ Param | Units | Range/Enum | Description
if val.minValue or val.maxValue:
rangeVal = f"[{val.minValue if val.minValue else '-'} : {val.maxValue if val.maxValue else '-' }]"
units_str = ", ".join(val.units)
enums_str = ', '.join("[{}](#{})".format(e, e) for e in val.enums)
output+=f"{i} | {units_str}|{enums_str}{rangeVal} | {val.description}\n"
output+=f"{i} | {", ".join(val.units)}|{', '.join(f"[{e}](#{e})" for e in val.enums)}{rangeVal} | {val.description}\n"
else:
output+=f"{i} | | | ?\n"
-84
View File
@@ -1,84 +0,0 @@
# syntax=docker/dockerfile:1
# PX4 SITL Gazebo Harmonic runtime image
# Runs PX4 SITL with Gazebo Harmonic. Supports X11 forwarding for GUI.
#
# Build:
# make px4_sitl_default && cd build/px4_sitl_default && cpack -G DEB && cd ../..
# docker build -f Tools/packaging/Dockerfile.gazebo -t px4io/px4-sitl-gazebo:v1.17.0 build/px4_sitl_default/
#
# Run (headless):
# docker run --rm -it --network host px4io/px4-sitl-gazebo:v1.17.0
#
# Run (X11 GUI):
# xhost +local:docker
# docker run --rm -it --network host \
# -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \
# --gpus all px4io/px4-sitl-gazebo:v1.17.0
FROM ubuntu:24.04 AS extract
COPY px4-gazebo_*.deb /tmp/
RUN apt-get update && apt-get install -y --no-install-recommends binutils \
&& dpkg -x /tmp/px4-gazebo_*.deb /staging \
&& strip /staging/opt/px4-gazebo/bin/px4 \
&& rm -rf /var/lib/apt/lists/*
FROM ubuntu:24.04
LABEL maintainer="PX4 Development Team"
LABEL description="PX4 SITL with Gazebo Harmonic simulation"
ENV DEBIAN_FRONTEND=noninteractive
ENV RUNS_IN_DOCKER=true
# Install Gazebo Harmonic with buildkit cache mounts for apt
# The --mount=type=cache persists /var/cache/apt and /var/lib/apt across builds
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends \
bc \
ca-certificates \
gnupg \
lsb-release \
wget \
&& wget -q https://packages.osrfoundation.org/gazebo.gpg \
-O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" \
> /etc/apt/sources.list.d/gazebo-stable.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
gz-harmonic
# Install PX4 files from .deb
COPY --from=extract /staging/opt/px4-gazebo /opt/px4-gazebo
RUN ln -sf /opt/px4-gazebo/bin/px4-gazebo /usr/bin/px4-gazebo
# Create the DART physics engine symlink (avoids needing the -dev package)
RUN GZ_PHYSICS_DIR=$(find /usr/lib -maxdepth 3 -type d -name "engine-plugins" -path "*/gz-physics-7/*" 2>/dev/null | head -1) \
&& if [ -n "$GZ_PHYSICS_DIR" ] && [ -d "$GZ_PHYSICS_DIR" ]; then \
VERSIONED=$(ls "$GZ_PHYSICS_DIR"/libgz-physics*-dartsim-plugin.so.* 2>/dev/null | head -1) \
&& [ -n "$VERSIONED" ] \
&& ln -sf "$(basename "$VERSIONED")" "$GZ_PHYSICS_DIR/libgz-physics-dartsim-plugin.so"; \
fi
# Gazebo resource paths
ENV GZ_SIM_RESOURCE_PATH=/opt/px4-gazebo/share/gz/models:/opt/px4-gazebo/share/gz/worlds
ENV GZ_SIM_SYSTEM_PLUGIN_PATH=/opt/px4-gazebo/lib/gz/plugins
ENV GZ_SIM_SERVER_CONFIG_PATH=/opt/px4-gazebo/share/gz/server.config
ENV PX4_GZ_MODELS=/opt/px4-gazebo/share/gz/models
ENV PX4_GZ_WORLDS=/opt/px4-gazebo/share/gz/worlds
ENV PX4_SIM_MODEL=gz_x500
ENV HOME=/root
# MAVLink, MAVSDK, DDS
EXPOSE 14550/udp 14540/udp 8888/udp
# Platform-adaptive entrypoint: detects Docker Desktop (macOS/Windows) via
# host.docker.internal and configures MAVLink + DDS to target the host.
COPY px4-entrypoint.sh /opt/px4-gazebo/bin/px4-entrypoint.sh
RUN chmod +x /opt/px4-gazebo/bin/px4-entrypoint.sh
WORKDIR /root
ENTRYPOINT ["/opt/px4-gazebo/bin/px4-entrypoint.sh"]
CMD []
-49
View File
@@ -1,49 +0,0 @@
# syntax=docker/dockerfile:1
# PX4 SITL SIH runtime image
# Minimal container that runs PX4 with the SIH physics engine (no Gazebo).
#
# Build:
# make px4_sitl_sih && cd build/px4_sitl_sih && cpack -G DEB && cd ../..
# docker build -f Tools/packaging/Dockerfile.sih -t px4io/px4-sitl:v1.17.0 build/px4_sitl_sih/
#
# Run (Linux):
# docker run --rm -it --network host px4io/px4-sitl:v1.17.0
#
# Run (macOS / Windows):
# docker run --rm -it -p 14550:14550/udp -p 14540:14540/udp -p 19410:19410/udp -p 8888:8888/udp px4io/px4-sitl:v1.17.0
FROM ubuntu:24.04 AS build
COPY px4_*.deb /tmp/
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends binutils \
&& dpkg -x /tmp/px4_*.deb /staging \
&& strip /staging/opt/px4/bin/px4
FROM ubuntu:24.04
LABEL maintainer="PX4 Development Team"
LABEL description="PX4 SITL with SIH physics (no simulator dependencies)"
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends bc
COPY --from=build /staging/opt/px4 /opt/px4
RUN ln -sf /opt/px4/bin/px4 /usr/bin/px4
# Platform-adaptive entrypoint: detects Docker Desktop (macOS/Windows) via
# host.docker.internal and configures MAVLink + DDS to target the host.
COPY px4-entrypoint.sh /opt/px4/bin/px4-entrypoint.sh
RUN chmod +x /opt/px4/bin/px4-entrypoint.sh
ENV PX4_SIM_MODEL=sihsim_quadx
ENV HOME=/root
# MAVLink (QGC, MAVSDK), DDS (ROS 2), jMAVSim/viewer display
EXPOSE 14550/udp 14540/udp 19410/udp 8888/udp
WORKDIR /root
ENTRYPOINT ["/opt/px4/bin/px4-entrypoint.sh"]
CMD []
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
set -e
ln -sf /opt/px4-gazebo/bin/px4-gazebo /usr/bin/px4-gazebo
exit 0
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
set -e
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
rm -f /usr/bin/px4-gazebo
fi
exit 0
-35
View File
@@ -1,35 +0,0 @@
#!/bin/sh
# Docker entrypoint for PX4 SITL containers.
#
# On Docker Desktop (macOS/Windows), host.docker.internal resolves to the
# host machine. We detect this and configure MAVLink + DDS to send to the
# host IP instead of localhost (which stays inside the container VM).
#
# On Linux with --network host, host.docker.internal does not resolve and
# PX4 defaults work without modification.
set -e
# Detect install prefix (SIH uses /opt/px4, Gazebo uses /opt/px4-gazebo)
if [ -d /opt/px4-gazebo ]; then
PX4_PREFIX=/opt/px4-gazebo
else
PX4_PREFIX=/opt/px4
fi
# Resolve host.docker.internal to an IPv4 address. mavlink and uxrce_dds_client
# only parse IPv4, and on Docker Desktop for Windows the default `getent hosts`
# lookup can return an IPv6 ULA first, which both modules then reject.
DOCKER_HOST_IP=$(getent ahostsv4 host.docker.internal 2>/dev/null | awk '/STREAM/ {print $1; exit}')
if [ -n "$DOCKER_HOST_IP" ]; then
# MAVLink: replace default target (127.0.0.1) with the Docker host IP
sed -i "s/mavlink start -x -u/mavlink start -x -t $DOCKER_HOST_IP -u/g" \
"$PX4_PREFIX/etc/init.d-posix/px4-rc.mavlink"
# DDS: point uXRCE-DDS client at the host
sed -i "s|uxrce_dds_client start -t udp|uxrce_dds_client start -t udp -h $DOCKER_HOST_IP|" \
"$PX4_PREFIX/etc/init.d-posix/rcS"
fi
exec "$PX4_PREFIX/bin/px4" "$@"
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env bash
# px4-gazebo: Launch PX4 SITL with Gazebo from the installed .deb package
set -e
PX4_GAZEBO_DIR="$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)"
PX4_BINARY="${PX4_GAZEBO_DIR}/bin/px4"
# Set Gazebo resource paths so gz-sim finds PX4 models, worlds, and plugins.
export PX4_GZ_MODELS="${PX4_GAZEBO_DIR}/share/gz/models"
export PX4_GZ_WORLDS="${PX4_GAZEBO_DIR}/share/gz/worlds"
export PX4_GZ_PLUGINS="${PX4_GAZEBO_DIR}/lib/gz/plugins"
export PX4_GZ_SERVER_CONFIG="${PX4_GAZEBO_DIR}/share/gz/server.config"
export GZ_SIM_RESOURCE_PATH="${GZ_SIM_RESOURCE_PATH}:${PX4_GZ_MODELS}:${PX4_GZ_WORLDS}"
export GZ_SIM_SYSTEM_PLUGIN_PATH="${GZ_SIM_SYSTEM_PLUGIN_PATH}:${PX4_GZ_PLUGINS}"
export GZ_SIM_SERVER_CONFIG_PATH="${PX4_GZ_SERVER_CONFIG}"
# Gazebo's Physics system searches for "gz-physics-dartsim-plugin" which maps
# to the unversioned libgz-physics-dartsim-plugin.so. The runtime package only
# ships versioned .so files; the unversioned symlink lives in the -dev package.
# Create it if missing so Gazebo finds the DART engine without installing -dev.
GZ_PHYSICS_ENGINE_DIR=$(find /usr/lib -maxdepth 3 -type d -name "engine-plugins" -path "*/gz-physics-7/*" 2>/dev/null | head -1)
if [ -n "$GZ_PHYSICS_ENGINE_DIR" ] && [ -d "$GZ_PHYSICS_ENGINE_DIR" ]; then
UNVERSIONED="$GZ_PHYSICS_ENGINE_DIR/libgz-physics-dartsim-plugin.so"
if [ ! -e "$UNVERSIONED" ]; then
VERSIONED=$(ls "$GZ_PHYSICS_ENGINE_DIR"/libgz-physics*-dartsim-plugin.so.* 2>/dev/null | head -1)
if [ -n "$VERSIONED" ]; then
ln -sf "$(basename "$VERSIONED")" "$UNVERSIONED" 2>/dev/null || true
fi
fi
fi
exec "${PX4_BINARY}" "$@"
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
set -e
ln -sf /opt/px4/bin/px4 /usr/bin/px4
exit 0
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
set -e
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
rm -f /usr/bin/px4
fi
exit 0
-144
View File
@@ -1,144 +0,0 @@
#!/usr/bin/env python3
"""
MAVSDK mission test for PX4 SIH SITL in Docker.
Takes off to 100m, flies a short 4-waypoint box mission, then lands.
Validates that the SIH Docker container works end-to-end with MAVSDK.
Prerequisites:
- Docker container running:
docker run --rm --network host px4io/px4-sitl:v1.17.0-alpha1
- pip install mavsdk
- mavsim-viewer running (optional):
/path/to/mavsim-viewer -n 1
Usage:
python3 Tools/packaging/test_sih_mission.py
python3 Tools/packaging/test_sih_mission.py --speed 10 # faster-than-realtime
"""
import asyncio
import argparse
import sys
import time
from mavsdk import System
from mavsdk.mission import MissionItem, MissionPlan
async def run_mission(speed_factor: int = 1):
drone = System()
print(f"Connecting to drone on udp://:14540 ...")
await drone.connect(system_address="udp://:14540")
print("Waiting for drone to connect...")
async for state in drone.core.connection_state():
if state.is_connected:
print(f"Connected (UUID: {state.uuid if hasattr(state, 'uuid') else 'N/A'})")
break
print("Waiting for global position estimate...")
async for health in drone.telemetry.health():
if health.is_global_position_ok and health.is_home_position_ok:
print("Global position OK")
break
# Get home position for reference
async for pos in drone.telemetry.position():
home_lat = pos.latitude_deg
home_lon = pos.longitude_deg
print(f"Home position: {home_lat:.6f}, {home_lon:.6f}")
break
# Build a small box mission at 100m AGL
# ~100m offset in each direction
offset = 0.001 # roughly 111m at equator
mission_items = [
MissionItem(
home_lat + offset, home_lon,
100, 10, True, float('nan'), float('nan'),
MissionItem.CameraAction.NONE,
float('nan'), float('nan'), float('nan'),
float('nan'), float('nan'),
MissionItem.VehicleAction.NONE,
),
MissionItem(
home_lat + offset, home_lon + offset,
100, 10, True, float('nan'), float('nan'),
MissionItem.CameraAction.NONE,
float('nan'), float('nan'), float('nan'),
float('nan'), float('nan'),
MissionItem.VehicleAction.NONE,
),
MissionItem(
home_lat, home_lon + offset,
100, 10, True, float('nan'), float('nan'),
MissionItem.CameraAction.NONE,
float('nan'), float('nan'), float('nan'),
float('nan'), float('nan'),
MissionItem.VehicleAction.NONE,
),
MissionItem(
home_lat, home_lon,
100, 10, True, float('nan'), float('nan'),
MissionItem.CameraAction.NONE,
float('nan'), float('nan'), float('nan'),
float('nan'), float('nan'),
MissionItem.VehicleAction.NONE,
),
]
mission_plan = MissionPlan(mission_items)
print(f"Uploading mission ({len(mission_items)} waypoints, 100m AGL)...")
await drone.mission.upload_mission(mission_plan)
print("Mission uploaded")
print("Arming...")
await drone.action.arm()
print("Armed")
t0 = time.time()
print("Starting mission...")
await drone.mission.start_mission()
# Monitor mission progress
async for progress in drone.mission.mission_progress():
elapsed = time.time() - t0
print(f" [{elapsed:6.1f}s] Waypoint {progress.current}/{progress.total}")
if progress.current == progress.total:
print(f"Mission complete in {elapsed:.1f}s (speed factor: {speed_factor}x)")
break
print("Returning to launch...")
await drone.action.return_to_launch()
# Wait for landing
async for in_air in drone.telemetry.in_air():
if not in_air:
print("Landed")
break
print("Disarming...")
await drone.action.disarm()
print("Test PASSED")
def main():
parser = argparse.ArgumentParser(description="PX4 SIH Docker mission test")
parser.add_argument("--speed", type=int, default=1,
help="PX4_SIM_SPEED_FACTOR (must match container)")
args = parser.parse_args()
try:
asyncio.run(run_mission(args.speed))
except KeyboardInterrupt:
print("\nInterrupted")
sys.exit(1)
except Exception as e:
print(f"Test FAILED: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# Script to run ShellCheck (a static analysis tool for shell scripts) over a
# script directory
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
GREEN='\033[0;32m'
NO_COLOR='\033[0m' # No Color
-7
View File
@@ -79,13 +79,6 @@ if [[ $INSTALL_SIM == "--sim-tools" ]]; then
elif [[ $REINSTALL_FORMULAS == "--reinstall" ]]; then
brew reinstall px4-sim
fi
# jMAVSim requires a JDK (Java 17 LTS recommended)
if ! brew ls --versions openjdk@17 > /dev/null; then
echo "[macos.sh] Installing OpenJDK 17 (required for jMAVSim)"
brew install openjdk@17
sudo ln -sfn $(brew --prefix openjdk@17)/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk
fi
fi
echo "[macos.sh] All set! The PX4 Autopilot toolchain was installed."
+1 -1
View File
@@ -23,7 +23,7 @@ pyserial
pyulog>=0.5.0
pyyaml
requests
setuptools>=39.2.0,<=81.0.0
setuptools>=39.2.0
six>=1.12.0
sympy>=1.10.1
toml>=0.9
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
#
# Setup environment to make PX4 visible to Gazebo.
#
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# run multiple instances of the 'px4' binary, with the gazebo SITL simulation
# It assumes px4 is already built, with 'make px4_sitl_default sitl_gazebo-classic'
+11 -14
View File
@@ -1,28 +1,25 @@
#!/usr/bin/env bash
# Run multiple instances of the 'px4' binary, without starting an external simulator.
# It assumes px4 is already built with the specified build target.
#
# Usage: ./Tools/simulation/sitl_multiple_run.sh [num_instances] [model] [build_target]
# Examples:
# ./Tools/simulation/sitl_multiple_run.sh 3 sihsim_quadx px4_sitl_sih
# ./Tools/simulation/sitl_multiple_run.sh 2 gazebo-classic_iris px4_sitl_default
# ./Tools/simulation/sitl_multiple_run.sh # defaults: 2 instances, gazebo-classic_iris, px4_sitl_default
#!/bin/bash
# run multiple instances of the 'px4' binary, but w/o starting the simulator.
# It assumes px4 is already built, with 'make px4_sitl_default'
sitl_num=${1:-2}
sim_model=${2:-gazebo-classic_iris}
build_target=${3:-px4_sitl_default}
# The simulator is expected to send to TCP port 4560+i for i in [0, N-1]
# For example jmavsim can be run like this:
#./Tools/simulation/jmavsim/jmavsim_run.sh -p 4561 -l
sitl_num=2
[ -n "$1" ] && sitl_num="$1"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
src_path="$SCRIPT_DIR/../../"
build_path=${src_path}/build/${build_target}
build_path=${src_path}/build/px4_sitl_default
echo "killing running instances"
pkill -x px4 || true
sleep 1
export PX4_SIM_MODEL=${sim_model}
export PX4_SIM_MODEL=gazebo-classic_iris
n=0
while [ $n -lt $sitl_num ]; do
@@ -42,8 +42,6 @@ px4_add_module(
syslink_bridge.cpp
syslink_memory.cpp
syslink.c
MODULE_CONFIG
syslink_params.yaml
DEPENDS
battery
)
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2026 PX4 Development Team. All rights reserved.
* Copyright (c) 2016 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,8 @@
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the distribution.
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
@@ -30,39 +31,42 @@
*
****************************************************************************/
#include "rallyPointCheck.hpp"
/**
* @file syslink_params.c
*
* Parameters defined by the syslink module and the exposed NRF51 radio
*
* @author Dennis Shtatnov <densht@gmail.com>
*/
RallyPointChecks::RallyPointChecks()
: _param_rtl_type_handle(param_find("RTL_TYPE"))
{
}
/**
* Operating channel of the NRF51
*
* @min 0
* @max 125
* @group Syslink
*/
PARAM_DEFINE_INT32(SLNK_RADIO_CHAN, 80);
void RallyPointChecks::checkAndReport(const Context &context, Report &reporter)
{
int32_t rtl_type = 0;
/**
* Operating datarate of the NRF51
*
* @min 0
* @max 2
* @group Syslink
*/
PARAM_DEFINE_INT32(SLNK_RADIO_RATE, 2);
if (param_get(_param_rtl_type_handle, &rtl_type) != 0 || rtl_type != 5) {
// Only enforce rally point requirement when RTL_TYPE == 5 (safe points only)
return;
}
/**
* Operating address of the NRF51 (most significant byte)
*
* @group Syslink
*/
PARAM_DEFINE_INT32(SLNK_RADIO_ADDR1, 231); // 0xE7
rtl_status_s rtl_status;
if (!_rtl_status_sub.copy(&rtl_status) || rtl_status.safe_point_index == UINT8_MAX) {
/* EVENT
* @description
* Upload at least one rally point before arming, or change <param>RTL_TYPE</param>.
*
* <profile name="dev">
* This check is active when RTL_TYPE is set to 5 (safe points only).
* </profile>
*/
reporter.armingCheckFailure(NavModes::All, health_component_t::system,
events::ID("check_rally_point_missing"),
events::Log::Error, "No rally point available");
if (reporter.mavlink_log_pub()) {
mavlink_log_critical(reporter.mavlink_log_pub(), "Preflight Fail: No rally point available\t");
}
}
}
/**
* Operating address of the NRF51 (least significant 4 bytes)
*
* @group Syslink
*/
PARAM_DEFINE_INT32(SLNK_RADIO_ADDR2, 3890735079); // 0xE7E7E7E7
@@ -1,28 +0,0 @@
module_name: syslink
parameters:
- group: Syslink
definitions:
SLNK_RADIO_CHAN:
description:
short: Operating channel of the NRF51
type: int32
default: 80
min: 0
max: 125
SLNK_RADIO_RATE:
description:
short: Operating datarate of the NRF51
type: int32
default: 2
min: 0
max: 2
SLNK_RADIO_ADDR1:
description:
short: Operating address of the NRF51 (most significant byte)
type: int32
default: 231
SLNK_RADIO_ADDR2:
description:
short: Operating address of the NRF51 (least significant 4 bytes)
type: int32
default: -404232217
Binary file not shown.
+1 -1
View File
@@ -104,7 +104,7 @@
#define OSC_FREQ 8
#define BOARD_PIN_LED_ACTIVITY GPIO_nLED_BLUE // BLUE
#define BOARD_PIN_LED_BOOTLOADER GPIO_nLED_RED // RED
#define BOARD_PIN_LED_BOOTLOADER GPIO_nLED_GREEN // GREEN
#define BOARD_LED_ON 0
#define BOARD_LED_OFF 1
-1
View File
@@ -169,7 +169,6 @@ __EXPORT int board_app_initialize(uintptr_t arg)
drv_led_start();
led_off(LED_RED);
led_off(LED_BLUE);
led_off(LED_GREEN);
if (board_hardfault_init(2, true) != 0) {
led_on(LED_BLUE);
+3 -22
View File
@@ -63,23 +63,12 @@ extern void led_toggle(int led);
__END_DECLS
# define xlat(p) (p)
// index: 0=BLUE, 1=RED, 2=SAFETY, 3=GREEN
static uint32_t g_ledmap[] = {
GPIO_nLED_BLUE, // LED_BLUE (0)
GPIO_nLED_RED, // LED_RED (1)
0, // LED_SAFETY (2) - no independent safety LED, use 0 placeholder
GPIO_nLED_GREEN, // LED_GREEN (3)
GPIO_nLED_GREEN, // Indexed by BOARD_LED_GREEN
GPIO_nLED_BLUE, // Indexed by BOARD_LED_BLUE
GPIO_nLED_RED, // Indexed by BOARD_LED_RED
};
#ifndef arraySize
#define arraySize(a) (sizeof((a))/sizeof(((a)[0])))
#endif
static inline bool valid_led_index(int led)
{
return (led >= 0) && ((size_t)led < arraySize(g_ledmap));
}
__EXPORT void led_init(void)
{
/* Configure LED GPIOs for output */
@@ -92,10 +81,6 @@ __EXPORT void led_init(void)
static void phy_set_led(int led, bool state)
{
if (!valid_led_index(led)) {
return;
}
/* Drive Low to switch on */
if (g_ledmap[led] != 0) {
stm32_gpiowrite(g_ledmap[led], !state);
@@ -104,10 +89,6 @@ static void phy_set_led(int led, bool state)
static bool phy_get_led(int led)
{
if (!valid_led_index(led)) {
return false;
}
/* If Low it is on */
if (g_ledmap[led] != 0) {
return !stm32_gpioread(g_ledmap[led]);
@@ -37,8 +37,6 @@ px4_add_module(
SRCS
pwm_voltage.cpp
MODULE_CONFIG
parameters.yaml
DEPENDS
px4_work_queue
)
@@ -0,0 +1,44 @@
/****************************************************************************
*
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* Control PWM output voltage
*
* Enable: PWM output voltage 5V
* Disable: PWM output voltage 3.3V
*
* @boolean
* @reboot_required true
* @group PWM Outputs
*/
PARAM_DEFINE_INT32(PWM_LEVEL_CONT, 0);
@@ -1,13 +0,0 @@
module_name: pwm_voltage
parameters:
- group: PWM Outputs
definitions:
PWM_LEVEL_CONT:
description:
short: Control PWM output voltage
long: |-
Enable: PWM output voltage 5V
Disable: PWM output voltage 3.3V
type: boolean
default: 0
reboot_required: true
@@ -37,8 +37,6 @@ px4_add_module(
SRCS
pwm_voltage.cpp
MODULE_CONFIG
parameters.yaml
DEPENDS
px4_work_queue
)
@@ -0,0 +1,43 @@
/****************************************************************************
*
* Copyright (c) 2025 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* Control PWM output voltage
*
* @value 0 3.3V
* @value 1 5.0V
*
* @reboot_required true
* @group PWM Outputs
*/
PARAM_DEFINE_INT32(PWM_VOLT_SEL, 0);
@@ -1,13 +0,0 @@
module_name: pwm_voltage
parameters:
- group: PWM Outputs
definitions:
PWM_VOLT_SEL:
description:
short: Control PWM output voltage
type: enum
values:
0: 3.3V
1: 5.0V
default: 0
reboot_required: true
@@ -37,8 +37,6 @@ px4_add_module(
SRCS
pwm_voltage.cpp
MODULE_CONFIG
parameters.yaml
DEPENDS
px4_work_queue
)
@@ -0,0 +1,43 @@
/****************************************************************************
*
* Copyright (c) 2025 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* Control PWM output voltage
*
* @value 0 3.3V
* @value 1 5.0V
*
* @reboot_required true
* @group PWM Outputs
*/
PARAM_DEFINE_INT32(PWM_VOLT_SEL, 0);
@@ -1,13 +0,0 @@
module_name: pwm_voltage
parameters:
- group: PWM Outputs
definitions:
PWM_VOLT_SEL:
description:
short: Control PWM output voltage
type: enum
values:
0: 3.3V
1: 5.0V
default: 0
reboot_required: true
@@ -37,8 +37,6 @@ px4_add_module(
SRCS
pwm_voltage.cpp
MODULE_CONFIG
parameters.yaml
DEPENDS
px4_work_queue
)
@@ -10,7 +10,8 @@
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the distribution.
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
@@ -30,24 +31,13 @@
*
****************************************************************************/
#pragma once
#include "../Common.hpp"
#include <uORB/Subscription.hpp>
#include <uORB/topics/rtl_status.h>
#include <parameters/param.h>
class RallyPointChecks : public HealthAndArmingCheckBase
{
public:
RallyPointChecks();
~RallyPointChecks() = default;
void checkAndReport(const Context &context, Report &reporter) override;
private:
uORB::Subscription _rtl_status_sub{ORB_ID(rtl_status)};
const param_t _param_rtl_type_handle;
};
/**
* Control PWM output voltage
*
* @value 0 3.3V
* @value 1 5.0V
*
* @reboot_required true
* @group PWM Outputs
*/
PARAM_DEFINE_INT32(PWM_VOLT_SEL, 0);

Some files were not shown because too many files have changed in this diff Show More