{"id":50400350,"url":"https://github.com/px4/flight-review-rs","last_synced_at":"2026-05-30T23:03:10.222Z","repository":{"id":348108571,"uuid":"1195592763","full_name":"PX4/flight-review-rs","owner":"PX4","description":"Flight Review v2 — ULog to Parquet conversion and metadata extraction","archived":false,"fork":false,"pushed_at":"2026-04-06T00:05:12.000Z","size":6397,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-06T01:29:36.649Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PX4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-29T21:04:01.000Z","updated_at":"2026-04-06T00:05:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/PX4/flight-review-rs","commit_stats":null,"previous_names":["mrpollo/flight-review-rs","px4/flight-review-rs"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/PX4/flight-review-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PX4%2Fflight-review-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PX4%2Fflight-review-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PX4%2Fflight-review-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PX4%2Fflight-review-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PX4","download_url":"https://codeload.github.com/PX4/flight-review-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PX4%2Fflight-review-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33712582,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T23:03:04.459Z","updated_at":"2026-05-30T23:03:10.215Z","avatar_url":"https://github.com/PX4.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flight Review v2\n\n## Table of Contents\n\n- [Introduction](#introduction)\n- [Architecture](#architecture)\n  - [Upload Workflow](#upload-workflow)\n  - [Backend Dependencies](#backend-dependencies)\n  - [Converter Crate (`flight-review`)](#converter-crate-flight-review)\n  - [Server Crate (`flight-review-server`)](#server-crate-flight-review-server)\n  - [Frontend](#frontend)\n  - [CLI Tool (`ulog-convert`)](#cli-tool-ulog-convert)\n  - [Two Paths](#two-paths)\n  - [Workspace Layout](#workspace-layout)\n  - [What Gets Stored Per Log](#what-gets-stored-per-log)\n  - [API](#api)\n- [Tech Stack](#tech-stack)\n- [Build](#build)\n  - [Development](#development)\n  - [Seeding Data](#seeding-data)\n  - [Release Build](#release-build)\n  - [Feature Flags](#feature-flags)\n  - [Database Support](#database-support)\n  - [Storage Support](#storage-support)\n- [Deploy](#deploy)\n  - [Minimal (single binary)](#minimal-single-binary)\n  - [Docker](#docker)\n  - [Production (AWS)](#production-aws)\n- [Migrate from v1](#migrate-from-v1)\n- [CLI](#cli)\n- [Upload Context Fields](#upload-context-fields)\n- [Diagnostics](#diagnostics)\n  - [Available Analyzers](#available-analyzers)\n  - [Adding a New Analyzer](#adding-a-new-analyzer)\n- [Roadmap](#roadmap)\n- [License](#license)\n\n## Introduction\n\nFlight Review v2 is a complete rewrite of [PX4 Flight Review](https://github.com/PX4/flight_review) in Rust. It replaces the \"parse every time you view\" model with a **parse-once-store-review** architecture: ULog files are converted to per-topic [Parquet](https://parquet.apache.org/) files and a rich metadata JSON at upload time, then served as static files for client-side analysis via [DuckDB](https://duckdb.org/)-WASM. The frontend is a SvelteKit single-page application that queries Parquet files directly via DuckDB-WASM in the browser. The result is sub-second log viewing with zero server-side compute, support for SQLite or Postgres, and local filesystem or S3 storage -- deployable as a single binary or a Docker container.\n\n## Architecture\n\n### Upload Workflow\n\n```\nUpload .ulg --\u003e Rust converter --\u003e Per-topic Parquet + metadata.json --\u003e Storage\n```\n\nAt upload time the server parses the ULog file once, writes compressed Parquet files (one per topic) and a `metadata.json` containing all extracted metadata and flight analysis results. From that point on the browser queries Parquet directly via DuckDB-WASM and HTTP Range requests -- the server never re-parses the log.\n\n### Backend Dependencies\n\nThe converter and server are built on these key libraries:\n\n- [px4-ulog-rs](https://github.com/Auterion/px4-ulog-rs) (Auterion) -- streaming ULog parser\n- [Apache Arrow](https://arrow.apache.org/) / [Parquet](https://parquet.apache.org/) -- columnar format and serialization\n- [rustfft](https://github.com/LabBros/rustfft) -- FFT for PID analysis\n\nOn top of these, the workspace provides two crates: `flight-review` (converter library + CLI) and `flight-review-server` (HTTP API).\n\n### Converter Crate (`flight-review`)\n\nThe converter library handles all ULog processing:\n\n- ULog parsing via px4-ulog-rs\n- Per-topic Parquet conversion with ZSTD compression\n- Metadata extraction (all 13 ULog message types)\n- Flight analysis (modes, stats, battery, GPS quality, vibration, param diff, GPS track)\n- Diagnostic analyzers (motor failure, GPS interference, battery brownout, EKF failure, RC loss)\n- PID step response analysis (Wiener deconvolution)\n\n### Server Crate (`flight-review-server`)\n\nThe HTTP API server built on axum:\n\n- axum-based REST API\n- Upload, list, search, get, delete endpoints\n- File serving with HTTP Range requests (for DuckDB-WASM)\n- Pluggable database (SQLite, Postgres)\n- Pluggable storage (local filesystem, S3)\n- v1 migration and lazy conversion\n\n### Frontend\n\nThe web frontend is a SvelteKit 5 single-page application using Svelte 5 runes for reactivity. It is built with the static adapter, producing a set of static files that can be served by the backend or any static host.\n\nKey technologies:\n\n- **SvelteKit 5** with static adapter -- client-side routing, no SSR\n- **Svelte 5 runes** -- `$state`, `$derived`, `$effect` for reactive state\n- **Tailwind CSS v4** -- utility-first styling via Vite plugin\n- **uPlot** -- high-performance time-series plotting for sensor data\n- **DuckDB-WASM** -- in-browser SQL queries over Parquet files via HTTP Range requests\n- **Mapbox GL JS** -- interactive GPS track maps\n- **Chart.js** -- statistical charts on the stats page\n- **TypeScript** throughout\n\n### CLI Tool (`ulog-convert`)\n\n`ulog-convert` is a standalone command-line tool for converting, diagnosing, and analyzing PX4 ULog files. No server, no database -- purely file-based. Designed for both individual file processing and batch workflows over entire flight log datasets.\n\nKey capabilities:\n\n- **Convert** ULog to per-topic Parquet files with metadata\n- **Diagnose** flight anomalies (motor failure, GPS interference, battery brownout, EKF failure, RC loss)\n- **Analyze** signal processing (PID step response via Wiener deconvolution)\n- **Batch process** directories of ULog files with parallel execution via rayon\n\nEvery conversion produces a `manifest.json` that maps the output (source file, topics to Parquet paths, diagnostic results). Batch conversions additionally produce an `index.json` at the output root that indexes all converted logs.\n\n### Two Paths\n\nThere are two ways to use the project -- through the server for production deployments, or through the CLI for local and scripted workflows:\n\n```\n                    +-----------------------+\n                    |   .ulg file input     |\n                    +----------+------------+\n                               |\n                    +----------+------------+\n                    |                       |\n              +-----v------+       +--------v--------+\n              | ulog-convert|      | flight-review-  |\n              |   (CLI)    |       |    server        |\n              +-----+------+       +--------+--------+\n                    |                       |\n              Local files            API + Storage\n              (Parquet +             (S3 / local fs\n               metadata.json)        + SQLite/Postgres)\n```\n\n### Workspace Layout\n\n```\nflight-review-rs/\n├── crates/\n│   ├── converter/          # Library + CLI\n│   │   ├── src/\n│   │   │   ├── lib.rs\n│   │   │   ├── converter.rs    # ULog --\u003e per-topic ZSTD Parquet files\n│   │   │   ├── metadata.rs     # All 13 ULog message types --\u003e metadata.json\n│   │   │   ├── analysis.rs     # Flight modes, stats, battery, GPS, vibration, param diff\n│   │   │   ├── diagnostics/    # Diagnostic analyzers (motor, GPS, battery, EKF, RC)\n│   │   │   ├── signal_processing/ # Signal processing framework (PID step response, DSP)\n│   │   │   ├── pid_analysis.rs # Backward-compat facade for signal_processing\n│   │   │   └── bin/\n│   │   │       └── ulog_convert.rs\n│   │   ├── benches/            # Criterion benchmarks\n│   │   ├── tests/fixtures/     # ULog test fixtures (normal + failure cases)\n│   │   └── Cargo.toml\n│   └── server/             # HTTP API server\n│       ├── src/\n│       │   ├── main.rs\n│       │   ├── lib.rs\n│       │   ├── api/        # Upload, list, get, delete, file serving (Range requests)\n│       │   ├── db/         # LogStore trait -- SQLite and Postgres backends\n│       │   └── storage/    # object_store -- local filesystem and S3\n│       └── Cargo.toml\n├── frontend/               # SvelteKit web application\n│   ├── src/\n│   │   ├── routes/         # SvelteKit pages and layouts\n│   │   ├── lib/            # Components, stores, utilities\n│   │   └── app.css         # Tailwind entry point\n│   ├── package.json\n│   ├── svelte.config.js\n│   └── vite.config.ts\n├── scripts/\n│   ├── download-logs.sh    # Seed local instance with real logs from v1\n│   └── ci/\n│       └── check-analyzer.sh  # CI validation for new diagnostic analyzers\n├── Dockerfile\n├── Cargo.toml              # Workspace root\n└── README.md\n```\n\n### What Gets Stored Per Log\n\nAll files live under a single UUID directory:\n\n```\n\u003cuuid\u003e/\n├── metadata.json           # Metadata + flight analysis + diagnostics\n├── \u003cuuid\u003e.ulg              # Original upload\n├── vehicle_attitude.parquet\n├── sensor_combined.parquet\n├── battery_status.parquet\n└── ...                     # One Parquet file per ULog topic\n```\n\nThe `metadata.json` includes flight modes, stats, battery summary, GPS quality, vibration status, GPS track, parameter diffs, and diagnostic results. Diagnostics are automatically detected during upload and included in the `analysis.diagnostics` array.\n\n### API\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `GET` | `/health` | Health check |\n| `POST` | `/api/upload` | Multipart upload -- accepts `.ulg` file + optional context fields |\n| `GET` | `/api/logs` | List/search logs (paginated, filtered by hardware, diagnostics, etc.) |\n| `GET` | `/api/logs/facets` | Distinct values for filterable fields (hardware, vehicle type, etc.) |\n| `GET` | `/api/logs/:id` | Single log record |\n| `GET` | `/api/logs/:id/track` | GeoJSON GPS track for a single log |\n| `DELETE` | `/api/logs/:id?token=\u003ctoken\u003e` | Delete log (requires delete token from upload) |\n| `GET` | `/api/logs/:id/data/:filename` | Serve Parquet/JSON/ULG files with HTTP Range support |\n| `GET` | `/api/stats` | Aggregate statistics (upload counts, vehicle types, etc.) |\n\n## Tech Stack\n\n| Layer | Stack |\n|-------|-------|\n| Backend | Rust, axum, SQLite/Postgres, object_store |\n| Converter | px4-ulog-rs, Apache Arrow/Parquet |\n| Frontend | SvelteKit 5, Svelte 5, Tailwind v4, TypeScript |\n| Visualization | uPlot, Chart.js, Mapbox GL JS |\n| Client-side data | DuckDB-WASM, Apache Arrow |\n\n## Build\n\nWe support Linux, macOS, and any platform Rust targets. The project compiles to native binaries with no runtime dependencies beyond libc. Both the CLI tool and server are built from the same workspace.\n\n### Development\n\nPrerequisites: Rust toolchain (stable) and Node.js 18+.\n\n```bash\n# Clone\ngit clone https://github.com/mrpollo/flight-review-rs.git\ncd flight-review-rs\n\n# Build backend (debug)\ncargo build\n\n# Run the server locally with SQLite\ncargo run -p flight-review-server -- serve \\\n  --db \"sqlite://data/flight-review.db?mode=rwc\" \\\n  --storage \"file://data/files\"\n\n# Run the CLI\ncargo run -p flight-review --bin ulog-convert -- --help\n```\n\nIn a second terminal, start the frontend dev server:\n\n```bash\ncd frontend\nnpm install\nnpm run dev\n```\n\nThe Vite dev server runs on `http://localhost:5173` and proxies all `/api` requests to the backend at `http://localhost:8080`. Open the Vite URL in the browser for development.\n\n**Tests:**\n\n```bash\n# Backend\ncargo test\n\n# Frontend\ncd frontend \u0026\u0026 npm test\n```\n\n**Type checking:**\n\n```bash\ncd frontend \u0026\u0026 npm run check\n```\n\n### Seeding Data\n\nThe `scripts/download-logs.sh` script downloads real ULog files from the v1 Flight Review instance at review.px4.io and optionally uploads them to a local v2 server. Useful for populating a development instance with realistic data.\n\n```bash\n# Download 50 logs (no upload)\nCOUNT=50 ./scripts/download-logs.sh\n\n# Download 20 logs and upload to local server\nCOUNT=20 UPLOAD_URL=http://localhost:8080 ./scripts/download-logs.sh\n\n# Upload previously downloaded logs only (skip download)\nUPLOAD_ONLY=true UPLOAD_URL=http://localhost:8080 ./scripts/download-logs.sh\n```\n\nKey environment variables:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `COUNT` | `100` | Number of logs to download |\n| `UPLOAD_URL` | (empty) | Server URL to upload to; empty skips upload |\n| `UPLOAD_ONLY` | `false` | Skip downloading, upload existing files from output dir |\n| `RATING_FILTER` | `good\\|great` | Pipe-separated ratings to include; `none` for any |\n| `GPS_ONLY` | `true` | Only download logs with GPS-dependent flight modes |\n| `VERIFY` | `true` | Verify each file with `ulog-convert` before uploading |\n| `MIN_VERSION` | `v1.14` | Minimum PX4 version |\n\n### Release Build\n\n```bash\ncargo build --release\n```\n\nBuild the frontend for production:\n\n```bash\ncd frontend \u0026\u0026 npm run build\n```\n\nThis produces static files in `frontend/build/` that can be served by the backend or any static file server.\n\n### Feature Flags\n\n| Feature | Crate | Description | Default |\n|---------|-------|-------------|---------|\n| `sqlite` | server | SQLite database backend | Yes |\n| `postgres` | server | PostgreSQL database backend | No |\n| `s3` | server | Amazon S3 storage backend | No |\n\nBuild with specific features:\n\n```bash\n# With Postgres support\ncargo build --release -p flight-review-server --features postgres\n\n# With S3 support\ncargo build --release -p flight-review-server --features s3\n\n# With everything\ncargo build --release -p flight-review-server --features \"postgres,s3\"\n```\n\n### Database Support\n\n- **SQLite** (default) -- zero setup, single file, ideal for self-hosted\n- **PostgreSQL** -- production deployments, concurrent access, managed hosting (AWS RDS, etc.)\n\nBoth backends auto-create the schema on startup.\n\n### Storage Support\n\n- **Local filesystem** (`file:///path`) -- simplest, no cloud needed\n- **Amazon S3** (`s3://bucket/prefix`) -- production, scalable, integrates with CloudFront\n\n## Deploy\n\nThe deployment spectrum ranges from a single self-contained binary on a Raspberry Pi to a production setup with CloudFront CDN, S3 storage, and managed Postgres. The same codebase supports all deployment models.\n\n### Minimal (single binary)\n\nJust run the binary with SQLite and local files -- no external services required:\n\n```bash\n./flight-review-server serve \\\n  --db sqlite:///data/flight-review.db \\\n  --storage file:///data/files \\\n  --port 8080\n\n# Upload a log\ncurl -X POST http://localhost:8080/api/upload \\\n  -F \"file=@flight.ulg\" \\\n  -F \"is_public=true\" \\\n  -F \"description=Test flight\"\n\n# List logs\ncurl http://localhost:8080/api/logs\n```\n\n### Docker\n\nThe Dockerfile currently builds the backend only. The frontend must be built separately (`cd frontend \u0026\u0026 npm run build`) and served via a reverse proxy or integrated into the container build.\n\n```bash\n# Build\ndocker build -t flight-review .\n\n# Run with local storage\ndocker run -p 8080:8080 -v /data:/data flight-review\n```\n\n### Production (AWS)\n\nPostgres for the database, S3 for file storage, and optionally CloudFront for CDN:\n\n```bash\n# Run with Postgres + S3\ndocker run -p 8080:8080 \\\n  flight-review serve \\\n  --db postgres://user:pass@host/flightreview \\\n  --storage s3://my-bucket/logs\n\n# Full AWS example with credentials\ndocker run -p 8080:8080 \\\n  -e AWS_ACCESS_KEY_ID=... \\\n  -e AWS_SECRET_ACCESS_KEY=... \\\n  -e AWS_REGION=us-east-1 \\\n  flight-review serve \\\n  --db postgres://user:pass@rds-host.amazonaws.com/flightreview \\\n  --storage s3://px4-flight-review \\\n  --v1-ulg-prefix flight_review/log_files\n```\n\n## Migrate from v1\n\nThe migration tool imports metadata from a v1 Flight Review SQLite database into v2, preserving all UUIDs, delete tokens, and public/private flags. No log files are moved -- the original `.ulg` files stay in their existing storage location. Logs are converted to Parquet lazily on first view, or optionally in batch.\n\nThe migration extracts what it can from v1's `LogsGenerated` table (vehicle type from `MavType`, error/warning counts, vehicle UUID, software git hash). Fields that require parsing the `.ulg` file (vibration status, GPS quality, battery stats, localization sources, flight distance) remain unpopulated until the log is converted -- either lazily on first view or via batch conversion. Search and statistics results for these fields will be incomplete until conversion occurs.\n\n### Metadata import + lazy conversion (recommended)\n\nImport database records instantly. Logs are converted to Parquet on first view. No downtime, no batch job required.\n\n```bash\n# Import metadata from v1 SQLite\n./flight-review-server migrate \\\n  --v1-db sqlite:///path/to/logs.sqlite \\\n  --db postgres://user:pass@host/flightreview\n\n# Start server with lazy conversion (converts .ulg --\u003e Parquet on first view)\n./flight-review-server serve \\\n  --db postgres://user:pass@host/flightreview \\\n  --storage s3://px4-flight-review \\\n  --v1-ulg-prefix flight_review/log_files\n```\n\n### Metadata import + batch conversion (optional)\n\nImport database records, then pre-convert all logs in the background. Useful for pre-warming cache or populating search indexes.\n\n```bash\n# Import metadata\n./flight-review-server migrate \\\n  --v1-db sqlite:///path/to/logs.sqlite \\\n  --db postgres://user:pass@host/flightreview\n\n# Batch-convert all pending logs\n./flight-review-server convert-all \\\n  --db postgres://user:pass@host/flightreview \\\n  --storage s3://px4-flight-review \\\n  --v1-ulg-prefix flight_review/log_files\n```\n\n## CLI\n\n`ulog-convert` is a standalone command-line tool for converting PX4 ULog files to Parquet and JSON. It can extract metadata, run flight analysis and diagnostics, perform PID step response analysis, and batch-scan directories for anomalies -- all without running the server or touching a database.\n\n```bash\n# Single file conversion (produces Parquet + metadata.json + manifest.json)\nulog-convert flight.ulg output_dir/\n\n# Metadata + diagnostics only (JSON to stdout)\nulog-convert --metadata-only flight.ulg\n\n# Compact JSON (for scripting)\nulog-convert --metadata-only --output-format compact flight.ulg | jq .\n\n# Signal processing analysis\nulog-convert analyze flight.ulg\nulog-convert analyze flight.ulg -m pid_step_response\n\n# Batch: convert a directory to Parquet (parallel, produces index.json)\nulog-convert batch logs/ -o dataset/\n\n# Batch: scan for anomalies\nulog-convert batch logs/ --diagnostics-only\n\n# Batch: convert + diagnose + analyze\nulog-convert batch logs/ -o dataset/ --diagnostics --analyze\n\n# Batch: filter to specific analyzers\nulog-convert batch logs/ --diagnostics-only --analyzer gps_interference,ekf_failure\n\n# JSON output for scripting\nulog-convert batch logs/ --diagnostics-only --format json\n```\n\n### Conversion Output\n\nEvery conversion produces a self-describing output directory:\n\n```\noutput/\n├── manifest.json              # what's here: source, topics, file map, diagnostics\n├── metadata.json              # full flight metadata and analysis\n├── vehicle_attitude.parquet   # one Parquet file per ULog topic\n├── sensor_combined.parquet\n└── ...\n```\n\nBatch conversions add an `index.json` at the output root:\n\n```\ndataset/\n├── index.json                 # indexes all logs with manifest paths\n├── sample/\n│   ├── manifest.json\n│   ├── metadata.json\n│   └── *.parquet\n├── motor_failure/\n│   ├── manifest.json\n│   └── ...\n```\n\n## Upload Context Fields\n\nThe upload endpoint accepts optional pilot-provided metadata as multipart form fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `file` | file | The `.ulg` file (required) |\n| `is_public` | bool | Show in public listings (default: false) |\n| `description` | text | Flight description |\n| `pilot_name` | text | Who flew |\n| `vehicle_name` | text | Vehicle callsign |\n| `tags` | text | Comma-separated labels |\n| `rating` | int | Flight quality 1-5 |\n| `wind_speed` | text | calm, breeze, gale, storm |\n| `mission_type` | text | survey, inspection, test, recreational |\n| `source` | text | web, CI, QGC, API |\n| `feedback` | text | Pilot notes |\n| `video_url` | text | Link to flight video |\n| `location_name` | text | Human-readable location |\n\n## Diagnostics\n\nFlight Review automatically detects flight anomalies during upload. Diagnostic analyzers run inside the existing `analyze()` streaming pass -- no separate processing step, no background jobs. Results are stored in `metadata.json`, the `log_diagnostics` database table, and returned via the API.\n\n### Available Analyzers\n\n| Analyzer | Detects | Severity | Topics |\n|----------|---------|----------|--------|\n| `motor_failure` | PWM drop to zero or locked at max while armed | Critical/Warning | `actuator_outputs`, `vehicle_status` |\n| `gps_interference` | EPH/EPV spikes, satellite count drops | Critical/Warning | `vehicle_gps_position` |\n| `battery_brownout` | Voltage below critical threshold during flight | Critical | `battery_status`, `vehicle_status` |\n| `ekf_failure` | Sustained EKF innovation test ratio exceedance | Critical/Warning | `estimator_status` |\n| `rc_loss` | RC signal loss during armed flight | Critical/Warning | `input_rc`, `vehicle_status` |\n\nQuery logs by diagnostic:\n\n```bash\n# Logs with motor failures\ncurl \"http://localhost:8080/api/logs?diagnostic=motor_failure\"\n\n# Logs with any critical diagnostic\ncurl \"http://localhost:8080/api/logs?diagnostic_severity=critical\"\n```\n\n### Adding a New Analyzer\n\nSee the full contributor guide at [`crates/converter/src/diagnostics/CONTRIBUTING.md`](crates/converter/src/diagnostics/CONTRIBUTING.md). It walks through the five steps (add an `Evidence` variant, create the analyzer file, register it, write the required tests, run the CI gates locally), includes a copy-pasteable skeleton, and points at [`rc_loss.rs`](crates/converter/src/diagnostics/rc_loss.rs) as the shortest complete reference implementation.\n\nCI (`diagnostics.yml`) validates the pattern automatically on PRs that touch the diagnostics directory. Run `scripts/ci/check-analyzer.sh` locally to verify before pushing.\n\n## For Researchers\n\nThere are two paths for working with flight log data, depending on your tools and workflow.\n\n### Path 1: Parquet export for Python / ML workflows\n\nConvert ULog files to Parquet and work with them using your existing tools (polars, pandas, DuckDB, scikit-learn, PyTorch). The CLI handles all the ULog parsing and produces a self-describing dataset.\n\n```bash\n# Convert a directory of flight logs to Parquet\nulog-convert batch logs/ -o dataset/ --diagnostics\n\n# Output structure:\n# dataset/\n# ├── index.json              ← entry point: lists all logs\n# ├── log_001/\n# │   ├── manifest.json       ← file map + diagnostic labels\n# │   ├── metadata.json       ← full flight metadata\n# │   ├── vehicle_attitude.parquet\n# │   ├── sensor_combined.parquet\n# │   └── ...\n# └── log_002/\n#     └── ...\n```\n\nFrom Python:\n\n```python\nimport json, polars as pl\n\n# Load the dataset index\nwith open(\"dataset/index.json\") as f:\n    index = json.load(f)\n\n# Find logs with motor failures\ncrashes = [log for log in index[\"logs\"] if log[\"diagnostic_count\"] \u003e 0]\n\n# Load a specific topic as a dataframe\ndf = pl.read_parquet(f\"dataset/{crashes[0]['path']}/vehicle_attitude.parquet\")\n```\n\nThe diagnostic labels in each `manifest.json` provide pre-computed anomaly annotations with timestamps and severity -- usable as training labels for supervised learning without manually reviewing flights.\n\n### Path 2: Rust-native signal processing modules\n\nFor analyses that need to run at scale across thousands of logs, or that you want to contribute back to the tool, write a Rust module that plugs into the signal processing framework. You declare what signals you need, the framework extracts them from the ULog file, and you receive buffered time-series data ready for FFT, spectral analysis, or deconvolution.\n\n```bash\n# Run signal processing on a single file\nulog-convert analyze flight.ulg\n\n# Batch across a directory (parallel)\nulog-convert batch logs/ --analyze -m pid_step_response\n```\n\n#### Available Modules\n\n| Module | Description | Topics |\n|--------|-------------|--------|\n| `pid_step_response` | PID controller step response via Wiener deconvolution | `vehicle_rates_setpoint`, `vehicle_angular_velocity` |\n\n#### Adding a New Module\n\n1. Create `crates/converter/src/signal_processing/your_module.rs`\n2. Implement the `SignalAnalysis` trait (`id`, `description`, `required_signals`, `analyze`)\n3. Register in `create_analyses()` in `signal_processing/mod.rs`\n4. Use shared DSP utilities from `signal_processing/dsp.rs` (resampling, windowing, sample rate estimation)\n5. Add tests following the pattern in `signal_processing/testing.rs`\n\nShared DSP functions available in `dsp.rs`: `median_sample_rate`, `resample_uniform`, `hanning_window`.\n\n## Roadmap\n\n- **User Accounts** -- optional authentication with email magic links, layered on top of the existing anonymous upload model\n- **PID Analysis API** -- server-side endpoint exposing the existing PID step response analysis for frontend consumption\n- **Dark Mode Polish** -- consistent dark mode across all pages (foundation exists but not fully polished)\n- **Frontend Production Build** -- integrate static frontend build into Docker image and server binary\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpx4%2Fflight-review-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpx4%2Fflight-review-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpx4%2Fflight-review-rs/lists"}