gokul/usb3-sd-hub
Engineering draft: four-layer, computer-powered USB 3 hub with one USB-C host socket, two USB-C and two USB-A accessory sockets, and a full-size SD slot. Board and three schematic previews are available. Routing and production checks are incomplete; not fabrication ready.
- Version
- 0.1.5
- License
- MIT
- Stars
- 0
verification/pair-measurements.ts
PCB
Schematic
type Point = { x: number; y: number }
type Segment = { start: Point; end: Point; layer: string; length: number }
export type TraceForAudit = { connection_name?: string; route: Array<{ route_type: string; x: number; y: number; layer?: string }> }
export function getWireSegments(trace: TraceForAudit): Segment[] {
return trace.route.slice(1).flatMap((end, index) => {
const start = trace.route[index]
return start.route_type === "wire" && end.route_type === "wire" && start.layer && start.layer === end.layer
? [{ start, end, layer: start.layer, length: Math.hypot(end.x - start.x, end.y - start.y) }]
: []
})
}
function distanceToSegment(point: Point, segment: Segment) {
const dx = segment.end.x - segment.start.x
const dy = segment.end.y - segment.start.y
const fraction = segment.length === 0 ? 0 : Math.max(0, Math.min(1, ((point.x - segment.start.x) * dx + (point.y - segment.start.y) * dy) / segment.length ** 2))
return Math.hypot(point.x - segment.start.x - fraction * dx, point.y - segment.start.y - fraction * dy)
}
export function inspectPairSeparation({ trace, pairedTrace }: { trace: TraceForAudit; pairedTrace: TraceForAudit }) {
const segments = getWireSegments(trace)
const pairedSegments = getWireSegments(pairedTrace)
let continuousUncoupledLengthMm = 0
let longestProvenUncoupledLengthMm = 0
let continuousPotentialUncoupledLengthMm = 0
let longestPotentialUncoupledLengthMm = 0
for (const segment of segments) {
const sampleCount = Math.max(1, Math.ceil(segment.length / 0.02))
const sampleLengthMm = segment.length / sampleCount
for (let index = 0; index < sampleCount; index++) {
const fraction = (index + 0.5) / sampleCount
const midpoint = { x: segment.start.x + fraction * (segment.end.x - segment.start.x), y: segment.start.y + fraction * (segment.end.y - segment.start.y) }
const nearestDistanceMm = Math.min(...pairedSegments.filter((pairedSegment) => pairedSegment.layer === segment.layer).map((pairedSegment) => distanceToSegment(midpoint, pairedSegment)))
if (nearestDistanceMm + sampleLengthMm / 2 > 0.336) {
continuousPotentialUncoupledLengthMm += sampleLengthMm
longestPotentialUncoupledLengthMm = Math.max(longestPotentialUncoupledLengthMm, continuousPotentialUncoupledLengthMm)
} else {
continuousPotentialUncoupledLengthMm = 0
}
if (nearestDistanceMm - sampleLengthMm / 2 > 0.336) {
continuousUncoupledLengthMm += sampleLengthMm
longestProvenUncoupledLengthMm = Math.max(longestProvenUncoupledLengthMm, continuousUncoupledLengthMm)
} else {
continuousUncoupledLengthMm = 0
}
}
}
return { connection_name: trace.connection_name, copper_length_mm: segments.reduce((sum, segment) => sum + segment.length, 0), vias: trace.route.filter((point) => point.route_type === "via").length, longest_proven_uncoupled_length_mm: longestProvenUncoupledLengthMm, longest_potential_uncoupled_length_mm: longestPotentialUncoupledLengthMm }
}