UNPKG

a429-flight-display

Version:

React components for ARINC 429 Flight Display with primary flight instruments

453 lines (452 loc) 20.4 kB
var P = Object.defineProperty; var O = (t, i, n) => i in t ? P(t, i, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[i] = n; var N = (t, i, n) => O(t, typeof i != "symbol" ? i + "" : i, n); import { jsxs as a, jsx as e, Fragment as A } from "react/jsx-runtime"; class g { constructor(i, n, c, o) { N(this, "label"); N(this, "sdi"); N(this, "data"); N(this, "ssm"); N(this, "parity"); this.label = i, this.sdi = n, this.data = c, this.ssm = o, this.parity = this.calculateParity(); } calculateParity() { const i = this.label << 24 | this.sdi << 22 | this.data << 3 | this.ssm << 1; let n = 0; for (let c = 0; c < 31; c++) i & 1 << c && n++; return n % 2 === 0 ? 1 : 0; } } const u = { ALTITUDE: 131, AIRSPEED: 134, MACH: 135, HEADING: 146, VERTICAL_SPEED: 245, PITCH: 212, ROLL: 213, TEMPERATURE: 137 }, p = { NORMAL_OPERATION: 3 }, R = ({ flightData: t }) => { const i = () => [ new g(u.ALTITUDE, 0, Math.round(t.altitude), p.NORMAL_OPERATION), new g(u.AIRSPEED, 0, Math.round(t.airspeed), p.NORMAL_OPERATION), new g(u.MACH, 0, Math.round(t.mach * 1e5), p.NORMAL_OPERATION), new g(u.HEADING, 0, Math.round(t.heading), p.NORMAL_OPERATION), new g(u.VERTICAL_SPEED, 0, Math.round(t.verticalSpeed), p.NORMAL_OPERATION), new g(u.PITCH, 0, Math.round(t.pitch * 100), p.NORMAL_OPERATION), new g(u.ROLL, 0, Math.round(t.roll * 100), p.NORMAL_OPERATION) ]; return /* @__PURE__ */ a("div", { className: "bg-gray-900 rounded-xl p-6 shadow-2xl", children: [ /* @__PURE__ */ a("div", { className: "flex justify-between items-center mb-4", children: [ /* @__PURE__ */ e("h2", { className: "text-xl font-bold text-white", children: "Live ARINC 429 Data Bus" }), /* @__PURE__ */ e("div", { className: "text-green-400 font-mono text-sm", children: t.timestamp.toLocaleTimeString() }) ] }), /* @__PURE__ */ e("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4", children: i().map((n, c) => { const o = Object.keys(u).find( (r) => u[r] === n.label ), s = "0x" + (n.label << 24 | n.sdi << 22 | n.data << 3 | n.ssm << 1 | n.parity).toString(16).toUpperCase().padStart(8, "0"); return /* @__PURE__ */ a("div", { className: "bg-gray-800 rounded-lg p-4 border border-gray-600", children: [ /* @__PURE__ */ e("div", { className: "text-yellow-400 font-semibold text-sm mb-2", children: o }), /* @__PURE__ */ a("div", { className: "text-white font-mono text-xs space-y-1", children: [ /* @__PURE__ */ a("div", { children: [ "Label: ", n.label.toString(8).padStart(3, "0") ] }), /* @__PURE__ */ a("div", { children: [ "Data: ", n.data ] }), /* @__PURE__ */ a("div", { children: [ "Hex: ", s ] }) ] }), /* @__PURE__ */ e("div", { className: "text-green-400 text-xs mt-2", children: "● NORMAL" }) ] }, c); }) }), /* @__PURE__ */ e("div", { className: "mt-6 text-center", children: /* @__PURE__ */ a("div", { className: "inline-flex items-center space-x-4 bg-gray-800 px-6 py-3 rounded-lg", children: [ /* @__PURE__ */ e("div", { className: "w-3 h-3 bg-green-400 rounded-full animate-pulse" }), /* @__PURE__ */ e("span", { className: "text-white font-semibold", children: "ARINC 429 Bus Active" }), /* @__PURE__ */ e("span", { className: "text-gray-400 font-mono text-sm", children: "12.5 kHz" }) ] }) }) ] }); }, T = ({ pitch: t, roll: i }) => { const c = () => { const s = []; for (let r = -30; r <= 30; r += 5) { if (r === 0) continue; const l = r % 10 === 0; s.push( /* @__PURE__ */ e("div", { className: "absolute flex items-center justify-center w-full", children: /* @__PURE__ */ a( "div", { className: "flex items-center justify-center", style: { transform: `translateY(${-r * 3}px)` }, children: [ /* @__PURE__ */ e( "div", { className: `bg-white ${l ? "h-0.5 w-16" : "h-0.5 w-8"}` } ), l && /* @__PURE__ */ a(A, { children: [ /* @__PURE__ */ e("span", { className: "text-white text-xs font-mono ml-2 select-none", children: Math.abs(r) }), /* @__PURE__ */ e("div", { className: "bg-white h-0.5 w-16 ml-2" }), /* @__PURE__ */ e("span", { className: "text-white text-xs font-mono ml-2 select-none", children: Math.abs(r) }) ] }) ] } ) }, r) ); } return s; }, o = () => { const s = []; return [-60, -45, -30, -20, -10, 0, 10, 20, 30, 45, 60].forEach((l) => { const d = [0, -30, 30, -60, 60].includes(l), h = [-10, -20, 10, 20, -45, 45].includes(l); s.push( /* @__PURE__ */ a( "div", { className: "absolute top-1 left-1/2 origin-bottom transform -translate-x-1/2", style: { transform: `translateX(-50%) rotate(${l}deg)`, transformOrigin: "center 92px" }, children: [ /* @__PURE__ */ e( "div", { className: `bg-white ${d ? "w-0.5 h-4" : h ? "w-0.5 h-3" : "w-0.5 h-2"}` } ), (l === -30 || l === 30 || l === -60 || l === 60) && /* @__PURE__ */ e("div", { className: "text-white text-xs font-mono text-center mt-1 select-none", children: Math.abs(l) }) ] }, l ) ); }), s; }; return /* @__PURE__ */ a("div", { className: "relative w-48 h-48 rounded-full overflow-hidden border-4 border-gray-600 shadow-2xl bg-gray-900", children: [ /* @__PURE__ */ a( "div", { className: "absolute inset-0 bg-gradient-to-b from-sky-400 via-sky-300 to-amber-500", style: { transform: `rotate(${i}deg) translateY(${t * 3}px)`, transformOrigin: "center center", width: "200%", height: "200%", left: "-50%", top: "-50%" }, children: [ /* @__PURE__ */ e( "div", { className: "absolute left-0 right-0 h-1 bg-white", style: { top: "50%", transform: "translateY(-50%)" } } ), /* @__PURE__ */ e( "div", { className: "absolute inset-0 flex justify-center items-center", style: { top: "50%", left: "50%", transform: "translate(-50%, -50%)" }, children: c() } ) ] } ), /* @__PURE__ */ e("div", { className: "absolute inset-0", children: o() }), /* @__PURE__ */ e( "div", { className: "absolute top-1 left-1/2 w-0 h-0 border-l-3 border-r-3 border-b-6 border-transparent border-b-yellow-400 transform -translate-x-1/2 z-20", style: { transform: `translateX(-50%) rotate(${i}deg)`, transformOrigin: "center 92px" } } ), /* @__PURE__ */ e("div", { className: "absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-30", children: /* @__PURE__ */ a("div", { className: "relative flex items-center justify-center", children: [ /* @__PURE__ */ e("div", { className: "w-12 h-1 bg-yellow-400" }), /* @__PURE__ */ e("div", { className: "absolute w-1 h-6 bg-yellow-400" }), /* @__PURE__ */ e("div", { className: "absolute w-2 h-2 bg-yellow-400 rounded-full" }) ] }) }), /* @__PURE__ */ e("div", { className: "absolute top-2 left-1/2 w-0 h-0 border-l-2 border-r-2 border-b-4 border-transparent border-b-white transform -translate-x-1/2 z-10" }) ] }); }, S = ({ value: t, title: i, unit: n, unitTooltip: c, generateMarks: o, getValueColor: s = () => "text-white", isMajorTick: r = (m) => m % 20 === 0, formatValue: l = (m) => m.toString(), formatCurrentValue: d = (m) => Math.round(m).toString(), pixelsPerUnit: h = 2, pointerSide: b = "right", className: x = "" }) => { const m = o(t), f = b === "left"; return console.log(), /* @__PURE__ */ a("div", { className: `bg-gray-900 text-white rounded-lg w-32 h-64 relative overflow-hidden border-2 border-gray-600 flex flex-col ${x}`, children: [ /* @__PURE__ */ e("div", { className: "text-center text-xs font-bold text-gray-300 py-1 border-b border-gray-600", children: i }), /* @__PURE__ */ a("div", { className: "flex-1 relative overflow-hidden", children: [ /* @__PURE__ */ e("div", { className: "absolute inset-0 flex flex-col justify-center", children: /* @__PURE__ */ e("div", { className: "relative h-48 mx-2", children: m.map((v) => { const w = (t - v) * h, y = 96 + w; if (Math.abs(w) > 90 || y < 0 || y > 202) return null; const M = r(v); return /* @__PURE__ */ a( "div", { className: "absolute flex items-center w-full", style: { top: `${y}px`, transform: "translateY(-50%)", flexDirection: f ? "row-reverse" : "row" }, children: [ M && /* @__PURE__ */ e( "div", { className: `text-xs font-mono w-8 ${f ? "text-left" : "text-right"} ${s(v)}`, children: l(v) } ), /* @__PURE__ */ e( "div", { className: `${f ? "mr-1" : "ml-1"} bg-white ${M ? "h-0.5 w-4" : "h-0.5 w-2"}` } ) ] }, v ); }) }) }), /* @__PURE__ */ e( "div", { className: `absolute ${f ? "left-2" : "right-2"} top-1/2 w-0 h-0 ${f ? "border-t-4 border-b-4 border-r-6 border-transparent border-r-yellow-400" : "border-t-4 border-b-4 border-l-6 border-transparent border-l-yellow-400"} transform -translate-y-1/2 z-20` } ), /* @__PURE__ */ e( "div", { className: `absolute top-1/2 ${f ? "left-8" : "right-8"} bg-black px-2 py-1 border border-white text-yellow-400 font-bold text-sm transform -translate-y-1/2 z-10 min-w-[3rem] text-center`, children: d(t) } ) ] }), /* @__PURE__ */ a("div", { className: "relative group text-center text-xs text-gray-300 py-1 border-t border-gray-600", children: [ /* @__PURE__ */ e("span", { className: "cursor-help", children: n }), c && /* @__PURE__ */ a("div", { className: "absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-50 pointer-events-none max-w-32 text-center whitespace-normal", children: [ c, /* @__PURE__ */ e("div", { className: "absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-2 border-r-2 border-t-2 border-transparent border-t-gray-800" }) ] }) ] }) ] }); }, E = ({ airspeed: t }) => /* @__PURE__ */ e( S, { value: t, title: "AIRSPEED", unit: "KTS", unitTooltip: "Knots (nautical mph)", generateMarks: (o) => { const s = [], d = Math.max( 0, Math.floor((o - 100) / 10) * 10 ), h = Math.ceil((o + 100) / 10) * 10; for (let b = d; b <= h; b += 10) s.push(b); return s; }, getValueColor: (o) => o >= 40 && o <= 100 ? "text-white" : o >= 100 && o <= 180 ? "text-green-400" : o >= 180 && o <= 220 ? "text-yellow-400" : o > 220 ? "text-red-400" : "text-gray-400", isMajorTick: (o) => o % 20 === 0, pixelsPerUnit: 2, pointerSide: "right" } ), I = ({ altitude: t }) => /* @__PURE__ */ e( S, { value: t, title: "ALTITUDE", unit: "FT", unitTooltip: "Feet above sea level", generateMarks: (s) => { const r = [], h = Math.max( 0, Math.floor((s - 5e3) / 100) * 100 ), b = Math.ceil((s + 5e3) / 100) * 100; for (let x = h; x <= b; x += 100) r.push(x); return r; }, getValueColor: (s) => s < 1e3 ? "text-red-400" : s >= 1e3 && s <= 1e4 ? "text-white" : s > 1e4 && s <= 4e4 ? "text-green-400" : s > 4e4 ? "text-yellow-400" : "text-gray-400", isMajorTick: (s) => s % 500 === 0, formatValue: (s) => s.toString(), pixelsPerUnit: 0.1, pointerSide: "left" } ), L = ({ heading: t }) => { const n = (() => { const r = []; for (let l = 0; l < 72; l++) { const d = l * 5, h = d % 90 === 0, b = d % 30 === 0, x = d % 10 === 0; let m = ""; d === 0 ? m = "N" : d === 90 ? m = "E" : d === 180 ? m = "S" : d === 270 ? m = "W" : b && (m = (d / 10).toString().padStart(2, "0")), r.push({ angle: d, label: m, isCardinal: h, isMajor: b, isMinor: x, x: l * 4 }); } return r; })(), c = (t % 360 + 360) % 360, s = 96 - c * 4; return /* @__PURE__ */ a("div", { className: "bg-gray-900 text-white rounded-lg w-48 h-16 relative overflow-hidden border-2 border-gray-600 shadow-lg", children: [ /* @__PURE__ */ e("div", { className: "absolute top-0 left-1/2 w-0 h-0 border-l-3 border-r-3 border-b-6 border-transparent border-b-yellow-400 transform -translate-x-1/2 z-10" }), /* @__PURE__ */ e( "div", { className: "absolute top-1 flex transition-transform duration-200 ease-out", style: { transform: `translateX(${s}px)` }, children: n.concat(n).concat(n).map((r, l) => /* @__PURE__ */ a("div", { className: "relative flex flex-col items-center", style: { width: "4px" }, children: [ /* @__PURE__ */ e("div", { className: `bg-white ${r.isCardinal ? "w-0.5 h-4" : r.isMajor ? "w-0.5 h-3" : r.isMinor ? "w-px h-2" : "w-px h-1"}` }), r.label && /* @__PURE__ */ e("div", { className: `mt-0.5 text-xs font-mono whitespace-nowrap ${r.isCardinal ? "text-yellow-400 font-bold" : "text-white"}`, style: { fontSize: "10px" }, children: r.label }) ] }, l)) } ), /* @__PURE__ */ a("div", { className: "absolute bottom-1 left-1/2 bg-black px-2 py-0.5 border border-gray-400 text-green-400 font-bold text-xs transform -translate-x-1/2 rounded whitespace-nowrap", children: [ "HDG ", Math.round(c).toString().padStart(3, "0"), "°" ] }), /* @__PURE__ */ e("div", { className: "absolute top-0 left-0 w-4 h-full bg-gradient-to-r from-gray-900 to-transparent pointer-events-none z-20" }), /* @__PURE__ */ e("div", { className: "absolute top-0 right-0 w-4 h-full bg-gradient-to-l from-gray-900 to-transparent pointer-events-none z-20" }) ] }); }, C = ({ verticalSpeed: t }) => { const n = (() => { const r = []; for (let h = -3e3; h <= 3e3; h += 200) { const b = 50 - h / 3e3 * 40, x = h % 1e3 === 0, m = x; r.push({ value: h, yPercent: b, isMajor: x, showLabel: m, label: m ? (h / 1e3).toString() : "" }); } return r; })(), o = 50 - Math.max(-3e3, Math.min(3e3, t)) / 3e3 * 40, s = () => { const r = Math.abs(t); return r < 50 ? "00" : Math.floor(r / 100).toString().padStart(2, "0"); }; return /* @__PURE__ */ a("div", { className: "bg-gray-900 text-white rounded-lg w-20 h-64 relative border-2 border-gray-600 shadow-lg overflow-hidden", children: [ /* @__PURE__ */ e("div", { className: "absolute top-1 left-1/2 transform -translate-x-1/2 text-xs font-mono text-gray-300 font-semibold", children: "V/S" }), /* @__PURE__ */ e("div", { className: "absolute right-1 top-8 bottom-8 w-16", children: n.map((r, l) => /* @__PURE__ */ a( "div", { className: "absolute w-full flex items-center justify-end", style: { top: `${r.yPercent}%` }, children: [ r.showLabel && /* @__PURE__ */ e("div", { className: "mr-1 text-xs font-mono text-white", children: r.label }), /* @__PURE__ */ e("div", { className: `bg-white h-0.5 ${r.isMajor ? "w-4" : "w-2"} ${r.value === 0 ? "bg-yellow-400 w-6" : "bg-white"}` }) ] }, l )) }), /* @__PURE__ */ e( "div", { className: "absolute left-2 top-1/2 w-0 h-0 border-t-2 border-b-2 border-r-4 border-transparent border-r-yellow-400 transform -translate-y-1/2 z-10" } ), /* @__PURE__ */ e( "div", { className: "absolute left-8 w-6 h-0.5 bg-cyan-400 transform -translate-y-1/2 transition-all duration-300 z-20", style: { top: `${o}%` } } ), /* @__PURE__ */ a("div", { className: "absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-black px-2 py-1 border border-gray-400 rounded z-30", children: [ /* @__PURE__ */ e("div", { className: "text-cyan-400 font-bold text-lg font-mono", children: s() }), /* @__PURE__ */ e("div", { className: "text-gray-400 text-xs text-center", children: t >= 0 ? "▲" : "▼" }) ] }), /* @__PURE__ */ e("div", { className: "absolute bottom-1 left-1/2 transform -translate-x-1/2 text-xs text-gray-400 font-mono", children: "FPM" }), /* @__PURE__ */ e("div", { className: "absolute top-0 left-0 w-full h-8 bg-gradient-to-b from-gray-900 to-transparent pointer-events-none z-40" }), /* @__PURE__ */ e("div", { className: "absolute bottom-0 left-0 w-full h-8 bg-gradient-to-t from-gray-900 to-transparent pointer-events-none z-40" }) ] }); }; function j({ flightData: t }) { return /* @__PURE__ */ e(A, { children: /* @__PURE__ */ a("div", { className: "bg-black rounded-xl p-8 shadow-2xl mb-6", children: [ /* @__PURE__ */ a("div", { className: "flex justify-center items-center space-x-8", children: [ /* @__PURE__ */ e("p", { children: "AirSpeed" }), /* @__PURE__ */ e(E, { airspeed: t.airspeed }), /* @__PURE__ */ a("div", { className: "flex flex-col items-center space-y-4", children: [ /* @__PURE__ */ e( T, { pitch: t.pitch, roll: t.roll } ), /* @__PURE__ */ e(L, { heading: t.heading }) ] }), /* @__PURE__ */ e(I, { altitude: t.altitude }), /* @__PURE__ */ e(C, { verticalSpeed: t.verticalSpeed }) ] }), /* @__PURE__ */ a("div", { className: "flex justify-center space-x-8 mt-6 text-white", children: [ /* @__PURE__ */ a("div", { className: "bg-gray-800 px-4 py-2 rounded border", children: [ /* @__PURE__ */ e("span", { className: "text-gray-400", children: "MACH:" }), /* @__PURE__ */ e("span", { className: "text-cyan-400 ml-2 font-mono", children: t.mach.toFixed(3) }) ] }), /* @__PURE__ */ a("div", { className: "bg-gray-800 px-4 py-2 rounded border", children: [ /* @__PURE__ */ e("span", { className: "text-gray-400", children: "OAT:" }), /* @__PURE__ */ a("span", { className: "text-blue-300 ml-2 font-mono", children: [ Math.round(t.temperature), "°C" ] }) ] }), /* @__PURE__ */ a("div", { className: "bg-gray-800 px-4 py-2 rounded border", children: [ /* @__PURE__ */ e("span", { className: "text-gray-400", children: "GS:" }), /* @__PURE__ */ e("span", { className: "text-green-400 ml-2 font-mono", children: "420 KT" }) ] }) ] }) ] }) }); } const V = ({ flightData: t }) => /* @__PURE__ */ e("div", { className: "min-h-screen bg-gray-800 p-6", children: /* @__PURE__ */ a("div", { className: "max-w-7xl mx-auto", children: [ /* @__PURE__ */ e("div", { className: "mb-6 text-center", children: /* @__PURE__ */ e("div", { className: "bg-gray-900 rounded-lg p-4 inline-block", children: /* @__PURE__ */ a("div", { className: "text-green-400 font-mono text-lg", children: [ t.timestamp.toLocaleDateString(), " - ", t.timestamp.toLocaleTimeString() ] }) }) }), /* @__PURE__ */ e(j, { flightData: t }), /* @__PURE__ */ e(R, { flightData: t }) ] }) }); export { R as ARINC429DataBus, g as ARINC429Word, u as ARINC429_LABELS, V as AircraftPFD, E as AirspeedIndicator, I as AltitudeIndicator, T as AttitudeIndicator, L as HeadingIndicator, p as SSM, C as VerticalSpeedIndicator };