/** * GpsNeo6mElement.ts — `` u-blox NEO-6M GPS module. * * Visual follows the common GY-GPS6MV2 breakout: blue PCB, u-blox chip, * stacked beige ceramic patch antenna and a 4-pin header (VCC / RX / TX / * GND) on the left edge. * * The behaviour (a 9600-baud NMEA stream — GPGGA + GPRMC every second — * pushed out of the TX pin into the wired board UART) lives in * `simulation/parts/GpsParts.ts`. * * Runtime surface used by the part: * - `lat` / `lng` / `altitude` / `speed` properties (position fed into * the NMEA sentences; synced from metadata defaults + property dialog * by DynamicComponent, and live-updated via the SensorControlPanel). * - `set pps(on)` — pulses the red PPS LED once per emitted NMEA cycle. * * Per CLAUDE.md §6a this MUST be a real Web Component — the wire system * reads `pinInfo` from the DOM node to place wire endpoints on pin tips. */ const BODY_W = 150; const BODY_H = 100; // Left-edge 0.1" header (GY-GPS6MV2 silkscreen order top→bottom). const PIN_X = 9; const PIN_SPACING = 18; const PIN_Y0 = 23; const PIN_NAMES = ['VCC', 'RX', 'TX', 'GND'] as const; const PIN_RING = ['#C08540', '#8F5DBF', '#2E9E5B', '#B4AEAB']; function signalsFor(name: string) { if (name === 'GND') return [{ type: 'power', signal: 'GND' }]; if (name === 'VCC') return [{ type: 'power', signal: 'VCC' }]; // The module's TX carries the NMEA stream towards the board's RX. if (name === 'TX') return [{ type: 'usart', signal: 'TX' }]; if (name === 'RX') return [{ type: 'usart', signal: 'RX' }]; return []; } class GpsNeo6mElement extends HTMLElement { /** Decimal-degree position encoded into the NMEA sentences. */ lat = 40.4168; lng = -3.7038; /** Altitude above mean sea level, metres (GPGGA field 9). */ altitude = 667; /** Speed over ground, knots (GPRMC field 7). */ speed = 0; private _pps = false; constructor() { super(); this.attachShadow({ mode: 'open' }); } connectedCallback(): void { this.render(); } /** Wire system reads this from the DOM — do NOT memoize. */ get pinInfo() { return PIN_NAMES.map((name, i) => ({ name, x: PIN_X, y: PIN_Y0 + i * PIN_SPACING, number: i + 1, signals: signalsFor(name), })); } /** PPS indicator — the part pulses this once per emitted NMEA cycle. */ set pps(on: boolean) { this._pps = Boolean(on); const led = this.shadowRoot?.querySelector('[data-pps-led]'); if (led) led.setAttribute('fill', this._pps ? '#FF3B30' : '#5A1512'); } get pps(): boolean { return this._pps; } private render(): void { if (!this.shadowRoot) return; this.shadowRoot.innerHTML = ` u-blox NEO-6M PPS GPS NEO-6M NMEA 9600 baud ${PIN_NAMES.map( (name, i) => `${name}`, ).join('')} ${PIN_NAMES.map( (_, i) => ``, ).join('')} `; } } if (typeof customElements !== 'undefined' && !customElements.get('velxio-gps-neo6m')) { customElements.define('velxio-gps-neo6m', GpsNeo6mElement); } export type { GpsNeo6mElement };