69 行
3.6 KiB
Python
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'}
|