Evolved from the original KTrie custom AM proposal (preserved as KTRIE-SPEC-ORIGINAL.md). Key design decisions: 2-level trie (SMA + inclination) instead of 5, SP-GiST framework instead of custom AM, query-time RAAN filter instead of trie level, propagation-aware cost estimation via traversalValue.
545 lines
28 KiB
JavaScript
545 lines
28 KiB
JavaScript
import { useState } from "react";
|
||
|
||
const MONO = "'SF Mono', 'Cascadia Code', 'Fira Code', monospace";
|
||
const SANS = "'Segoe UI', system-ui, sans-serif";
|
||
|
||
const colors = {
|
||
bg: "#0a0e17",
|
||
surface: "#111827",
|
||
surface2: "#1a2332",
|
||
border: "#1e3a5f",
|
||
borderHi: "#2563eb",
|
||
text: "#e2e8f0",
|
||
textDim: "#64748b",
|
||
textMuted: "#475569",
|
||
accent: "#3b82f6",
|
||
accentDim: "#1e40af",
|
||
green: "#10b981",
|
||
greenDim: "#064e3b",
|
||
amber: "#f59e0b",
|
||
amberDim: "#78350f",
|
||
red: "#ef4444",
|
||
redDim: "#7f1d1d",
|
||
purple: "#a78bfa",
|
||
purpleDim: "#4c1d95",
|
||
cyan: "#22d3ee",
|
||
cyanDim: "#164e63",
|
||
orange: "#fb923c",
|
||
};
|
||
|
||
const levelMeta = [
|
||
{ name: "Semi-Major Axis (a)", unit: "km", example: "6,798 km (ISS)", color: colors.accent },
|
||
{ name: "Inclination (i)", unit: "deg", example: "51.6° (ISS)", color: colors.green },
|
||
{ name: "RAAN (Ω)", unit: "deg", example: "Right Ascension", color: colors.amber },
|
||
{ name: "Eccentricity (e)", unit: "", example: "0.0001 (ISS)", color: colors.purple },
|
||
{ name: "Arg. Perigee (ω)", unit: "deg", example: "Argument of Perigee", color: colors.orange },
|
||
];
|
||
|
||
const ByteBlock = ({ label, bytes, color, detail, dimColor }) => (
|
||
<div style={{
|
||
display: "flex", flexDirection: "column", gap: 2,
|
||
padding: "6px 10px", borderRadius: 4,
|
||
background: dimColor || color + "15",
|
||
border: `1px solid ${color}40`,
|
||
minWidth: 0, flex: "1 1 auto",
|
||
}}>
|
||
<div style={{ fontFamily: MONO, fontSize: 10, color, fontWeight: 600, whiteSpace: "nowrap" }}>{label}</div>
|
||
<div style={{ fontFamily: MONO, fontSize: 9, color: colors.textDim }}>{bytes}B</div>
|
||
{detail && <div style={{ fontFamily: MONO, fontSize: 9, color: colors.textMuted, marginTop: 1 }}>{detail}</div>}
|
||
</div>
|
||
);
|
||
|
||
const StructField = ({ type, name, size, comment, color }) => (
|
||
<div style={{
|
||
display: "grid", gridTemplateColumns: "100px 160px 44px 1fr",
|
||
gap: 8, padding: "3px 0",
|
||
fontFamily: MONO, fontSize: 11, lineHeight: 1.5,
|
||
borderBottom: `1px solid ${colors.border}30`,
|
||
}}>
|
||
<span style={{ color: colors.cyan }}>{type}</span>
|
||
<span style={{ color: color || colors.text }}>{name}</span>
|
||
<span style={{ color: colors.textDim, textAlign: "right" }}>{size}B</span>
|
||
<span style={{ color: colors.textMuted, fontStyle: "italic" }}>{comment}</span>
|
||
</div>
|
||
);
|
||
|
||
const SectionHeader = ({ children, color = colors.accent }) => (
|
||
<div style={{
|
||
fontFamily: SANS, fontSize: 13, fontWeight: 700,
|
||
color, textTransform: "uppercase", letterSpacing: "0.08em",
|
||
padding: "12px 0 6px", borderBottom: `1px solid ${color}40`,
|
||
marginBottom: 8,
|
||
}}>{children}</div>
|
||
);
|
||
|
||
const Badge = ({ children, color }) => (
|
||
<span style={{
|
||
fontFamily: MONO, fontSize: 9, fontWeight: 600,
|
||
color, background: color + "20",
|
||
padding: "2px 6px", borderRadius: 3,
|
||
border: `1px solid ${color}30`,
|
||
}}>{children}</span>
|
||
);
|
||
|
||
const PageDiagram = ({ type }) => {
|
||
const isInternal = type === "internal";
|
||
const isLeaf = type === "leaf";
|
||
const isCompressed = type === "compressed";
|
||
|
||
const headerColor = colors.cyan;
|
||
const entryColor = isInternal ? colors.accent : isLeaf ? colors.green : colors.purple;
|
||
const entryLabel = isInternal ? "KTrieChildEntry" : isLeaf ? "KTrieLeafEntry" : "CompressedPath + Entries";
|
||
const entrySize = isInternal ? 24 : isLeaf ? 68 : "variable";
|
||
const capacity = isInternal ? "~337 children" : isLeaf ? "~119 TLEs" : "path + leaves";
|
||
|
||
return (
|
||
<div style={{
|
||
background: colors.surface, borderRadius: 8,
|
||
border: `1px solid ${colors.border}`,
|
||
padding: 16, flex: 1,
|
||
}}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
|
||
<div style={{ fontFamily: SANS, fontSize: 13, fontWeight: 700, color: entryColor }}>
|
||
{isInternal ? "Internal Node" : isLeaf ? "Leaf Node" : "Compressed Node"}
|
||
</div>
|
||
<Badge color={entryColor}>{capacity}</Badge>
|
||
</div>
|
||
|
||
{/* Page visualization */}
|
||
<div style={{
|
||
borderRadius: 6, overflow: "hidden",
|
||
border: `1px solid ${colors.border}`,
|
||
background: colors.bg,
|
||
}}>
|
||
{/* PG Header */}
|
||
<div style={{
|
||
padding: "6px 10px", background: colors.textMuted + "20",
|
||
display: "flex", justifyContent: "space-between", alignItems: "center",
|
||
borderBottom: `1px solid ${colors.border}`,
|
||
}}>
|
||
<span style={{ fontFamily: MONO, fontSize: 10, color: colors.textDim }}>PageHeaderData</span>
|
||
<span style={{ fontFamily: MONO, fontSize: 9, color: colors.textMuted }}>24B</span>
|
||
</div>
|
||
|
||
{/* KTrie Node Header */}
|
||
<div style={{
|
||
padding: "8px 10px", background: headerColor + "10",
|
||
borderBottom: `1px solid ${headerColor}30`,
|
||
}}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
|
||
<span style={{ fontFamily: MONO, fontSize: 10, color: headerColor, fontWeight: 600 }}>KTrieNodeHeader</span>
|
||
<span style={{ fontFamily: MONO, fontSize: 9, color: colors.textMuted }}>72B</span>
|
||
</div>
|
||
<div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>
|
||
<ByteBlock label="type" bytes={4} color={headerColor} detail={isInternal ? "INTERNAL" : isLeaf ? "LEAF" : "COMPRESSED"} />
|
||
<ByteBlock label="level" bytes={4} color={headerColor} />
|
||
<ByteBlock label="n_entries" bytes={2} color={headerColor} />
|
||
<ByteBlock label="flags" bytes={2} color={headerColor} />
|
||
<ByteBlock label="range_low" bytes={8} color={headerColor} detail="float8" />
|
||
<ByteBlock label="range_high" bytes={8} color={headerColor} detail="float8" />
|
||
<ByteBlock label="compressed" bytes={4} color={colors.purple} detail="skip levels" />
|
||
<ByteBlock label="skip_bounds" bytes={40} color={colors.purple} detail="5×float8" />
|
||
</div>
|
||
</div>
|
||
|
||
{/* Entries section */}
|
||
<div style={{ padding: "8px 10px" }}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
|
||
<span style={{ fontFamily: MONO, fontSize: 10, color: entryColor, fontWeight: 600 }}>
|
||
{entryLabel} × N
|
||
</span>
|
||
<span style={{ fontFamily: MONO, fontSize: 9, color: colors.textMuted }}>
|
||
{entrySize}B each → {capacity}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Show 3 sample entries */}
|
||
{[0, 1, 2].map(i => (
|
||
<div key={i} style={{
|
||
display: "flex", gap: 4, marginBottom: 4, flexWrap: "wrap",
|
||
padding: 6, borderRadius: 4,
|
||
background: entryColor + (i === 0 ? "12" : "08"),
|
||
border: i === 0 ? `1px solid ${entryColor}30` : `1px solid transparent`,
|
||
}}>
|
||
{isInternal ? (
|
||
<>
|
||
<ByteBlock label="lower" bytes={8} color={entryColor} detail="float8" />
|
||
<ByteBlock label="upper" bytes={8} color={entryColor} detail="float8" />
|
||
<ByteBlock label="child_blk" bytes={4} color={colors.amber} detail="BlockNumber" />
|
||
<ByteBlock label="pop" bytes={2} color={colors.textDim} detail="subtree count" />
|
||
<ByteBlock label="flags" bytes={2} color={colors.textDim} />
|
||
</>
|
||
) : (
|
||
<>
|
||
<ByteBlock label="norad_id" bytes={4} color={entryColor} detail="int32" />
|
||
<ByteBlock label="epoch" bytes={8} color={entryColor} detail="Julian date" />
|
||
<ByteBlock label="a" bytes={8} color={colors.accent} />
|
||
<ByteBlock label="i" bytes={8} color={colors.green} />
|
||
<ByteBlock label="Ω" bytes={8} color={colors.amber} />
|
||
<ByteBlock label="e" bytes={8} color={colors.purple} />
|
||
<ByteBlock label="ω" bytes={8} color={colors.orange} />
|
||
<ByteBlock label="M" bytes={8} color={colors.red} />
|
||
<ByteBlock label="tle_tid" bytes={6} color={colors.cyan} detail="→ heap" />
|
||
<ByteBlock label="fl" bytes={2} color={colors.textDim} />
|
||
</>
|
||
)}
|
||
</div>
|
||
))}
|
||
<div style={{
|
||
textAlign: "center", padding: 4,
|
||
fontFamily: MONO, fontSize: 10, color: colors.textMuted,
|
||
}}>⋮</div>
|
||
</div>
|
||
|
||
{/* Page footer */}
|
||
<div style={{
|
||
padding: "4px 10px", background: colors.textMuted + "10",
|
||
borderTop: `1px solid ${colors.border}`,
|
||
display: "flex", justifyContent: "space-between",
|
||
}}>
|
||
<span style={{ fontFamily: MONO, fontSize: 9, color: colors.textMuted }}>special: free_offset</span>
|
||
<span style={{ fontFamily: MONO, fontSize: 9, color: colors.textDim }}>8,192 bytes total</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const TreeViz = () => {
|
||
const nodeStyle = (color, label, sub, pop) => (
|
||
<div style={{
|
||
display: "flex", flexDirection: "column", alignItems: "center", gap: 2,
|
||
padding: "6px 12px", borderRadius: 6,
|
||
background: color + "15", border: `1px solid ${color}40`,
|
||
minWidth: 80,
|
||
}}>
|
||
<div style={{ fontFamily: MONO, fontSize: 10, fontWeight: 600, color }}>{label}</div>
|
||
<div style={{ fontFamily: MONO, fontSize: 8, color: colors.textMuted }}>{sub}</div>
|
||
{pop && <Badge color={color}>{pop}</Badge>}
|
||
</div>
|
||
);
|
||
|
||
const connector = (color = colors.border) => (
|
||
<div style={{ width: 1, height: 16, background: color, margin: "0 auto" }} />
|
||
);
|
||
|
||
return (
|
||
<div style={{
|
||
background: colors.surface, borderRadius: 8,
|
||
border: `1px solid ${colors.border}`,
|
||
padding: 16,
|
||
}}>
|
||
<SectionHeader color={colors.cyan}>Adaptive Trie Traversal — ISS Query Path</SectionHeader>
|
||
<div style={{ fontFamily: MONO, fontSize: 10, color: colors.textMuted, marginBottom: 12, padding: "4px 8px", background: colors.bg, borderRadius: 4 }}>
|
||
SELECT * FROM satellites WHERE sgp4_passes(tle, observer(43.70, -116.35), now(), '2h') — Eagle, ID
|
||
</div>
|
||
|
||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 0 }}>
|
||
{/* Level 0: Semi-major axis */}
|
||
<div style={{ fontFamily: MONO, fontSize: 9, color: colors.accent, marginBottom: 4 }}>L0: Semi-Major Axis</div>
|
||
<div style={{ display: "flex", gap: 8, justifyContent: "center", flexWrap: "wrap" }}>
|
||
{nodeStyle(colors.accent, "6,378–6,978", "sub-LEO → LEO", "28,441")}
|
||
{nodeStyle(colors.textMuted, "6,978–13,000", "MEO-low", "412")}
|
||
{nodeStyle(colors.textMuted, "13,000–30,000", "MEO-high", "89")}
|
||
{nodeStyle(colors.textMuted, "30,000–42,200", "GEO region", "1,247")}
|
||
{nodeStyle(colors.textMuted, "42,200+", "super-GEO", "18")}
|
||
</div>
|
||
{connector(colors.accent)}
|
||
<div style={{ fontFamily: MONO, fontSize: 8, color: colors.green, background: colors.greenDim + "40", padding: "2px 8px", borderRadius: 3 }}>
|
||
✓ ISS at 6,798km → first bin, prune 4 branches
|
||
</div>
|
||
{connector(colors.green)}
|
||
|
||
{/* Level 1: Inclination — adaptive! */}
|
||
<div style={{ fontFamily: MONO, fontSize: 9, color: colors.green, marginBottom: 4 }}>L1: Inclination (adaptive: 12 bins in LEO vs 3 in GEO)</div>
|
||
<div style={{ display: "flex", gap: 6, justifyContent: "center", flexWrap: "wrap" }}>
|
||
{nodeStyle(colors.textMuted, "0°–28°", "equatorial", "2,104")}
|
||
{nodeStyle(colors.textMuted, "28°–45°", "mid-lat", "1,856")}
|
||
{nodeStyle(colors.green, "45°–55°", "ISS band", "8,912")}
|
||
{nodeStyle(colors.textMuted, "55°–70°", "GPS-ish", "3,201")}
|
||
{nodeStyle(colors.textMuted, "70°–82°", "polar-adj", "1,877")}
|
||
{nodeStyle(colors.textMuted, "82°–99°", "polar/SSO", "9,441")}
|
||
{nodeStyle(colors.textMuted, "99°+", "retro", "1,050")}
|
||
</div>
|
||
{connector(colors.green)}
|
||
<div style={{ fontFamily: MONO, fontSize: 8, color: colors.green, background: colors.greenDim + "40", padding: "2px 8px", borderRadius: 3 }}>
|
||
✓ 51.6° → ISS band, 8,912 candidates remain
|
||
</div>
|
||
{connector(colors.amber)}
|
||
|
||
{/* Level 2: RAAN */}
|
||
<div style={{ fontFamily: MONO, fontSize: 9, color: colors.amber, marginBottom: 4 }}>L2: RAAN (coarse — changes rapidly due to J2 precession)</div>
|
||
<div style={{ display: "flex", gap: 6, justifyContent: "center", flexWrap: "wrap" }}>
|
||
{nodeStyle(colors.textMuted, "0°–90°", "Q1", "2,156")}
|
||
{nodeStyle(colors.amber, "90°–180°", "Q2", "2,304")}
|
||
{nodeStyle(colors.textMuted, "180°–270°", "Q3", "2,211")}
|
||
{nodeStyle(colors.textMuted, "270°–360°", "Q4", "2,241")}
|
||
</div>
|
||
{connector(colors.amber)}
|
||
|
||
{/* Leaf level */}
|
||
<div style={{
|
||
display: "flex", gap: 8, justifyContent: "center", flexWrap: "wrap",
|
||
padding: 12, background: colors.bg, borderRadius: 6,
|
||
border: `1px dashed ${colors.green}40`,
|
||
}}>
|
||
<div style={{ textAlign: "center" }}>
|
||
<div style={{ fontFamily: MONO, fontSize: 10, color: colors.green, fontWeight: 600 }}>~2,304 leaf entries</div>
|
||
<div style={{ fontFamily: MONO, fontSize: 9, color: colors.textMuted, marginTop: 4 }}>
|
||
→ SGP4 propagate only these TLEs<br />
|
||
→ Check elevation from observer<br />
|
||
→ Return visible passes
|
||
</div>
|
||
<div style={{
|
||
marginTop: 8, padding: "4px 8px", borderRadius: 4,
|
||
background: colors.greenDim, fontFamily: MONO, fontSize: 9, color: colors.green,
|
||
}}>
|
||
Pruned 92.3% of catalog before propagation
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default function KTrieLayout() {
|
||
const [activeTab, setActiveTab] = useState("pages");
|
||
|
||
const tabs = [
|
||
{ id: "pages", label: "Page Layout" },
|
||
{ id: "structs", label: "C Structs" },
|
||
{ id: "tree", label: "Query Traversal" },
|
||
{ id: "levels", label: "Level Semantics" },
|
||
];
|
||
|
||
return (
|
||
<div style={{
|
||
background: colors.bg, color: colors.text,
|
||
fontFamily: SANS, minHeight: "100vh",
|
||
padding: 24,
|
||
}}>
|
||
{/* Header */}
|
||
<div style={{ marginBottom: 24 }}>
|
||
<div style={{ display: "flex", alignItems: "baseline", gap: 12, marginBottom: 4 }}>
|
||
<h1 style={{ fontFamily: MONO, fontSize: 20, fontWeight: 700, color: colors.text, margin: 0 }}>
|
||
KTrie
|
||
</h1>
|
||
<span style={{ fontFamily: MONO, fontSize: 12, color: colors.textDim }}>
|
||
Keplerian Patricia Trie — PostgreSQL Index AM
|
||
</span>
|
||
</div>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.textMuted }}>
|
||
Adaptive branching · Fixed level semantics · 8kB page-aligned · Patricia path compression
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tabs */}
|
||
<div style={{ display: "flex", gap: 2, marginBottom: 20, borderBottom: `1px solid ${colors.border}` }}>
|
||
{tabs.map(t => (
|
||
<button key={t.id} onClick={() => setActiveTab(t.id)} style={{
|
||
fontFamily: MONO, fontSize: 11, fontWeight: activeTab === t.id ? 700 : 400,
|
||
color: activeTab === t.id ? colors.accent : colors.textDim,
|
||
background: activeTab === t.id ? colors.accent + "15" : "transparent",
|
||
border: "none", borderBottom: activeTab === t.id ? `2px solid ${colors.accent}` : "2px solid transparent",
|
||
padding: "8px 16px", cursor: "pointer",
|
||
transition: "all 0.15s ease",
|
||
}}>{t.label}</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* Page Layout Tab */}
|
||
{activeTab === "pages" && (
|
||
<div>
|
||
<SectionHeader>8kB Page Layouts — Internal vs Leaf</SectionHeader>
|
||
<div style={{ display: "flex", gap: 16, flexWrap: "wrap" }}>
|
||
<PageDiagram type="internal" />
|
||
<PageDiagram type="leaf" />
|
||
</div>
|
||
|
||
<div style={{
|
||
marginTop: 16, padding: 12, borderRadius: 6,
|
||
background: colors.surface, border: `1px solid ${colors.border}`,
|
||
}}>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, fontWeight: 600, marginBottom: 6 }}>
|
||
Capacity Math
|
||
</div>
|
||
<div style={{ fontFamily: MONO, fontSize: 10, color: colors.textDim, lineHeight: 1.8 }}>
|
||
Page: 8,192B — PG header: 24B — Node header: 72B → <span style={{ color: colors.text }}>8,096B available</span><br />
|
||
Internal: 8,096 / 24B per child = <span style={{ color: colors.accent }}>337 max children</span> (adaptive: use 4–337 based on density)<br />
|
||
Leaf: 8,096 / 68B per entry = <span style={{ color: colors.green }}>119 max TLEs per page</span><br />
|
||
Split threshold: 85% fill → split along next orbital element dimension<br />
|
||
Merge threshold: 25% fill → merge with sibling if combined < 70%
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* C Structs Tab */}
|
||
{activeTab === "structs" && (
|
||
<div style={{
|
||
background: colors.surface, borderRadius: 8,
|
||
border: `1px solid ${colors.border}`,
|
||
padding: 16,
|
||
}}>
|
||
<SectionHeader color={colors.cyan}>ktrie.h — Core Data Structures</SectionHeader>
|
||
|
||
<div style={{ marginBottom: 16 }}>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, fontWeight: 600, marginBottom: 8 }}>
|
||
/* Node type enum */
|
||
</div>
|
||
<StructField type="enum" name="KTRIE_INTERNAL" size={0} comment="= 0 splits orbital element space" color={colors.accent} />
|
||
<StructField type="enum" name="KTRIE_LEAF" size={0} comment="= 1 holds TLE references" color={colors.green} />
|
||
<StructField type="enum" name="KTRIE_COMPRESSED" size={0} comment="= 2 Patricia path-compressed" color={colors.purple} />
|
||
</div>
|
||
|
||
<div style={{ marginBottom: 16 }}>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, fontWeight: 600, marginBottom: 8 }}>
|
||
/* Level semantics — fixed regardless of branching */
|
||
</div>
|
||
{levelMeta.map((l, i) => (
|
||
<StructField key={i} type={`Level ${i}`} name={l.name} size={0} comment={l.example} color={l.color} />
|
||
))}
|
||
</div>
|
||
|
||
<div style={{ marginBottom: 16 }}>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, fontWeight: 600, marginBottom: 8 }}>
|
||
typedef struct KTrieNodeHeader {"{"} <span style={{ color: colors.textMuted }}>/* 72 bytes */</span>
|
||
</div>
|
||
<StructField type="uint8" name="type" size={1} comment="INTERNAL | LEAF | COMPRESSED" />
|
||
<StructField type="uint8" name="level" size={1} comment="which orbital element (0-4)" />
|
||
<StructField type="uint16" name="num_entries" size={2} comment="current entry count" />
|
||
<StructField type="uint16" name="flags" size={2} comment="DIRTY | NEEDS_SPLIT | RESONANT" />
|
||
<StructField type="uint16" name="padding" size={2} comment="alignment" />
|
||
<StructField type="float8" name="range_low" size={8} comment="this node covers [low, high)" />
|
||
<StructField type="float8" name="range_high" size={8} comment="in units of current level element" />
|
||
<StructField type="uint8" name="compressed_depth" size={1} comment="Patricia: levels skipped" />
|
||
<StructField type="uint8" name="pad[7]" size={7} comment="alignment to 8-byte boundary" />
|
||
<StructField type="float8" name="skip_bounds[5]" size={40} comment="bounds for compressed levels" />
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, marginTop: 4 }}>{"}"}</div>
|
||
</div>
|
||
|
||
<div style={{ marginBottom: 16 }}>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, fontWeight: 600, marginBottom: 8 }}>
|
||
typedef struct KTrieChildEntry {"{"} <span style={{ color: colors.textMuted }}>/* 24 bytes — internal node */</span>
|
||
</div>
|
||
<StructField type="float8" name="lower_bound" size={8} comment="element range start" color={colors.accent} />
|
||
<StructField type="float8" name="upper_bound" size={8} comment="element range end" color={colors.accent} />
|
||
<StructField type="BlockNumber" name="child" size={4} comment="→ child page" color={colors.amber} />
|
||
<StructField type="uint16" name="population" size={2} comment="objects in subtree (for cost est.)" />
|
||
<StructField type="uint16" name="flags" size={2} comment="SPARSE | DENSE | HAS_RESONANT" />
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, marginTop: 4 }}>{"}"}</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, fontWeight: 600, marginBottom: 8 }}>
|
||
typedef struct KTrieLeafEntry {"{"} <span style={{ color: colors.textMuted }}>/* 68 bytes — leaf node */</span>
|
||
</div>
|
||
<StructField type="int32" name="norad_id" size={4} comment="NORAD catalog number" color={colors.green} />
|
||
<StructField type="float8" name="epoch" size={8} comment="TLE epoch (Julian date)" color={colors.green} />
|
||
<StructField type="float8" name="sma" size={8} comment="semi-major axis (km)" color={colors.accent} />
|
||
<StructField type="float8" name="inc" size={8} comment="inclination (rad)" color={colors.green} />
|
||
<StructField type="float8" name="raan" size={8} comment="right ascension (rad)" color={colors.amber} />
|
||
<StructField type="float8" name="ecc" size={8} comment="eccentricity" color={colors.purple} />
|
||
<StructField type="float8" name="argp" size={8} comment="argument of perigee (rad)" color={colors.orange} />
|
||
<StructField type="float8" name="mean_anomaly" size={8} comment="mean anomaly (rad)" color={colors.red} />
|
||
<StructField type="ItemPointerData" name="tle_tid" size={6} comment="→ heap tuple (full TLE)" color={colors.cyan} />
|
||
<StructField type="uint16" name="flags" size={2} comment="DECAYING | MANEUVERING | DEEP_SPACE" />
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.amber, marginTop: 4 }}>{"}"}</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Query Traversal Tab */}
|
||
{activeTab === "tree" && <TreeViz />}
|
||
|
||
{/* Level Semantics Tab */}
|
||
{activeTab === "levels" && (
|
||
<div>
|
||
<SectionHeader>Fixed Level Semantics with Adaptive Fan-Out</SectionHeader>
|
||
|
||
{levelMeta.map((level, i) => (
|
||
<div key={i} style={{
|
||
background: colors.surface, borderRadius: 8,
|
||
border: `1px solid ${level.color}30`,
|
||
padding: 16, marginBottom: 12,
|
||
borderLeft: `3px solid ${level.color}`,
|
||
}}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||
<span style={{ fontFamily: MONO, fontSize: 12, fontWeight: 700, color: level.color }}>
|
||
Level {i}
|
||
</span>
|
||
<span style={{ fontFamily: SANS, fontSize: 12, color: colors.text }}>{level.name}</span>
|
||
</div>
|
||
<Badge color={level.color}>{level.unit || "dimensionless"}</Badge>
|
||
</div>
|
||
|
||
<div style={{ fontFamily: MONO, fontSize: 10, color: colors.textDim, lineHeight: 1.8 }}>
|
||
{i === 0 && (
|
||
<>
|
||
<span style={{ color: colors.text }}>Regime boundaries:</span> sub-LEO (<300km alt), LEO (300–2,000), MEO (2,000–35,000), GEO (35,000–36,000), HEO/beyond<br />
|
||
<span style={{ color: colors.text }}>Adaptive range:</span> 5–20 bins typical. LEO subdivides heavily (75% of catalog)<br />
|
||
<span style={{ color: colors.text }}>Split strategy:</span> Equal-population splits, not equal-range. 6,378–6,578 might be one bin (sparse), 6,578–6,978 might be 8 bins (dense LEO)<br />
|
||
<span style={{ color: colors.text }}>Query predicate:</span> Altitude range directly maps → instant prune
|
||
</>
|
||
)}
|
||
{i === 1 && (
|
||
<>
|
||
<span style={{ color: colors.text }}>Key clusters:</span> 0° (equatorial), 28.5° (Cape Canaveral), 51.6° (ISS), 55° (GPS), 63.4° (Molniya critical), 97-98° (SSO), 180° (retrograde limit)<br />
|
||
<span style={{ color: colors.text }}>Adaptive range:</span> 4–32 bins. Fine-grained near SSO (huge population), coarse near 180°<br />
|
||
<span style={{ color: colors.text }}>Note:</span> Inclination is the most stable element — rarely changes except under thrust. Best discriminator after SMA<br />
|
||
<span style={{ color: colors.text }}>Special:</span> Flag nodes containing 63.4° ± 0.5° as HAS_RESONANT (Molniya critical inclination)
|
||
</>
|
||
)}
|
||
{i === 2 && (
|
||
<>
|
||
<span style={{ color: colors.text }}>Range:</span> 0°–360°, wraps around<br />
|
||
<span style={{ color: colors.text }}>Caution:</span> RAAN precesses rapidly due to J2 (~0.5–7°/day depending on altitude/inclination)<br />
|
||
<span style={{ color: colors.text }}>Adaptive range:</span> 4–8 coarse bins (fine subdivision pointless — RAAN drifts between TLE updates)<br />
|
||
<span style={{ color: colors.text }}>Reindex trigger:</span> When mean RAAN drift since last reindex exceeds bin width<br />
|
||
<span style={{ color: colors.text }}>Patricia compression:</span> This level frequently compressed away for near-circular orbits
|
||
</>
|
||
)}
|
||
{i === 3 && (
|
||
<>
|
||
<span style={{ color: colors.text }}>Range:</span> 0.0 (circular) to ~0.95 (extreme HEO)<br />
|
||
<span style={{ color: colors.text }}>Distribution:</span> Massively skewed — 90%+ of LEO objects have e < 0.02<br />
|
||
<span style={{ color: colors.text }}>Adaptive range:</span> 2–4 bins. Usually just "near-circular" vs "eccentric" vs "highly elliptical"<br />
|
||
<span style={{ color: colors.text }}>Patricia compression:</span> Almost always compressed in LEO branches (everything is near-circular)<br />
|
||
<span style={{ color: colors.text }}>Query relevance:</span> Critical for pass prediction — eccentric orbits have variable ground speed
|
||
</>
|
||
)}
|
||
{i === 4 && (
|
||
<>
|
||
<span style={{ color: colors.text }}>Range:</span> 0°–360°<br />
|
||
<span style={{ color: colors.text }}>Stability:</span> Precesses due to J2, rate depends on inclination and eccentricity<br />
|
||
<span style={{ color: colors.text }}>Adaptive range:</span> 2–6 bins, often compressed away entirely<br />
|
||
<span style={{ color: colors.text }}>Patricia compression:</span> Most aggressively compressed level — rarely needed for spatial queries<br />
|
||
<span style={{ color: colors.text }}>Primary use:</span> Discriminating within dense clusters (e.g., Starlink shells at same a/i/Ω)
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
|
||
<div style={{
|
||
marginTop: 12, padding: 12, borderRadius: 6,
|
||
background: colors.surface, border: `1px solid ${colors.border}`,
|
||
}}>
|
||
<div style={{ fontFamily: MONO, fontSize: 11, color: colors.purple, fontWeight: 600, marginBottom: 6 }}>
|
||
Patricia Path Compression
|
||
</div>
|
||
<div style={{ fontFamily: MONO, fontSize: 10, color: colors.textDim, lineHeight: 1.8 }}>
|
||
When a subtree has a single child at levels 2–4 (common in LEO), compress the path:<br />
|
||
<span style={{ color: colors.text }}>Before:</span> L0(a) → L1(i) → L2(Ω) → L3(e) → L4(ω) → leaf — 5 page reads<br />
|
||
<span style={{ color: colors.text }}>After:</span> L0(a) → L1(i) → COMPRESSED[Ω,e,ω stored in header] → leaf — 3 page reads<br />
|
||
<span style={{ color: colors.text }}>Savings:</span> 40% fewer I/O ops for typical LEO queries. Decompresses on split when population grows.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|