Mapping Equipment Locations Across Multi-Building Campuses

On this page

Problem statement

You need a Python resolver that assigns every grant-funded instrument to a validated campus → building → floor → room coordinate even when primary geolocation fails — Wi-Fi triangulation dies inside an RF-shielded lab, a portal API times out, or a coordinate payload arrives malformed — so that a network partition never produces a misplaced asset that becomes a 2 CFR §200.313 property finding or an OSHA hazard-zone violation.

This task sits under Lab Location & Asset Mapping, part of the broader Equipment Calibration & Lab Inventory Tracking practice. It is intentionally narrow: it resolves a location, degrades gracefully when telemetry is unreliable, and fingerprints every decision. It does not own the authorized facility registry — that canonical hierarchy and its quarantine path belong to the parent guide — and it does not adjudicate hazard policy; it produces the tamper-evident evidence a compliance officer relies on.

Prerequisites

Before deploying the resolver, confirm the following environment and policy configuration:

  • Python 3.10+ for modern type hints, the union (X | None) syntax, and datetime.timezone. The resolution and hashing path uses only the standard library (hashlib, json, logging), which keeps the daemon runnable during a network partition with no outbound dependency.
  • Pydantic 2.x for payload validation. Inbound telemetry from RFID readers, the procurement ERP, and the laboratory information management system (LIMS) is validated identically to every other entry point through the shared Schema Validation Pipelines, so a malformed coordinate is rejected the same way regardless of source.
  • A locally cached facility registry. The authorized campus → building → floor → room hierarchy must be replicated to disk so fallback resolution never reaches across the network. Treat it as a version-controlled artifact synced through your CI/CD pipeline, not a value hard-coded in a telemetry collector.
  • A confidence threshold from policy. Fallback resolution must never override primary telemetry when its confidence exceeds the institutional threshold (90% in the examples below). That number is owned by compliance, governed by the University Policy Mapping Frameworks, and read at runtime — not edited in code.

Policy and compliance context

Spatial asset tracking is a regulatory requirement, not an IT convenience. The resolver is architected so the regulatory boundary is satisfied by design:

  • 2 CFR §200.313 (Uniform Guidance). NIH and NSF require a documented location for federally funded equipment and periodic physical-inventory reconciliation. A partition that loses an asset’s room of record is a control gap; deterministic fallback keeps every instrument anchored to an authorized zone.
  • OSHA Laboratory Standard (29 CFR 1910.1450). Safety apparatus — fume hoods, emergency showers, radiation monitors — must remain inside designated hazard zones. Fallback resolution prevents the false negative that a network outage would otherwise produce when a responder pulls a hazard map.
  • EPA EPCRA / RCRA. Sensor and reagent placement must align with permitted containment boundaries. Spatial drift in telemetry can invalidate an environmental compliance report, so each decision is hashed and timestamped for the record.

Every routing decision must be timestamped, hashed, and appended to an append-only ledger, satisfying the audit-trail requirement without continuous cloud connectivity. A record that cannot be resolved to an authorized zone is escalated, never silently assigned — the same guarantee the platform inherits from the Grant Lifecycle Architecture Design.

Step-by-step implementation

The flow the resolver enforces: validate the inbound payload, attempt primary geolocation, and on failure or low confidence degrade deterministically to the cached building registry rather than failing the resolution — then fingerprint every outcome into an append-only ledger. Identical inputs always produce identical outputs, so a re-processed payload writes no duplicate entry.

Deterministic location-resolution decision flow with confidence gating and quarantine An equipment payload enters a decision: are the WGS84 coordinates valid? If yes, primary geolocation resolution runs and a second decision checks whether confidence meets the policy threshold. A passing fix flows straight to the audit-hash step. A failing fix, or an invalid coordinate, degrades to the fallback path: resolve against the building registry, parse the building code from the asset_id, then decide whether that code is in the registry. A known code maps to the default room for the building and joins the audit-hash step; an unknown code is routed to the quarantine queue for principal-investigator sign-off rather than mis-assigned. Both successful paths converge on computing the audit hash and appending it to the ledger. yes no low pass yes no Equipment payload Valid WGS84coordinates? Primary geolocationresolution Fallback to buildingregistry Confidence ≥ policythreshold? Parse building codefrom asset_id Code inregistry? Route to quarantinequeue Default room forbuilding Compute audit hash +append to ledger

Figure: missing, malformed, or low-confidence coordinates degrade to a deterministic local-registry lookup; an unknown building code is quarantined for PI sign-off rather than mis-assigned.

Step 1 — Model the payload and the authorized registry

Validate the inbound telemetry with Pydantic so a malformed coordinate is rejected at construction, and replicate the authorized building hierarchy to a local cache the resolver can read with no network. The registry is the determinism anchor: the same building code always resolves to the same default room.

python
import hashlib
import json
import logging
from datetime import datetime, timezone
from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field, ValidationError

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s",
    handlers=[logging.FileHandler("location_audit.log", mode="a")],
)
logger = logging.getLogger("campus_location_resolver")

# Confidence below this (read from policy at runtime) forces a fallback.
PRIMARY_CONFIDENCE_FLOOR = 0.90


