krishnax12/watchy-eink-smartwatch
Unfinished prototype (not production-ready): 4 native DRC errors, 2 display-flex intersections, and missing antenna radiator copper remain
- Version
- 1.2.7
- License
- MIT
- Stars
- 1
scripts/check-reference-components.ts
import type { AnyCircuitElement } from "circuit-json";
import { parseKicadSch } from "kicadts";
const schematicPath = process.argv[2];
if (!schematicPath) throw new Error("Pass the Watchy v3 KiCad schematic path");
const circuitPath = process.argv[3] ?? "dist/index/circuit.json";
const schematic = parseKicadSch(await Bun.file(schematicPath).text());
const circuit: AnyCircuitElement[] = await Bun.file(circuitPath).json();
const unitMultipliers: Record<string, number> = {
"": 1,
p: 1e-12,
n: 1e-9,
u: 1e-6,
m: 1e-3,
k: 1e3,
K: 1e3,
M: 1e6,
G: 1e9,
};
function parseNominalQuantity(nominal: string) {
const quantity = nominal.split(/[\s/]/)[0];
const match = quantity.match(
/^([0-9]+(?:\.[0-9]+)?)([pnumkKMG]?)(?:Hz|F|H)?$/,
);
if (!match) throw new Error(`Unsupported reference quantity: ${nominal}`);
return Number(match[1]) * unitMultipliers[match[2]];
}
const comparisons = [];
for (const symbol of schematic.symbols) {
const reference = symbol.properties.find(
(property) => property.key === "Reference",
)?.value;
if (!reference || !/^[CRLY][0-9]+$/.test(reference)) continue;
const nominal = symbol.properties.find(
(property) => property.key === "Value",
)?.value;
if (!nominal) throw new Error(`Missing reference quantity: ${reference}`);
const component = circuit.find(
(element) =>
element.type === "source_component" && element.name === reference,
);
if (!component || component.type !== "source_component")
throw new Error(`Missing compiled component: ${reference}`);
let compiledQuantity: number | string | undefined;
if (component.ftype === "simple_capacitor")
compiledQuantity = component.capacitance;
else if (component.ftype === "simple_resistor")
compiledQuantity = component.resistance;
else if (component.ftype === "simple_inductor")
compiledQuantity = component.inductance;
else if (component.ftype === "simple_crystal")
compiledQuantity = component.frequency;
const actualQuantity =
typeof compiledQuantity === "string"
? parseNominalQuantity(compiledQuantity)
: compiledQuantity;
const expectedQuantity = parseNominalQuantity(nominal);
const expectedVoltage = nominal.includes("/")
? Number(nominal.split("/")[1].replace(/V$/, ""))
: undefined;
const actualVoltage =
component.ftype === "simple_capacitor"
? component.max_voltage_rating
: undefined;
comparisons.push({
reference,
nominal,
expectedQuantity,
actualQuantity,
expectedVoltage,
actualVoltage,
matches:
actualQuantity !== undefined &&
Math.abs(actualQuantity - expectedQuantity) <= expectedQuantity * 1e-9 &&
(expectedVoltage === undefined || actualVoltage === expectedVoltage),
});
}
const mismatches = comparisons.filter((comparison) => !comparison.matches);
await Bun.write(
"reports/reference-components.json",
JSON.stringify(
{
schematicPath,
circuitPath,
scope:
"Compiled R/C/L quantities, explicit capacitor voltage ratings, and crystal frequencies versus KiCad schematic. Does not certify purchased MPN, tolerance, crystal load capacitance, or PCB tuning.",
comparisons,
},
null,
2,
),
);
console.log({ checked: comparisons.length, mismatches });
if (mismatches.length) process.exitCode = 1;