Skip to content

Bokjan/BravoFinder

Repository files navigation

BravoFinder

CI release license C++20 sanitizers

A flight route finder written in modern C++, v3 — a complete rewrite.

About

BravoFinder builds a graph from navigation data (waypoints, navaids, airways, and SID/STAR/approach procedures) and finds routes between two airports. Unlike earlier versions, which computed a purely geographic shortest path, v3 is a realistic / compliant route engine: routes respect real-world constraints such as airway directionality, high/low airway levels, segment altitude bands, and terminal procedures.

Navigation data is read through a pluggable Loader interface (lib/io/loaders/) that abstracts the source format. The default (and currently only) loader parses X-Plane 12 native .dat files; a future loader could read a Little Navmap SQLite database or another format without changing the route engine. The loader name is recorded as source_loader provenance in every .bfdb cache header.

Status

The tool loads navigation data through a pluggable source loader (default: X-Plane 12), builds a directed graph honoring airway directionality and high/low levels, and finds routes between two airports (or waypoints) with A* and Yen K-shortest. Airports connect to the enroute network through their real SID/STAR procedures (parsed from ARINC 424 / CIFP), falling back to a direct link where no procedure data exists. For example, KJFK KLAX resolves to a filed-flight-plan-style route such as KJFK DEEZZ5 TOWIN ... PGS BASET5 KLAX of ~2160 NM.

A single loaded database is safe to query concurrently from multiple threads.

Building

Requires a C++20 compiler and CMake (3.21+). Dependencies (Catch2, CLI11, RapidJSON) are fetched automatically via FetchContent.

cmake --preset debug              # or: release
cmake --build --preset debug      # parallel build (use --preset, not the path form)
ctest --preset debug              # unit tests run always; integration tests need data

Build only what you need with --target:

Target What it builds
bf CLI tool (apps/cli/)
bf_mcp_stdio MCP stdio server (apps/mcp_stdio/)
bf_mcp_lib MCP server library (static)
bf_tests Test runner
bf3 The unified static library (lib/, alias bf::bravofinder3)
cmake --build --preset debug --target bf_mcp_stdio    # just the MCP server
cmake --build --preset debug --target bf bf_mcp_stdio # both binaries

A tsan preset (ThreadSanitizer) is available to verify concurrency safety:

cmake --preset tsan && cmake --build --preset tsan && ctest --preset tsan

Using the library (SDK)

The route engine ships as a self-contained static library. Two ways to consume it, both under the single target name bf::bravofinder3:

Pre-built SDK — download a bravofinder-sdk-* archive from a release, unpack it, and:

find_package(bravofinder3 REQUIRED)
target_link_libraries(my_app PRIVATE bf::bravofinder3)

The static archive (libbravofinder3.a / bravofinder3.lib) folds in the SQLite amalgamation, so no separate sqlite dependency is needed. On MSVC the SDK uses the default dynamic CRT (/MD); match that in the consuming project.

From source (FetchContent):

include(FetchContent)
FetchContent_Declare(
  BravoFinder
  GIT_REPOSITORY https://github.com/Bokjan/BravoFinder.git
  GIT_TAG v3.7.2)
FetchContent_MakeAvailable(BravoFinder)
target_link_libraries(my_app PRIVATE bf::bravofinder3)