class Coordinates(BaseModel):
    """WGS84 point; bounds are enforced at construction so a malformed read is rejected."""
    lat: float = Field(..., ge=-90.0, le=90.0)
    lon: float = Field(..., ge=-180.0, le=180.0)
    accuracy_m: float = Field(..., gt=0.0, description="Reported horizontal accuracy in metres")


class EquipmentPayload(BaseModel):
    asset_id: str = Field(..., min_length=8, description="Institutional asset tag, e.g. SCI-04-8821")
    serial_number: str
    raw_coordinates: Optional[Coordinates] = None  # None simulates a partition / API failure
    timestamp: str


# Deterministic local cache; synced from the authorized facility master via CI/CD.
BUILDING_REGISTRY: dict[str, dict[str, object]] = {
    "ENG-01": {"campus": "MAIN", "floors": ["B1", "1", "2", "3"], "default_room": "AA-101"},
    "SCI-04": {"campus": "MAIN", "floors": ["1", "2", "3", "4"], "default_room": "BC-205"},
    "BIO-02": {"campus": "WEST", "floors": ["1", "2"], "default_room": "DA-110"},
}

Step 2 — Resolve primary telemetry, with confidence gating

Attempt the primary geolocation first. A point that validates and clears the policy confidence floor is accepted; anything weaker is treated as unreliable and handed to the fallback. Wide accuracy radii — common after a Wi-Fi handoff in a steel-framed building — collapse confidence so a near-miss never masquerades as a confirmed room.

python
class RoutingMethod(str, Enum):
    PRIMARY = "primary_geolocation"
    FALLBACK = "local_registry_fallback"
    QUARANTINED = "quarantined"


class LocationResolution(BaseModel):
    asset_id: str
    campus_id: str
    building_code: str
    floor_identifier: str
    room_code: str
    routing_method: RoutingMethod
    confidence_score: float
    audit_hash: str = ""


def _coordinate_confidence(coords: Coordinates) -> float:
    """Map reported accuracy to a confidence score; a 50 m radius is effectively useless indoors."""
    if coords.accuracy_m <= 5.0:
        return 0.98
    if coords.accuracy_m <= 15.0:
        return 0.92
    return 0.60  # too coarse to pin a room — forces fallback


def _resolve_primary(payload: EquipmentPayload) -> Optional[LocationResolution]:
    """Return a resolution only if telemetry is present and clears the policy floor."""
    if payload.raw_coordinates is None:
        return None
    confidence = _coordinate_confidence(payload.raw_coordinates)
    if confidence < PRIMARY_CONFIDENCE_FLOOR:
        logger.info("Primary telemetry below floor (%.2f) for %s; deferring to fallback.",
                    confidence, payload.asset_id)
        return None
    # A real deployment reverse-geocodes the point against floor polygons here.
    return LocationResolution(
        asset_id=payload.asset_id, campus_id="MAIN", building_code="GEO",
        floor_identifier="AUTO", room_code="AUTO-001",
        routing_method=RoutingMethod.PRIMARY, confidence_score=confidence,
    )

Step 3 — Degrade deterministically to the cached registry

When primary telemetry is absent or too coarse, parse the building code from the asset tag and resolve against the local registry. An unrecognised code is quarantined, not defaulted to an arbitrary building — silently assigning an unknown asset to ENG-01 would corrupt the property record and any hazard map drawn from it.

python
def _resolve_fallback(payload: EquipmentPayload) -> LocationResolution:
    """Deterministic resolution against the offline registry; unknown codes are quarantined."""
    building_code = payload.asset_id.split("-")[0] if "-" in payload.asset_id else ""

    if building_code not in BUILDING_REGISTRY:
        logger.warning("Unregistered building code '%s' for %s; routing to quarantine.",
                       building_code, payload.asset_id)
        return LocationResolution(
            asset_id=payload.asset_id, campus_id="UNKNOWN", building_code=building_code or "UNKNOWN",
            floor_identifier="UNKNOWN", room_code="QUARANTINE",
            routing_method=RoutingMethod.QUARANTINED, confidence_score=0.0,
        )

    entry = BUILDING_REGISTRY[building_code]
    return LocationResolution(
        asset_id=payload.asset_id, campus_id=str(entry["campus"]), building_code=building_code,
        floor_identifier=str(entry["floors"][0]), room_code=str(entry["default_room"]),
        routing_method=RoutingMethod.FALLBACK, confidence_score=0.85,
    )

Step 4 — Fingerprint each decision and run the resolver idempotently

Bind the asset identity, resolved location, and timestamp into a single SHA-256 digest computed from canonical (sort_keys=True) JSON, then cache by asset_id so a replayed payload returns the recorded decision and appends nothing. This is what makes a cron overlap or a re-uploaded export safe.

