diff --git a/public/analytics.js b/public/analytics.js index a02ec40b..7ada19f8 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -69,7 +69,8 @@ - + + @@ -112,6 +113,7 @@ case 'topology': renderTopology(el, d.topoData); break; case 'channels': renderChannels(el, d.chanData); break; case 'hashsizes': renderHashSizes(el, d.hashData); break; + case 'collisions': renderCollisionTab(el, d.hashData); break; case 'subpaths': renderSubpaths(el); break; } // Auto-apply column resizing to all analytics tables @@ -705,10 +707,14 @@ + `; + } + function renderCollisionTab(el, data) { + el.innerHTML = `

1-Byte Hash Usage Matrix

-

Each cell shows how many packets used that 1-byte hop prefix (row=high nibble, col=low nibble). Darker = more traffic.

+

Click a cell to see which nodes share that prefix. Darker = more traffic.

@@ -1027,6 +1033,29 @@ ${data.totalMatches.toLocaleString()} occurrences + ${nodesWithLoc.length >= 2 ? `
+
📏 Hop Distances
+ ${(() => { + const dists = []; + let total = 0; + for (let i = 0; i < data.nodes.length - 1; i++) { + const a = data.nodes[i], b = data.nodes[i+1]; + if (a.lat && a.lon && b.lat && b.lon && !(a.lat===0&&a.lon===0) && !(b.lat===0&&b.lon===0)) { + const dLat = (a.lat - b.lat) * 111; + const dLon = (a.lon - b.lon) * 85; + const km = Math.sqrt(dLat*dLat + dLon*dLon); + total += km; + const cls = km > 200 ? 'color:#ef4444;font-weight:bold' : km > 50 ? 'color:#f59e0b' : 'color:#22c55e'; + dists.push(`
${km < 1 ? (km*1000).toFixed(0)+'m' : km.toFixed(1)+'km'} ${esc(a.name)} → ${esc(b.name)}
`); + } else { + dists.push(`
? ${esc(a.name)} → ${esc(b.name)} (no coords)
`); + } + } + if (dists.length > 1) dists.push(`
Total: ${total < 1 ? (total*1000).toFixed(0)+'m' : total.toFixed(1)+'km'}
`); + return dists.join(''); + })()} +
` : ''} + ${hasMap ? '
' : ''}