A flight route finder written in modern C++, v3 — a complete rewrite.
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.
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.
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 dataBuild 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 binariesA tsan preset (ThreadSanitizer) is available to verify concurrency safety:
cmake --preset tsan && cmake --build --preset tsan && ctest --preset tsanThe 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/.
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-stdioPoint 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/cachesTools 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.
# 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 --versionEndpoints 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 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).
In-depth technical articles (in Chinese) live under docs/ — start with the routing-algorithm primer and follow the index from there.
MIT. Third-party dependencies: see THIRD_PARTY_LICENSES.md. Contributing conventions: see docs/CONTRIBUTING.md.