Files
gps/soc/software/gps_system/tests/test_acq_search_model.py
T

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()