initial commit
This commit is contained in:
284
tests/conftest.py
Normal file
284
tests/conftest.py
Normal file
@@ -0,0 +1,284 @@
|
||||
"""Shared test fixtures.
|
||||
|
||||
Generates synthetic MME test data that mimics a realistic frontal crash test.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from impakt.channel.code import ChannelCode
|
||||
from impakt.channel.model import (
|
||||
Channel,
|
||||
ChannelGroup,
|
||||
DummyInfo,
|
||||
ImpactConfig,
|
||||
TestData,
|
||||
TestMetadata,
|
||||
VehicleInfo,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_rate() -> float:
|
||||
return 20000.0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def duration() -> float:
|
||||
return 0.2 # 200 ms
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def time_array(sample_rate: float, duration: float) -> np.ndarray:
|
||||
"""Time array: -0.01s to 0.19s (10 ms pre-trigger + 190 ms event)."""
|
||||
n = int(duration * sample_rate)
|
||||
pre = int(0.01 * sample_rate)
|
||||
return np.arange(n, dtype=np.float64) / sample_rate - pre / sample_rate
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def head_accel_x(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic head X acceleration: half-sine pulse peaking at ~40g."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0) & (t <= 0.1)
|
||||
data[mask] = 40.0 * np.sin(np.pi * t[mask] / 0.1)
|
||||
# Add some noise
|
||||
data += np.random.default_rng(42).normal(0, 0.5, len(data))
|
||||
|
||||
return Channel(
|
||||
name="11HEAD0000ACXA",
|
||||
code=ChannelCode.parse("11HEAD0000ACXA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="g",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def head_accel_y(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic head Y acceleration: smaller lateral component."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0) & (t <= 0.1)
|
||||
data[mask] = 8.0 * np.sin(np.pi * t[mask] / 0.1) * np.cos(3 * np.pi * t[mask] / 0.1)
|
||||
data += np.random.default_rng(43).normal(0, 0.3, len(data))
|
||||
|
||||
return Channel(
|
||||
name="11HEAD0000ACYA",
|
||||
code=ChannelCode.parse("11HEAD0000ACYA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="g",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def head_accel_z(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic head Z acceleration."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0) & (t <= 0.1)
|
||||
data[mask] = 15.0 * np.sin(np.pi * t[mask] / 0.1)
|
||||
data += np.random.default_rng(44).normal(0, 0.3, len(data))
|
||||
|
||||
return Channel(
|
||||
name="11HEAD0000ACZA",
|
||||
code=ChannelCode.parse("11HEAD0000ACZA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="g",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def head_group(head_accel_x: Channel, head_accel_y: Channel, head_accel_z: Channel) -> ChannelGroup:
|
||||
return ChannelGroup(
|
||||
key="11HEAD0000AC_A",
|
||||
x=head_accel_x,
|
||||
y=head_accel_y,
|
||||
z=head_accel_z,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def chest_deflection_channel(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic chest deflection: ramps to 35mm peak."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0.01) & (t <= 0.08)
|
||||
t_event = t[mask] - 0.01
|
||||
data[mask] = 35.0 * np.sin(np.pi * t_event / 0.07)
|
||||
|
||||
return Channel(
|
||||
name="11CHST0000DCXA",
|
||||
code=ChannelCode.parse("11CHST0000DCXA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="mm",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def neck_fz_channel(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic neck Fz: tension pulse peaking at ~3000N."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0.005) & (t <= 0.06)
|
||||
t_event = t[mask] - 0.005
|
||||
data[mask] = 3000.0 * np.sin(np.pi * t_event / 0.055)
|
||||
|
||||
return Channel(
|
||||
name="11NECKUP00FOZA",
|
||||
code=ChannelCode.parse("11NECKUP00FOZA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="N",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def neck_my_channel(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic neck My: flexion moment peaking at ~80 Nm."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0.01) & (t <= 0.07)
|
||||
t_event = t[mask] - 0.01
|
||||
data[mask] = 80.0 * np.sin(np.pi * t_event / 0.06)
|
||||
|
||||
return Channel(
|
||||
name="11NECKUP00MOYA",
|
||||
code=ChannelCode.parse("11NECKUP00MOYA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="N·m",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def femur_left_channel(time_array: np.ndarray, sample_rate: float) -> Channel:
|
||||
"""Synthetic left femur load: compressive pulse peaking at ~4500N."""
|
||||
t = time_array
|
||||
data = np.zeros_like(t)
|
||||
mask = (t >= 0.02) & (t <= 0.09)
|
||||
t_event = t[mask] - 0.02
|
||||
data[mask] = -4500.0 * np.sin(np.pi * t_event / 0.07)
|
||||
|
||||
return Channel(
|
||||
name="11FEMRLE00FOZA",
|
||||
code=ChannelCode.parse("11FEMRLE00FOZA"),
|
||||
data=data,
|
||||
time=t,
|
||||
unit="N",
|
||||
sample_rate=sample_rate,
|
||||
source_test_id="TEST_001",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_metadata() -> TestMetadata:
|
||||
return TestMetadata(
|
||||
test_number="TEST_001",
|
||||
vehicle=VehicleInfo(make="Toyota", model="Camry", year=2024, mass_kg=1523.0),
|
||||
dummy=DummyInfo(dummy_type="H3-50M", position="Driver", mass_kg=78.0),
|
||||
impact=ImpactConfig(
|
||||
test_type="Full Frontal",
|
||||
speed_kmh=56.3,
|
||||
barrier_type="Rigid",
|
||||
overlap_percent=100.0,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_test_data(
|
||||
sample_metadata: TestMetadata,
|
||||
head_accel_x: Channel,
|
||||
head_accel_y: Channel,
|
||||
head_accel_z: Channel,
|
||||
chest_deflection_channel: Channel,
|
||||
neck_fz_channel: Channel,
|
||||
neck_my_channel: Channel,
|
||||
femur_left_channel: Channel,
|
||||
) -> TestData:
|
||||
"""Full synthetic test data with multiple channels."""
|
||||
channels = {
|
||||
head_accel_x.name: head_accel_x,
|
||||
head_accel_y.name: head_accel_y,
|
||||
head_accel_z.name: head_accel_z,
|
||||
chest_deflection_channel.name: chest_deflection_channel,
|
||||
neck_fz_channel.name: neck_fz_channel,
|
||||
neck_my_channel.name: neck_my_channel,
|
||||
femur_left_channel.name: femur_left_channel,
|
||||
}
|
||||
return TestData(
|
||||
test_id="TEST_001",
|
||||
metadata=sample_metadata,
|
||||
channels=channels,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_mme_dir(tmp_path: Path, head_accel_x: Channel) -> Path:
|
||||
"""Create a minimal MME directory structure for reader tests."""
|
||||
test_dir = tmp_path / "test_001"
|
||||
test_dir.mkdir()
|
||||
|
||||
# MME.ini
|
||||
ini = test_dir / "MME.ini"
|
||||
ini.write_text(
|
||||
"[Test]\n"
|
||||
"test_number = TEST_001\n"
|
||||
"test_type = Full Frontal\n"
|
||||
"test_speed = 56.3\n"
|
||||
"\n"
|
||||
"[Vehicle]\n"
|
||||
"vehicle_make = Toyota\n"
|
||||
"vehicle_model = Camry\n"
|
||||
"vehicle_year = 2024\n"
|
||||
"\n"
|
||||
"[Dummy]\n"
|
||||
"dummy_type = H3-50M\n"
|
||||
"dummy_position = Driver\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
# Channel directory
|
||||
ch_dir = test_dir / "channels"
|
||||
ch_dir.mkdir()
|
||||
|
||||
# .chn file
|
||||
chn = ch_dir / "11HEAD0000ACXA.chn"
|
||||
chn.write_text(
|
||||
"[Channel]\n"
|
||||
"channel_code = 11HEAD0000ACXA\n"
|
||||
"unit = g\n"
|
||||
"sample_rate = 20000\n"
|
||||
"num_samples = 4000\n"
|
||||
"pre_trigger = 200\n"
|
||||
"cfc = 1000\n"
|
||||
"data_format = ascii\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
# .dat file (ASCII, one value per line)
|
||||
dat = ch_dir / "11HEAD0000ACXA.dat"
|
||||
np.savetxt(str(dat), head_accel_x.data, fmt="%.6f")
|
||||
|
||||
return test_dir
|
||||
Reference in New Issue
Block a user