astra/f1c100s

This code defines and renders a four-layer, top-mounted F1C100S module with detailed pin assignments, schematic annotations, and layout profiles, facilitating design, validation, and routing of the hardware including support for decoupling capacitors, crystal oscillators, and external connectors.

Version
0.11.0
License
unset
Stars
0

scripts/save-trace-paths.ts

import { EXTERNAL_NETS } from "../src/pin-map";
import { readFile, writeFile } from "node:fs/promises";
import { fanoutTracePath } from "@tscircuit/props";
import { LAYOUT_PROFILES } from "../src/profiles";
const reverse = (route: any[]) =>
	route
		.toReversed()
		.map((p) =>
			p.route_type === "via"
				? { ...p, from_layer: p.to_layer, to_layer: p.from_layer }
				: { ...p },
		);
for (const profile of LAYOUT_PROFILES) {
	const json = JSON.parse(
		await readFile(`src/generated/${profile}.circuit.json`, "utf8"),
	);
	const ports = json.filter((e: any) => e.type === "pcb_port");
	const sourcePorts = new Map<string, any>(
		json
			.filter((e: any) => e.type === "source_port")
			.map((e: any) => [e.source_port_id, e]),
	);
	const components = new Map<string, any>(
		json
			.filter((e: any) => e.type === "source_component")
			.map((e: any) => [e.source_component_id, e]),
	);
	const paths: any[] = [];
	for (const net of json.filter((e: any) => e.type === "source_trace")) {
		const members = ports.filter((p: any) =>
			net.connected_source_port_ids.includes(p.source_port_id),
		);
		const name = (p: any) =>
			components.get(sourcePorts.get(p.source_port_id).source_component_id)
				.name;
		const root =
			members.find((p: any) =>
				(EXTERNAL_NETS as readonly string[]).includes(name(p)),
			) ??
			members.find((p: any) => name(p) === "Y1") ??
			members[0];
		const graph = new Map<string, { to: string; route: any[] }[]>();
		for (const trace of json.filter(
			(e: any) =>
				e.type === "pcb_trace" && e.source_trace_id === net.source_trace_id,
		)) {
			const route = trace.route.map((p: any) =>
				p.route_type === "wire"
					? {
							route_type: "wire",
							x: p.x,
							y: p.y,
							layer: p.layer,
							width: p.width,
						}
					: {
							route_type: "via",
							x: p.x,
							y: p.y,
							from_layer: p.from_layer,
							to_layer: p.to_layer,
							via_diameter: 0.45,
							via_hole_diameter: 0.2,
						},
			);
			const find = (q: any) =>
				members.find(
					(p: any) =>
						Math.hypot(p.x - q.x, p.y - q.y) < 1e-4 &&
						p.layers.includes(q.layer),
				);
			const a = find(route[0]),
				b = find(route.at(-1));
			if (!a || !b)
				throw Error(`Missing endpoint ${profile} ${trace.pcb_trace_id}`);
			for (const [from, to, r] of [
				[a, b, route],
				[b, a, reverse(route)],
			] as any) {
				if (!graph.has(from.pcb_port_id)) graph.set(from.pcb_port_id, []);
				graph.get(from.pcb_port_id)!.push({ to: to.pcb_port_id, route: r });
			}
		}
		const routes = new Map<string, any[]>([[root.pcb_port_id, []]]),
			queue = [root.pcb_port_id];
		while (queue.length) {
			const id = queue.shift()!;
			for (const edge of graph.get(id) ?? []) {
				if (routes.has(edge.to)) continue;
				routes.set(edge.to, [
					...reverse(edge.route),
					...routes.get(id)!.slice(1),
				]);
				queue.push(edge.to);
			}
		}
		for (const port of members) {
			if (port === root) continue;
			const route = routes.get(port.pcb_port_id);
			if (!route) throw Error(`Disconnected stored net ${net.name}`);
			const source = sourcePorts.get(port.source_port_id);
			paths.push(
				fanoutTracePath.parse({
					connection: `${name(port)}.pin${source.pin_number}`,
					route,
				}),
			);
		}
	}
	await writeFile(
		`src/generated/${profile}.trace-paths.json`,
		JSON.stringify(paths, null, 2) + "\n",
	);
	console.log(profile, paths.length, "saved paths");
}