imrishabh18/pedometer

This code defines and assembles a simple radio receiver hardware circuit using specific imported capacitors, inductors, RF connectors, and oscillator components with precise footprints and schematic attributes.

Version
1.1.3
License
unset
Stars
0

scripts/review-exports.ts

import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import { nets, intentionallyUnconnected } from "../net-map";
import { convertCircuitJsonToPcbSvg, convertCircuitJsonToStackedSchematicSheetsSvg } from "circuit-to-svg";
import { Resvg } from "@resvg/resvg-js";

const circuitPath="dist/index/circuit.json";
const c: any[]=JSON.parse(fs.readFileSync(circuitPath,"utf8"));
fs.mkdirSync("review",{recursive:true});
const board=c.find(x=>x.type==="pcb_board");
const components=c.filter(x=>x.type==="source_component");
const ports=c.filter(x=>x.type==="source_port");
const byId=new Map(components.map(x=>[x.source_component_id,x]));
const physical=new Set(ports.filter(p=>byId.has(p.source_component_id)&&p.pin_number!==undefined).map(p=>`${byId.get(p.source_component_id)?.name}.${p.pin_number}`));
const assignments=new Map<string,string>();const audit:string[]=[];
for(const [net,pins] of Object.entries(nets)) for(const pin of pins){
 if(assignments.has(pin))audit.push(`${pin} assigned twice: ${assignments.get(pin)} / ${net}`);
 if(!physical.has(pin))audit.push(`Missing source pin ${pin}`);assignments.set(pin,net);
}
for(const pin of physical)if(!assignments.has(pin)&&!intentionallyUnconnected.includes(pin))audit.push(`Unaccounted pin ${pin}`);
for(const pin of intentionallyUnconnected)if(assignments.has(pin))audit.push(`NC pin connected: ${pin}`);
const errors=c.filter(x=>x.type.endsWith("_error"));
const warnings=c.filter(x=>x.type.endsWith("_warning"));
const report={date:new Date().toISOString().slice(0,10),release:errors.length||audit.length?"HOLD — CAD errors require correction":"CAD checks clear — prototype validation still required",
 boardSizeMm:{width:board.width,height:board.height,thickness:board.thickness},
 components:components.length,nets:Object.keys(nets).length,assignedPins:assignments.size,
 traces:c.filter(x=>x.type==="pcb_trace").length,vias:c.filter(x=>x.type==="pcb_via").length,
 fabricationStatus:"Not reviewed",connectivityAudit:audit,errors,warnings,circuitSha256:crypto.createHash("sha256").update(fs.readFileSync(circuitPath)).digest("hex")};
const fabPath="review/fabrication-review.json";
if(fs.existsSync(fabPath)){
 const fab=JSON.parse(fs.readFileSync(fabPath,"utf8"));
 report.fabricationStatus=fab.circuitSha256!==report.circuitSha256?"HOLD — fabrication review is stale":fab.status==="hold"?"HOLD — open findings in docs/fabrication-review.md":"See docs/fabrication-review.md";
}
fs.writeFileSync("review/validation.json",JSON.stringify(report,null,2));
const issueList=errors.map((e,i)=>`${i+1}. **${e.type}**: ${e.message||JSON.stringify(e)}`).join("\n");
fs.writeFileSync("docs/validation.md",`# Validation status\n\n**${report.release}**\n\nGenerated ${report.date} from the bundled Circuit JSON.\n\n- Board: ${report.boardSizeMm.width} × ${report.boardSizeMm.height} × ${report.boardSizeMm.thickness} mm.\n- ${report.components} components; ${report.nets} named nets; ${report.assignedPins} assigned pins.\n- ${report.traces} routed trace records; ${report.vias} vias.\n- Source pin accounting: ${audit.length?"FAIL":"PASS"}.\n- CAD errors: **${errors.length}**; CAD warnings: ${warnings.length}.\n\n## Fabrication readiness\n\n**${report.fabricationStatus}**. See [fabrication review](fabrication-review.md). The CAD checks below do not resolve the separate fabrication findings.\n\n## Remaining CAD errors\n\n${issueList||"None reported by this build."}\n\n${audit.join("\n")}\n\n## Limits\n\nThe tscircuit CLI can exit 0 despite CAD errors; scripts/check-release.ts fails when this report contains errors. Full diagnostic records, including style/ERC warnings, are in review/validation.json. Connectors, the pushbutton, crystal and MOSFET use their corresponding component types, with explicit electrical pin attributes for the active devices. Passing these checks does not establish analog performance, RF performance or manufacturing suitability.\n\nThe C core passed host tests with address/undefined-behavior sanitizers, including step rollover/reset, the 14-byte BLE payload, PMIC settings/display isolation and BQ27427 block offsets/checksum. No physical board, radio, charging cycle, battery-life test or TI target firmware build has been performed. The target SDK/GATT/NVS adapters remain to be implemented. External battery selection, factory process acceptance and physical bring-up remain open. All 62 PCB parts have exact MPNs and JLCPCB codes.\n`);

