Skip to content

Commit c3d5568

Browse files
committed
Map NE, S, M, and L motor delays
1 parent a379dc1 commit c3d5568

2 files changed

Lines changed: 54 additions & 38 deletions

File tree

scripts/build_database.py

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020

2121
_DELAY_SUFFIX_RE = re.compile(r'-(\d+|[pP])$')
2222
_SIMPLIFY_CN_RE = re.compile(r'^[0-9]*[ -]*([A-Z][0-9]+)')
23+
_TEXTUAL_DELAY_VALUE_MAP = {
24+
'S': 6.0,
25+
'M': 10.0,
26+
'L': 14.0,
27+
}
2328

2429

2530
def ensure_curve_starts_at_zero(points):
@@ -37,6 +42,28 @@ def curve_signature(points):
3742
return tuple((round(time_s, 6), round(thrust_n, 6)) for time_s, thrust_n in points)
3843

3944

45+
def normalize_delay_token(token):
46+
"""Canonicalize known textual delay tokens."""
47+
normalized = str(token).strip()
48+
upper = normalized.upper()
49+
if upper in ('P', 'PLUGGED', 'NE'):
50+
return 'P'
51+
return upper
52+
53+
54+
def delay_token_to_seconds(token):
55+
"""Map known textual delay tokens to representative numeric values."""
56+
normalized = normalize_delay_token(token)
57+
if normalized in _TEXTUAL_DELAY_VALUE_MAP:
58+
return _TEXTUAL_DELAY_VALUE_MAP[normalized]
59+
return float(normalized)
60+
61+
62+
def normalize_delay_field(raw_value):
63+
"""Convert a raw delay field into canonical comma-separated form."""
64+
return merge_delays(raw_value)
65+
66+
4067
def parse_delays(delays):
4168
"""Normalize delay representations into a unique ordered list."""
4269
if delays is None:
@@ -59,7 +86,7 @@ def parse_delays(delays):
5986
token = part.strip()
6087
if not token:
6188
continue
62-
normalized = 'P' if token.upper() == 'P' else token
89+
normalized = normalize_delay_token(token)
6390
if normalized not in seen:
6491
tokens.append(normalized)
6592
seen.add(normalized)
@@ -80,13 +107,16 @@ def merge_delays(*delay_values):
80107
if not merged:
81108
return None
82109

83-
numeric = sorted(
84-
(token for token in merged if token != 'P'),
85-
key=lambda token: (float(token), token),
86-
)
87-
if 'P' in seen:
88-
numeric.append('P')
89-
return ','.join(numeric)
110+
numeric = []
111+
textual = []
112+
for token in merged:
113+
try:
114+
numeric.append((delay_token_to_seconds(token), token))
115+
except ValueError:
116+
textual.append(token)
117+
118+
ordered_numeric = [token for _, token in sorted(numeric, key=lambda item: (item[0], item[1]))]
119+
return ','.join(ordered_numeric + textual)
90120

91121

92122
def normalize_designation(designation):
@@ -416,13 +446,7 @@ def parse_rasp(filepath):
416446

417447
try:
418448
delays_raw = parts[3]
419-
if delays_raw.upper() == 'P':
420-
delays = 'P'
421-
elif delays_raw.strip() == '':
422-
delays = None
423-
else:
424-
delay_parts = [d.strip() for d in re.split(r'[-,]+', delays_raw) if d.strip()]
425-
delays = ','.join(delay_parts) if delay_parts else None
449+
delays = normalize_delay_field(delays_raw)
426450

427451
# RASP weights are in kg, convert to grams for DB consistency
428452
prop_weight_kg = float(parts[4])
@@ -490,13 +514,7 @@ def parse_rse(filepath):
490514

491515
# Extract Metadata
492516
delays_raw = engine.get('delays', '')
493-
if delays_raw.upper() == 'P':
494-
delays = 'P'
495-
elif not delays_raw.strip():
496-
delays = None
497-
else:
498-
delay_parts = [d.strip() for d in re.split(r'[-,]+', delays_raw) if d.strip()]
499-
delays = ','.join(delay_parts) if delay_parts else None
517+
delays = normalize_delay_field(delays_raw)
500518

