Impakt
Crash test data analysis, visualization, and reporting.
Impakt is a modular, scriptable Python toolkit for working with automotive crash test data. It reads ISO 13499 MME data natively, provides non-destructive signal processing, interactive web-based visualization, injury criteria calculation, and protocol-compliant report generation for Euro NCAP, US NCAP, and IIHS.
Quick Start
# Install
uv sync --dev
# Run tests (258 tests, 70% coverage)
uv run pytest tests/
# View test metadata
uv run impakt info tests/mme_data/3239
# Launch web UI
uv run impakt serve tests/mme_data/3239
# Python scripting
uv run python -c "
from impakt import Session
s = Session.open('tests/mme_data/3239')
print(s) # Session(3239, 133 channels)
# Fluent transforms
ch = s.channel('11HEAD0000H3ACXP').transform.cfc(1000).transform.y_align()
print(f'Peak: {ch.peak:.1f} {ch.unit}')
# One-call protocol evaluation
result = s.evaluate('euro_ncap')
print(f'{result.stars} stars ({result.percentage:.0f}%)')
"
See docs/STATUS.md for detailed project state, known issues, and next steps.
Contents
- #Design Principles
- #Architecture Overview
- #Configuration System
- #Module Breakdown
- #impakt.config — Configuration
- #impakt.io — Data IO
- #impakt.channel — Channel Model
- #impakt.transform — Signal Processing
- #impakt.criteria — Injury Criteria
- #impakt.protocol — Rating Protocols
- #impakt.plot — Visualization
- #impakt.report — Report Generation
- #impakt.template — Templates and Sessions
- #impakt.web — Web UI
- #impakt.plugin — Plugin System
- #impakt.script — Scripting API
- #Data Flow
- #Configuration Layers
- #ISO Channel Naming Intelligence
- #Directory Structure
- #Scripting Examples
Design Principles
- Immutable raw data. Original MME files are never modified. All state — sessions, configuration, cached results — lives in a
.impakt/subfolder alongside the test data. - Non-destructive transforms. Filtering, alignment, and math operations produce new channel views via
TransformChain. The chain is serializable and reproducible. - Scriptable first. Every operation available in the UI is accessible through a Python API. The web UI calls the same
SessionandPlotEngineused by scripts. - Layered configuration. All behavior is configurable via YAML files at three levels: package defaults, user defaults (
~/.impakt/), and per-test session overrides (.impakt/). - Template-driven workflow. Reusable templates define channel selections, filter chains, corridors, and report configs. Sessions bind templates to specific test data.
- Protocol-aware. Built-in Euro NCAP, US NCAP, and IIHS scoring with versioned YAML threshold files.
- Plugin-extensible. Custom readers, transforms, criteria, and protocols registered via entry points, directory scanning, or API.
Architecture Overview
graph TB
subgraph "Data Layer"
IO[impakt.io]
CH[impakt.channel]
end
subgraph "Processing Layer"
TR[impakt.transform]
CR[impakt.criteria]
PR[impakt.protocol]
end
subgraph "Presentation Layer"
PL[impakt.plot]
RP[impakt.report]
WB[impakt.web]
end
subgraph "Orchestration Layer"
CF[impakt.config]
TM[impakt.template]
SC[impakt.script]
PG[impakt.plugin]
end
IO -->|"parse MME"| CH
CH -->|"channel data"| TR
TR -->|"filtered/aligned"| CR
CR -->|"injury values"| PR
PR -->|"scores & ratings"| RP
CH -->|"raw & derived"| PL
TR -->|"processed"| PL
CR -->|"criteria results"| PL
PL -->|"figures"| RP
PL -->|"interactive plots"| WB
CF -.->|"configures"| PL
CF -.->|"configures"| TR
CF -.->|"configures"| CR
CF -.->|"configures"| PR
SC -->|"drives"| TM
SC -->|"drives"| IO
SC -->|"drives"| TR
SC -->|"drives"| PL
PG -.->|"extends"| IO
PG -.->|"extends"| TR
PG -.->|"extends"| CR
PG -.->|"extends"| PR
WB -->|"calls"| SC
Configuration System
All configurable behavior is defined in YAML files with three-layer resolution:
graph LR
PKG["Package Defaults\nsrc/impakt/defaults/config.yaml"] --> USER["User Defaults\n~/.impakt/config.yaml"]
USER --> SESSION["Session Overrides\n<test_dir>/.impakt/config.yaml"]
SESSION --> ACTIVE["Active Config"]
style PKG fill:#e1f5fe
style USER fill:#fff9c4
style SESSION fill:#c8e6c9
style ACTIVE fill:#f3e5f5
Configurable sections:
| Section | Examples |
|---|---|
plot |
Color palette, line widths, focus styling, margins, cursor colors, grid, resampling |
transforms |
Default CFC class, Y-align, X-align method |
criteria |
Channel patterns for auto-detection (per criterion, glob-style) |
protocols |
Default protocol, version mapping, threshold file locations |
session |
Auto-save behavior, .impakt/ directory name |
web |
Default layout, cursor poll interval, port |
When a session is saved, configuration and protocol thresholds are copied into the test's .impakt/ folder, making it self-contained and reproducible.
Module Breakdown
impakt.config — Configuration
Layered YAML configuration with typed Python dataclasses.
from impakt.config import Config
config = Config.load(session_path="tests/mme_data/3239")
config.plot.line_width # 1.5 (from package default)
config.transforms.default_cfc # None (no filter by default)
config.protocols.default # "euro_ncap"
config.transforms.default_cfc = 600
config.save_session("tests/mme_data/3239") # writes .impakt/config.yaml
impakt.io — Data IO
Reads crash test data via the ReaderProtocol. The MMEReader handles real ISO 13499 format (.mme master + .chn index + .NNN data files) and simplified INI format.
classDiagram
class ReaderProtocol {
<<protocol>>
+read(path: Path) TestData
+supports(path: Path) bool
+metadata(path: Path) TestMetadata
}
class MMEReader
class TDMSReader
class CSVReader
class ReaderRegistry {
+register(reader: ReaderProtocol)
+detect(path: Path) ReaderProtocol
+read(path: Path) TestData
}
ReaderProtocol <|.. MMEReader
ReaderProtocol <|.. TDMSReader
ReaderProtocol <|.. CSVReader
ReaderRegistry o-- ReaderProtocol
Tested against 5 real ISO 13499 datasets from NHTSA/Calspan, BASt, Volkswagen, and UTAC.
impakt.channel — Channel Model
Immutable Channel objects wrapping NumPy time-series data with ISO channel code intelligence.
The ChannelCode parser auto-detects 14-char and 16-char ISO codes:
| Format | Example | Positions 11-12 |
|---|---|---|
| 16-char (with dummy) | 11HEAD0000H3ACXP |
H3 = Hybrid III |
| 14-char (simplified) | 11HEAD0000ACXA |
AC = Acceleration |
Auto-grouping: Channels sharing all fields except direction (X/Y/Z) are grouped for one-call resultant computation.
impakt.transform — Signal Processing
Non-destructive pipeline via TransformChain. Each transform produces a new Channel.
| Transform | Description |
|---|---|
CFCFilter |
SAE J211 CFC filtering (4th-order Butterworth, zero-phase) |
XAlign |
Time-zero shifting (manual, threshold, trigger) |
YAlign |
Zero-offset correction from baseline window |
Resultant |
Vector magnitude from X/Y/Z components |
MathExpr |
Free-form math with safe numpy evaluation |
Trim |
Extract time range |
Resample |
Change sample rate (Fourier-based) |
impakt.criteria — Injury Criteria
Auto-detection of channels by ISO naming patterns, with configurable patterns in config.yaml.
| Criterion | Method | Reference |
|---|---|---|
| HIC15/36 | Cumulative integration, optimal window search | FMVSS 208 |
| 3ms Clip | Cumulative exceedance | SAE J211 |
| Nij | 4 modes (NTE/NTF/NCE/NCF), per-dummy intercepts | FMVSS 208 |
| Chest Deflection | Peak sternal displacement | FMVSS 208 |
| Femur Load | Peak compressive axial force | FMVSS 208 |
| Tibia Index | M/Mc + F/Fc with intercepts | Euro NCAP |
| Viscous Criterion | V(t) * C(t) | Euro NCAP |
impakt.protocol — Rating Protocols
Versioned YAML threshold files loaded from defaults/protocols/, ~/.impakt/protocols/, or <test>/.impakt/protocols/.
| Protocol | Output |
|---|---|
| Euro NCAP | Stars (0-5), body region colors (Green/Yellow/Orange/Brown/Red), points |
| US NCAP | Stars (1-5), injury probability per body region |
| IIHS | Good/Acceptable/Marginal/Poor per body region, overall rating |
impakt.plot — Visualization
Single rendering path via PlotEngine.render(PlotSpec). Both the scripting API and web UI construct PlotSpec objects and delegate to the same engine.
- Compact mode for web UI (no legend, tight margins, disabled hover)
- Standard mode for scripting (full tooltips, legend)
- FigureResampler integration for LTTB downsampling on zoom/pan
- Focus channel rendering (amber highlight, rendered on top)
- Corridor fills (upper/lower bands from CSV or programmatic)
impakt.report — Report Generation
Jinja2 HTML templates rendered to PDF via WeasyPrint. Three report types: plot sheets, injury summary, and full protocol reports.
impakt.template — Templates and Sessions
| Concept | Template | Session |
|---|---|---|
| Lives in | ~/.impakt/templates/ |
<test_dir>/.impakt/ |
| Contains | Channel patterns, filter chains, corridors, protocol config | Template ref + overrides + config + cached results |
| Purpose | Reusable recipe | Per-test instance with user modifications |
impakt.web — Web UI
Dash application with two tabs:
Data Tab:
- Resizable left panel (draggable splitter) with channel grid + transform controls
- Channel grid: flat sortable DataTable, wildcard filter, facet dropdowns, consistent color coding
- Plot area with multi-pane layout (1x1 through 3x1)
- Channel Values table: combined statistics + live cursor values (#, ISO Code, Description, Unit, Min@Time, Max@Time, X1, X2, Cursor)
- Custom JS cursor tracker (mousemove → pixel-to-data-X conversion)
Analysis Tab:
- Injury criteria auto-computation with protocol scoring
- Math expression builder with variable binding
- Template management (library browser, save/apply/delete)
- Corridor upload (CSV)
- Export (CSV, PNG/SVG/PDF, protocol report)
impakt.plugin — Plugin System
Entry point + directory + API discovery. PluginRegistry.register_reader() forwards to the IO registry. Plugins discovered on first Session.open().
impakt.script — Scripting API
Session is the primary entry point. The web UI's AppState holds Session objects — the same code path for scripts and UI.
Data Flow
graph TB
MME["MME Directory\n(immutable)"] -->|"impakt.io"| TD[TestData]
TD --> CH[Channels]
CH -->|"TransformChain"| TCH[Transformed Channels]
TCH -->|"criteria"| CR[CriterionResults]
CR -->|"protocol"| SC[ProtocolResult]
CH & TCH -->|"PlotSpec"| PE[PlotEngine]
PE --> FIG[Plotly Figure]
SC --> RP[Report Engine]
FIG --> RP
RP --> PDF[PDF/HTML]
CFG["Config\n(layered YAML)"] -.-> PE
CFG -.-> CR
CFG -.-> SC
TD -.->|"state"| IMPAKT[".impakt/"]
IMPAKT -.->|"restore"| TD
style MME fill:#e1f5fe
style IMPAKT fill:#fff9c4
style PDF fill:#c8e6c9
style CFG fill:#f3e5f5
Configuration Layers
graph TB
subgraph "Package (shipped with pip install)"
PKG_CFG[defaults/config.yaml]
PKG_PROTO[defaults/protocols/*.yaml]
end
subgraph "User (~/.impakt/)"
USR_CFG[config.yaml]
USR_PROTO[protocols/*.yaml]
USR_TMPL[templates/*.yaml]
USR_CORR[corridors/*.csv]
end
subgraph "Test Session (<test_dir>/.impakt/)"
SES_CFG[config.yaml]
SES_PROTO[protocols/*.yaml]
SES_SESS[session.yaml]
SES_CORR[corridors/]
SES_DER[derived/]
end
PKG_CFG -->|"overridden by"| USR_CFG
USR_CFG -->|"overridden by"| SES_CFG
PKG_PROTO -->|"copied to"| USR_PROTO
USR_PROTO -->|"copied to"| SES_PROTO
Save Session copies config + protocols into the test's .impakt/ folder. The test directory becomes self-contained and reproducible.
ISO Channel Naming Intelligence
graph LR
RAW["11HEAD0000H3ACXP"] --> PARSER[ChannelCode Parser]
PARSER --> OBJ["Object: 11\nDriver"]
PARSER --> LOC["Location: HEAD"]
PARSER --> FINE["Fine: 0000\nCenter of Gravity"]
PARSER --> DUMMY["Dummy: H3\nHybrid III"]
PARSER --> MEAS["Measurement: AC\nAcceleration"]
PARSER --> DIR["Direction: X"]
PARSER --> SENSE["Sense: P"]
OBJ & LOC & FINE & DUMMY & MEAS & DIR --> GROUP["Group Key\n(X/Y/Z family)"]
GROUP --> AUTO["Auto-features:\n- Resultant computation\n- Criteria channel matching\n- Channel grid placement\n- Human-readable labels"]
Directory Structure
impakt/
├── pyproject.toml # PEP 621, uv dependency-groups
├── uv.lock
├── .gitignore
├── README.md # This file
├── BRAINSTORM.md # Feature ideas (80+)
├── docs/
│ ├── STATUS.md # Detailed project state
│ └── QA-*.md # Quality assessment scorecards
├── research/
│ └── landscape.md # Competitive landscape analysis
├── src/impakt/
│ ├── __init__.py # exports Session, Template
│ ├── config/ # Layered YAML configuration
│ │ ├── __init__.py
│ │ └── model.py # Config, PlotConfig, TransformConfig, etc.
│ ├── defaults/ # Package-level defaults (shipped)
│ │ ├── config.yaml # All configurable fields with comments
│ │ └── protocols/ # Euro NCAP, IIHS threshold YAMLs
│ ├── channel/ # Data model + ISO naming
│ │ ├── code.py # ChannelCode parser (14/16-char auto-detect)
│ │ ├── model.py # Channel, ChannelGroup, TestData, TestMetadata
│ │ ├── group.py # Auto-grouping utilities
│ │ └── lookup.py # ISO naming lookup tables (150+ entries)
│ ├── io/ # Data readers
│ │ ├── reader.py # ReaderProtocol, ReaderRegistry
│ │ ├── mme.py # ISO 13499 MME reader (real + simplified)
│ │ ├── tdms.py # TDMS stub
│ │ └── csv.py # CSV stub
│ ├── transform/ # Signal processing
│ │ ├── base.py # Transform protocol, TransformChain
│ │ ├── cfc.py # SAE J211 CFC filter
│ │ ├── align.py # X-align, Y-align
│ │ ├── resultant.py # Vector magnitude
│ │ ├── math_expr.py # Safe math expressions
│ │ └── resample.py # Trim, Resample
│ ├── criteria/ # Injury criteria calculations
│ ├── protocol/ # Rating protocol scorers
│ │ ├── thresholds/ # Versioned YAML threshold files
│ │ └── *.py # Euro NCAP, US NCAP, IIHS
│ ├── plot/ # Plotly rendering engine
│ │ ├── engine.py # PlotEngine (single rendering path)
│ │ ├── spec.py # PlotSpec, ChannelRef, Corridor
│ │ └── export.py # Image/HTML export
│ ├── report/ # PDF/HTML report generation
│ │ ├── engine.py # Jinja2 + WeasyPrint
│ │ └── templates/ # HTML report templates
│ ├── template/ # Template + session persistence
│ ├── web/ # Dash web application
│ │ ├── app.py # App factory
│ │ ├── state.py # AppState (holds Sessions server-side)
│ │ ├── layout.py # Two-tab layout (Data + Analysis)
│ │ ├── components/ # 10 reusable UI components
│ │ ├── callbacks/ # 9 feature-specific callback modules
│ │ └── assets/ # CSS, JS (splitter, cursor tracker)
│ ├── plugin/ # Plugin registry + discovery
│ └── script/ # Session API + CLI
│ ├── api.py # Session, ChannelHandle, Template
│ └── cli.py # impakt serve/info/channels/evaluate
├── tests/
│ ├── fixtures/ # Synthetic MME test data (26 channels)
│ ├── mme_data/ # 5 real ISO 13499 datasets
│ └── test_*/ # 258 tests across all modules
Scripting Examples
One-call evaluation
from impakt import Session
s = Session.open("tests/mme_data/3239")
# Auto-detect channels and score against Euro NCAP
result = s.evaluate("euro_ncap")
print(result.summary())
# Euro NCAP 2024
# Rating: ***** (5/5 stars)
# Score: 14.0/16.0 (88%)
# Head: 233.08 [green] (4.0/4.0)
# Chest: 44.04 g [yellow] (3.0/4.0)
# ...
Fluent transform chaining
from impakt import Session
s = Session.open("tests/mme_data/3239")
# Each transform returns a ChannelHandle — fully chainable
ch = (
s.channel("11HEAD0000H3ACXP")
.transform.cfc(1000)
.transform.y_align()
.transform.trim(t_start=0.0, t_end=0.1)
)
print(f"Peak: {ch.peak:.1f} {ch.unit}")
Configuration override
from impakt import Session
s = Session.open("tests/mme_data/3239")
# Override default CFC for this session
s.config.transforms.default_cfc = 600
s.config.plot.line_width = 2.0
s.save_config() # writes to 3239/.impakt/config.yaml
Custom math channel
from impakt import Session
from impakt.transform import math_expr
s = Session.open("tests/mme_data/3239")
custom = math_expr(
expression="sqrt(a**2 + b**2)",
channels={
"a": s.channel("11HEAD0000H3ACXP").raw,
"b": s.channel("11HEAD0000H3ACZP").raw,
},
name="Head XZ Resultant",
unit="m/s²",
)
print(f"Peak: {custom.peak:.1f} {custom.unit}")
Launch web UI
from impakt.web import serve
from impakt import Session
s = Session.open("tests/mme_data/3239")
serve(s, port=8050)
# Opens browser at http://localhost:8050