const csv=(rows:any[][])=>rows.map(r=>r.map(v=>'"'+String(v??"").replace(/"/g,'""')+'"').join(",")).join("\n")+"\n";
const packages:Record<string,string>={U1:"QFN24 RGE / 4x4mm",ANT1:"Johanson 1206 ceramic antenna",Y2:"Abracon ABM11W / 2.0x1.6mm",L1:"0603",L2:"0201",L3:"0402",L4:"0402",C19:"0402",C20:"0201",C21:"0603",C22:"0402",C23:"0402",C24:"0402",C25:"0402",C26:"0201",C27:"0201",C28:"0402",U2:"DSBGA-20 YFP / 0.4mm",U3:"DSBGA-9 YZF / 0.5mm",U4:"SOT23-5 DBV",U5:"LGA-12 / 2x2mm",U6:"SSOP-8 DCT",Q1:"SOT23",Y1:"ABS07 / 3.2x1.5mm",SW1:"TS-1088-AR02016",J1:"JST-SH 3-pin top entry 1mm",J2:"USB-C 6P / SHOU HAN",D1:"SOD-523",J3:"JST-SH 4-pin top entry 1mm",J4:"JST-SH 5-pin top entry 1mm"};
const bom:any[][]=[["Reference","Qty","Part or nominal value","Package","Supplier reference (stock scope: docs/stock-check.md)","Procurement / assembly note"]];
for(const s of components){
 const cap=s.name.startsWith("C"),res=s.name.startsWith("R");
 const footprint=packages[s.name]||(res?"0402":s.name==="C4"?"0805":["C1","C11","C12","C14","C15","C17","C18"].includes(s.name)?"0402":"0603");
 const mpnNames:Record<string,string>={RF_BM_2340C2:"RF-BM-2340C2",ABS07_32_768KHZ_7_T:"ABS07-32.768KHZ-7-T",TS_1088_AR02016:"TS-1088-AR02016",BM04B_SRSS_TB_LF__SN_:"BM04B-SRSS-TB(LF)(SN)"};
 const value=mpnNames[s.manufacturer_part_number]||s.manufacturer_part_number||s.display_capacitance||(s.resistance!==undefined?`${s.resistance} ohm`:s.name);
 let note=cap?`${s.max_voltage_rating} V min; ${["C14","C15","C18"].includes(s.name)?"C0G, 5%":"X7R preferred / X5R, 10%; verify effective capacitance at bias"}; final passive MPN open`:res?"1%, 0.063W min; final passive MPN open":"Verify footprint orientation and package with assembler";
 if(s.name==="J1")note="Mates SHR-03V-S; 1 PACK+, 2 GND, 3 NTC; retention tabs grounded";
 if(s.name==="J2")note="USB-C charging-only sink; CC1/CC2 each 5.1k to GND; 4 plated shell slots; pinout in interfaces.md";
 if(s.name==="R12"||s.name==="R13")note="5.1k, 1%, 0402, 0.063W; separate USB-C Rd resistor";
 if(s.name==="D1")note="VBUS transient clamp; cathode pin1 to VIN5, anode pin2 to GND";
 if(s.name==="J4")note="Mates SHR-05V-S; custom SWD cable pinout in interfaces.md; retention tabs grounded";
 if(s.name==="U1")note="Bare CC2340R52E0RGER; QFN24 + exposed ground pad; no module";
 if(s.name==="ANT1")note="Pin1 RF feed; pin2 mechanical anchor only, electrically NC; corner keepout";
 if(s.name==="Y2")note="48MHz / CL8pF / ESR<=40ohm; pins1&3 crystal, 2&4 grounded case";
 if((cap||res)&&s.manufacturer_part_number&&s.supplier_part_numbers?.jlcpcb?.length) note="Exact MPN selected; verify ratings and assembly rotation against supplier library";
 if(["L2","L3","L4","C26","C27","C28"].includes(s.name))note="RF component; preserve package and placement; antenna match requires board/enclosure tuning";
 bom.push([s.name,1,value,footprint,s.supplier_part_numbers?.jlcpcb?.join(" ")||"",note]);
}
bom.push(["DISPLAY",1,"Adafruit 4440 / SSD1306 128x32","External 35x20mm board","Adafruit 4440","3.3V I2C / 0x3C; cable separately"],
 ["BATTERY",1,"Protected 100mAh 1S 4.2V LiPo","Final cell MPN and size OPEN","","Must permit 20mA charging, 2mA termination and defined temperature range"],
 ["NTC",1,"10k at 25C battery-mounted NTC","External","","Select curve against BQ25150 thresholds and chosen cell; R4 bias remains on board"],
 ["CABLE",1,"4-wire JST-SH 1mm cable","External","","Verify pin order, length and enclosure routing"],
 ["BATTERY_CABLE",1,"SHR-03V-S cable assembly","External JST-SH 3-pin","","3 x SSH-003T-P0.2-H contacts or precrimped assembly; PACK+, GND, NTC"],
 ["CHARGE_CABLE",1,"Standard USB-C charging cable","External USB-C cable","","5V charging only; no USB data or PD"],
 ["DEBUG_CABLE",1,"SHR-05V-S to SWD debugger adapter","External JST-SH 5-pin","","5 x SSH-003T-P0.2-H contacts or precrimped assembly; pinout in interfaces.md"],
 ["CHARGER_SUPPLY",1,"5V regulated supply","External","","USB 5V source; USB-C cable listed separately"]);
fs.writeFileSync("review/bom.csv",csv(bom));
const pcbRows=bom.slice(1,1+components.length);
const grouped=new Map<string,{refs:string[];mpn:string;footprint:string;code:string}>();
for(const row of pcbRows){
 const s=components.find(s=>s.name===row[0]);
 if(!s?.manufacturer_part_number || !s.supplier_part_numbers?.jlcpcb?.[0]) throw new Error(`Missing assembly MPN/code for ${row[0]}`);
 const key=`${row[2]}|${row[3]}|${row[4]}`;
 const item=grouped.get(key)??{refs:[] as string[],mpn:row[2],footprint:row[3],code:row[4]};
 item.refs.push(row[0]);grouped.set(key,item);
}
fs.writeFileSync("review/jlc-bom.csv",csv([["Comment","Designator","Footprint","LCSC Part #"],...Array.from(grouped.values(),p=>[p.mpn,p.refs.join(","),p.footprint,p.code])]));
fs.writeFileSync("review/jlc-cpl.csv",csv([["Designator","Mid X","Mid Y","Layer","Rotation"],...c.filter(x=>x.type==="pcb_component"&&!x.do_not_place).map(p=>[byId.get(p.source_component_id)?.name,p.center.x.toFixed(4),p.center.y.toFixed(4),p.layer,p.rotation])]));
fs.writeFileSync("review/placement-review.csv",csv([["Reference","Center X mm","Center Y mm","Rotation degrees","Side","Status"],...c.filter(x=>x.type==="pcb_component"&&!x.do_not_place).map(p=>[byId.get(p.source_component_id)?.name,p.center.x,p.center.y,p.rotation,p.layer,"REVIEW: reconcile library origin / assembler rotation"]) ]));
fs.writeFileSync("review/nets.json",JSON.stringify({nets,intentionallyUnconnected},null,2));
fs.copyFileSync(circuitPath,"review/circuit.json");
for(const [name,svg] of [["pcb",convertCircuitJsonToPcbSvg(c,{width:1400,height:1150})],["schematic",convertCircuitJsonToStackedSchematicSheetsSvg(c)]] as const){
 fs.writeFileSync(`review/${name}.svg`,svg);fs.writeFileSync(`review/${name}.png`,new Resvg(svg,{fitTo:{mode:"width",value:name==="pcb"?1400:2400}}).render().asPng());
}
// Assembly reference: preserve pads, outlines and designators while hiding
// routed copper and pours that obscure the component locations.
fs.writeFileSync("review/assembly.svg",convertCircuitJsonToPcbSvg(c.filter(e=>!["pcb_trace","pcb_via","pcb_copper_pour"].includes(e.type)),{width:1600,height:1400}));
const vendor=fs.readdirSync("firmware/vendor").filter(f=>/\.[ch]$/.test(f)).map(f=>({file:f,sha256:crypto.createHash("sha256").update(fs.readFileSync(path.join("firmware/vendor",f))).digest("hex")}));
fs.writeFileSync("firmware/vendor/provenance.json",JSON.stringify({source:"https://github.com/boschsensortec/BMA400_SensorAPI",retrieved:"2026-09-04",branchAtDownload:"master",license:"BSD-3-Clause; original notices retained in each source file",files:vendor},null,2));
console.log(JSON.stringify({release:report.release,components:report.components,errors:errors.length,audit}));
await import("./render-schematics");