fix: align local S-52 CSP decisions with OpenCPN
This commit is contained in:
@@ -145,13 +145,14 @@ def _optional_float(value: Any) -> float | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _light_color_ref(colours: list[int]) -> str:
|
def _light_color_ref(colours: list[int], *, sector: bool = False) -> str:
|
||||||
if len(colours) == 1:
|
if len(colours) == 1:
|
||||||
if 3 in colours:
|
if 3 in colours:
|
||||||
return "LITRD"
|
return "LITRD"
|
||||||
if 4 in colours:
|
if 4 in colours:
|
||||||
return "LITGN"
|
return "LITGN"
|
||||||
if any(code in colours for code in (1, 6, 11)):
|
# OpenCPN uses orange (11) for sectors, amber (9) for flares/circles.
|
||||||
|
if any(code in colours for code in (1, 6, 11 if sector else 9)):
|
||||||
return "LITYW"
|
return "LITYW"
|
||||||
return "CHMGD"
|
return "CHMGD"
|
||||||
if len(colours) == 2:
|
if len(colours) == 2:
|
||||||
@@ -193,7 +194,7 @@ _LIGHT_CHARACTER = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _light_description(attrs: dict[str, Any], *, has_sector: bool) -> str:
|
def _light_description(attrs: dict[str, Any]) -> str:
|
||||||
try:
|
try:
|
||||||
litchr = int(attrs.get("LITCHR"))
|
litchr = int(attrs.get("LITCHR"))
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
@@ -201,18 +202,18 @@ def _light_description(attrs: dict[str, Any], *, has_sector: bool) -> str:
|
|||||||
text = _LIGHT_CHARACTER.get(litchr, "")
|
text = _LIGHT_CHARACTER.get(litchr, "")
|
||||||
two_groups = litchr in {13, 14, 15, 16, 25, 26, 27, 29}
|
two_groups = litchr in {13, 14, 15, 16, 25, 26, 27, 29}
|
||||||
siggrp = str(attrs.get("SIGGRP") or "").strip()
|
siggrp = str(attrs.get("SIGGRP") or "").strip()
|
||||||
if siggrp:
|
if two_groups:
|
||||||
groups = [part for part in siggrp.replace(")(", ",").strip("()").split(",") if part]
|
groups = [part for part in siggrp.replace("(", ")").split(")") if part]
|
||||||
if two_groups and groups:
|
first = groups[0][0] if groups else "?"
|
||||||
first = groups[0]
|
left, right = text.split(" + ", 1)
|
||||||
if " + " in text:
|
text = f"{left}({first}) + {right}"
|
||||||
left, right = text.split(" + ", 1)
|
if len(groups) > 1 and groups[1] != "1":
|
||||||
text = f"{left}({first}) + {right}"
|
text += f"({groups[1]})"
|
||||||
if len(groups) > 1 and groups[1] != "1":
|
elif siggrp and siggrp != "(1)":
|
||||||
text += f"({groups[1]})"
|
text += siggrp
|
||||||
elif siggrp != "(1)":
|
|
||||||
text += siggrp
|
|
||||||
|
|
||||||
|
# Description suppression depends on SECTR1 presence, not sector validity.
|
||||||
|
has_sector = "SECTR1" in attrs
|
||||||
if not has_sector:
|
if not has_sector:
|
||||||
colour_codes = {1: "W", 3: "R", 4: "G", 6: "Y"}
|
colour_codes = {1: "W", 3: "R", 4: "G", 6: "Y"}
|
||||||
colours = "".join(colour_codes.get(code, "") for code in _int_list(attrs.get("COLOUR")))
|
colours = "".join(colour_codes.get(code, "") for code in _int_list(attrs.get("COLOUR")))
|
||||||
@@ -243,7 +244,7 @@ def resolve_lights05_instruction(attrs: dict[str, Any]) -> tuple[str, dict[str,
|
|||||||
|
|
||||||
sectr1 = _optional_float(attrs.get("SECTR1"))
|
sectr1 = _optional_float(attrs.get("SECTR1"))
|
||||||
sectr2 = _optional_float(attrs.get("SECTR2"))
|
sectr2 = _optional_float(attrs.get("SECTR2"))
|
||||||
has_sector = sectr1 is not None and sectr2 is not None
|
has_sector = sectr1 not in (None, -9.0) and sectr2 not in (None, -9.0)
|
||||||
colours = _int_list(attrs.get("COLOUR")) or [12]
|
colours = _int_list(attrs.get("COLOUR")) or [12]
|
||||||
extra: dict[str, Any] = {
|
extra: dict[str, Any] = {
|
||||||
"s52_light_nominal_range_nm": float(valnmr),
|
"s52_light_nominal_range_nm": float(valnmr),
|
||||||
@@ -266,7 +267,7 @@ def resolve_lights05_instruction(attrs: dict[str, Any]) -> tuple[str, dict[str,
|
|||||||
else:
|
else:
|
||||||
radius = _light_radius_mm(valnmr)
|
radius = _light_radius_mm(valnmr)
|
||||||
color = _light_color_ref(colours)
|
color = _light_color_ref(colours)
|
||||||
radius_adjust = {"LITRD": 1.0, "LITGN": 0.0, "LITYW": 2.0, "CHMGD": 3.0}.get(color, 5.0)
|
radius_adjust = {"LITRD": 1.0, "LITGN": 0.0, "LITYW": 2.0}.get(color, 3.0 if colours == [12] else 5.0)
|
||||||
instruction = f"CA(OUTLW,4,{color},2,0,360,{radius + radius_adjust:.1f},0)"
|
instruction = f"CA(OUTLW,4,{color},2,0,360,{radius + radius_adjust:.1f},0)"
|
||||||
extra["s52_light_mode"] = "all_round"
|
extra["s52_light_mode"] = "all_round"
|
||||||
else:
|
else:
|
||||||
@@ -275,12 +276,12 @@ def resolve_lights05_instruction(attrs: dict[str, Any]) -> tuple[str, dict[str,
|
|||||||
if sweep < 1.0:
|
if sweep < 1.0:
|
||||||
radius = _light_radius_mm(valnmr)
|
radius = _light_radius_mm(valnmr)
|
||||||
color = _light_color_ref(colours)
|
color = _light_color_ref(colours)
|
||||||
radius_adjust = {"LITRD": 1.0, "LITGN": 0.0, "LITYW": 2.0, "CHMGD": 3.0}.get(color, 5.0)
|
radius_adjust = {"LITRD": 1.0, "LITGN": 0.0, "LITYW": 2.0}.get(color, 3.0 if colours == [12] else 5.0)
|
||||||
instruction = f"CA(OUTLW,4,{color},2,0,360,{radius + radius_adjust:.1f},0)"
|
instruction = f"CA(OUTLW,4,{color},2,0,360,{radius + radius_adjust:.1f},0)"
|
||||||
extra["s52_light_mode"] = "all_round"
|
extra["s52_light_mode"] = "all_round"
|
||||||
else:
|
else:
|
||||||
arc_radius = 10.0 if 0 < valnmr < 15.0 else 15.0 if valnmr < 30.0 else 20.0
|
arc_radius = 10.0 if 0 < valnmr < 15.0 else 15.0 if 15.0 <= valnmr < 30.0 else 20.0
|
||||||
color = _light_color_ref(colours)
|
color = _light_color_ref(colours, sector=True)
|
||||||
outline_color, outline_width, arc_width = "OUTLW", 4, 2
|
outline_color, outline_width, arc_width = "OUTLW", 4, 2
|
||||||
litvis = _int_list(attrs.get("LITVIS"))
|
litvis = _int_list(attrs.get("LITVIS"))
|
||||||
if any(code in litvis for code in (3, 7, 8)):
|
if any(code in litvis for code in (3, 7, 8)):
|
||||||
@@ -298,7 +299,7 @@ def resolve_lights05_instruction(attrs: dict[str, Any]) -> tuple[str, dict[str,
|
|||||||
"s52_sector_sweep_deg": float(sweep),
|
"s52_sector_sweep_deg": float(sweep),
|
||||||
})
|
})
|
||||||
|
|
||||||
description = _light_description(attrs, has_sector=has_sector)
|
description = _light_description(attrs)
|
||||||
if description:
|
if description:
|
||||||
safe = description.replace("'", "’")
|
safe = description.replace("'", "’")
|
||||||
instruction += f";TX('{safe}',3,2,3,'15110',2,0,CHBLK,23)"
|
instruction += f";TX('{safe}',3,2,3,'15110',2,0,CHBLK,23)"
|
||||||
@@ -760,10 +761,16 @@ def resolve_wrecks02_instruction(
|
|||||||
quapos = _int_attr(attrs, "QUAPOS", 0)
|
quapos = _int_attr(attrs, "QUAPOS", 0)
|
||||||
if 2 <= quapos < 10:
|
if 2 <= quapos < 10:
|
||||||
line = "LC(LOWACC41)"
|
line = "LC(LOWACC41)"
|
||||||
elif udw or valsou is None or valsou <= 20.0:
|
elif udw or (valsou is not None and valsou <= 20.0):
|
||||||
line = "LS(DOTT,2,CHBLK)" if udw or valsou is not None else "LS(DOTT,2,CSTLN)"
|
line = "LS(DOTT,2,CHBLK)"
|
||||||
else:
|
elif valsou is not None:
|
||||||
line = "LS(DASH,2,CHBLK)"
|
line = "LS(DASH,2,CHBLK)"
|
||||||
|
elif watlev in {1, 2}:
|
||||||
|
line = "LS(SOLD,2,CSTLN)"
|
||||||
|
elif watlev == 4:
|
||||||
|
line = "LS(DASH,2,CSTLN)"
|
||||||
|
else:
|
||||||
|
line = "LS(DOTT,2,CSTLN)"
|
||||||
instruction = line
|
instruction = line
|
||||||
if valsou is not None and valsou <= 20.0:
|
if valsou is not None and valsou <= 20.0:
|
||||||
instruction += ";" + udw if udw else ""
|
instruction += ";" + udw if udw else ""
|
||||||
@@ -836,8 +843,9 @@ def resolve_slcons03_instruction(
|
|||||||
if geom == "AREA":
|
if geom == "AREA":
|
||||||
instructions.append("AP(CROSSX01)")
|
instructions.append("AP(CROSSX01)")
|
||||||
|
|
||||||
if quapos_present and 2 <= quapos < 10:
|
if quapos_present:
|
||||||
instructions.append("LC(LOWACC21)")
|
if 2 <= quapos < 10:
|
||||||
|
instructions.append("LC(LOWACC01)")
|
||||||
return ";".join(instructions)
|
return ";".join(instructions)
|
||||||
|
|
||||||
condtn = _int_attr(attrs, "CONDTN", 0)
|
condtn = _int_attr(attrs, "CONDTN", 0)
|
||||||
@@ -860,13 +868,13 @@ def resolve_restrn01_instruction(attrs: dict[str, Any]) -> str:
|
|||||||
if not restrn:
|
if not restrn:
|
||||||
return ""
|
return ""
|
||||||
if restrn & {7, 8, 14}:
|
if restrn & {7, 8, 14}:
|
||||||
symbol = "ENTRES61" if restrn & {1, 2, 3, 4, 5, 6} else "ENTRES71" if restrn & {9, 10, 11, 12, 13, 14, 15} else "ENTRES51"
|
symbol = "ENTRES61" if restrn & {1, 2, 3, 4, 5, 6} else "ENTRES71" if restrn & {9, 10, 11, 12, 13} else "ENTRES51"
|
||||||
elif restrn & {1, 2}:
|
elif restrn & {1, 2}:
|
||||||
symbol = "ACHRES61" if restrn & {3, 4, 5, 6} else "ACHRES71" if restrn & {9, 10, 11, 12, 13, 14, 15} else "ACHRES51"
|
symbol = "ACHRES61" if restrn & {3, 4, 5, 6} else "ACHRES71" if restrn & {9, 10, 11, 12, 13} else "ACHRES51"
|
||||||
elif restrn & {3, 4, 5, 6}:
|
elif restrn & {3, 4, 5, 6}:
|
||||||
symbol = "FSHRES71" if restrn & {9, 10, 11, 12, 13, 14, 15} else "FSHRES51"
|
symbol = "FSHRES71" if restrn & {9, 10, 11, 12, 13} else "FSHRES51"
|
||||||
else:
|
else:
|
||||||
symbol = "INFARE51" if restrn & {9, 10, 11, 12, 13, 14, 15} else "RSRDEF51"
|
symbol = "INFARE51" if restrn & {9, 10, 11, 12, 13} else "RSRDEF51"
|
||||||
return f"SY({symbol})"
|
return f"SY({symbol})"
|
||||||
|
|
||||||
|
|
||||||
@@ -878,18 +886,18 @@ def resolve_resare02_instruction(
|
|||||||
group_a = {1, 8, 9, 12, 14, 19, 21, 25}
|
group_a = {1, 8, 9, 12, 14, 19, 21, 25}
|
||||||
group_b = {4, 5, 6, 7, 10, 18, 20, 22, 23, 24}
|
group_b = {4, 5, 6, 7, 10, 18, 20, 22, 23, 24}
|
||||||
priority = False
|
priority = False
|
||||||
if restrn:
|
if "RESTRN" in attrs:
|
||||||
if restrn & {7, 8, 14}:
|
if restrn & {7, 8, 14}:
|
||||||
symbol = "ENTRES61" if (restrn & {1,2,3,4,5,6} or catrea & group_a) else "ENTRES71" if (restrn & {9,10,11,12,13,14,15} or catrea & group_b) else "ENTRES51"
|
symbol = "ENTRES61" if (restrn & {1,2,3,4,5,6} or catrea & group_a) else "ENTRES71" if (restrn & {9,10,11,12,13} or catrea & group_b) else "ENTRES51"
|
||||||
line_code = "RESARE51"; priority = True
|
line_code = "RESARE51"; priority = True
|
||||||
elif restrn & {1, 2}:
|
elif restrn & {1, 2}:
|
||||||
symbol = "ACHRES61" if (restrn & {3,4,5,6} or catrea & group_a) else "ACHRES71" if (restrn & {9,10,11,12,13,14,15} or catrea & group_b) else "RESTRN51"
|
symbol = "ACHRES61" if (restrn & {3,4,5,6} or catrea & group_a) else "ACHRES71" if (restrn & {9,10,11,12,13} or catrea & group_b) else "RESTRN51"
|
||||||
line_code = "RESARE51"; priority = True
|
line_code = "RESARE51"; priority = True
|
||||||
elif restrn & {3,4,5,6}:
|
elif restrn & {3,4,5,6}:
|
||||||
symbol = "FSHRES51" if catrea & group_a else "FSHRES71" if (restrn & {9,10,11,12,13,14,15} or catrea & group_b) else "FSHRES51"
|
symbol = "FSHRES51" if catrea & group_a else "FSHRES71" if (restrn & {9,10,11,12,13} or catrea & group_b) else "FSHRES51"
|
||||||
line_code = "FSHRES51"; priority = True
|
line_code = "FSHRES51"; priority = True
|
||||||
else:
|
else:
|
||||||
symbol = "INFARE51" if restrn & {9,10,11,12,13,14,15} else "RSRDEF51"
|
symbol = "INFARE51" if restrn & {9,10,11,12,13} else "RSRDEF51"
|
||||||
line_code = "CTYARE51"
|
line_code = "CTYARE51"
|
||||||
else:
|
else:
|
||||||
symbol = "CTYARE71" if (catrea & group_a and catrea & group_b) else "CTYARE51" if catrea & group_a else "INFARE51" if catrea & group_b else "RSRDEF51"
|
symbol = "CTYARE71" if (catrea & group_a and catrea & group_b) else "CTYARE51" if catrea & group_a else "INFARE51" if catrea & group_b else "RSRDEF51"
|
||||||
|
|||||||
Reference in New Issue
Block a user