118 lines
4.0 KiB
Python
118 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Model-level checks for acquisition search edge cases."""
|
|
|
|
from pathlib import Path
|
|
import re
|
|
|
|
|
|
TEST_DIR = Path(__file__).resolve().parent
|
|
REPO_ROOT = TEST_DIR.parents[3]
|
|
DEFINITIONS = REPO_ROOT / "hardware/external/gps/rtl/common/definitions.sv"
|
|
ACQ_TOP = REPO_ROOT / "hardware/external/gps/rtl/acq_src/acq_top.sv"
|
|
|
|
|
|
def read_parameters(path):
|
|
text = path.read_text(encoding="utf-8")
|
|
params = {}
|
|
parameter_re = (
|
|
r"parameter\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([^;]+);"
|
|
)
|
|
for match in re.finditer(parameter_re, text):
|
|
value = match.group(2).strip()
|
|
if re.fullmatch(r"\d+", value):
|
|
params[match.group(1)] = int(value)
|
|
elif re.fullmatch(r"\d+\.\d+", value):
|
|
params[match.group(1)] = float(value)
|
|
return params
|
|
|
|
|
|
def check(condition, message):
|
|
if not condition:
|
|
raise AssertionError(message)
|
|
|
|
|
|
def main():
|
|
params = read_parameters(DEFINITIONS)
|
|
acq_top = ACQ_TOP.read_text(encoding="utf-8")
|
|
|
|
samples_per_code = params["SAMPLES_PER_CODE"]
|
|
num_freq_bin = params["NUM_FREQ_BIN"]
|
|
freq_bin_wl = params["FREQ_BIN_WL"]
|
|
dopp_step = params["DOPP_STEP"]
|
|
pullin_freq_step = params["PULLIN_FREQ_STEP"]
|
|
num_pullin = params["NUM_PULLIN"]
|
|
acq_base_freq = params["ACQ_BASE_FREQ"]
|
|
|
|
check(samples_per_code == 16368, "unexpected sample count model")
|
|
check(num_freq_bin == 41, "unexpected coarse Doppler bin count")
|
|
check(num_pullin == 11, "unexpected pull-in bin count")
|
|
check(dopp_step == 500, "unexpected coarse Doppler step")
|
|
check(pullin_freq_step == 100, "unexpected pull-in Doppler step")
|
|
check(acq_base_freq == 4082000, "unexpected acquisition base frequency")
|
|
|
|
check(
|
|
"freq_bin_cnt <= (max_freq_bin_idx == 0) ? 0 : "
|
|
"(max_freq_bin_idx - 1);" in acq_top,
|
|
"RTL must saturate pull-in search at coarse bin zero",
|
|
)
|
|
check(
|
|
"rf_wr_addr == 2*SAMPLES_PER_CODE - 1" in acq_top,
|
|
"RTL must capture exactly two 1 ms code windows",
|
|
)
|
|
|
|
max_encoded_bin = (1 << freq_bin_wl) - 1
|
|
legacy_bin_zero_start = (0 - 1) & max_encoded_bin
|
|
check(
|
|
legacy_bin_zero_start >= num_freq_bin,
|
|
"legacy bin-zero decrement no longer demonstrates an out-of-range wrap",
|
|
)
|
|
|
|
covered_frequencies = set()
|
|
for coarse_bin in range(num_freq_bin):
|
|
pullin_start = 0 if coarse_bin == 0 else coarse_bin - 1
|
|
check(0 <= pullin_start < num_freq_bin, "pull-in start out of coarse range")
|
|
|
|
coarse_frequency = acq_base_freq + dopp_step * coarse_bin
|
|
for pullin_bin in range(num_pullin):
|
|
pullin_frequency = (
|
|
acq_base_freq +
|
|
dopp_step * pullin_start +
|
|
pullin_freq_step * pullin_bin
|
|
)
|
|
covered_frequencies.add(pullin_frequency)
|
|
if coarse_bin == 0:
|
|
check(
|
|
pullin_frequency >= acq_base_freq,
|
|
"coarse bin zero pull-in searched below the table",
|
|
)
|
|
else:
|
|
check(
|
|
coarse_frequency - dopp_step <= pullin_frequency <=
|
|
coarse_frequency + dopp_step,
|
|
"pull-in search does not cover the expected +/-500 Hz window",
|
|
)
|
|
|
|
lowest_expected = acq_base_freq
|
|
highest_expected = (
|
|
acq_base_freq +
|
|
dopp_step * (num_freq_bin - 2) +
|
|
pullin_freq_step * (num_pullin - 1)
|
|
)
|
|
check(min(covered_frequencies) == lowest_expected, "unexpected low search edge")
|
|
check(max(covered_frequencies) == highest_expected, "unexpected high search edge")
|
|
check(len(covered_frequencies) >= num_freq_bin * 5, "pull-in coverage collapsed")
|
|
|
|
last_sample_written = (2 * samples_per_code) - 1
|
|
check(last_sample_written == 32735, "unexpected two-code capture fencepost")
|
|
|
|
print(
|
|
"ACQ search model PASS: "
|
|
f"coarse_bins={num_freq_bin} pullin_bins={num_pullin} "
|
|
f"freq_range={lowest_expected}..{highest_expected}Hz "
|
|
f"last_sample={last_sample_written}"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|