summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/tests
diff options
context:
space:
mode:
authorjaseg <git@jaseg.de>2022-01-22 19:26:48 +0100
committerjaseg <git@jaseg.de>2022-01-22 19:26:48 +0100
commit242f4033c661d70c0d2722050370307f4d9b678a (patch)
tree9076a1d0f45a76c49a465f74903071671006b6de /gerbonara/gerber/tests
parent7cf41c6a72e52a63b4f4d4497732a72d6623eec8 (diff)
downloadgerbonara-242f4033c661d70c0d2722050370307f4d9b678a.tar.gz
gerbonara-242f4033c661d70c0d2722050370307f4d9b678a.tar.bz2
gerbonara-242f4033c661d70c0d2722050370307f4d9b678a.zip
Make excellon tests pass
Diffstat (limited to 'gerbonara/gerber/tests')
-rw-r--r--gerbonara/gerber/tests/test_excellon.py98
-rw-r--r--gerbonara/gerber/tests/utils.py12
2 files changed, 81 insertions, 29 deletions
diff --git a/gerbonara/gerber/tests/test_excellon.py b/gerbonara/gerber/tests/test_excellon.py
index 9aa5232..fb0ebb8 100644
--- a/gerbonara/gerber/tests/test_excellon.py
+++ b/gerbonara/gerber/tests/test_excellon.py
@@ -4,50 +4,94 @@
import math
import pytest
+from scipy.spatial import KDTree
from ..excellon import ExcellonFile
+from ..rs274x import GerberFile
from ..cam import FileSettings
+from ..graphic_objects import Flash
from .image_support import *
from .utils import *
from ..utils import Inch, MM
-REFERENCE_FILES = [
- 'easyeda/Gerber_Drill_NPTH.DRL',
- 'easyeda/Gerber_Drill_PTH.DRL',
- 'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-SlotHoles.TXT',
- 'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-RoundHoles.TXT',
- 'pcb-rnd/power-art.xln',
- 'siemens/80101_0125_F200_ThruHoleNonPlated.ncd',
- 'siemens/80101_0125_F200_ThruHolePlated.ncd',
- 'siemens/80101_0125_F200_ContourPlated.ncd',
- 'Target3001/IRNASIoTbank1.2.Drill',
- 'altium-old-composite-drill.txt',
- 'fritzing/combined.txt',
- 'ncdrill.DRD',
- 'upverter/design_export.drl',
- 'diptrace/mainboard.drl',
- 'diptrace/panel.drl',
- 'diptrace/keyboard.drl',
- ]
+REFERENCE_FILES = {
+ 'easyeda/Gerber_Drill_NPTH.DRL': (None, None),
+ 'easyeda/Gerber_Drill_PTH.DRL': (None, 'easyeda/Gerber_TopLayer.GTL'),
+ # Altium uses an excellon format specification format that gerbv doesn't understand, so we have to fix that.
+ 'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-SlotHoles.TXT': (('mm', 'leading', 4), None),
+ 'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-RoundHoles.TXT': (('mm', 'leading', 4), 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.GTL'),
+ 'pcb-rnd/power-art.xln': (None, 'pcb-rnd/power-art.gtl'),
+ 'siemens/80101_0125_F200_ThruHoleNonPlated.ncd': (None, None),
+ 'siemens/80101_0125_F200_ThruHolePlated.ncd': (None, 'siemens/80101_0125_F200_L01_Top.gdo'),
+ 'siemens/80101_0125_F200_ContourPlated.ncd': (None, None),
+ 'Target3001/IRNASIoTbank1.2.Drill': (None, 'Target3001/IRNASIoTbank1.2.Top'),
+ 'altium-old-composite-drill.txt': (None, None),
+ 'fritzing/combined.txt': (None, 'fritzing/combined.gtl'),
+ 'ncdrill.DRD': (None, None),
+ 'upverter/design_export.drl': (None, 'upverter/design_export.gtl'),
+ 'diptrace/mainboard.drl': (None, 'diptrace/mainboard_Top.gbr'),
+ 'diptrace/panel.drl': (None, None),
+ 'diptrace/keyboard.drl': (None, 'diptrace/keyboard_Bottom.gbr'),
+ }
@filter_syntax_warnings
-@pytest.mark.parametrize('reference', REFERENCE_FILES, indirect=True)
+@pytest.mark.parametrize('reference', list(REFERENCE_FILES.items()), indirect=True)
def test_round_trip(reference, tmpfile):
+ reference, (unit_spec, _) = reference
tmp = tmpfile('Output excellon', '.drl')
- # Altium uses an excellon format specification format that gerbv doesn't understand, so we have to fix that.
- unit_spec = ('mm', 'leading', 4) if 'altium-composite-drill' in str(reference) else None
- # pcb-rnd does not include any unit specification at all
- if 'pcb-rnd' in str(reference):
- settings = FileSettings(unit=Inch, zeros='leading', number_format=(2,4))
- else:
- settings = None
- ExcellonFile.open(reference, settings=settings).save(tmp)
+ ExcellonFile.open(reference).save(tmp)
mean, _max, hist = gerber_difference(reference, tmp, diff_out=tmpfile('Difference', '.png'), ref_unit_spec=unit_spec)
assert mean < 5e-5
assert hist[9] == 0
assert hist[3:].sum() < 5e-5*hist.size
+@filter_syntax_warnings
+@pytest.mark.parametrize('reference', list(REFERENCE_FILES.items()), indirect=True)
+def test_gerber_alignment(reference, tmpfile, print_on_error):
+ reference, (unit_spec, gerber) = reference
+ tmp = tmpfile('Output excellon', '.drl')
+
+ if gerber is None:
+ pytest.skip()
+
+ excf = ExcellonFile.open(reference)
+ gerf_path = reference_path(gerber)
+ print_on_error('Reference gerber file:', gerf_path)
+ gerf = GerberFile.open(gerf_path)
+ print('bounds excellon:', excf.bounding_box(MM))
+ print('bounds gerber:', gerf.bounding_box(MM))
+ excf.save('/tmp/test.xnc')
+
+ flash_coords = []
+ for obj in gerf.objects:
+ if isinstance(obj, Flash):
+ x, y = obj.unit.convert_to(MM, obj.x), obj.unit.convert_to(MM, obj.y)
+ if abs(x - 121.525) < 2 and abs(y - 64) < 2:
+ print(obj)
+ flash_coords.append((x, y))
+
+ tree = KDTree(flash_coords, copy_data=True)
+
+ tolerance = 0.05 # mm
+ matches, total = 0, 0
+ for obj in excf.objects:
+ if isinstance(obj, Flash):
+ if obj.plated in (True, None):
+ total += 1
+ x, y = obj.unit.convert_to(MM, obj.x), obj.unit.convert_to(MM, obj.y)
+ print((x, y), end=' ')
+ if abs(x - 121.525) < 2 and abs(y - 64) < 2:
+ print(obj)
+ print(' ', tree.query_ball_point((x, y), r=tolerance))
+ if tree.query_ball_point((x, y), r=tolerance):
+ matches += 1
+
+ # Some PCB tools, notably easyeda, are dumb and export certain pads as regions, not apertures. Thus, we have to
+ # tolerate some non-matches.
+ assert matches > 10
+ assert matches/total > 0.5
+
diff --git a/gerbonara/gerber/tests/utils.py b/gerbonara/gerber/tests/utils.py
index f63f3c6..bf30a1f 100644
--- a/gerbonara/gerber/tests/utils.py
+++ b/gerbonara/gerber/tests/utils.py
@@ -61,8 +61,16 @@ def tmpfile(request):
@pytest.fixture
def reference(request, print_on_error):
- ref = reference_path(request.param)
- yield ref
+ ref = request.param
+ if isinstance(ref, tuple):
+ ref, args = ref
+ ref = reference_path(ref)
+ yield ref, args
+
+ else:
+ ref = reference_path(request.param)
+ yield ref
+
print_on_error(f'Reference file: {ref}')
def filter_syntax_warnings(fun):