Exclude ETH_MDIO/MDC from IEEE MDI 100 Ω. Deduplicate RD+/RX and TD+/TX pair aliases to one cert per channel; inherit LAN8720A 10/100 when the magnetics-side nets lack the PHY. USB D+/D− skip AF tr/f (90 Ω cert owns them); RMII stays on the English PE-AF-003 AN-133 line.
Periscope
Periscope is full PCB analysis for a board you intend to send to fab with confidence. Schematic review is a stage, not the deliverable.
Give it a netlist, a BOM, datasheet PDFs, and a KiCad PCB. It builds a queryable graph of the design, extracts manufacturer constraints into a shared library, then checks both the circuit and the copper: pads, tracks, vias, zones, measured geometry, and the interfaces that are actually on this board.
Live: https://periscope.michelebigi.it
Version: 2.87.2 (see periscope/src/frontend/content/changelog.md)
Operator: Michele Bigi
What you get
- PCB + schematic review — same finding object for
MODE=run(schematic) andMODE=pcb(layout). Findings keep FACT, REQUIREMENT, and INFERENCE apart. - Protocol-aware certification — gated on parts that are present. USB 2.0 D+/D− (pair Z vs packed 90 Ω), Ethernet MDI / RJ45, and MAC–PHY buses (RMII / MII / RGMII / SGMII) with catalog cites from packed specs. Missing evidence is silence or
not certified — because …, not invented ohms or timing. - Findings tree — Error / Warning / Info first, then domains, rules, Certifications, and Protocols. Report export is Save as Markdown (
.md). - Apple / iPad-style shell — thin top bar, tab navigation, light theme by default, dark mode in Settings.
What it will not invent
Semantic objects stay distinct. A via is not a pad. A track is not a via. A zone is not a pad. Sharing a net or sitting inside a courtyard does not change the object type.
Evidence before conclusion. If the datasheet, netlist, BOM, or .kicad_pcb does not supply the number, Periscope records INSUFFICIENT (or skips). It does not invent current, impedance, copper weight, geometry, or “typical” bus amps.
Trace current uses datasheet current only. Width versus load (PE-PWR) runs when the part reports I_load / Imax / I_abs. Missing that current is a skip.
Certifiers are gated on parts that are present. No USB-C receptacle → no USB-C findings. A bare RJ45 is Ethernet, not PoE. RMII PHY–MAC nets are not treated as RJ45/MDI 100 Ω pairs. Recognition alone is not certification.
Periscope does not claim FEM, thermal spreading, OpenEMS, or a field solver. Closed-form Z0 comes from vendored ImpedenceFinder (vendor/impedancefinder/; upstream license UNKNOWN).
Repository layout
| Path | Role |
|---|---|
periscope/src/ |
Product code — finding engine, PCB/placement, protocol catalog, DeepSeek pipelines, Next.js UI, Dockerfiles, KiCad plugin |
periscope/dependency/ |
Shared AGPL overlays (parsers, extraction shell, UI primitives) merged at build/runtime — do not delete |
backend/__init__.py |
Merges the two backend packages for local imports |
standards/protocol-specs/ |
On-disk protocol PDF archive (PDFs gitignored; INDEX.md committed) |
scripts/ |
update-periscope.sh, materialize-frontend.sh, smoke wrapper |
vendor/impedancefinder/ |
Third-party Z0 core (not FEM) |
LICENSE |
GNU AGPL v3 |
See periscope/README.md for the physical split.
Run locally
Python 3.12+, Node 20+, and a DeepSeek API key for live extraction and review. Offline graph/smoke does not need the key.
python3 -m venv .venv
source .venv/bin/activate
pip install -r periscope/src/backend/requirements.txt
cp periscope/src/backend/.env.example .env # set DEEPSEEK_API_KEY
python3 -m uvicorn backend.main:app --reload --host 127.0.0.1 --port 18741
Frontend — materialize the UI tree, then start Next.js:
./scripts/materialize-frontend.sh
cd .merge/frontend && npm install
NEXT_PUBLIC_API_URL=http://127.0.0.1:18741 npm run dev -- --port 18742 --hostname 127.0.0.1
Open the frontend URL, create a project, and upload a netlist plus BOM. A .kicad_pcb is required for the PCB job; without it the project can complete schematic review only. periscope/dependency/simple_project/ is a small bundled schematic fixture (MSPM0G3507 + CH340E + SPX3819).
Projects and the extraction library live in data/ (gitignored). Extraction skills are local periscope/src/skills/*/SKILL.md.
Tests from the repo root:
pip install pytest pytest-asyncio
pytest tests/ -q
python3 scripts/smoke_simple_project.py # offline
Docker
cp periscope/src/backend/.env.example .env # set DEEPSEEK_API_KEY
docker compose up --build
Backend listens on port 8080, frontend on port 3000 (docker-compose.yml).
Live host
The public site is rebuilt from /root/periscope:
cd /root/periscope
./scripts/update-periscope.sh
--no-pull skips git. SITE=https://other.host ./scripts/update-periscope.sh overrides the public URL. The script refuses to run unless the checkout is /root/periscope (override with CANONICAL_ROOT only if the host layout differs). It does not touch data/.
Source of truth
GitHub is the primary remote for this product. Push main there first.
| Remote | URL | Role |
|---|---|---|
| GitHub | https://github.com/manvalan/periscope (git@github.com:manvalan/periscope.git) |
Primary — source of truth |
| Gitea | http://192.168.1.71:3000/michele/periscope (git@gitea:michele/periscope.git, SSH port 2223) |
Secondary / mirror — do not delete |
git push github main
git push gitea main # optional mirror
Do not force-push main.
License
AGPL-3.0 — GNU Affero General Public License v3.0.
For this Periscope instance, contact Michele Bigi (mikbigi@gmail.com).