A PTZ/varifocal datasheet FOV is a capability range, not the lens's
current zoom, so replacing the frustum's estimate with its wide end
presented unknown optical state as known. Parse the full horizontal
range (ignoring vertical, "combined" panoramic and lens-note figures),
carry it as specFovMinDeg/specFovMaxDeg, and substitute the datasheet
FOV only for fixed single-lens hardware. The HUD labels the range as
capability beside the estimate. This also stops the Q6000-E Mk II's
"360 combined" figure from being read as a 360-degree lens.
Some catalogs publish a full product title rather than the bare model, e.g.
"AXIS Q6155-E PTZ Dome Network Camera" — which the exact model/alias match
misses. Add a fallback: after the exact match, scan for a record whose
DISTINCTIVE token (a >=4-char model/alias word containing a digit or hyphen,
so "dome"/"network"/"ptz"/"hd" never qualify) appears as a whole word in the
query. The most specific (longest) matched token wins; if two records tie on
the longest matched token the result is ambiguous and returns null, so the
lookup never guesses. Exact matches are unchanged and still take precedence.
Grow the enrichment table from the 4-model starter to the hardware the
currently integrated public catalogs actually publish, so identified
cameras render datasheet FOV cones and spec cards instead of estimates:
- King County (WA) road cameras: Cohu 3950 / 3960 (3965) / 3920 iDome
(3925), Bosch AUTODOME IP 5000i.
- Sarasota County (FL) + Sioux Falls (SD) open data: Bosch AUTODOME IP
starlight 7000i / starlight 7000 HD / dynamic 7000 HD, the VG4 AUTODOME
(500i), Bosch AUTODOME 800 (VG5-836), and Axis Q6155-E, Q6054-E,
Q6000-E Mk II, and P3707-PE.
All records are vendored verbatim from the CCTV Camera Database (CC0),
each transcribed from a manufacturer datasheet. Analog Cohu positioners
carry no fov_deg (their datasheets publish none), so they enrich the HUD
specs without a datasheet cone; every other model carries a real FOV.
Adds a test asserting the vendored table covers each integrated catalog's
published hardware strings.
When a CCTV source pack (or a seed) names its hardware `model`, resolve it
against a camera-model spec table: the coverage frustum then uses the
manufacturer's horizontal FOV instead of the pose estimate, and the HUD meta
line gains the model's resolution, night-vision, and PTZ specs. Cameras with
no identified model are unchanged — unknown never implies anything, matching
the layer's existing honesty about RAW PRIOR poses.
Spec records are vendored verbatim from the CCTV Camera Database
(https://github.com/ch-bas/cctv-camera-database, CC0-1.0), an open dataset of
12,000+ camera models transcribed from manufacturer datasheets. This lands a
small starter table plus the join + parsing helpers; source packs that publish
their hardware can grow it. Records describe camera products (optics,
resolution, night vision) only — no endpoints or access data.
- src/layers/cctv/modelSpecs.js: spec table, case-insensitive/alias lookup,
wide-end FOV parser, HUD summary formatter
- src/layers/cctv/modelSpecs.test.mjs: lookup/alias/unknown, FOV parsing
across the real fov string shapes, summary rendering
- src/layers/cctv/catalog.js: apply enrichment in both catalog builders
- src/layers/cctv/presentation.js: label a datasheet FOV, append the spec
summary, and pass fovSource + spec through the public camera state
Every pack sets headingConfidence on each camera, but nothing consumed it:
a bearing synthesized by fallbackHeadingFromId (a hash of the id string,
no geographic input) rendered exactly like a surveyed one — 2,827 of 3,997
cameras in a default catalog. Fixes#639.
Two consumers, plus a passthrough:
- HUD: the heading token is tagged when the bearing is synthetic —
`HDG 194° (ESTIMATED)` — so a hash never reads as a measured facing.
- Wireframe: a low-confidence camera draws its coverage frustum with dashed
polylines instead of solid ones. Colors, widths, and the active/idle
emphasis are unchanged; the dash is the only signal.
- Public camera state gains `headingConfidence` (the raw pack flag) and
`headingEstimated` (the derived bit) so UI consumers never re-derive it.
A human-vouched pose always wins over the pack flag: a manually saved
calibration (calSource:'manual') or a curated catalog entry
(poseSource:'curated') is never presented as estimated, mirroring the CAL
badge states in calibration.js.
- src/layers/cctv/headingConfidence.js: pure helpers (isHeadingEstimated,
headingHudToken)
- src/layers/cctv/headingConfidence.test.mjs: confidence normalization,
calibration/curation overrides, HUD token formatting
- src/layers/cctv/presentation.js: HUD token + public-state passthrough
- src/layers/cctv/rendering.js: dashed materials for estimated bearings