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/check-conventions.ts

import type { CircuitJson } from "circuit-json";

/** Inspect emitted copper, rather than relying on router settings. */
export function checkConventionalRouting(json: CircuitJson): string[] {
	const errors: string[] = [];
	for (const t of json as any[])
		if (t.type === "pcb_trace") {
			for (let i = 1; i < t.route.length; i++) {
				const a = t.route[i - 1],
					b = t.route[i];
				// Include wire-to-via approaches as well as wire-to-wire runs.
				// A layer change at one via has zero XY length.
				const dx = Math.abs(b.x - a.x),
					dy = Math.abs(b.y - a.y);
				if (Math.min(dx, dy, Math.abs(dx - dy)) > 1e-6)
					errors.push(`${t.pcb_trace_id}[${i}]: non-45-degree segment`);
			}
		}
	const components = (json as any[]).filter(
		(e) => e.type === "source_component",
	);
	const pcbComponents = (json as any[]).filter(
		(e) => e.type === "pcb_component",
	);
	for (const e of json as any[]) {
		if (e.type !== "pcb_component" && e.type !== "pcb_smtpad") continue;
		const pcb =
			e.type === "pcb_component"
				? e
				: pcbComponents.find((p) => p.pcb_component_id === e.pcb_component_id);
		const c = components.find(
			(c) => c.source_component_id === pcb?.source_component_id,
		);
		const underside =
			c?.name.startsWith("C_B_") ||
			["C_TV_VRN", "C_TV_VRP", "C_TV_REF"].includes(c?.name);
		if (e.layer !== (underside ? "bottom" : "top"))
			errors.push(`${c?.name}: component copper is on the wrong side`);
	}

	const nets = (json as any[]).filter(
		(e) => e.type === "source_trace" && /^N_HOSC[IO]$/.test(e.name),
	);
	for (const net of nets) {
		const routes = (json as any[]).filter(
			(e) =>
				e.type === "pcb_trace" && e.source_trace_id === net.source_trace_id,
		);
		if (
			routes.some((t) =>
				t.route.some((p: any) => p.route_type !== "wire" || p.layer !== "top"),
			)
		)
			errors.push(
				`${net.name}: crystal signal must remain on top without vias`,
			);
		for (const t of routes) {
			const length = t.route.reduce(
				(sum: number, b: any, i: number) =>
					i
						? sum + Math.hypot(b.x - t.route[i - 1].x, b.y - t.route[i - 1].y)
						: sum,
				0,
			);
			if (length > 10) errors.push(`${net.name}: crystal route exceeds 10 mm`);
		}
	}
	return errors;
}