修正基线系标定默认:机械初值、地面ROI与航向偏移可配,并补充G90窗导出与契约测试
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -131,6 +131,30 @@ def parse_heading(line: str) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def parse_gnhpr(line: str) -> dict:
|
||||
"""Parse Wheeltec G90 ``$GNHPR`` heading/pitch output."""
|
||||
|
||||
fields = line[:line.rfind("*")].split(",")
|
||||
if len(fields) < 7:
|
||||
raise ValueError("GNHPR has too few fields")
|
||||
quality = safe_int(fields[5], -1)
|
||||
return {
|
||||
"type": "GNHPR",
|
||||
"position_time_utc": fields[1],
|
||||
"raw_heading_deg": safe_float(fields[2]),
|
||||
"pitch_deg": safe_float(fields[3]),
|
||||
"roll_deg": safe_float(fields[4]),
|
||||
"heading_quality": quality,
|
||||
"satellites": safe_int(fields[6], -1),
|
||||
"heading_solution": f"GNHPR_QUALITY_{quality}",
|
||||
"baseline_length_m": None,
|
||||
"heading_stddev_deg": None,
|
||||
"pitch_stddev_deg": None,
|
||||
"solution_satellites": safe_int(fields[6], -1),
|
||||
"heading_valid": quality in {4, 5},
|
||||
}
|
||||
|
||||
|
||||
def parse_pvtslna(line: str) -> dict:
|
||||
"""Parse Unicore/G90 ``#PVTSLNA`` into GGA-compatible position fields.
|
||||
|
||||
@@ -221,6 +245,8 @@ def parse_rtk_capture(capture: CaptureFile) -> list[dict]:
|
||||
row.update(parse_pvtslna(line))
|
||||
elif line.startswith("#UNIHEADINGA"):
|
||||
row.update(parse_heading(line))
|
||||
elif line.startswith("$GNHPR"):
|
||||
row.update(parse_gnhpr(line))
|
||||
except ValueError as ex:
|
||||
row["parse_error"] = str(ex)
|
||||
rows.append(row)
|
||||
|
||||
@@ -41,8 +41,14 @@ def source_for_span(chunks: list[RawChunk], start: int, end: int, segment_id: in
|
||||
}
|
||||
|
||||
|
||||
def parse_rtk_capture(capture: CaptureFile) -> list[dict]:
|
||||
def parse_rtk_capture(
|
||||
capture: CaptureFile,
|
||||
accepted_prefixes: tuple[str, ...] | None = None,
|
||||
) -> list[dict]:
|
||||
rows = []
|
||||
accepted_prefix_bytes = (
|
||||
tuple(prefix.encode("ascii") for prefix in accepted_prefixes) if accepted_prefixes is not None else None
|
||||
)
|
||||
for segment_id, chunks in iter_contiguous_segments(capture.chunks):
|
||||
stream = b"".join(chunk.raw for chunk in chunks)
|
||||
cursor = 0
|
||||
@@ -56,6 +62,8 @@ def parse_rtk_capture(capture: CaptureFile) -> list[dict]:
|
||||
cursor = end
|
||||
if not raw_line:
|
||||
continue
|
||||
if accepted_prefix_bytes is not None and not raw_line.startswith(accepted_prefix_bytes):
|
||||
continue
|
||||
line = raw_line.decode("ascii", "replace")
|
||||
row = {"type": "UNKNOWN", "raw_line": line, "checksum_valid": parse_checksum(line)}
|
||||
row.update(source_for_span(chunks, start, end, segment_id))
|
||||
@@ -66,6 +74,8 @@ def parse_rtk_capture(capture: CaptureFile) -> list[dict]:
|
||||
row.update(parse_pvtslna(line))
|
||||
elif line.startswith("#UNIHEADINGA"):
|
||||
row.update(parse_heading(line))
|
||||
elif line.startswith("$GNHPR"):
|
||||
row.update(parse_gnhpr(line))
|
||||
except ValueError as ex:
|
||||
row["parse_error"] = str(ex)
|
||||
rows.append(row)
|
||||
|
||||
Reference in New Issue
Block a user