board
Diode-Bridge Voltage-Controlled Attenuator
Public Unreviewedby John Lauer
Two-layer through-hole audio attenuator: four 1N4148s as a current-biased shunt bridge, 0 to 2 V control, designed by Saif Abdessayed with Adom Hydrogen
main
John Lauer
Saif Abdessayed's diode-bridge VCA: KiCad project, Gerbers, BOM, simulations, reports, and the earlier RC filter test
45f3ff6
11d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
#!/usr/bin/env python3
"""Build the fab subset, checksum manifest, and complete deliverables ZIP."""
from __future__ import annotations
import hashlib
from pathlib import Path
import shutil
import zipfile
ROOT = Path(__file__).resolve().parents[1]
FAB_SRC = ROOT / "manufacturing" / "gerbers"
FAB_DST = ROOT / "manufacturing" / "fabrication-release"
ZIP_PATH = ROOT.parent / "diode-vca-attenuator-complete-deliverables.zip"
FAB_FILES = (
"diode-vca-attenuator-F_Cu.gtl",
"diode-vca-attenuator-B_Cu.gbl",
"diode-vca-attenuator-F_Mask.gts",
"diode-vca-attenuator-B_Mask.gbs",
"diode-vca-attenuator-F_Silkscreen.gto",
"diode-vca-attenuator-Edge_Cuts.gm1",
"diode-vca-attenuator.drl",
"diode-vca-attenuator-drl_map.pdf",
"diode-vca-attenuator-job.gbrjob",
"drill-report.txt",
)
EXCLUDED_PARTS = {
"__pycache__",
"schematic-new",
"schematic-final",
"schematic-latest",
}
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def included_files() -> list[Path]:
result = []
for path in ROOT.rglob("*"):
if not path.is_file():
continue
if any(part in EXCLUDED_PARTS for part in path.relative_to(ROOT).parts):
continue
if path == ROOT / "reports" / "SHA256SUMS.txt":
continue
result.append(path)
return sorted(result, key=lambda item: item.relative_to(ROOT).as_posix())
def main() -> None:
for name in FAB_FILES:
source = FAB_SRC / name
if not source.is_file() or source.stat().st_size == 0:
raise SystemExit(f"missing or empty manufacturing output: {source}")
shutil.copy2(source, FAB_DST / name)
required = (
ROOT / "diode-vca-attenuator.kicad_pro",
ROOT / "diode-vca-attenuator.kicad_sch",
ROOT / "diode-vca-attenuator.kicad_pcb",
ROOT / "reports" / "erc-raw.json",
ROOT / "reports" / "drc-raw.json",
ROOT / "reports" / "board-lint-parity-raw.json",
ROOT / "images" / "schematic" / "diode-vca-attenuator.png",
ROOT / "images" / "board-top" / "diode-vca-attenuator-top.png",
ROOT / "images" / "board-bottom" / "diode-vca-attenuator-bottom.png",
ROOT / "simulation" / "results" / "transient_summary.csv",
ROOT / "bom" / "diode-vca-attenuator-bom.csv",
)
for path in required:
if not path.is_file() or path.stat().st_size == 0:
raise SystemExit(f"missing or empty required deliverable: {path}")
checksum_path = ROOT / "reports" / "SHA256SUMS.txt"
lines = [f"{sha256(path)} {path.relative_to(ROOT).as_posix()}" for path in included_files()]
checksum_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
files = included_files() + [checksum_path]
with zipfile.ZipFile(ZIP_PATH, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
for path in files:
archive.write(path, Path(ROOT.name) / path.relative_to(ROOT))
print(f"wrote {ZIP_PATH} ({ZIP_PATH.stat().st_size} bytes, {len(files)} files)")
if __name__ == "__main__":
main()