python
class CampusLocationResolver:
    """Idempotent, offline-tolerant resolver with a cryptographic audit trail."""

    def __init__(self, registry: dict[str, dict[str, object]]):
        self.registry = registry
        self._ledger: dict[str, LocationResolution] = {}

    def _audit_hash(self, payload: EquipmentPayload, res: LocationResolution) -> str:
        canonical = json.dumps(
            {
                "asset_id": payload.asset_id,
                "timestamp": payload.timestamp,
                "campus": res.campus_id,
                "building": res.building_code,
                "floor": res.floor_identifier,
                "room": res.room_code,
            },
            sort_keys=True,
        )
        return hashlib.sha256(canonical.encode("utf-8")).hexdigest()

    def resolve(self, payload: EquipmentPayload) -> LocationResolution:
        if payload.asset_id in self._ledger:
            logger.info("Idempotent replay: %s already resolved.", payload.asset_id)
            return self._ledger[payload.asset_id]

        resolution = _resolve_primary(payload) or _resolve_fallback(payload)
        resolution.audit_hash = self._audit_hash(payload, resolution)
        self._ledger[payload.asset_id] = resolution
        logger.info("Resolved %s via %s | hash=%s",
                    payload.asset_id, resolution.routing_method.value, resolution.audit_hash)
        return resolution


if __name__ == "__main__":
    resolver = CampusLocationResolver(BUILDING_REGISTRY)
    try:
        payload = EquipmentPayload(
            asset_id="SCI-04-8821",
            serial_number="SN-99421",
            raw_coordinates=None,  # simulate an RF-shielded lab / API timeout
            timestamp=datetime.now(timezone.utc).isoformat(),
        )
    except ValidationError as exc:
        logger.error("Rejected malformed payload: %s", exc.errors())
        raise
    print(resolver.resolve(payload).model_dump_json(indent=2))

Schema and field reference

The fields the resolver reads and stamps. Widen the registry in your version-controlled facility config, not in code.

Field Type Constraint Source / rule
asset_id str required, min_length=8, encodes building code prefix 2 CFR §200.313 equipment tracking
raw_coordinates.lat float -90 ≤ lat ≤ 90 WGS84 bounds
raw_coordinates.lon float -180 ≤ lon ≤ 180 WGS84 bounds
raw_coordinates.accuracy_m float > 0; drives confidence Indoor positioning quality gate
confidence_score float fallback below PRIMARY_CONFIDENCE_FLOOR Institutional policy threshold
routing_method enum primary | fallback | quarantined Resolution provenance for audit
room_code str resolved against registry Authorized facility master
audit_hash str 64-char SHA-256, canonical JSON Non-repudiation (NIH/OSHA/EPA)

Verification

Confirm a run behaved correctly before trusting it in production:

  1. Deterministic replay. Resolve the same payload twice. The second call must log Idempotent replay and return the identical audit_hash — proof that re-processing writes no duplicate ledger entry.
  2. Reproduce the hash. Recompute sha256 over the canonical {asset_id, timestamp, campus, building, floor, room} dict and confirm it equals the stored audit_hash. An inspector can repeat this independently to tie a physical location to a tamper-evident digest.
  3. Force the fallback path. Pass raw_coordinates=None (or an accuracy_m above 15) and confirm routing_method is local_registry_fallback with a confidence of 0.85, not a failed resolution.
  4. Confirm the quarantine branch. Resolve an asset whose prefix is absent from the registry and assert routing_method == quarantined and room_code == "QUARANTINE" — never an arbitrary default building.

Troubleshooting

Three gotchas specific to this resolver:

  • Coordinate drift during Wi-Fi handoffs. A single roaming event can emit one wild outlier point with a tight reported accuracy, briefly clearing the confidence floor. Apply a sliding-window filter upstream — require three consecutive payloads within a small radius before accepting a primary fix — so one bad read cannot relocate an asset.
  • Registry schema mismatch after a new building is commissioned. A freshly opened building absent from BUILDING_REGISTRY will quarantine every asset tagged with its code. Sync the registry through your CI/CD pipeline before the assets arrive, and during a sustained sync outage divert through your Fallback Routing Protocols rather than letting quarantine saturate.
  • Audit-hash verification failure. A mismatch almost always means the JSON was serialized without sort_keys=True, or a timestamp was rewritten after the fact. Always hash the canonical form, and treat a non-reproducing digest as a potential integrity event — restricting who may regenerate a hash is governed by the Security Boundary Configuration.

Frequently asked questions

Why quarantine an unknown building code instead of defaulting to a known building?
A silent default writes a false location into the property record and any hazard map derived from it — exactly the failure a 2 CFR §200.313 or OSHA inspection surfaces. Routing the asset to a quarantine queue holds it for principal-investigator sign-off; once the registry is corrected, re-resolving is safe because resolution is deterministic and idempotent.
How does the resolver keep working during a full network partition?
The authorized building hierarchy is replicated to a local cache and the hashing path uses only the standard library, so fallback resolution makes no outbound call. Primary geolocation is attempted first; when it is unreachable or returns a low-confidence fix, the resolver degrades to the cached registry and still produces a deterministic, hashed decision.
Should fallback resolution ever override a primary coordinate fix?
No. When primary telemetry clears the policy confidence floor it always wins; fallback only engages when telemetry is absent or too coarse to pin a room. The threshold itself is a policy value read at runtime, so compliance can raise or lower it without a code change.