文件
phonerail/kicad/scripts/kelvin_checks.py
T

69 行
3.6 KiB
Python

"""Check registered sense copper against native physical connectivity."""
import pcbnew as pcb
def check_measurement_branches(board, branches):
if not branches:
return {'branches': [], 'errors': []}
board.BuildConnectivity()
connection = board.GetConnectivity()
tracks = {t.m_Uuid.AsString(): t for t in board.GetTracks()}
pads = {(f.GetReference(), p.GetNumber()): p for f in board.GetFootprints() for p in f.Pads()}
results, errors = [], []
for branch in branches:
name = branch['connection']
ids = set(branch['track_uuids'])
source_key, target_key = tuple(branch['from']), tuple(branch['to'])
source, target = pads.get(source_key), pads.get(target_key)
issues = []
if source is None or target is None or not ids <= tracks.keys():
errors.append({'kind': 'kelvin_missing_items', 'connection': name})
continue
if source.GetNetname() != target.GetNetname() or source.GetNetname() != branch['net']:
issues.append('net_changed')
if source_key[1] not in ('9', '10') or not target_key[0].startswith('Rs'):
issues.append('unexpected_measurement_endpoints')
seen = set()
pending = {t.m_Uuid.AsString() for t in connection.GetConnectedTracks(source)} & ids
while pending:
uid = pending.pop()
if uid in seen:
continue
seen.add(uid)
pending.update({t.m_Uuid.AsString() for t in connection.GetConnectedTracks(tracks[uid])} & ids - seen)
if seen != ids:
issues.append('disconnected_registered_segments')
touched = set()
external = set()
for uid in ids:
track = tracks[uid]
if isinstance(track, pcb.PCB_VIA) or track.GetLayer() != pcb.F_Cu:
issues.append('measurement_changed_layer')
if track.GetWidth() != pcb.FromMM(branch['width_mm']) or track.GetNetname() != branch['net']:
issues.append('measurement_geometry_or_net_changed')
touched.update((p.GetParentFootprint().GetReference(), p.GetNumber()) for p in connection.GetConnectedPads(track))
for other in connection.GetConnectedTracks(track):
other_id = other.m_Uuid.AsString()
if other_id in ids:
continue
# Future load copper may join at the shunt land, never on the sense run.
shared = [q for q in (track.GetStart(), track.GetEnd())
if q == other.GetStart() or q == other.GetEnd()]
if not shared or not all(target.HitTest(q) for q in shared):
external.add(other_id)
if touched != {source_key, target_key}:
issues.append('unexpected_or_missing_pad_contact')
if external:
issues.append('branch_join_outside_shunt_land')
length = sum(pcb.ToMM(tracks[uid].GetLength()) for uid in ids)
if abs(length - branch['length_mm']) > .001:
issues.append('measurement_length_changed_requires_review')
result = {'connection': name, 'from': source_key, 'to': target_key,
'physically_touched_pads': sorted(touched), 'length_mm': round(length, 5),
'external_track_joins': sorted(external), 'passed': not issues}
results.append(result)
if issues:
errors.append({'kind': 'kelvin_branch', 'connection': name, 'issues': sorted(set(issues))})
return {'branches': results, 'errors': errors,
'limits': 'Zones and final shunt load-current entry geometry require review after power copper is added'}