501519
designation = engine.get('code', 'Unknown')
502520
# Extract common name from designation (strip propellant suffix letters)
@@ -643,14 +661,7 @@ def try_header(parts):
643661
return None
644662
try:
645663
raw_delays = parts[3]
646-
if raw_delays.upper() == 'P':
647-
delays = 'P'
648-
elif raw_delays.strip() == '':
649-
delays = None
650-
else:
651-
# Normalize dash-separated to comma-separated
652-
delay_parts = [d.strip() for d in re.split(r'[-,]+', raw_delays) if d.strip()]
653-
delays = ','.join(delay_parts) if delay_parts else None
664+
delays = normalize_delay_field(raw_delays)
654665
return {
655666
'common_name': parts[0], 'designation': parts[0],
656667
'diameter': float(parts[1]), 'length': float(parts[2]),
@@ -737,13 +748,7 @@ def parse_rse_all(filepath):
737748
tree = ET.parse(filepath)
738749
for engine in tree.getroot().findall(".//engine"):
739750
raw_delays = engine.get('delays', '')
740-
if raw_delays.upper() == 'P':
741-
delays = 'P'
742-
elif raw_delays.strip() == '':
743-
delays = None
744-
else:
745-
delay_parts = [d.strip() for d in re.split(r'[-,]+', raw_delays) if d.strip()]
746-
delays = ','.join(delay_parts) if delay_parts else None
751+
delays = normalize_delay_field(raw_delays)
747752
designation = engine.get('code', 'Unknown')
748753
cn_match = re.match(r'^([A-Z][0-9]+)', designation)
749754
meta = {

tests/test_build_database.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ def test_parse_rasp_delays_normalized(tmp_path):
215215
("5-10-15", "5,10,15"),
216216
("0", "0"),
217217
("P", "P"),
218+
("NE", "P"),
218219
("p", "P"),
219220
("3", "3"),
220221
]
@@ -236,7 +237,7 @@ def make_rse(delays_attr):
236237
"<eng-data t=\"0.4\" f=\"0.0\" /></data></engine>"
237238
"</engine-list></engine-database>"
238239
)
239-
cases = [("5-10-15", "5,10,15"), ("0", "0"), ("P", "P"), ("6,10", "6,10")]
240+
cases = [("5-10-15", "5,10,15"), ("0", "0"), ("P", "P"), ("NE", "P"), ("6,10", "6,10")]
240241
for raw, expected in cases:
241242
path = tmp_path / f"motor_{raw}.rse"
242243
path.write_text(make_rse(raw))
@@ -256,10 +257,20 @@ def test_calculate_thrust_stats():
256257

257258
def test_merge_delays_combines_unique_values():
258259
assert build_db.merge_delays("0,5", "5-10", "P") == "0,5,10,P"
260+
assert build_db.merge_delays("Plugged", "0-3-5-7") == "0,3,5,7,P"
261+
assert build_db.merge_delays("S,M", "L", "P") == "S,M,L,P"
262+
assert build_db.merge_delays("NE", "P") == "P"
259263
assert build_db.merge_delays(None, "", "0") == "0"
260264
assert build_db.merge_delays(None, "") is None
261265

262266

267+
def test_delay_token_to_seconds_maps_textual_delays():
268+
assert build_db.delay_token_to_seconds("S") == pytest.approx(6.0)
269+
assert build_db.delay_token_to_seconds("M") == pytest.approx(10.0)
270+
assert build_db.delay_token_to_seconds("L") == pytest.approx(14.0)
271+
assert build_db.delay_token_to_seconds("7") == pytest.approx(7.0)
272+
273+
263274
def test_extract_simfile_info_from_filename():
264275
mapping = {"abcdef123456abcdef123456": "motor-1"}
265276
sim_id, info = build_db.extract_simfile_info_from_filename(

0 commit comments

Comments
 (0)