betaflight-chirp-core
The compute core for Betaflight closed-loop chirp / blackbox analysis:
decode a .bbl/.bfl, estimate the frequency response (FRF/Bode), step
response and noise spectrum, and render a self-contained HTML report.
betaflight-chirp-coreknows nothing about MCP, HTTP, Docker, the CLI or the filesystem. Input: bytes. Output: objects + HTML.
Install
pip install betaflight-chirp-core
Or pin an exact commit straight from git:
pip install "betaflight-chirp-core @ git+https://github.com/SebGalina/betaflight-chirp-core@v0.1.5"
Usage
from betaflight_chirp_core import decode, analyse_log, build_report, run
# low-level: decode -> analyse -> render, step by step
df, fs, config = decode(open("log.bbl", "rb").read())
# df: decoded frames (pandas) fs: loop/log rate (Hz) config: PID/filter settings
a_pass = analyse_log(df, fs, config) # one log -> one self-contained pass dict
html = build_report([a_pass]) # passes -> self-contained HTML report
# single call: decode + analyse + report in one shot
result = run(open("log.bbl", "rb").read())
result.metrics # == result.raw["axes"] — per-axis indicators, render as-is
result.report_html # the self-contained HTML report (a full <html> string)
result.raw # the complete pass dict (see Output below)
Importing the package is light: numpy/scipy/pandas load lazily, only when an
analysis runs. from betaflight_chirp_core import decoder stays stdlib-only, so
decode-only callers pull no heavy deps.
Output
Four return surfaces, from raw to ready-to-render:
| Call | Returns | Is |
|---|---|---|
decode(bytes) |
(df, fs, config) |
pandas frames, log rate (Hz), header tune dict |
analyse_log(df,…) |
pass dict | one log → all indicators (below) |
build_report(passes) |
str |
one self-contained <html> page |
run(bytes) |
AnalysisResult |
.metrics (= raw["axes"]), .report_html, .raw (pass dict) |
The pass dict
analyse_log() / result.raw — one self-contained analysis of one log:
{
"timestamp": "2026-06-12T09:00:00", # ISO, when analysed
"file": "log.bbl",
"sample_rate_hz": 8000, # loop/log rate
"input_col": "debug[3]", # FRF input column (chirp setpoint channel)
"band_hz": [1.0, 1000.0], # analysed frequency band [fmin, fmax]
"throttle_max": 1850, # peak flying throttle (or None)
"is_chirp": True, # chirp excitation detected? gates step_flight (normal-log only)
"frf_reliable": True, # is the FRF trustworthy? (coherent band fraction ≥ 0.10)
"frf_coherent_frac": 0.30, # fraction of the band clearing the coherence gate
"config": {…}, # PID / filter settings parsed from the header
"axes": {"roll": {…}, "pitch": {…}, "yaw": {…}}, # per-axis, see below
"tune_score": {"overall": 76.0, "grade": "B", "axes": {"roll": {"score": …, "subs": {…}}}},
"throttle_map": {…}, # resonance vs throttle (heatmap payload + motor_orders)
"noise_spectrum": {…}, # gyro PSD raw vs filtered (see below)
"filter_quality": {…}, # empirical raw→filtered attenuation/preservation gauge
"filter_model": {…}, # analytic filter response + group-delay budget (ms) from config
"pid_balance": {"roll": {"pct_p":…, "pct_i":…, "pct_d":…, "err_rms":…, "err_ratio":…}, …},
"step_flight": {"roll": {"small": {…}|None, "large": {…}|None}, …}, # amplitude-binned real-flight step
"spectrogram": {…}, # chirp sweep time×freq (heatmap payload)
"synthesis": [{"fr": "...", "en": "..."}, …], # plain-language read, bilingual
"filter_suggestions": [ … ], # filter change hints (only when config present)
"noise_suggestions": [ … ], # noise/peak hints
}
Per axis (axes["roll"] etc.) — the Bode + step + verdict for one axis:
{
"band_hz": [1.0, 1000.0], "n_samples": 48000,
"freq": [...], "gain_db": [...], "phase_deg": [...], "coherence": [...], # Bode curves
"peaks": [ … ], # gain-resonance peaks in band
"crossover_hz": 32.0, # 0 dB crossover
"phase_margin_deg": 41.0, "phase_margin_unc_deg": 6.0,
"ms": 4.8, "f_ms_hz": 70.0, "pm_guaranteed_deg": 34.0, # peak sensitivity (robustness)
"mt": 1.2, "f_mt_hz": 28.0, # peak complementary sensitivity max|T| (closed-loop resonance / delay robustness)
"step": { # setpoint→gyro step response
"t_ms": [...], "y": [...], "y_lo": [...], "y_hi": [...],
"metrics": {"overshoot_pct": 12.0, "rise_ms": 18.0, "delay_ms": 3.0,
"settle_ms": 60.0, "peak": 1.12},
},
"diagnosis": [ … ], "step_diagnosis": [ … ], # short verdict strings
}
tune_score.grade is an A–F letter (A ≥ 85 … F < 40); overall is the mean
of the per-axis scores. noise_spectrum carries freqs, raw_db/filt_db curves
(0 dB = raw broadband floor) and a peaks list with above_floor_db / resid_db
(filtered residual) / atten_db (raw→filtered cut) per peak.
Array fields (
freq,gain_db,*_db, …) are JSON-ready (rounded floats), so the whole pass dict serialises straight to a front-end or a history store. For the exact nested fields, readanalysis/chirp.py:build_pass.
Layout
| Module | Role |
|---|---|
decoder.py |
pure-Python .bbl frame decoder (stdlib only) |
signal.py |
decode_dataframe (bytes → frames), sample_rate, active_mask |
config.py |
PID / filter settings parsed from the header |
analysis/ |
chirp (FRF/Bode), spectral, step response |
report.py |
self-contained HTML report (inlines the renderer assets) |
report_assets/ |
shared report renderer (chirp_report.{js,css} + glossary/strings JSON) — inlined by report.py, mountable by a web front |
Develop
pip install -e ".[test]"
pytest
Tests run on .bbl fixtures in tests/data/. One GPS-free log (8.bbl) ships so
the suite runs out of the box; drop your own logs there for more coverage. Every
other .bbl/.bfl is git-ignored — never commit a real flight log, it
carries GPS home-point coordinates (only 8.bbl is whitelisted, after verifying
it has no GPS frame).
License
Apache-2.0.
Metadata
Release files for betaflight-chirp-core 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| betaflight_chirp_core-0.4.1.tar.gz | 153.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| betaflight_chirp_core-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 301.8 kB
Release files / betaflight_chirp_core-0.4.1.tar.gz
| Download URL | betaflight_chirp_core-0.4.1.tar.gz |
|---|---|
| Size | 153.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3ec3ab1ce45e147cf4454cf526fd7cc4c0c2fc379e86729bdfc5bdeaddd4f5cf
|
|
BLAKE2b-256 checksum How to use checksums |
a2f4d72fbf382176ee1eaa9fc398054c2ceb180016c3e9f1ae75735e1e480c7b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / betaflight_chirp_core-0.4.1-py3-none-any.whl
| Download URL | betaflight_chirp_core-0.4.1-py3-none-any.whl |
|---|---|
| Size | 148.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
229ec46c57c428eda1fa071cdf216186638564f2b1a598277ab00e0bdc59a3ed
|
|
BLAKE2b-256 checksum How to use checksums |
01d0c9c5aecc4421d57998b5d5a5860c16da8b9aa34e49a4aab5395bd6dc7c4b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log