The public entry point is bf::NavDatabase (#include "io/nav_database.h"); headers are included as core/... / io/... rooted at bf/.

Usage

MCP server (bf-mcp-stdio)

BravoFinder also ships a local MCP server that exposes bf route and bf query as MCP tools over stdio, so an LLM client can ask for routes and look up navigation data directly. It is a thin, zero-dependency (beyond the project's own library) stdio JSON-RPC server.

It is pointed at a directory of .bfdb caches (not a single file) and can serve multiple AIRAC cycles from it: each cycle's database is opened lazily on first use and cached. Every tool takes an optional cycle argument (omit for the newest), and a list_cycles tool enumerates what is available. A single-cycle deployment is just a directory holding one cache.

Build it alongside the CLI:

cmake --preset release && cmake --build --preset release   # or: debug
# binary: build/release/apps/mcp_stdio/bf-mcp-stdio

Point it at a directory and run it (it fails fast at startup if the directory holds no nav_<cycle>.bfdb cache):

# The directory is --db-dir, else BRAVOFINDER_NAVDATA, else ./navdata.
BRAVOFINDER_NAVDATA=navdata bf-mcp-stdio
bf-mcp-stdio --db-dir /path/to/caches

Tools exposed: find_routes and parse_route (mirroring bf route), the lookup_waypoints / lookup_airports / lookup_procedures / lookup_airways / lookup_navaid_detail / lookup_holds batch lookups (mirroring bf query), and list_cycles. See apps/mcp_stdio/README.md for the full tool reference, argument semantics, and client configuration.

bf build (cache creation) remains a CLI concern and is not exposed as a tool.

CLI (bf)

# Build a binary cache once per AIRAC cycle for fast startup (~1.5s -> ~50ms).
# The default name encodes the cycle so a directory of caches can hold several
# AIRACs. One unified .bfdb holds the graph, the CIFP procedures, and the navaid
# detail, so deployment needs only that file, not the CIFP/ directory.
bf build navdata                    # writes navdata/nav_<cycle>.bfdb
bf build /path/to/xplane -o my.bfdb # explicit name: writes my.bfdb
bf build navdata --loader xplane12  # select source loader (default; only one today)

# Find a route (reads navigation data from ./navdata by default)
bf route KJFK KLAX
bf route EGLL LFPG --format json
bf route KSEA KBOS --data /path/to/xplane/data

# Load a prebuilt cache to skip parsing. The one .bfdb carries the graph, the
# CIFP procedures, and the navaid detail, so the CIFP/ directory is not needed
# at all.
bf route KJFK KLAX --db navdata/nav_2601.bfdb

# Procedure cache load mode: on-demand (default, ~1.5 MB, best for one-shot
# queries) or eager (loads all procedures up front, ~100 MB then lock-free,
# best for servers / batch routing)
bf route KJFK KLAX --db navdata/nav_2601.bfdb --cifp-load eager

# Constrain by cruise altitude (enables altitude-band and MORA filtering).
# A single level or an inclusive range (any level in the band is acceptable).
bf route KJFK KLAX --alt 350
bf route KJFK KLAX --alt 300-400

# Prefer high (Jet) or low (Victor) airways; ask for several candidates
bf route KJFK KLAX --level high -k 3

# Restrict the departure/arrival runway used for SID/STAR selection
bf route KJFK KLAX --rwy-dep RW31L

# Select a specific SID/STAR by name (bare name matches any transition;
# NAME.TRANSITION pins the transition). An unknown name is a clean error.
bf route KJFK KLAX --sid DEEZZ5
bf route KJFK KLAX --star LENDY6.HAAYS

# Force the route through waypoints, in order (via points); ident or IDENT/REGION.
bf route KJFK KLAX --via DBL
bf route KJFK KLAX --via PSB --via DBL

# Route around waypoints or airways. A bare ident avoids all its regional
# matches; an airway designator also blocks its concurrency segments.
bf route KJFK KLAX --avoid-wpt CANDR
bf route KJFK KLAX --avoid-awy J60

# Diversify the route reproducibly: the same seed always yields the same route,
# different seeds explore alternative (still valid) routes.
bf route KJFK KLAX --seed 42

# Validate and expand a filed route string (the reverse of route): checks that
# each airway connects its bracketing fixes, expands airways to their
# intermediate points, and totals the distance. Errors name the bad token.
bf parse-route "KJFK DEEZZ5 CANDR Q480 HOTEE J80 MCI ... KLAX" --db navdata/nav_2601.bfdb

# Look up navigation data: waypoints, airports, procedures, airways, navaid
# details, or holds. Each accepts one or more ids (a batch), and --format json
# emits an array parallel to the input (a not-found id becomes null).
bf query waypoint --db navdata/nav_2601.bfdb NINOX DGC
bf query airport  --db navdata/nav_2601.bfdb KJFK KLAX
bf query procedure --db navdata/nav_2601.bfdb KJFK
bf query airway   --db navdata/nav_2601.bfdb Y28 --format json
# navaid_detail: frequency, service range, elevation, station variation/bearing.
bf query navaid_detail --db navdata/nav_2601.bfdb SEA DGC
# hold: holding-pattern parameters (inbound course, leg length, turn, altitude).
bf query hold --db navdata/nav_2601.bfdb AE701

# Print the program version
bf --version

Endpoints are airport ICAO codes or waypoint idents, case-insensitive. When an airport has procedure data, the route and its legs name the SID and STAR used (and the interchangeable procedures that share the same connection fix).

The .bfdb cache is a portable, little-endian binary snapshot. One unified file holds three sections sharing a global string pool: the route graph, the per-airport CIFP procedures (loaded on demand), and the radio-navaid attributes and holding patterns for the navaid_detail / hold lookups. The canonical name is nav_<cycle>.bfdb, encoding the AIRAC cycle so a directory can hold several cycles. It is derived from Navigraph/Jeppesen data and, like the source data, must not be redistributed (it is git-ignored).

Navigation Data

Navigation data is not included and must be supplied by the user. It is copyrighted (Navigraph / Jeppesen), licensed for recreational simulation use only, and must not be redistributed. Place your local data under navdata/ (git-ignored).

Documentation

In-depth technical articles (in Chinese) live under docs/ — start with the routing-algorithm primer and follow the index from there.

License

MIT. Third-party dependencies: see THIRD_PARTY_LICENSES.md. Contributing conventions: see docs/CONTRIBUTING.md.

About

A flight route planner toolset written in C++

Topics

Resources

License

Contributing

Stars

27 stars

Watchers

4 watching

Forks

Packages

 
 
 

Contributors

Languages