[Position][Added] Experimental support for gerber position files

Closes #500
This commit is contained in:
Salvador E. Tropea 2023-10-02 12:36:16 -03:00
parent bd081609d5
commit d0c489e2cc
7 changed files with 75 additions and 16 deletions

View File

@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for CURRENT_DATE text variable - Support for CURRENT_DATE text variable
- Populate: - Populate:
- Basic support for regular list items (#480) - Basic support for regular list items (#480)
- Position:
- Experimental support for gerber position files (#500)
- Help for the error levels - Help for the error levels
- Warnings: - Warnings:
- Explain about wrong dir/output separation (#493) - Explain about wrong dir/output separation (#493)

View File

@ -2769,15 +2769,19 @@ outputs:
# [string|list(string)='_none'] Name of the filter to mark components as not fitted. # [string|list(string)='_none'] Name of the filter to mark components as not fitted.
# A short-cut to use for simple cases where a variant is an overkill # A short-cut to use for simple cases where a variant is an overkill
dnf_filter: '_none' dnf_filter: '_none'
# [string='ASCII'] [ASCII,CSV] Format for the position file # [string='ASCII'] [ASCII,CSV,GBR] Format for the position file.
# Note that the gerber format (GBR) needs KiCad 7+ and doesn't support most of the options.
# Only the options that explicitly say the format is supported
format: 'ASCII' format: 'ASCII'
# [boolean=false] Include the board edge in the gerber output
gerber_board_edge: false
# [boolean=false] Include virtual components. For special purposes, not pick & place. # [boolean=false] Include virtual components. For special purposes, not pick & place.
# Note that virtual components is a KiCad 5 concept. # Note that virtual components is a KiCad 5 concept.
# For KiCad 6+ we replace this concept by the option to exclude from position file # For KiCad 6+ we replace this concept by the option to exclude from position file
include_virtual: false include_virtual: false
# [boolean=true] Only include the surface mount components # [boolean=true] Only include the surface mount components
only_smd: true only_smd: true
# [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'). # [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'|'gbr').
# Important: when using separate files you must use `%i` to differentiate them. Affected by global options # Important: when using separate files you must use `%i` to differentiate them. Affected by global options
output: '%f-%i%I%v.%x' output: '%f-%i%I%v.%x'
# [string|list(string)='_none'] Name of the filter to transform fields before applying other filters. # [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
@ -2791,7 +2795,8 @@ outputs:
separate_files_for_front_and_back: true separate_files_for_front_and_back: true
# [string='millimeters'] [millimeters,inches,mils] Units used for the positions. Affected by global options # [string='millimeters'] [millimeters,inches,mils] Units used for the positions. Affected by global options
units: 'millimeters' units: 'millimeters'
# [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default) # [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default).
# Supported by the gerber format
use_aux_axis_as_origin: true use_aux_axis_as_origin: true
# [string=''] Board variant to apply # [string=''] Board variant to apply
variant: '' variant: ''
@ -2924,8 +2929,8 @@ outputs:
options: options:
# [string='QR'] Short name for the library # [string='QR'] Short name for the library
lib: 'QR' lib: 'QR'
# [string='%f-%i%I%v.%x'] Filename/dirname for the output (%i=qr, %x=lib/kicad_sym/pretty). # [string='%f-%i%I%v.%x'] Filename/dirname for the output library (%i=qr, %x=lib/kicad_sym/pretty).
# You must use %x in the name to get a symbols lib and a footprint. Affected by global options # You must use %x in the name to get a symbols lib and a footprints lib. Affected by global options
output: '%f-%i%I%v.%x' output: '%f-%i%I%v.%x'
# [list(dict)] QR codes to include in the library # [list(dict)] QR codes to include in the library
qrs: qrs:

View File

@ -24,9 +24,11 @@ Parameters:
- Valid keys: - Valid keys:
- **format** :index:`: <pair: output - position - options; format>` [string='ASCII'] [ASCII,CSV] Format for the position file. - **format** :index:`: <pair: output - position - options; format>` [string='ASCII'] [ASCII,CSV,GBR] Format for the position file.
Note that the gerber format (GBR) needs KiCad 7+ and doesn't support most of the options.
Only the options that explicitly say the format is supported.
- **only_smd** :index:`: <pair: output - position - options; only_smd>` [boolean=true] Only include the surface mount components. - **only_smd** :index:`: <pair: output - position - options; only_smd>` [boolean=true] Only include the surface mount components.
- **output** :index:`: <pair: output - position - options; output>` [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'). - **output** :index:`: <pair: output - position - options; output>` [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'|'gbr').
Important: when using separate files you must use `%i` to differentiate them. Affected by global options. Important: when using separate files you must use `%i` to differentiate them. Affected by global options.
- **separate_files_for_front_and_back** :index:`: <pair: output - position - options; separate_files_for_front_and_back>` [boolean=true] Generate two separated files, one for the top and another for the bottom. - **separate_files_for_front_and_back** :index:`: <pair: output - position - options; separate_files_for_front_and_back>` [boolean=true] Generate two separated files, one for the top and another for the bottom.
- **units** :index:`: <pair: output - position - options; units>` [string='millimeters'] [millimeters,inches,mils] Units used for the positions. Affected by global options. - **units** :index:`: <pair: output - position - options; units>` [string='millimeters'] [millimeters,inches,mils] Units used for the positions. Affected by global options.
@ -41,6 +43,7 @@ Parameters:
- ``dnf_filter`` :index:`: <pair: output - position - options; dnf_filter>` [string|list(string)='_none'] Name of the filter to mark components as not fitted. - ``dnf_filter`` :index:`: <pair: output - position - options; dnf_filter>` [string|list(string)='_none'] Name of the filter to mark components as not fitted.
A short-cut to use for simple cases where a variant is an overkill. A short-cut to use for simple cases where a variant is an overkill.
- ``gerber_board_edge`` :index:`: <pair: output - position - options; gerber_board_edge>` [boolean=false] Include the board edge in the gerber output.
- ``include_virtual`` :index:`: <pair: output - position - options; include_virtual>` [boolean=false] Include virtual components. For special purposes, not pick & place. - ``include_virtual`` :index:`: <pair: output - position - options; include_virtual>` [boolean=false] Include virtual components. For special purposes, not pick & place.
Note that virtual components is a KiCad 5 concept. Note that virtual components is a KiCad 5 concept.
For KiCad 6+ we replace this concept by the option to exclude from position file. For KiCad 6+ we replace this concept by the option to exclude from position file.
@ -50,6 +53,7 @@ Parameters:
- ``quote_all`` :index:`: <pair: output - position - options; quote_all>` [boolean=false] When generating the CSV quote all values, even numbers. - ``quote_all`` :index:`: <pair: output - position - options; quote_all>` [boolean=false] When generating the CSV quote all values, even numbers.
- ``right_digits`` :index:`: <pair: output - position - options; right_digits>` [number=4] number of digits for mantissa part of coordinates (0 is auto). - ``right_digits`` :index:`: <pair: output - position - options; right_digits>` [number=4] number of digits for mantissa part of coordinates (0 is auto).
- ``use_aux_axis_as_origin`` :index:`: <pair: output - position - options; use_aux_axis_as_origin>` [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default). - ``use_aux_axis_as_origin`` :index:`: <pair: output - position - options; use_aux_axis_as_origin>` [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default).
Supported by the gerber format.
- ``variant`` :index:`: <pair: output - position - options; variant>` [string=''] Board variant to apply. - ``variant`` :index:`: <pair: output - position - options; variant>` [string=''] Board variant to apply.
- **type** :index:`: <pair: output - position; type>` [string=''] Type of output. - **type** :index:`: <pair: output - position; type>` [string=''] Type of output.

View File

@ -30,8 +30,8 @@ Parameters:
- Valid keys: - Valid keys:
- **lib** :index:`: <pair: output - qr_lib - options; lib>` [string='QR'] Short name for the library. - **lib** :index:`: <pair: output - qr_lib - options; lib>` [string='QR'] Short name for the library.
- **output** :index:`: <pair: output - qr_lib - options; output>` [string='%f-%i%I%v.%x'] Filename/dirname for the output (%i=qr, %x=lib/kicad_sym/pretty). - **output** :index:`: <pair: output - qr_lib - options; output>` [string='%f-%i%I%v.%x'] Filename/dirname for the output library (%i=qr, %x=lib/kicad_sym/pretty).
You must use %x in the name to get a symbols lib and a footprint. Affected by global options. You must use %x in the name to get a symbols lib and a footprints lib. Affected by global options.
- **qrs** :index:`: <pair: output - qr_lib - options; qrs>` [list(dict)] QR codes to include in the library. - **qrs** :index:`: <pair: output - qr_lib - options; qrs>` [list(dict)] QR codes to include in the library.
- Valid keys: - Valid keys:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# Copyright (c) 2019 Romain Deterre (@rdeterre) # Copyright (c) 2019 Romain Deterre (@rdeterre)
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
@ -10,6 +10,7 @@ from re import compile
from datetime import datetime from datetime import datetime
from collections import OrderedDict from collections import OrderedDict
from .gs import GS from .gs import GS
from .kiplot import run_command
from .misc import UI_SMD, UI_VIRTUAL, MOD_THROUGH_HOLE, MOD_SMD, MOD_EXCLUDE_FROM_POS_FILES from .misc import UI_SMD, UI_VIRTUAL, MOD_THROUGH_HOLE, MOD_SMD, MOD_EXCLUDE_FROM_POS_FILES
from .optionable import Optionable from .optionable import Optionable
from .out_base import VariantOptions from .out_base import VariantOptions
@ -60,13 +61,15 @@ class PositionOptions(VariantOptions):
def __init__(self): def __init__(self):
with document: with document:
self.format = 'ASCII' self.format = 'ASCII'
""" *[ASCII,CSV] Format for the position file """ """ *[ASCII,CSV,GBR] Format for the position file.
Note that the gerber format (GBR) needs KiCad 7+ and doesn't support most of the options.
Only the options that explicitly say the format is supported """
self.separate_files_for_front_and_back = True self.separate_files_for_front_and_back = True
""" *Generate two separated files, one for the top and another for the bottom """ """ *Generate two separated files, one for the top and another for the bottom """
self.only_smd = True self.only_smd = True
""" *Only include the surface mount components """ """ *Only include the surface mount components """
self.output = GS.def_global_output self.output = GS.def_global_output
""" *Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'). """ *Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'|'gbr').
Important: when using separate files you must use `%i` to differentiate them """ Important: when using separate files you must use `%i` to differentiate them """
self.units = 'millimeters' self.units = 'millimeters'
""" *[millimeters,inches,mils] Units used for the positions. Affected by global options """ """ *[millimeters,inches,mils] Units used for the positions. Affected by global options """
@ -78,13 +81,16 @@ class PositionOptions(VariantOptions):
self.bottom_negative_x = False self.bottom_negative_x = False
""" Use negative X coordinates for footprints on bottom layer """ """ Use negative X coordinates for footprints on bottom layer """
self.use_aux_axis_as_origin = True self.use_aux_axis_as_origin = True
""" Use the auxiliary axis as origin for coordinates (KiCad default) """ """ Use the auxiliary axis as origin for coordinates (KiCad default).
Supported by the gerber format """
self.include_virtual = False self.include_virtual = False
""" Include virtual components. For special purposes, not pick & place. """ Include virtual components. For special purposes, not pick & place.
Note that virtual components is a KiCad 5 concept. Note that virtual components is a KiCad 5 concept.
For KiCad 6+ we replace this concept by the option to exclude from position file """ For KiCad 6+ we replace this concept by the option to exclude from position file """
self.quote_all = False self.quote_all = False
""" When generating the CSV quote all values, even numbers """ """ When generating the CSV quote all values, even numbers """
self.gerber_board_edge = False
""" Include the board edge in the gerber output """
super().__init__() super().__init__()
self._expand_id = 'position' self._expand_id = 'position'
@ -105,7 +111,7 @@ class PositionOptions(VariantOptions):
new_name = col.name if col.name else new_col new_name = col.name if col.name else new_col
new_columns[new_col] = new_name new_columns[new_col] = new_name
self.columns = new_columns self.columns = new_columns
self._expand_ext = 'pos' if self.format == 'ASCII' else 'csv' self._expand_ext = 'pos' if self.format == 'ASCII' else self.format.lower()
def _do_position_plot_ascii(self, output_dir, columns, modulesStr, maxSizes, modules_side): def _do_position_plot_ascii(self, output_dir, columns, modulesStr, maxSizes, modules_side):
topf = None topf = None
@ -224,10 +230,31 @@ class PositionOptions(VariantOptions):
self.expand_filename(out_dir, self.output, 'bottom_pos', ext)] self.expand_filename(out_dir, self.output, 'bottom_pos', ext)]
return [self.expand_filename(out_dir, self.output, 'both_pos', ext)] return [self.expand_filename(out_dir, self.output, 'both_pos', ext)]
def run_gerber(self, output_dir):
if not GS.ki7:
raise KiPlotConfigurationError("Gerber position needs KiCad 7+")
pcb_name = self.save_tmp_board_if_variant()
cmd_base = ['kicad-cli', 'pcb', 'export', 'pos', '--format', 'gerber']
if self.use_aux_axis_as_origin:
cmd_base.append('--use-drill-file-origin')
if self.gerber_board_edge:
cmd_base.append('--gerber-board-edge')
cmd_base.append('--side')
fname = self.expand_filename(output_dir, self.output, 'top_pos', self._expand_ext)
run_command(cmd_base+['front', '-o', fname, pcb_name])
fname = self.expand_filename(output_dir, self.output, 'bottom_pos', self._expand_ext)
run_command(cmd_base+['back', '-o', fname, pcb_name])
def run(self, fname): def run(self, fname):
super().run(fname) super().run(fname)
self.filter_pcb_components()
output_dir = os.path.dirname(fname) output_dir = os.path.dirname(fname)
if self.format == 'GBR':
self.run_gerber(output_dir)
return
self.filter_pcb_components()
columns = self.columns.values() columns = self.columns.values()
conv = GS.unit_name_to_scale_factor(self.units) conv = GS.unit_name_to_scale_factor(self.units)
# Format all strings # Format all strings

View File

@ -339,3 +339,13 @@ def test_position_flags_3(test_dir):
ctx.run() ctx.run()
check_comp_list(ctx.load_csv(prj+'-both_pos.csv')[0], {'R1', 'R2', 'R3'}) check_comp_list(ctx.load_csv(prj+'-both_pos.csv')[0], {'R1', 'R2', 'R3'})
ctx.clean_up() ctx.clean_up()
@pytest.mark.skipif(not context.ki7(), reason="needs kicad-cli")
def test_position_gerber_1(test_dir):
prj = 'light_control'
ctx = context.TestContext(test_dir, prj, 'simple_position_gbr', POS_DIR)
ctx.run()
ctx.expect_out_file_d(prj+'-top_pos.gbr')
ctx.expect_out_file_d(prj+'-bottom_pos.gbr')
ctx.clean_up(keep_project=True)

View File

@ -0,0 +1,11 @@
# Example KiBot config file for a basic 2-layer board
kibot:
version: 1
outputs:
- name: 'position'
type: position
dir: positiondir
options:
format: GBR