# 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 ```bash # 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 1. **Immutable raw data.** Original MME files are never modified. All state — sessions, configuration, cached results — lives in a `.impakt/` subfolder alongside the test data. 2. **Non-destructive transforms.** Filtering, alignment, and math operations produce new channel views via `TransformChain`. The chain is serializable and reproducible. 3. **Scriptable first.** Every operation available in the UI is accessible through a Python API. The web UI calls the same `Session` and `PlotEngine` used by scripts. 4. **Layered configuration.** All behavior is configurable via YAML files at three levels: package defaults, user defaults (`~/.impakt/`), and per-test session overrides (`.impakt/`). 5. **Template-driven workflow.** Reusable templates define channel selections, filter chains, corridors, and report configs. Sessions bind templates to specific test data. 6. **Protocol-aware.** Built-in Euro NCAP, US NCAP, and IIHS scoring with versioned YAML threshold files. 7. **Plugin-extensible.** Custom readers, transforms, criteria, and protocols registered via entry points, directory scanning, or API. --- ## Architecture Overview ```mermaid 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: ```mermaid graph LR PKG["Package Defaults\nsrc/impakt/defaults/config.yaml"] --> USER["User Defaults\n~/.impakt/config.yaml"] USER --> SESSION["Session Overrides\n/.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. ```python 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. ```mermaid classDiagram class ReaderProtocol { <> +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 `/.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/` | `/.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 ```mermaid 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 ```mermaid 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 (/.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 ```mermaid 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 ```python 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 ```python 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 ```python 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 ```python 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 ```python 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 ```