Browse Source
ADDED: Stacked pin notation support
ADDED: Stacked pin notation support
Implement bracket notation for stacked pins ([1,2,3], [1-4], [1,3,5-7]). Automatic net naming proceeds based on the smallest logical pin number in stacked groups. Provide explode/reform commands in symbol editor for conversion. Supports arbitrary ranges including BGA alphanum ranges like [AA1-AA3,CD14-CD22] Adds some additional QA and trace logging Fixes https://gitlab.com/kicad/code/kicad/-/issues/2004master
49 changed files with 6379 additions and 286 deletions
-
122common/string_utils.cpp
-
14cvpcb/cvpcb_mainframe.cpp
-
8cvpcb/footprints_listbox.cpp
-
41eeschema/dialogs/dialog_field_properties.cpp
-
2eeschema/dialogs/dialog_lib_fields_table.cpp
-
43eeschema/erc/erc.cpp
-
6eeschema/erc/erc.h
-
6eeschema/erc/erc_item.cpp
-
1eeschema/erc/erc_item.h
-
1eeschema/erc/erc_settings.cpp
-
3eeschema/erc/erc_settings.h
-
42eeschema/fields_grid_table.cpp
-
69eeschema/lib_symbol.cpp
-
29eeschema/lib_symbol.h
-
67eeschema/multiline_pin_text.cpp
-
40eeschema/multiline_pin_text.h
-
31eeschema/netlist_exporters/netlist_exporter_base.cpp
-
6eeschema/netlist_exporters/netlist_exporter_base.h
-
78eeschema/netlist_exporters/netlist_exporter_xml.cpp
-
277eeschema/pin_layout_cache.cpp
-
12eeschema/pin_layout_cache.h
-
6eeschema/sch_file_versions.h
-
2eeschema/sch_io/cadstar/cadstar_sch_archive_loader.cpp
-
456eeschema/sch_painter.cpp
-
386eeschema/sch_pin.cpp
-
14eeschema/sch_pin.h
-
10eeschema/sch_symbol.cpp
-
14eeschema/tools/sch_actions.cpp
-
2eeschema/tools/sch_actions.h
-
2eeschema/tools/sch_point_editor.cpp
-
394eeschema/tools/symbol_editor_edit_tool.cpp
-
2eeschema/tools/symbol_editor_edit_tool.h
-
2eeschema/widgets/panel_symbol_chooser.cpp
-
18include/string_utils.h
-
21pcbnew/footprint_chooser_frame.cpp
-
15pcbnew/netlist_reader/board_netlist_updater.cpp
-
19pcbnew/netlist_reader/kicad_netlist_reader.cpp
-
44pcbnew/netlist_reader/pcb_netlist.cpp
-
128qa/data/eeschema/stacked_pin_nomenclature.kicad_sch
-
872qa/data/pcbnew/stacked/stacked.kicad_pcb
-
632qa/data/pcbnew/stacked/stacked.kicad_pro
-
827qa/data/pcbnew/stacked/stacked.kicad_sch
-
4qa/tests/eeschema/CMakeLists.txt
-
150qa/tests/eeschema/test_netlist_exporter_xml_stacked.cpp
-
543qa/tests/eeschema/test_pin_stacked_layout.cpp
-
742qa/tests/eeschema/test_stacked_pin_conversion.cpp
-
101qa/tests/eeschema/test_stacked_pin_nomenclature.cpp
-
1qa/tests/pcbnew/CMakeLists.txt
-
266qa/tests/pcbnew/test_stacked_pin_netlist.cpp
@ -0,0 +1,67 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright The KiCad Developers |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 3 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one at |
|||
* http://www.gnu.org/licenses/
|
|||
*/ |
|||
|
|||
|
|||
#include "multiline_pin_text.h"
|
|||
|
|||
#include <wx/tokenzr.h>
|
|||
|
|||
MULTILINE_PIN_TEXT_LAYOUT ComputeMultiLinePinNumberLayout( const wxString& aText, |
|||
const VECTOR2D& aAnchorPos, const TEXT_ATTRIBUTES& aAttrs ) |
|||
{ |
|||
MULTILINE_PIN_TEXT_LAYOUT layout; |
|||
layout.m_StartPos = aAnchorPos; |
|||
|
|||
if( !( aText.StartsWith( "[" ) && aText.EndsWith( "]" ) && aText.Contains( "\n" ) ) ) |
|||
return layout; // not multi-line stacked
|
|||
|
|||
wxString content = aText.Mid( 1, aText.Length() - 2 ); |
|||
wxArrayString lines; wxStringSplit( content, lines, '\n' ); |
|||
if( lines.size() <= 1 ) |
|||
return layout; |
|||
|
|||
layout.m_IsMultiLine = true; |
|||
|
|||
layout.m_Lines = lines; |
|||
for( size_t i = 0; i < layout.m_Lines.size(); ++i ) |
|||
layout.m_Lines[i].Trim( true ).Trim( false ); |
|||
|
|||
layout.m_LineSpacing = KiROUND( aAttrs.m_Size.y * 1.3 ); |
|||
|
|||
// Apply alignment-dependent origin shift identical to sch_painter logic
|
|||
if( aAttrs.m_Angle == ANGLE_VERTICAL ) |
|||
{ |
|||
int totalWidth = ( (int) layout.m_Lines.size() - 1 ) * layout.m_LineSpacing; |
|||
if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT ) |
|||
layout.m_StartPos.x -= totalWidth; |
|||
else if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_CENTER ) |
|||
layout.m_StartPos.x -= totalWidth / 2; |
|||
} |
|||
else |
|||
{ |
|||
int totalHeight = ( (int) layout.m_Lines.size() - 1 ) * layout.m_LineSpacing; |
|||
if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_BOTTOM ) |
|||
layout.m_StartPos.y -= totalHeight; |
|||
else if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_CENTER ) |
|||
layout.m_StartPos.y -= totalHeight / 2; |
|||
} |
|||
|
|||
return layout; |
|||
} |
@ -0,0 +1,40 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright The KiCad Developers |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 3 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one at |
|||
* http://www.gnu.org/licenses/ |
|||
*/ |
|||
|
|||
|
|||
#pragma once |
|||
|
|||
#include <wx/string.h> |
|||
#include <wx/arrstr.h> |
|||
#include <math/vector2d.h> |
|||
#include <font/text_attributes.h> |
|||
|
|||
struct MULTILINE_PIN_TEXT_LAYOUT |
|||
{ |
|||
bool m_IsMultiLine = false; // true if brace-wrapped multi-line stacked list |
|||
wxArrayString m_Lines; // individual numbered lines (trimmed) |
|||
VECTOR2D m_StartPos; // position used for line index 0 after alignment shift |
|||
int m_LineSpacing = 0; // inter-line spacing in IU (along secondary axis) |
|||
}; |
|||
|
|||
// Compute layout for a (possibly) multi-line stacked pin number string. If not multi-line, the |
|||
// returned layout has m_IsMultiLine=false and no further adjustments are required. |
|||
MULTILINE_PIN_TEXT_LAYOUT ComputeMultiLinePinNumberLayout( const wxString& aText, |
|||
const VECTOR2D& aAnchorPos, const TEXT_ATTRIBUTES& aAttrs ); |
@ -0,0 +1,128 @@ |
|||
(kicad_sch (version 20240910) (generator eeschema) |
|||
|
|||
(paper "A4") |
|||
|
|||
(lib_symbols |
|||
(symbol "Device:R" |
|||
(pin_names (offset 0)) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "R1" (at 6.35 0 90) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Value" "R" (at 3.81 0 90) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Footprint" "" (at -1.778 0 90) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Datasheet" "" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Description" "Resistor" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "ki_keywords" "R res resistor" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "ki_fp_filters" "R_*" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(symbol "R_0_1" |
|||
(rectangle (start -1.016 -2.54) (end 1.016 2.54) |
|||
(stroke (width 0.254) (type default)) (fill (type none)) ) ) |
|||
(symbol "R_1_1" |
|||
(pin passive line (at 0 6.35 270) (length 1.27) |
|||
(name "" (effects (font (size 1.27 1.27)))) |
|||
(number "[1-5]" (effects (font (size 1.27 1.27)))) ) |
|||
(pin passive line (at 0 -8.636 90) (length 1.27) |
|||
(name "" (effects (font (size 1.27 1.27)))) |
|||
(number "[6,7,9-11]" (effects (font (size 1.27 1.27)))) ) ) ) |
|||
|
|||
(symbol "power:GND" |
|||
(power global) |
|||
(pin_numbers (hide yes)) |
|||
(pin_names (offset 0) (hide yes)) |
|||
(exclude_from_sim no) (in_bom yes) (on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "#PWR" (at 0 -6.35 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Value" "GND" (at 0 -3.81 0) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Footprint" "" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Datasheet" "" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Description" "Power symbol creates a global label with name \"GND\" , ground" |
|||
(at 0 0 0) (hide yes) (effects (font (size 1.27 1.27))) ) |
|||
(property "ki_keywords" "global power" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(symbol "GND_0_1" |
|||
(polyline (pts (xy 0 0) (xy 0 -1.27) (xy 1.27 -1.27) (xy 0 -2.54) (xy -1.27 -1.27) (xy 0 -1.27)) |
|||
(stroke (width 0) (type default)) (fill (type none)) ) ) |
|||
(symbol "GND_1_1" |
|||
(pin power_in line (at 0 0 270) (length 0) |
|||
(name "" (effects (font (size 1.27 1.27)))) |
|||
(number "1" (effects (font (size 1.27 1.27)))) ) ) ) |
|||
|
|||
(symbol "power:VCC" |
|||
(power global) |
|||
(pin_numbers (hide yes)) |
|||
(pin_names (offset 0) (hide yes)) |
|||
(exclude_from_sim no) (in_bom yes) (on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "#PWR" (at 0 -3.81 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Value" "VCC" (at 0 3.556 0) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Footprint" "" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Datasheet" "" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(property "Description" "Power symbol creates a global label with name \"VCC\"" |
|||
(at 0 0 0) (hide yes) (effects (font (size 1.27 1.27))) ) |
|||
(property "ki_keywords" "global power" (at 0 0 0) (hide yes) |
|||
(effects (font (size 1.27 1.27))) ) |
|||
(symbol "VCC_0_1" |
|||
(polyline (pts (xy -0.762 1.27) (xy 0 2.54)) (stroke (width 0) (type default)) (fill (type none))) |
|||
(polyline (pts (xy 0 2.54) (xy 0.762 1.27)) (stroke (width 0) (type default)) (fill (type none))) |
|||
(polyline (pts (xy 0 0) (xy 0 2.54)) (stroke (width 0) (type default)) (fill (type none))) ) |
|||
(symbol "VCC_1_1" |
|||
(pin power_in line (at 0 0 90) (length 0) |
|||
(name "" (effects (font (size 1.27 1.27)))) |
|||
(number "1" (effects (font (size 1.27 1.27)))) ) ) ) |
|||
) |
|||
|
|||
(wire (pts (xy 134.62 83.82) (xy 134.62 76.2)) (stroke (width 0) (type default))) |
|||
(wire (pts (xy 100.33 87.63) (xy 100.33 83.82)) (stroke (width 0) (type default))) |
|||
(wire (pts (xy 100.33 83.82) (xy 109.22 83.82)) (stroke (width 0) (type default))) |
|||
(wire (pts (xy 124.206 83.82) (xy 134.62 83.82)) (stroke (width 0) (type default))) |
|||
|
|||
(symbol (lib_id "Device:R") (at 115.57 83.82 90) (unit 1) (body_style 1) |
|||
(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced yes) |
|||
(uuid "2166cf4e-5b03-495b-af0a-be2718b61b04") |
|||
(property "Reference" "R1" (at 115.57 77.47 90) (effects (font (size 1.27 1.27)))) |
|||
(property "Value" "R" (at 115.57 80.01 90) (effects (font (size 1.27 1.27)))) |
|||
(property "Footprint" "" (at 115.57 85.598 90) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Datasheet" "" (at 115.57 83.82 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Description" "Resistor" (at 115.57 83.82 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(pin "[6,7,9-11]" (uuid "f62cbf3f-56f0-413b-a710-93a48027efbb")) |
|||
(pin "[1-5]" (uuid "e856ce98-c7c6-4066-97a8-60e95541ae81")) ) |
|||
|
|||
(symbol (lib_id "power:VCC") (at 134.62 76.2 0) (unit 1) (body_style 1) |
|||
(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced yes) |
|||
(uuid "2b72c13a-dfcb-4980-a25b-daac640cdb9b") |
|||
(property "Reference" "#PWR01" (at 134.62 80.01 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Value" "VCC" (at 134.62 71.12 0) (effects (font (size 1.27 1.27)))) |
|||
(property "Footprint" "" (at 134.62 76.2 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Datasheet" "" (at 134.62 76.2 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Description" "Power symbol creates a global label with name \"VCC\"" |
|||
(at 134.62 76.2 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(pin "1" (uuid "ff7185fb-347f-48f5-bcf0-de5808edd725")) ) |
|||
|
|||
(symbol (lib_id "power:GND") (at 100.33 87.63 0) (unit 1) (body_style 1) |
|||
(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced yes) |
|||
(uuid "4e64c428-4aed-4183-826f-2fa5009f4177") |
|||
(property "Reference" "#PWR02" (at 100.33 93.98 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Value" "GND" (at 100.33 92.71 0) (effects (font (size 1.27 1.27)))) |
|||
(property "Footprint" "" (at 100.33 87.63 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Datasheet" "" (at 100.33 87.63 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(property "Description" "Power symbol creates a global label with name \"GND\" , ground" |
|||
(at 100.33 87.63 0) (hide yes) (effects (font (size 1.27 1.27)))) |
|||
(pin "1" (uuid "b8c00030-efa1-4421-9af4-303142b1c17d")) ) |
|||
) |
@ -0,0 +1,872 @@ |
|||
(kicad_pcb |
|||
(version 20250901) |
|||
(generator "pcbnew") |
|||
(generator_version "9.99") |
|||
(general |
|||
(thickness 1.6) |
|||
(legacy_teardrops no) |
|||
) |
|||
(paper "A4") |
|||
(layers |
|||
(0 "F.Cu" signal) |
|||
(2 "B.Cu" signal) |
|||
(9 "F.Adhes" user "F.Adhesive") |
|||
(11 "B.Adhes" user "B.Adhesive") |
|||
(13 "F.Paste" user) |
|||
(15 "B.Paste" user) |
|||
(5 "F.SilkS" user "F.Silkscreen") |
|||
(7 "B.SilkS" user "B.Silkscreen") |
|||
(1 "F.Mask" user) |
|||
(3 "B.Mask" user) |
|||
(17 "Dwgs.User" user "User.Drawings") |
|||
(19 "Cmts.User" user "User.Comments") |
|||
(21 "Eco1.User" user "User.Eco1") |
|||
(23 "Eco2.User" user "User.Eco2") |
|||
(25 "Edge.Cuts" user) |
|||
(27 "Margin" user) |
|||
(31 "F.CrtYd" user "F.Courtyard") |
|||
(29 "B.CrtYd" user "B.Courtyard") |
|||
(35 "F.Fab" user) |
|||
(33 "B.Fab" user) |
|||
(39 "User.1" user) |
|||
(41 "User.2" user) |
|||
(43 "User.3" user) |
|||
(45 "User.4" user) |
|||
) |
|||
(setup |
|||
(pad_to_mask_clearance 0) |
|||
(allow_soldermask_bridges_in_footprints no) |
|||
(tenting |
|||
(front yes) |
|||
(back yes) |
|||
) |
|||
(covering |
|||
(front no) |
|||
(back no) |
|||
) |
|||
(plugging |
|||
(front no) |
|||
(back no) |
|||
) |
|||
(capping no) |
|||
(filling no) |
|||
(pcbplotparams |
|||
(layerselection 0x00000000_00000000_55555555_5755f5ff) |
|||
(plot_on_all_layers_selection 0x00000000_00000000_00000000_00000000) |
|||
(disableapertmacros no) |
|||
(usegerberextensions no) |
|||
(usegerberattributes yes) |
|||
(usegerberadvancedattributes yes) |
|||
(creategerberjobfile yes) |
|||
(dashed_line_dash_ratio 12) |
|||
(dashed_line_gap_ratio 3) |
|||
(svgprecision 4) |
|||
(plotframeref no) |
|||
(mode 1) |
|||
(useauxorigin no) |
|||
(pdf_front_fp_property_popups yes) |
|||
(pdf_back_fp_property_popups yes) |
|||
(pdf_metadata yes) |
|||
(pdf_single_document no) |
|||
(dxfpolygonmode yes) |
|||
(dxfimperialunits yes) |
|||
(dxfusepcbnewfont yes) |
|||
(psnegative no) |
|||
(psa4output no) |
|||
(plot_black_and_white yes) |
|||
(sketchpadsonfab no) |
|||
(plotpadnumbers no) |
|||
(hidednponfab no) |
|||
(sketchdnponfab yes) |
|||
(crossoutdnponfab yes) |
|||
(subtractmaskfromsilk no) |
|||
(outputformat 1) |
|||
(mirror no) |
|||
(drillshape 1) |
|||
(scaleselection 1) |
|||
(outputdirectory "") |
|||
) |
|||
) |
|||
(net 0 "") |
|||
(net 1 "GND") |
|||
(net 2 "Net-(R1-Pad6)") |
|||
(net 3 "VCC") |
|||
(footprint "Connector:Tag-Connect_TC2050-IDC-FP_2x05_P1.27mm_Vertical" |
|||
(layer "F.Cu") |
|||
(uuid "6be81952-937a-4957-9eb2-85721ef5e835") |
|||
(at 99.15 82.8) |
|||
(descr "Tag-Connect programming header; http://www.tag-connect.com/Materials/TC2050-IDC-430%20Datasheet.pdf") |
|||
(tags "tag connect programming header pogo pins") |
|||
(property "Reference" "R1" |
|||
(at 0 5 0) |
|||
(layer "F.SilkS") |
|||
(uuid "a58bcfb3-ac5c-44e3-b078-f6fcbbff78fd") |
|||
(effects |
|||
(font |
|||
(size 1 1) |
|||
(thickness 0.15) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "R" |
|||
(at 0 -4.8 0) |
|||
(layer "F.Fab") |
|||
(uuid "e812b7b1-3211-42eb-ba2d-aa11bdc756e0") |
|||
(effects |
|||
(font |
|||
(size 1 1) |
|||
(thickness 0.15) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 0 0 0) |
|||
(unlocked yes) |
|||
(layer "F.Fab") |
|||
(hide yes) |
|||
(uuid "49f5b1e8-966f-4ec6-9eec-08f47e66d0f8") |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Resistor" |
|||
(at 0 0 0) |
|||
(unlocked yes) |
|||
(layer "F.Fab") |
|||
(hide yes) |
|||
(uuid "f75f79bb-0bac-443b-8a9e-c180b3d4a9f0") |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property ki_fp_filters "R_*") |
|||
(path "/2166cf4e-5b03-495b-af0a-be2718b61b04") |
|||
(sheetname "/") |
|||
(sheetfile "stacked.kicad_sch") |
|||
(attr exclude_from_pos_files) |
|||
(duplicate_pad_numbers_are_jumpers no) |
|||
(fp_line |
|||
(start -3.175 1.27) |
|||
(end -3.175 0.635) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "27aa3b0d-a092-4b0c-995a-8e3cec800dbe") |
|||
) |
|||
(fp_line |
|||
(start -2.54 1.27) |
|||
(end -3.175 1.27) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "bee2c633-f143-428a-9485-6a636fd6de5c") |
|||
) |
|||
(fp_line |
|||
(start -5.5 -4.25) |
|||
(end 4.75 -4.25) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "aae4190a-8a8d-4a1a-b456-2957c835b850") |
|||
) |
|||
(fp_line |
|||
(start -5.5 4.25) |
|||
(end -5.5 -4.25) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "921c2c67-e2cf-45e5-a409-21d7b43c6aa3") |
|||
) |
|||
(fp_line |
|||
(start 4.75 -4.25) |
|||
(end 4.75 4.25) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "f9324654-e75c-4d7f-adfb-f8bb0d60c1c9") |
|||
) |
|||
(fp_line |
|||
(start 4.75 4.25) |
|||
(end -5.5 4.25) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "bcca5d74-54fb-4c6c-a8dc-039d2882a8a4") |
|||
) |
|||
(fp_text user "KEEPOUT" |
|||
(at 0 0 0) |
|||
(layer "Cmts.User") |
|||
(uuid "3f2c9b29-16de-427a-938d-6921987ed7d4") |
|||
(effects |
|||
(font |
|||
(size 0.4 0.4) |
|||
(thickness 0.07) |
|||
) |
|||
) |
|||
) |
|||
(fp_text user "${REFERENCE}" |
|||
(at 0 0 0) |
|||
(layer "F.Fab") |
|||
(uuid "720463bd-308b-464f-83d1-706bd97b4b09") |
|||
(effects |
|||
(font |
|||
(size 1 1) |
|||
(thickness 0.15) |
|||
) |
|||
) |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at -3.81 -2.54) |
|||
(size 2.3749 2.3749) |
|||
(drill 2.3749) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "e669af60-91a5-43ae-9c40-cb3349ad6cd9") |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at -3.81 0) |
|||
(size 0.9906 0.9906) |
|||
(drill 0.9906) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "a25c573c-0aaa-4e39-8a8a-87edb26c8eba") |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at -3.81 2.54) |
|||
(size 2.3749 2.3749) |
|||
(drill 2.3749) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "28203947-6c7f-4fda-8630-083dbbb2065b") |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at 1.905 -2.54) |
|||
(size 2.3749 2.3749) |
|||
(drill 2.3749) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "d64457ca-c860-4e9e-8d02-773e7b597037") |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at 1.905 2.54) |
|||
(size 2.3749 2.3749) |
|||
(drill 2.3749) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "ecc5a508-d2f3-487b-98b8-7a2095646b49") |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at 3.81 -1.016) |
|||
(size 0.9906 0.9906) |
|||
(drill 0.9906) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "e35c5901-0bbb-48bd-bb2a-0e208b25c86d") |
|||
) |
|||
(pad "" np_thru_hole circle |
|||
(at 3.81 1.016) |
|||
(size 0.9906 0.9906) |
|||
(drill 0.9906) |
|||
(layers "*.Cu" "*.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "d17e6355-636f-4a2e-9a07-dac92e07dbd3") |
|||
) |
|||
(pad "1" connect circle |
|||
(at -2.54 0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 1 "GND") |
|||
(pinfunction "1") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "0d1d056c-2f36-4152-9a5c-117b6f43d4e3") |
|||
) |
|||
(pad "2" connect circle |
|||
(at -1.27 0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 1 "GND") |
|||
(pinfunction "2") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "7835c4d6-293f-470f-9732-986fc9802a90") |
|||
) |
|||
(pad "3" connect circle |
|||
(at 0 0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 1 "GND") |
|||
(pinfunction "3") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "ea592653-d244-4bb8-b4ed-ee92882d3777") |
|||
) |
|||
(pad "4" connect circle |
|||
(at 1.27 0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 1 "GND") |
|||
(pinfunction "4") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "5e78183f-956b-4f05-a129-15f4e5fe3a0c") |
|||
) |
|||
(pad "5" connect circle |
|||
(at 2.54 0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 1 "GND") |
|||
(pinfunction "5") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "6e86d938-6846-476f-83b5-1d8afafe7361") |
|||
) |
|||
(pad "6" connect circle |
|||
(at 2.54 -0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 2 "Net-(R1-Pad6)") |
|||
(pinfunction "6") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "6b67537c-a5db-4c19-a020-c8fcd9f7acf8") |
|||
) |
|||
(pad "7" connect circle |
|||
(at 1.27 -0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 2 "Net-(R1-Pad6)") |
|||
(pinfunction "7") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "d0255722-889d-41bc-920a-0463b6ac11fc") |
|||
) |
|||
(pad "8" connect circle |
|||
(at 0 -0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "0b67effe-459a-49bc-8c65-e7053f564265") |
|||
) |
|||
(pad "9" connect circle |
|||
(at -1.27 -0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 2 "Net-(R1-Pad6)") |
|||
(pinfunction "9") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "1dab4fb0-b39d-4753-ab2a-a5f4f1e8aac1") |
|||
) |
|||
(pad "10" connect circle |
|||
(at -2.54 -0.635) |
|||
(size 0.7874 0.7874) |
|||
(layers "F.Cu" "F.Mask") |
|||
(net 2 "Net-(R1-Pad6)") |
|||
(pinfunction "10") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "46e85dda-2b66-4d09-a062-916d33de6a9e") |
|||
) |
|||
(zone |
|||
(net 0) |
|||
(net_name "") |
|||
(layer "F.Cu") |
|||
(uuid "8ef75162-3f7e-4efc-8b0a-4b51b175c2d3") |
|||
(hatch full 0.508) |
|||
(connect_pads |
|||
(clearance 0) |
|||
) |
|||
(min_thickness 0.254) |
|||
(keepout |
|||
(tracks allowed) |
|||
(vias not_allowed) |
|||
(pads allowed) |
|||
(copperpour not_allowed) |
|||
(footprints not_allowed) |
|||
) |
|||
(placement |
|||
(enabled no) |
|||
(sheetname "") |
|||
) |
|||
(fill |
|||
(thermal_gap 0.508) |
|||
(thermal_bridge_width 0.508) |
|||
(island_removal_mode 0) |
|||
) |
|||
(polygon |
|||
(pts |
|||
(xy 96.61 83.435) (xy 101.69 83.435) (xy 101.69 82.165) (xy 96.61 82.165) |
|||
) |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
|||
(footprint "Capacitor_SMD:CP_Elec_3x5.4" |
|||
(layer "F.Cu") |
|||
(uuid "f38d6383-ca09-42d5-84bc-8ed2ee9e1fb5") |
|||
(at 96.5 90) |
|||
(descr "SMD capacitor, aluminum electrolytic, Nichicon, 3.0x5.4mm") |
|||
(tags "capacitor electrolytic") |
|||
(property "Reference" "X1" |
|||
(at 0 -2.7 0) |
|||
(layer "F.SilkS") |
|||
(uuid "8fda368e-86c3-4111-b138-69e3967c1f68") |
|||
(effects |
|||
(font |
|||
(size 1 1) |
|||
(thickness 0.15) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "C" |
|||
(at 0 2.7 0) |
|||
(layer "F.Fab") |
|||
(uuid "1e3a282d-c685-413d-bf0c-edca1617eef8") |
|||
(effects |
|||
(font |
|||
(size 1 1) |
|||
(thickness 0.15) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 0 0 0) |
|||
(unlocked yes) |
|||
(layer "F.Fab") |
|||
(hide yes) |
|||
(uuid "96d28378-e62a-4b33-90cc-9202b68830f3") |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Unpolarized capacitor" |
|||
(at 0 0 0) |
|||
(unlocked yes) |
|||
(layer "F.Fab") |
|||
(hide yes) |
|||
(uuid "78264291-21af-4cc0-b361-f72ab21292c3") |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property ki_fp_filters "C_*") |
|||
(path "/e65020b4-b867-4d63-a46c-163f7210b6bb") |
|||
(sheetname "/") |
|||
(sheetfile "stacked.kicad_sch") |
|||
(attr smd) |
|||
(duplicate_pad_numbers_are_jumpers no) |
|||
(fp_line |
|||
(start -2.375 -1.435) |
|||
(end -2 -1.435) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "7f79c690-cb60-4051-ad22-422351f6db5f") |
|||
) |
|||
(fp_line |
|||
(start -2.1875 -1.6225) |
|||
(end -2.1875 -1.2475) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "5c447aaa-d1e7-4fa3-9001-ac553d20662f") |
|||
) |
|||
(fp_line |
|||
(start -1.570563 -1.06) |
|||
(end -0.870563 -1.76) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "b111b33d-2499-4768-8eac-089403f3ec2b") |
|||
) |
|||
(fp_line |
|||
(start -1.570563 1.06) |
|||
(end -0.870563 1.76) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "edde24dd-77e1-41f4-89bb-5243e8aca75b") |
|||
) |
|||
(fp_line |
|||
(start -0.870563 -1.76) |
|||
(end 1.76 -1.76) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "d87eb71e-8e64-4d2a-a08f-3d9c164bd7b2") |
|||
) |
|||
(fp_line |
|||
(start -0.870563 1.76) |
|||
(end 1.76 1.76) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "66e18111-d83b-402d-b5bf-4272faf95588") |
|||
) |
|||
(fp_line |
|||
(start 1.76 -1.76) |
|||
(end 1.76 -1.06) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "3d5b5c44-9238-471b-8363-30d163faeaa9") |
|||
) |
|||
(fp_line |
|||
(start 1.76 1.76) |
|||
(end 1.76 1.06) |
|||
(stroke |
|||
(width 0.12) |
|||
(type solid) |
|||
) |
|||
(layer "F.SilkS") |
|||
(uuid "3f598ead-79d7-4fd5-8825-a2ede4af679e") |
|||
) |
|||
(fp_line |
|||
(start -2.85 -1.05) |
|||
(end -2.85 1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "334da6ac-b694-478b-9cd1-8c83468d7391") |
|||
) |
|||
(fp_line |
|||
(start -2.85 1.05) |
|||
(end -1.78 1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "31c9fc6d-fb51-4a4c-b881-c2c07c346518") |
|||
) |
|||
(fp_line |
|||
(start -1.78 -1.05) |
|||
(end -2.85 -1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "a00caec7-86a6-4c68-a3fc-934a3d99d2d4") |
|||
) |
|||
(fp_line |
|||
(start -1.78 -1.05) |
|||
(end -0.93 -1.9) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "322cbb81-71f3-413e-b949-ccee52c5fbb0") |
|||
) |
|||
(fp_line |
|||
(start -1.78 1.05) |
|||
(end -0.93 1.9) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "e530520a-ddcd-42a3-9c5d-4d2a1f3154d3") |
|||
) |
|||
(fp_line |
|||
(start -0.93 -1.9) |
|||
(end 1.9 -1.9) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "987d6ecb-c12d-45d9-9619-904468fee663") |
|||
) |
|||
(fp_line |
|||
(start -0.93 1.9) |
|||
(end 1.9 1.9) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "d355660a-f52e-4ee5-9fbb-9b86dbbe4282") |
|||
) |
|||
(fp_line |
|||
(start 1.9 -1.9) |
|||
(end 1.9 -1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "c9c0c7e3-9efc-4a85-8de7-3a6319ff7a44") |
|||
) |
|||
(fp_line |
|||
(start 1.9 -1.05) |
|||
(end 2.85 -1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "a5329e51-5a4e-4a5c-aafd-07e647458f36") |
|||
) |
|||
(fp_line |
|||
(start 1.9 1.05) |
|||
(end 1.9 1.9) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "4e327b6e-6096-4ec1-abdc-e5aadd5ce3b4") |
|||
) |
|||
(fp_line |
|||
(start 2.85 -1.05) |
|||
(end 2.85 1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "2ba849f1-a85e-4b96-baeb-988620c13ad1") |
|||
) |
|||
(fp_line |
|||
(start 2.85 1.05) |
|||
(end 1.9 1.05) |
|||
(stroke |
|||
(width 0.05) |
|||
(type solid) |
|||
) |
|||
(layer "F.CrtYd") |
|||
(uuid "cc4ec5b8-906b-4358-9df6-626341ba4078") |
|||
) |
|||
(fp_line |
|||
(start -1.65 -0.825) |
|||
(end -1.65 0.825) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "d09f4d86-628e-4106-8571-3557d500000e") |
|||
) |
|||
(fp_line |
|||
(start -1.65 -0.825) |
|||
(end -0.825 -1.65) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "5a3a7862-c16f-49e9-a4a5-7301da652b4b") |
|||
) |
|||
(fp_line |
|||
(start -1.65 0.825) |
|||
(end -0.825 1.65) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "6f9a2956-13af-4d8c-a116-e81678cf86b7") |
|||
) |
|||
(fp_line |
|||
(start -1.110469 -0.8) |
|||
(end -0.810469 -0.8) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "6480eda9-f610-45f9-9815-aa694aa23157") |
|||
) |
|||
(fp_line |
|||
(start -0.960469 -0.95) |
|||
(end -0.960469 -0.65) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "2f8eccda-c7ba-4ac5-8b4a-1bdee552ce2e") |
|||
) |
|||
(fp_line |
|||
(start -0.825 -1.65) |
|||
(end 1.65 -1.65) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "26170f0f-c996-4313-a1ba-3f7eb878fcb0") |
|||
) |
|||
(fp_line |
|||
(start -0.825 1.65) |
|||
(end 1.65 1.65) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "dfac16eb-141f-4383-93a1-185012c558dc") |
|||
) |
|||
(fp_line |
|||
(start 1.65 -1.65) |
|||
(end 1.65 1.65) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(layer "F.Fab") |
|||
(uuid "7ead1618-1e1b-485f-a82b-60b82c9a147a") |
|||
) |
|||
(fp_circle |
|||
(center 0 0) |
|||
(end 1.5 0) |
|||
(stroke |
|||
(width 0.1) |
|||
(type solid) |
|||
) |
|||
(fill no) |
|||
(layer "F.Fab") |
|||
(uuid "5ee3d052-a5df-4679-917d-290393b4ac4a") |
|||
) |
|||
(fp_text user "${REFERENCE}" |
|||
(at 0 0 0) |
|||
(layer "F.Fab") |
|||
(uuid "587b737e-6398-4d6d-9ee7-7db10fa2e562") |
|||
(effects |
|||
(font |
|||
(size 0.6 0.6) |
|||
(thickness 0.09) |
|||
) |
|||
) |
|||
) |
|||
(pad "1" smd roundrect |
|||
(at -1.5 0) |
|||
(size 2.2 1.6) |
|||
(layers "F.Cu" "F.Mask" "F.Paste") |
|||
(roundrect_rratio 0.15625) |
|||
(net 2 "Net-(R1-Pad6)") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "595baab1-9b66-41dd-838b-67f9f7f008e9") |
|||
) |
|||
(pad "2" smd roundrect |
|||
(at 1.5 0) |
|||
(size 2.2 1.6) |
|||
(layers "F.Cu" "F.Mask" "F.Paste") |
|||
(roundrect_rratio 0.15625) |
|||
(net 3 "VCC") |
|||
(pintype "passive") |
|||
(tenting |
|||
(front none) |
|||
(back none) |
|||
) |
|||
(uuid "d75d5164-5460-42c3-9d1d-54ff2da81393") |
|||
) |
|||
(embedded_fonts no) |
|||
(model "${KICAD8_3DMODEL_DIR}/Capacitor_SMD.3dshapes/CP_Elec_3x5.4.wrl" |
|||
(offset |
|||
(xyz 0 0 0) |
|||
) |
|||
(scale |
|||
(xyz 1 1 1) |
|||
) |
|||
(rotate |
|||
(xyz 0 0 0) |
|||
) |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
@ -0,0 +1,632 @@ |
|||
{ |
|||
"board": { |
|||
"3dviewports": [], |
|||
"design_settings": { |
|||
"defaults": { |
|||
"apply_defaults_to_fp_fields": false, |
|||
"apply_defaults_to_fp_shapes": false, |
|||
"apply_defaults_to_fp_text": false, |
|||
"board_outline_line_width": 0.05, |
|||
"copper_line_width": 0.2, |
|||
"copper_text_italic": false, |
|||
"copper_text_size_h": 1.5, |
|||
"copper_text_size_v": 1.5, |
|||
"copper_text_thickness": 0.3, |
|||
"copper_text_upright": false, |
|||
"courtyard_line_width": 0.05, |
|||
"dimension_precision": 4, |
|||
"dimension_units": 3, |
|||
"dimensions": { |
|||
"arrow_length": 1270000, |
|||
"extension_offset": 500000, |
|||
"keep_text_aligned": true, |
|||
"suppress_zeroes": true, |
|||
"text_position": 0, |
|||
"units_format": 0 |
|||
}, |
|||
"fab_line_width": 0.1, |
|||
"fab_text_italic": false, |
|||
"fab_text_size_h": 1.0, |
|||
"fab_text_size_v": 1.0, |
|||
"fab_text_thickness": 0.15, |
|||
"fab_text_upright": false, |
|||
"other_line_width": 0.1, |
|||
"other_text_italic": false, |
|||
"other_text_size_h": 1.0, |
|||
"other_text_size_v": 1.0, |
|||
"other_text_thickness": 0.15, |
|||
"other_text_upright": false, |
|||
"pads": { |
|||
"drill": 0.8, |
|||
"height": 1.27, |
|||
"width": 2.54 |
|||
}, |
|||
"silk_line_width": 0.1, |
|||
"silk_text_italic": false, |
|||
"silk_text_size_h": 1.0, |
|||
"silk_text_size_v": 1.0, |
|||
"silk_text_thickness": 0.1, |
|||
"silk_text_upright": false, |
|||
"zones": { |
|||
"min_clearance": 0.5 |
|||
} |
|||
}, |
|||
"diff_pair_dimensions": [], |
|||
"drc_exclusions": [], |
|||
"meta": { |
|||
"version": 2 |
|||
}, |
|||
"rule_severities": { |
|||
"annular_width": "error", |
|||
"clearance": "error", |
|||
"connection_width": "warning", |
|||
"copper_edge_clearance": "error", |
|||
"copper_sliver": "warning", |
|||
"courtyards_overlap": "error", |
|||
"creepage": "error", |
|||
"diff_pair_gap_out_of_range": "error", |
|||
"diff_pair_uncoupled_length_too_long": "error", |
|||
"drill_out_of_range": "error", |
|||
"duplicate_footprints": "warning", |
|||
"extra_footprint": "warning", |
|||
"footprint": "error", |
|||
"footprint_filters_mismatch": "ignore", |
|||
"footprint_symbol_mismatch": "warning", |
|||
"footprint_type_mismatch": "ignore", |
|||
"hole_clearance": "error", |
|||
"hole_to_hole": "warning", |
|||
"holes_co_located": "warning", |
|||
"invalid_outline": "error", |
|||
"isolated_copper": "warning", |
|||
"item_on_disabled_layer": "error", |
|||
"items_not_allowed": "error", |
|||
"length_out_of_range": "error", |
|||
"lib_footprint_issues": "warning", |
|||
"lib_footprint_mismatch": "warning", |
|||
"malformed_courtyard": "error", |
|||
"microvia_drill_out_of_range": "error", |
|||
"mirrored_text_on_front_layer": "warning", |
|||
"missing_courtyard": "ignore", |
|||
"missing_footprint": "warning", |
|||
"net_conflict": "warning", |
|||
"nonmirrored_text_on_back_layer": "warning", |
|||
"npth_inside_courtyard": "ignore", |
|||
"padstack": "warning", |
|||
"pth_inside_courtyard": "ignore", |
|||
"shorting_items": "error", |
|||
"silk_edge_clearance": "warning", |
|||
"silk_over_copper": "warning", |
|||
"silk_overlap": "warning", |
|||
"skew_out_of_range": "error", |
|||
"solder_mask_bridge": "error", |
|||
"starved_thermal": "error", |
|||
"text_height": "warning", |
|||
"text_on_edge_cuts": "error", |
|||
"text_thickness": "warning", |
|||
"through_hole_pad_without_hole": "error", |
|||
"too_many_vias": "error", |
|||
"track_angle": "error", |
|||
"track_dangling": "warning", |
|||
"track_segment_length": "error", |
|||
"track_width": "error", |
|||
"tracks_crossing": "error", |
|||
"unconnected_items": "error", |
|||
"unresolved_variable": "error", |
|||
"via_dangling": "warning", |
|||
"zones_intersect": "error" |
|||
}, |
|||
"rules": { |
|||
"max_error": 0.005, |
|||
"min_clearance": 0.0, |
|||
"min_connection": 0.0, |
|||
"min_copper_edge_clearance": 0.5, |
|||
"min_groove_width": 0.0, |
|||
"min_hole_clearance": 0.25, |
|||
"min_hole_to_hole": 0.25, |
|||
"min_microvia_diameter": 0.2, |
|||
"min_microvia_drill": 0.1, |
|||
"min_resolved_spokes": 2, |
|||
"min_silk_clearance": 0.0, |
|||
"min_text_height": 0.8, |
|||
"min_text_thickness": 0.08, |
|||
"min_through_hole_diameter": 0.3, |
|||
"min_track_width": 0.0, |
|||
"min_via_annular_width": 0.1, |
|||
"min_via_diameter": 0.5, |
|||
"solder_mask_to_copper_clearance": 0.0, |
|||
"use_height_for_length_calcs": true |
|||
}, |
|||
"teardrop_options": [ |
|||
{ |
|||
"td_onpthpad": true, |
|||
"td_onroundshapesonly": false, |
|||
"td_onsmdpad": true, |
|||
"td_ontrackend": false, |
|||
"td_onvia": true |
|||
} |
|||
], |
|||
"teardrop_parameters": [ |
|||
{ |
|||
"td_allow_use_two_tracks": true, |
|||
"td_curve_segcount": 0, |
|||
"td_height_ratio": 1.0, |
|||
"td_length_ratio": 0.5, |
|||
"td_maxheight": 2.0, |
|||
"td_maxlen": 1.0, |
|||
"td_on_pad_in_zone": false, |
|||
"td_target_name": "td_round_shape", |
|||
"td_width_to_size_filter_ratio": 0.9 |
|||
}, |
|||
{ |
|||
"td_allow_use_two_tracks": true, |
|||
"td_curve_segcount": 0, |
|||
"td_height_ratio": 1.0, |
|||
"td_length_ratio": 0.5, |
|||
"td_maxheight": 2.0, |
|||
"td_maxlen": 1.0, |
|||
"td_on_pad_in_zone": false, |
|||
"td_target_name": "td_rect_shape", |
|||
"td_width_to_size_filter_ratio": 0.9 |
|||
}, |
|||
{ |
|||
"td_allow_use_two_tracks": true, |
|||
"td_curve_segcount": 0, |
|||
"td_height_ratio": 1.0, |
|||
"td_length_ratio": 0.5, |
|||
"td_maxheight": 2.0, |
|||
"td_maxlen": 1.0, |
|||
"td_on_pad_in_zone": false, |
|||
"td_target_name": "td_track_end", |
|||
"td_width_to_size_filter_ratio": 0.9 |
|||
} |
|||
], |
|||
"track_widths": [], |
|||
"tuning_pattern_settings": { |
|||
"diff_pair_defaults": { |
|||
"corner_radius_percentage": 80, |
|||
"corner_style": 1, |
|||
"max_amplitude": 1.0, |
|||
"min_amplitude": 0.2, |
|||
"single_sided": false, |
|||
"spacing": 1.0 |
|||
}, |
|||
"diff_pair_skew_defaults": { |
|||
"corner_radius_percentage": 80, |
|||
"corner_style": 1, |
|||
"max_amplitude": 1.0, |
|||
"min_amplitude": 0.2, |
|||
"single_sided": false, |
|||
"spacing": 0.6 |
|||
}, |
|||
"single_track_defaults": { |
|||
"corner_radius_percentage": 80, |
|||
"corner_style": 1, |
|||
"max_amplitude": 1.0, |
|||
"min_amplitude": 0.2, |
|||
"single_sided": false, |
|||
"spacing": 0.6 |
|||
} |
|||
}, |
|||
"via_dimensions": [], |
|||
"zones_allow_external_fillets": false |
|||
}, |
|||
"ipc2581": { |
|||
"dist": "", |
|||
"distpn": "", |
|||
"internal_id": "", |
|||
"mfg": "", |
|||
"mpn": "" |
|||
}, |
|||
"layer_pairs": [], |
|||
"layer_presets": [], |
|||
"viewports": [] |
|||
}, |
|||
"boards": [], |
|||
"component_class_settings": { |
|||
"assignments": [], |
|||
"meta": { |
|||
"version": 0 |
|||
}, |
|||
"sheet_component_classes": { |
|||
"enabled": false |
|||
} |
|||
}, |
|||
"cvpcb": { |
|||
"equivalence_files": [] |
|||
}, |
|||
"erc": { |
|||
"erc_exclusions": [], |
|||
"meta": { |
|||
"version": 0 |
|||
}, |
|||
"pin_map": [ |
|||
[ |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
2, |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
2, |
|||
2, |
|||
2, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
1, |
|||
0, |
|||
1, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
1, |
|||
1, |
|||
2, |
|||
1, |
|||
1, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
2 |
|||
], |
|||
[ |
|||
1, |
|||
1, |
|||
1, |
|||
1, |
|||
1, |
|||
0, |
|||
1, |
|||
1, |
|||
1, |
|||
1, |
|||
1, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
0, |
|||
0, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
2, |
|||
1, |
|||
2, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
2, |
|||
2, |
|||
2, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
2, |
|||
0, |
|||
1, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
2, |
|||
0, |
|||
0, |
|||
2 |
|||
], |
|||
[ |
|||
0, |
|||
2, |
|||
1, |
|||
1, |
|||
0, |
|||
0, |
|||
1, |
|||
0, |
|||
2, |
|||
0, |
|||
0, |
|||
2 |
|||
], |
|||
[ |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2, |
|||
2 |
|||
] |
|||
], |
|||
"rule_severities": { |
|||
"bus_definition_conflict": "error", |
|||
"bus_entry_needed": "error", |
|||
"bus_to_bus_conflict": "error", |
|||
"bus_to_net_conflict": "error", |
|||
"different_unit_footprint": "error", |
|||
"different_unit_net": "error", |
|||
"duplicate_reference": "error", |
|||
"duplicate_sheet_names": "error", |
|||
"endpoint_off_grid": "warning", |
|||
"extra_units": "error", |
|||
"footprint_filter": "ignore", |
|||
"footprint_link_issues": "warning", |
|||
"four_way_junction": "ignore", |
|||
"ground_pin_not_ground": "warning", |
|||
"hier_label_mismatch": "error", |
|||
"isolated_pin_label": "warning", |
|||
"label_dangling": "error", |
|||
"label_multiple_wires": "warning", |
|||
"lib_symbol_issues": "warning", |
|||
"lib_symbol_mismatch": "warning", |
|||
"missing_bidi_pin": "warning", |
|||
"missing_input_pin": "warning", |
|||
"missing_power_pin": "error", |
|||
"missing_unit": "warning", |
|||
"multiple_net_names": "warning", |
|||
"net_not_bus_member": "warning", |
|||
"no_connect_connected": "warning", |
|||
"no_connect_dangling": "warning", |
|||
"pin_not_connected": "error", |
|||
"pin_not_driven": "error", |
|||
"pin_to_pin": "warning", |
|||
"power_pin_not_driven": "error", |
|||
"same_local_global_label": "warning", |
|||
"similar_label_and_power": "warning", |
|||
"similar_labels": "warning", |
|||
"similar_power": "warning", |
|||
"simulation_model_issue": "ignore", |
|||
"single_global_label": "ignore", |
|||
"stacked_pin_name": "warning", |
|||
"unannotated": "error", |
|||
"unconnected_wire_endpoint": "warning", |
|||
"undefined_netclass": "error", |
|||
"unit_value_mismatch": "error", |
|||
"unresolved_variable": "error", |
|||
"wire_dangling": "error" |
|||
} |
|||
}, |
|||
"libraries": { |
|||
"pinned_footprint_libs": [], |
|||
"pinned_symbol_libs": [] |
|||
}, |
|||
"meta": { |
|||
"filename": "stacked.kicad_pro", |
|||
"version": 3 |
|||
}, |
|||
"net_settings": { |
|||
"classes": [ |
|||
{ |
|||
"bus_width": 12, |
|||
"clearance": 0.2, |
|||
"diff_pair_gap": 0.25, |
|||
"diff_pair_via_gap": 0.25, |
|||
"diff_pair_width": 0.2, |
|||
"line_style": 0, |
|||
"microvia_diameter": 0.3, |
|||
"microvia_drill": 0.1, |
|||
"name": "Default", |
|||
"pcb_color": "rgba(0, 0, 0, 0.000)", |
|||
"priority": 2147483647, |
|||
"schematic_color": "rgba(0, 0, 0, 0.000)", |
|||
"track_width": 0.2, |
|||
"tuning_profile": "", |
|||
"via_diameter": 0.6, |
|||
"via_drill": 0.3, |
|||
"wire_width": 6 |
|||
} |
|||
], |
|||
"meta": { |
|||
"version": 5 |
|||
}, |
|||
"net_colors": null, |
|||
"netclass_assignments": null, |
|||
"netclass_patterns": [] |
|||
}, |
|||
"pcbnew": { |
|||
"last_paths": { |
|||
"idf": "", |
|||
"netlist": "", |
|||
"plot": "", |
|||
"specctra_dsn": "", |
|||
"vrml": "" |
|||
}, |
|||
"page_layout_descr_file": "" |
|||
}, |
|||
"schematic": { |
|||
"annotate_start_num": 0, |
|||
"annotation": { |
|||
"method": 0, |
|||
"sort_order": 0 |
|||
}, |
|||
"bom_export_filename": "${PROJECTNAME}.csv", |
|||
"bom_fmt_presets": [], |
|||
"bom_fmt_settings": { |
|||
"field_delimiter": ",", |
|||
"keep_line_breaks": false, |
|||
"keep_tabs": false, |
|||
"name": "CSV", |
|||
"ref_delimiter": ",", |
|||
"ref_range_delimiter": "", |
|||
"string_delimiter": "\"" |
|||
}, |
|||
"bom_presets": [], |
|||
"bom_settings": { |
|||
"exclude_dnp": false, |
|||
"fields_ordered": [ |
|||
{ |
|||
"group_by": false, |
|||
"label": "Reference", |
|||
"name": "Reference", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": false, |
|||
"label": "Qty", |
|||
"name": "${QUANTITY}", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": true, |
|||
"label": "Value", |
|||
"name": "Value", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": true, |
|||
"label": "DNP", |
|||
"name": "${DNP}", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": true, |
|||
"label": "Exclude from BOM", |
|||
"name": "${EXCLUDE_FROM_BOM}", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": true, |
|||
"label": "Exclude from Board", |
|||
"name": "${EXCLUDE_FROM_BOARD}", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": true, |
|||
"label": "Footprint", |
|||
"name": "Footprint", |
|||
"show": true |
|||
}, |
|||
{ |
|||
"group_by": false, |
|||
"label": "Datasheet", |
|||
"name": "Datasheet", |
|||
"show": true |
|||
} |
|||
], |
|||
"filter_string": "", |
|||
"group_symbols": true, |
|||
"include_excluded_from_bom": true, |
|||
"name": "Default Editing", |
|||
"sort_asc": true, |
|||
"sort_field": "Reference" |
|||
}, |
|||
"connection_grid_size": 50.0, |
|||
"drawing": { |
|||
"dashed_lines_dash_length_ratio": 12.0, |
|||
"dashed_lines_gap_length_ratio": 3.0, |
|||
"default_line_thickness": 6.0, |
|||
"default_text_size": 50.0, |
|||
"field_names": [], |
|||
"hop_over_size_choice": 0, |
|||
"intersheets_ref_own_page": false, |
|||
"intersheets_ref_prefix": "", |
|||
"intersheets_ref_short": false, |
|||
"intersheets_ref_show": false, |
|||
"intersheets_ref_suffix": "", |
|||
"junction_size_choice": 3, |
|||
"label_size_ratio": 0.375, |
|||
"operating_point_overlay_i_precision": 3, |
|||
"operating_point_overlay_i_range": "~A", |
|||
"operating_point_overlay_v_precision": 3, |
|||
"operating_point_overlay_v_range": "~V", |
|||
"overbar_offset_ratio": 1.23, |
|||
"pin_symbol_size": 25.0, |
|||
"text_offset_ratio": 0.15 |
|||
}, |
|||
"legacy_lib_dir": "", |
|||
"legacy_lib_list": [], |
|||
"meta": { |
|||
"version": 1 |
|||
}, |
|||
"page_layout_descr_file": "", |
|||
"plot_directory": "", |
|||
"reuse_designators": true, |
|||
"subpart_first_id": 65, |
|||
"subpart_id_separator": 0, |
|||
"used_designators": "C1-2" |
|||
}, |
|||
"sheets": [ |
|||
[ |
|||
"f3609c98-5e8f-46ae-a21a-09d0b96782a9", |
|||
"Root" |
|||
] |
|||
], |
|||
"text_variables": {}, |
|||
"time_domain_parameters": { |
|||
"delay_profiles_user_defined": [], |
|||
"meta": { |
|||
"version": 0 |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,827 @@ |
|||
(kicad_sch |
|||
(version 20250829) |
|||
(generator "eeschema") |
|||
(generator_version "9.99") |
|||
(uuid "f3609c98-5e8f-46ae-a21a-09d0b96782a9") |
|||
(paper "A4") |
|||
(lib_symbols |
|||
(symbol "Device:C" |
|||
(pin_numbers |
|||
(hide yes) |
|||
) |
|||
(pin_names |
|||
(offset 0.254) |
|||
) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "C" |
|||
(at 0.635 2.54 0) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
(justify left) |
|||
) |
|||
) |
|||
(property "Value" "C" |
|||
(at 0.635 -2.54 0) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
(justify left) |
|||
) |
|||
) |
|||
(property "Footprint" "" |
|||
(at 0.9652 -3.81 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Unpolarized capacitor" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "ki_keywords" "cap capacitor" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "ki_fp_filters" "C_*" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(symbol "C_0_1" |
|||
(polyline |
|||
(pts |
|||
(xy -2.032 0.762) (xy 2.032 0.762) |
|||
) |
|||
(stroke |
|||
(width 0.508) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
(polyline |
|||
(pts |
|||
(xy -2.032 -0.762) (xy 2.032 -0.762) |
|||
) |
|||
(stroke |
|||
(width 0.508) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
) |
|||
(symbol "C_1_1" |
|||
(pin passive line |
|||
(at 0 3.81 270) |
|||
(length 2.794) |
|||
(name "" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(number "1" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(pin passive line |
|||
(at 0 -3.81 90) |
|||
(length 2.794) |
|||
(name "" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(number "2" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
|||
(symbol "Device:R" |
|||
(pin_names |
|||
(offset 0) |
|||
) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "R1" |
|||
(at 6.35 0 90) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "R" |
|||
(at 3.81 0 90) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "" |
|||
(at -1.778 0 90) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Resistor" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "ki_keywords" "R res resistor" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "ki_fp_filters" "R_*" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(symbol "R_0_1" |
|||
(rectangle |
|||
(start -1.016 -2.54) |
|||
(end 1.016 2.54) |
|||
(stroke |
|||
(width 0.254) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
) |
|||
(symbol "R_1_1" |
|||
(pin passive line |
|||
(at 0 6.35 270) |
|||
(length 1.27) |
|||
(name "" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(number "[1-5]" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(pin passive line |
|||
(at 0 -8.636 90) |
|||
(length 1.27) |
|||
(name "" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(number "[6,7,9-11]" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
|||
(symbol "power:GND" |
|||
(power global) |
|||
(pin_numbers |
|||
(hide yes) |
|||
) |
|||
(pin_names |
|||
(offset 0) |
|||
(hide yes) |
|||
) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "#PWR" |
|||
(at 0 -6.35 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "GND" |
|||
(at 0 -3.81 0) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Power symbol creates a global label with name \"GND\" , ground" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "ki_keywords" "global power" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(symbol "GND_0_1" |
|||
(polyline |
|||
(pts |
|||
(xy 0 0) (xy 0 -1.27) (xy 1.27 -1.27) (xy 0 -2.54) (xy -1.27 -1.27) (xy 0 -1.27) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
) |
|||
(symbol "GND_1_1" |
|||
(pin power_in line |
|||
(at 0 0 270) |
|||
(length 0) |
|||
(name "" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(number "1" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
|||
(symbol "power:VCC" |
|||
(power global) |
|||
(pin_numbers |
|||
(hide yes) |
|||
) |
|||
(pin_names |
|||
(offset 0) |
|||
(hide yes) |
|||
) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(duplicate_pin_numbers_are_jumpers no) |
|||
(property "Reference" "#PWR" |
|||
(at 0 -3.81 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "VCC" |
|||
(at 0 3.556 0) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Power symbol creates a global label with name \"VCC\"" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "ki_keywords" "global power" |
|||
(at 0 0 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(symbol "VCC_0_1" |
|||
(polyline |
|||
(pts |
|||
(xy -0.762 1.27) (xy 0 2.54) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
(polyline |
|||
(pts |
|||
(xy 0 2.54) (xy 0.762 1.27) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
(polyline |
|||
(pts |
|||
(xy 0 0) (xy 0 2.54) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(fill |
|||
(type none) |
|||
) |
|||
) |
|||
) |
|||
(symbol "VCC_1_1" |
|||
(pin power_in line |
|||
(at 0 0 90) |
|||
(length 0) |
|||
(name "" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(number "1" |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
|||
) |
|||
(wire |
|||
(pts |
|||
(xy 124.206 83.82) (xy 134.62 83.82) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(uuid "977d311c-737b-4d33-a7c0-027068800d27") |
|||
) |
|||
(wire |
|||
(pts |
|||
(xy 100.33 87.63) (xy 100.33 83.82) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(uuid "a74e3420-cc0c-4d70-a108-b408a357a728") |
|||
) |
|||
(wire |
|||
(pts |
|||
(xy 100.33 83.82) (xy 109.22 83.82) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(uuid "b640eecc-a8af-45c1-97a3-9a874bceb1f7") |
|||
) |
|||
(wire |
|||
(pts |
|||
(xy 152.4 83.82) (xy 152.4 76.2) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(uuid "b86edfbb-be66-4823-bf66-7580fa3069d2") |
|||
) |
|||
(wire |
|||
(pts |
|||
(xy 142.24 83.82) (xy 152.4 83.82) |
|||
) |
|||
(stroke |
|||
(width 0) |
|||
(type default) |
|||
) |
|||
(uuid "fdc72005-7acb-48aa-85c0-6d26e865dc00") |
|||
) |
|||
(symbol |
|||
(lib_id "Device:R") |
|||
(at 115.57 83.82 90) |
|||
(unit 1) |
|||
(body_style 1) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(dnp no) |
|||
(fields_autoplaced yes) |
|||
(uuid "2166cf4e-5b03-495b-af0a-be2718b61b04") |
|||
(property "Reference" "R1" |
|||
(at 116.713 77.47 90) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "R" |
|||
(at 116.713 80.01 90) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "Connector:Tag-Connect_TC2050-IDC-FP_2x05_P1.27mm_Vertical" |
|||
(at 115.57 85.598 90) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 115.57 83.82 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Resistor" |
|||
(at 115.57 83.82 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(pin "[6,7,9-11]" |
|||
(uuid "f62cbf3f-56f0-413b-a710-93a48027efbb") |
|||
) |
|||
(pin "[1-5]" |
|||
(uuid "e856ce98-c7c6-4066-97a8-60e95541ae81") |
|||
) |
|||
(instances |
|||
(project "" |
|||
(path "/f3609c98-5e8f-46ae-a21a-09d0b96782a9" |
|||
(reference "R1") |
|||
(unit 1) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(symbol |
|||
(lib_id "power:VCC") |
|||
(at 152.4 76.2 0) |
|||
(unit 1) |
|||
(body_style 1) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(dnp no) |
|||
(fields_autoplaced yes) |
|||
(uuid "2b72c13a-dfcb-4980-a25b-daac640cdb9b") |
|||
(property "Reference" "#PWR01" |
|||
(at 152.4 80.01 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "VCC" |
|||
(at 152.4 71.12 0) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "" |
|||
(at 152.4 76.2 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 152.4 76.2 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Power symbol creates a global label with name \"VCC\"" |
|||
(at 152.4 76.2 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(pin "1" |
|||
(uuid "ff7185fb-347f-48f5-bcf0-de5808edd725") |
|||
) |
|||
(instances |
|||
(project "" |
|||
(path "/f3609c98-5e8f-46ae-a21a-09d0b96782a9" |
|||
(reference "#PWR01") |
|||
(unit 1) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(symbol |
|||
(lib_id "power:GND") |
|||
(at 100.33 87.63 0) |
|||
(unit 1) |
|||
(body_style 1) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(dnp no) |
|||
(fields_autoplaced yes) |
|||
(uuid "4e64c428-4aed-4183-826f-2fa5009f4177") |
|||
(property "Reference" "#PWR02" |
|||
(at 100.33 93.98 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "GND" |
|||
(at 100.33 92.71 0) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "" |
|||
(at 100.33 87.63 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 100.33 87.63 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Power symbol creates a global label with name \"GND\" , ground" |
|||
(at 100.33 87.63 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(pin "1" |
|||
(uuid "b8c00030-efa1-4421-9af4-303142b1c17d") |
|||
) |
|||
(instances |
|||
(project "" |
|||
(path "/f3609c98-5e8f-46ae-a21a-09d0b96782a9" |
|||
(reference "#PWR02") |
|||
(unit 1) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(symbol |
|||
(lib_id "Device:C") |
|||
(at 138.43 83.82 90) |
|||
(unit 1) |
|||
(body_style 1) |
|||
(exclude_from_sim no) |
|||
(in_bom yes) |
|||
(on_board yes) |
|||
(dnp no) |
|||
(fields_autoplaced yes) |
|||
(uuid "e65020b4-b867-4d63-a46c-163f7210b6bb") |
|||
(property "Reference" "X1" |
|||
(at 138.43 76.2 90) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Value" "C" |
|||
(at 138.43 78.74 90) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Footprint" "Capacitor_SMD:CP_Elec_3x5.4" |
|||
(at 142.24 82.8548 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Datasheet" "" |
|||
(at 138.43 83.82 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(property "Description" "Unpolarized capacitor" |
|||
(at 138.43 83.82 0) |
|||
(hide yes) |
|||
(effects |
|||
(font |
|||
(size 1.27 1.27) |
|||
) |
|||
) |
|||
) |
|||
(pin "2" |
|||
(uuid "30431dfb-acd4-46c0-af86-7738834b40fd") |
|||
) |
|||
(pin "1" |
|||
(uuid "047a8bd9-b214-498e-b161-2c9a3c6501f3") |
|||
) |
|||
(instances |
|||
(project "" |
|||
(path "/f3609c98-5e8f-46ae-a21a-09d0b96782a9" |
|||
(reference "X1") |
|||
(unit 1) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
(sheet_instances |
|||
(path "/" |
|||
(page "1") |
|||
) |
|||
) |
|||
(embedded_fonts no) |
|||
) |
@ -0,0 +1,150 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright The KiCad Developers |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 3 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one at |
|||
* http://www.gnu.org/licenses/
|
|||
*/ |
|||
|
|||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
|||
#include <schematic_utils/schematic_file_util.h>
|
|||
|
|||
#include <set>
|
|||
#include <vector>
|
|||
|
|||
#include <schematic.h>
|
|||
#include <settings/settings_manager.h>
|
|||
#include <netlist_exporter_xml.h>
|
|||
|
|||
#include <wx/filename.h>
|
|||
#include <wx/xml/xml.h>
|
|||
|
|||
struct XML_STACKED_PIN_FIXTURE |
|||
{ |
|||
XML_STACKED_PIN_FIXTURE() : m_settingsManager( true /* headless */ ) {} |
|||
|
|||
SETTINGS_MANAGER m_settingsManager; |
|||
std::unique_ptr<SCHEMATIC> m_schematic; |
|||
}; |
|||
|
|||
static std::set<wxString> as_set( const std::initializer_list<const char*>& init ) |
|||
{ |
|||
std::set<wxString> out; |
|||
for( const char* s : init ) |
|||
out.emplace( wxString::FromUTF8( s ) ); |
|||
return out; |
|||
} |
|||
|
|||
static wxXmlNode* find_child( wxXmlNode* parent, const wxString& name ) |
|||
{ |
|||
for( wxXmlNode* child = parent->GetChildren(); child; child = child->GetNext() ) |
|||
{ |
|||
if( child->GetName() == name ) |
|||
return child; |
|||
} |
|||
|
|||
return nullptr; |
|||
} |
|||
|
|||
static std::vector<wxXmlNode*> find_children( wxXmlNode* parent, const wxString& name ) |
|||
{ |
|||
std::vector<wxXmlNode*> out; |
|||
|
|||
for( wxXmlNode* child = parent->GetChildren(); child; child = child->GetNext() ) |
|||
{ |
|||
if( child->GetName() == name ) |
|||
out.push_back( child ); |
|||
} |
|||
|
|||
return out; |
|||
} |
|||
|
|||
BOOST_FIXTURE_TEST_CASE( NetlistExporterXML_StackedPinNomenclature, XML_STACKED_PIN_FIXTURE ) |
|||
{ |
|||
// Load schematic with stacked pin numbers
|
|||
KI_TEST::LoadSchematic( m_settingsManager, wxT( "stacked_pin_nomenclature" ), m_schematic ); |
|||
|
|||
// Write XML netlist to a test file next to the project
|
|||
wxFileName netFile = m_schematic->Project().GetProjectFullName(); |
|||
netFile.SetName( netFile.GetName() + wxT( "_xml_test" ) ); |
|||
netFile.SetExt( wxT( "xml" ) ); |
|||
|
|||
if( wxFileExists( netFile.GetFullPath() ) ) |
|||
wxRemoveFile( netFile.GetFullPath() ); |
|||
|
|||
WX_STRING_REPORTER reporter; |
|||
std::unique_ptr<NETLIST_EXPORTER_XML> exporter = |
|||
std::make_unique<NETLIST_EXPORTER_XML>( m_schematic.get() ); |
|||
|
|||
bool success = exporter->WriteNetlist( netFile.GetFullPath(), 0, reporter ); |
|||
BOOST_REQUIRE( success && reporter.GetMessages().IsEmpty() ); |
|||
|
|||
// Parse the XML back
|
|||
wxXmlDocument xdoc; |
|||
BOOST_REQUIRE( xdoc.Load( netFile.GetFullPath() ) ); |
|||
|
|||
wxXmlNode* root = xdoc.GetRoot(); |
|||
BOOST_REQUIRE( root ); |
|||
|
|||
wxXmlNode* nets = find_child( root, wxT( "nets" ) ); |
|||
BOOST_REQUIRE( nets ); |
|||
|
|||
// Collect pin sets for R1 on each power net
|
|||
std::set<wxString> setA; |
|||
std::set<wxString> setB; |
|||
int foundSets = 0; |
|||
|
|||
for( wxXmlNode* net : find_children( nets, wxT( "net" ) ) ) |
|||
{ |
|||
wxString netName = net->GetAttribute( wxT( "name" ), wxEmptyString ); |
|||
if( netName != wxT( "VCC" ) && netName != wxT( "GND" ) ) |
|||
continue; |
|||
|
|||
std::set<wxString>* target = ( foundSets == 0 ? &setA : &setB ); |
|||
|
|||
for( wxXmlNode* node : find_children( net, wxT( "node" ) ) ) |
|||
{ |
|||
if( node->GetAttribute( wxT( "ref" ), wxEmptyString ) != wxT( "R1" ) ) |
|||
continue; |
|||
|
|||
wxString pin = node->GetAttribute( wxT( "pin" ), wxEmptyString ); |
|||
wxString pinfunction = node->GetAttribute( wxT( "pinfunction" ), wxEmptyString ); |
|||
wxString pintype = node->GetAttribute( wxT( "pintype" ), wxEmptyString ); |
|||
|
|||
// Expect pinfunction to equal the expanded number when base name is empty
|
|||
BOOST_CHECK_EQUAL( pinfunction, pin ); |
|||
// Expect plain passive type (no +no_connect on these nets)
|
|||
BOOST_CHECK_EQUAL( pintype, wxT( "passive" ) ); |
|||
|
|||
target->insert( pin ); |
|||
} |
|||
|
|||
foundSets++; |
|||
} |
|||
|
|||
// We should have found two power nets with R1 nodes
|
|||
BOOST_REQUIRE_EQUAL( foundSets, 2 ); |
|||
|
|||
// Expect one side to be 1..5 and the other to be 6,7,9,10,11 (order independent)
|
|||
const std::set<wxString> expectedTop = as_set( { "1", "2", "3", "4", "5" } ); |
|||
const std::set<wxString> expectedBot = as_set( { "6", "7", "9", "10", "11" } ); |
|||
|
|||
bool matchA = ( setA == expectedTop && setB == expectedBot ); |
|||
bool matchB = ( setA == expectedBot && setB == expectedTop ); |
|||
BOOST_CHECK( matchA || matchB ); |
|||
|
|||
// Cleanup test artifact
|
|||
wxRemoveFile( netFile.GetFullPath() ); |
|||
} |
@ -0,0 +1,543 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright The KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
/**
|
|||
* @file test_pin_stacked_layout.cpp |
|||
* Test pin number layout for stacked multi-line numbers across all rotations |
|||
*/ |
|||
|
|||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
|||
|
|||
#include <sch_pin.h>
|
|||
#include <lib_symbol.h>
|
|||
#include <pin_layout_cache.h>
|
|||
#include <transform.h>
|
|||
#include <sch_io/sch_io_mgr.h>
|
|||
|
|||
#include <wx/log.h>
|
|||
#include <boost/test/unit_test.hpp>
|
|||
|
|||
BOOST_AUTO_TEST_SUITE( PinStackedLayout ) |
|||
|
|||
/**
|
|||
* Create a test symbol with stacked pin numbers for rotation testing |
|||
*/ |
|||
static std::unique_ptr<LIB_SYMBOL> createTestResistorSymbol() |
|||
{ |
|||
auto symbol = std::make_unique<LIB_SYMBOL>( wxT( "TestResistor" ) ); |
|||
|
|||
// Create first pin with stacked numbers [1-5]
|
|||
auto pin1 = std::make_unique<SCH_PIN>( symbol.get() ); |
|||
pin1->SetPosition( VECTOR2I( 0, schIUScale.MilsToIU( 250 ) ) ); // top pin
|
|||
pin1->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); |
|||
pin1->SetLength( schIUScale.MilsToIU( 50 ) ); |
|||
pin1->SetNumber( wxT( "[1-5]" ) ); |
|||
pin1->SetName( wxT( "A" ) ); // Short name
|
|||
pin1->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE ); |
|||
pin1->SetUnit( 1 ); |
|||
|
|||
// Create second pin with stacked numbers [6,7,9-11]
|
|||
auto pin2 = std::make_unique<SCH_PIN>( symbol.get() ); |
|||
pin2->SetPosition( VECTOR2I( 0, schIUScale.MilsToIU( -340 ) ) ); // bottom pin
|
|||
pin2->SetOrientation( PIN_ORIENTATION::PIN_UP ); |
|||
pin2->SetLength( schIUScale.MilsToIU( 50 ) ); |
|||
pin2->SetNumber( wxT( "[6,7,9-11]" ) ); |
|||
pin2->SetName( wxT( "B" ) ); // Short name
|
|||
pin2->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE ); |
|||
pin2->SetUnit( 1 ); |
|||
|
|||
// Add pins to symbol
|
|||
symbol->AddDrawItem( pin1.release() ); |
|||
symbol->AddDrawItem( pin2.release() ); |
|||
|
|||
return symbol; |
|||
} |
|||
|
|||
/**
|
|||
* Get pin geometry (line segment from connection point to pin end) |
|||
*/ |
|||
static VECTOR2I getPinLineEnd( const SCH_PIN* pin, const TRANSFORM& transform ) |
|||
{ |
|||
VECTOR2I start = pin->GetPosition(); |
|||
VECTOR2I end = start; |
|||
|
|||
int length = pin->GetLength(); |
|||
|
|||
switch( pin->PinDrawOrient( transform ) ) |
|||
{ |
|||
case PIN_ORIENTATION::PIN_UP: |
|||
end.y += length; |
|||
break; |
|||
case PIN_ORIENTATION::PIN_DOWN: |
|||
end.y -= length; |
|||
break; |
|||
case PIN_ORIENTATION::PIN_LEFT: |
|||
end.x -= length; |
|||
break; |
|||
case PIN_ORIENTATION::PIN_RIGHT: |
|||
end.x += length; |
|||
break; |
|||
case PIN_ORIENTATION::INHERIT: |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
return end; |
|||
} |
|||
|
|||
/**
|
|||
* Check if a box intersects with a line segment |
|||
*/ |
|||
static bool boxIntersectsLine( const BOX2I& box, const VECTOR2I& lineStart, const VECTOR2I& lineEnd ) |
|||
{ |
|||
// Simple bbox vs line segment intersection
|
|||
// First check if line bbox intersects text bbox
|
|||
BOX2I lineBbox; |
|||
lineBbox.SetOrigin( std::min( lineStart.x, lineEnd.x ), std::min( lineStart.y, lineEnd.y ) ); |
|||
lineBbox.SetEnd( std::max( lineStart.x, lineEnd.x ), std::max( lineStart.y, lineEnd.y ) ); |
|||
|
|||
if( !lineBbox.Intersects( box ) ) |
|||
return false; |
|||
|
|||
// For vertical/horizontal lines, do precise check
|
|||
if( lineStart.x == lineEnd.x ) // vertical line
|
|||
{ |
|||
int lineX = lineStart.x; |
|||
return ( lineX >= box.GetLeft() && lineX <= box.GetRight() && |
|||
box.GetTop() <= std::max( lineStart.y, lineEnd.y ) && |
|||
box.GetBottom() >= std::min( lineStart.y, lineEnd.y ) ); |
|||
} |
|||
else if( lineStart.y == lineEnd.y ) // horizontal line
|
|||
{ |
|||
int lineY = lineStart.y; |
|||
return ( lineY >= box.GetBottom() && lineY <= box.GetTop() && |
|||
box.GetLeft() <= std::max( lineStart.x, lineEnd.x ) && |
|||
box.GetRight() >= std::min( lineStart.x, lineEnd.x ) ); |
|||
} |
|||
|
|||
// For diagonal lines, use the bbox intersection as approximation
|
|||
return true; |
|||
} |
|||
|
|||
/**
|
|||
* Test that pin numbers don't overlap with pin geometry across all rotations |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( PinNumbersNoOverlapAllRotations ) |
|||
{ |
|||
// Create test symbol
|
|||
auto symbol = createTestResistorSymbol(); |
|||
BOOST_REQUIRE( symbol ); |
|||
|
|||
// Get the pins
|
|||
std::vector<SCH_PIN*> pins; |
|||
for( auto& item : symbol->GetDrawItems() ) |
|||
{ |
|||
if( item.Type() == SCH_PIN_T ) |
|||
pins.push_back( static_cast<SCH_PIN*>( &item ) ); |
|||
} |
|||
|
|||
BOOST_REQUIRE_EQUAL( pins.size(), 2 ); |
|||
|
|||
// Test rotations: 0°, 90°, 180°, 270°
|
|||
std::vector<TRANSFORM> rotations = { |
|||
TRANSFORM( 1, 0, 0, 1 ), // 0° (identity)
|
|||
TRANSFORM( 0, -1, 1, 0 ), // 90° CCW
|
|||
TRANSFORM( -1, 0, 0, -1 ), // 180°
|
|||
TRANSFORM( 0, 1, -1, 0 ) // 270° CCW (90° CW)
|
|||
}; |
|||
|
|||
std::vector<wxString> rotationNames = { wxT("0°"), wxT("90°"), wxT("180°"), wxT("270°") }; |
|||
|
|||
for( size_t r = 0; r < rotations.size(); r++ ) |
|||
{ |
|||
const TRANSFORM& transform = rotations[r]; |
|||
const wxString& rotName = rotationNames[r]; |
|||
|
|||
// Set global transform for this test
|
|||
TRANSFORM oldTransform = DefaultTransform; |
|||
DefaultTransform = transform; |
|||
|
|||
for( size_t p = 0; p < pins.size(); p++ ) |
|||
{ |
|||
SCH_PIN* pin = pins[p]; |
|||
|
|||
// Create layout cache for this pin
|
|||
PIN_LAYOUT_CACHE cache( *pin ); |
|||
|
|||
// Get pin number text info (shadow width 0 for testing)
|
|||
auto numberInfoOpt = cache.GetPinNumberInfo( 0 ); |
|||
|
|||
if( !numberInfoOpt.has_value() ) |
|||
continue; |
|||
|
|||
PIN_LAYOUT_CACHE::TEXT_INFO numberInfo = numberInfoOpt.value(); |
|||
|
|||
if( numberInfo.m_Text.IsEmpty() ) |
|||
continue; |
|||
|
|||
// Get pin line geometry
|
|||
VECTOR2I pinStart = pin->GetPosition(); |
|||
VECTOR2I pinEnd = getPinLineEnd( pin, transform ); |
|||
|
|||
// Get text bounding box - we need to estimate this since we don't have full font rendering
|
|||
// For now, use a simple estimation based on text size and string length
|
|||
int textHeight = numberInfo.m_TextSize; |
|||
int textWidth = numberInfo.m_Text.Length() * numberInfo.m_TextSize * 0.6; // rough char width
|
|||
|
|||
// Handle multi-line text
|
|||
if( numberInfo.m_Text.Contains( '\n' ) ) |
|||
{ |
|||
wxArrayString lines; |
|||
wxStringSplit( numberInfo.m_Text, lines, '\n' ); |
|||
|
|||
if( numberInfo.m_Angle == ANGLE_VERTICAL ) |
|||
{ |
|||
// For vertical text, lines are spaced horizontally
|
|||
int lineSpacing = textHeight * 1.3; |
|||
textWidth = lines.size() * lineSpacing; |
|||
// Find longest line for height
|
|||
size_t maxLen = 0; |
|||
for( const wxString& line : lines ) |
|||
maxLen = std::max( maxLen, line.Length() ); |
|||
textHeight = maxLen * textHeight * 0.6; |
|||
} |
|||
else |
|||
{ |
|||
// For horizontal text, lines are spaced vertically
|
|||
int lineSpacing = textHeight * 1.3; |
|||
textHeight = lines.size() * lineSpacing; |
|||
// Find longest line for width
|
|||
size_t maxLen = 0; |
|||
for( const wxString& line : lines ) |
|||
maxLen = std::max( maxLen, line.Length() ); |
|||
textWidth = maxLen * textHeight * 0.6; |
|||
} |
|||
} |
|||
|
|||
// Create text bounding box around text position
|
|||
BOX2I textBbox; |
|||
textBbox.SetOrigin( numberInfo.m_TextPosition.x - textWidth/2, |
|||
numberInfo.m_TextPosition.y - textHeight/2 ); |
|||
textBbox.SetSize( textWidth, textHeight ); |
|||
|
|||
// Check for intersection
|
|||
bool overlaps = boxIntersectsLine( textBbox, pinStart, pinEnd ); |
|||
|
|||
// Log detailed info for debugging
|
|||
wxLogMessage( wxT("Rotation %s, Pin %s: pos=(%d,%d) textPos=(%d,%d) pinLine=(%d,%d)-(%d,%d) textBox=(%d,%d,%dx%d) overlap=%s"), |
|||
rotName, pin->GetNumber(), |
|||
pinStart.x, pinStart.y, |
|||
numberInfo.m_TextPosition.x, numberInfo.m_TextPosition.y, |
|||
pinStart.x, pinStart.y, pinEnd.x, pinEnd.y, |
|||
(int)textBbox.GetLeft(), (int)textBbox.GetTop(), (int)textBbox.GetWidth(), (int)textBbox.GetHeight(), |
|||
overlaps ? wxT("YES") : wxT("NO") ); |
|||
|
|||
// Test assertion
|
|||
BOOST_CHECK_MESSAGE( !overlaps, |
|||
"Pin number '" << pin->GetNumber() << "' overlaps with pin geometry at rotation " << rotName ); |
|||
} |
|||
|
|||
// Restore original transform
|
|||
DefaultTransform = oldTransform; |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Test that multiline and non-multiline pin numbers/names are positioned consistently |
|||
* on the same side of the pin for each rotation |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( PinTextConsistentSidePlacement ) |
|||
{ |
|||
// Create test symbol with both types of pins
|
|||
auto symbol = createTestResistorSymbol(); |
|||
BOOST_REQUIRE( symbol ); |
|||
|
|||
// Get the pins - one will be multiline formatted, one will not
|
|||
std::vector<SCH_PIN*> pins; |
|||
for( auto& item : symbol->GetDrawItems() ) |
|||
{ |
|||
if( item.Type() == SCH_PIN_T ) |
|||
pins.push_back( static_cast<SCH_PIN*>( &item ) ); |
|||
} |
|||
|
|||
BOOST_REQUIRE_EQUAL( pins.size(), 2 ); |
|||
|
|||
// Test rotations
|
|||
std::vector<TRANSFORM> rotations = { |
|||
TRANSFORM( 1, 0, 0, 1 ), // 0° (identity)
|
|||
TRANSFORM( 0, -1, 1, 0 ), // 90° CCW
|
|||
TRANSFORM( -1, 0, 0, -1 ), // 180°
|
|||
TRANSFORM( 0, 1, -1, 0 ) // 270° CCW (90° CW)
|
|||
}; |
|||
|
|||
std::vector<wxString> rotationNames = { wxT("0°"), wxT("90°"), wxT("180°"), wxT("270°") }; |
|||
|
|||
for( size_t r = 0; r < rotations.size(); r++ ) |
|||
{ |
|||
const TRANSFORM& transform = rotations[r]; |
|||
const wxString& rotName = rotationNames[r]; |
|||
|
|||
// Set global transform for this test
|
|||
TRANSFORM oldTransform = DefaultTransform; |
|||
DefaultTransform = transform; |
|||
|
|||
// For each rotation, collect pin number and name positions relative to pin center
|
|||
struct PinTextInfo { |
|||
VECTOR2I pinPos; |
|||
VECTOR2I numberPos; |
|||
VECTOR2I namePos; |
|||
wxString pinNumber; |
|||
bool isMultiline; |
|||
}; |
|||
|
|||
std::vector<PinTextInfo> pinInfos; |
|||
|
|||
for( auto* pin : pins ) |
|||
{ |
|||
PinTextInfo info; |
|||
info.pinPos = pin->GetPosition(); |
|||
info.pinNumber = pin->GetNumber(); |
|||
|
|||
// Create layout cache for this pin
|
|||
PIN_LAYOUT_CACHE cache( *pin ); |
|||
|
|||
// Get number position (shadow width 0 for testing)
|
|||
auto numberInfoOpt = cache.GetPinNumberInfo( 0 ); |
|||
if( numberInfoOpt.has_value() ) |
|||
{ |
|||
auto numberInfo = numberInfoOpt.value(); |
|||
info.numberPos = numberInfo.m_TextPosition; |
|||
info.isMultiline = numberInfo.m_Text.Contains( '\n' ); |
|||
} |
|||
|
|||
// Get name position
|
|||
auto nameInfoOpt = cache.GetPinNameInfo( 0 ); |
|||
if( nameInfoOpt.has_value() ) |
|||
{ |
|||
auto nameInfo = nameInfoOpt.value(); |
|||
info.namePos = nameInfo.m_TextPosition; |
|||
} |
|||
|
|||
pinInfos.push_back( info ); |
|||
|
|||
wxLogDebug( "Rotation %s, Pin %s: pos=(%d,%d) numberPos=(%d,%d) namePos=(%d,%d) multiline=%s", |
|||
rotName, info.pinNumber, |
|||
info.pinPos.x, info.pinPos.y, |
|||
info.numberPos.x, info.numberPos.y, |
|||
info.namePos.x, info.namePos.y, |
|||
info.isMultiline ? wxT("YES") : wxT("NO") ); |
|||
} |
|||
|
|||
BOOST_REQUIRE_EQUAL( pinInfos.size(), 2 ); |
|||
|
|||
// New semantics:
|
|||
// * Vertical pins (UP/DOWN): numbers and names must be LEFT (x < pin.x)
|
|||
// * Horizontal pins (LEFT/RIGHT): numbers/names must be ABOVE (y < pin.y)
|
|||
PIN_ORIENTATION orient = pins[0]->PinDrawOrient( DefaultTransform ); |
|||
|
|||
if( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN ) |
|||
{ |
|||
for( const auto& inf : pinInfos ) |
|||
{ |
|||
BOOST_CHECK_MESSAGE( inf.numberPos.x < inf.pinPos.x, |
|||
"At rotation " << rotName << ", number for pin " << inf.pinNumber << " not left of vertical pin." ); |
|||
BOOST_CHECK_MESSAGE( inf.namePos.x < inf.pinPos.x, |
|||
"At rotation " << rotName << ", name for pin " << inf.pinNumber << " not left of vertical pin." ); |
|||
} |
|||
} |
|||
else if( orient == PIN_ORIENTATION::PIN_LEFT || orient == PIN_ORIENTATION::PIN_RIGHT ) |
|||
{ |
|||
for( const auto& inf : pinInfos ) |
|||
{ |
|||
BOOST_CHECK_MESSAGE( inf.numberPos.y < inf.pinPos.y, |
|||
"At rotation " << rotName << ", number for pin " << inf.pinNumber << " not above horizontal pin." ); |
|||
BOOST_CHECK_MESSAGE( inf.namePos.y < inf.pinPos.y, |
|||
"At rotation " << rotName << ", name for pin " << inf.pinNumber << " not above horizontal pin." ); |
|||
} |
|||
} |
|||
|
|||
// Restore original transform
|
|||
DefaultTransform = oldTransform; |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Test that multiline and non-multiline pin numbers/names have the same bottom coordinate |
|||
* (distance from pin along the axis connecting pin and text) |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( PinTextSameBottomCoordinate ) |
|||
{ |
|||
// Create test symbol with both types of pins
|
|||
auto symbol = createTestResistorSymbol(); |
|||
BOOST_REQUIRE( symbol ); |
|||
|
|||
// Get the pins - one will be multiline formatted, one will not
|
|||
std::vector<SCH_PIN*> pins; |
|||
for( auto& item : symbol->GetDrawItems() ) |
|||
{ |
|||
if( item.Type() == SCH_PIN_T ) |
|||
pins.push_back( static_cast<SCH_PIN*>( &item ) ); |
|||
} |
|||
|
|||
BOOST_REQUIRE_EQUAL( pins.size(), 2 ); |
|||
|
|||
// Test rotations
|
|||
std::vector<TRANSFORM> rotations = { |
|||
TRANSFORM( 1, 0, 0, 1 ), // 0° (identity)
|
|||
TRANSFORM( 0, -1, 1, 0 ), // 90° CCW
|
|||
TRANSFORM( -1, 0, 0, -1 ), // 180°
|
|||
TRANSFORM( 0, 1, -1, 0 ) // 270° CCW (90° CW)
|
|||
}; |
|||
|
|||
std::vector<wxString> rotationNames = { wxT("0°"), wxT("90°"), wxT("180°"), wxT("270°") }; |
|||
|
|||
for( size_t r = 0; r < rotations.size(); r++ ) |
|||
{ |
|||
const TRANSFORM& transform = rotations[r]; |
|||
const wxString& rotName = rotationNames[r]; |
|||
|
|||
// Set global transform for this test
|
|||
TRANSFORM oldTransform = DefaultTransform; |
|||
DefaultTransform = transform; |
|||
|
|||
// For each rotation, collect pin and text position data
|
|||
struct PinTextData { |
|||
VECTOR2I pinPos; |
|||
VECTOR2I numberPos; |
|||
VECTOR2I namePos; |
|||
wxString pinNumber; |
|||
bool isMultiline; |
|||
int numberBottomDistance; |
|||
int nameBottomDistance; |
|||
}; |
|||
|
|||
std::vector<PinTextData> pinData; |
|||
|
|||
for( auto* pin : pins ) |
|||
{ |
|||
PinTextData data; |
|||
data.pinPos = pin->GetPosition(); |
|||
data.pinNumber = pin->GetNumber(); |
|||
|
|||
// Create layout cache for this pin
|
|||
PIN_LAYOUT_CACHE cache( *pin ); |
|||
|
|||
// Get number position (shadow width 0 for testing)
|
|||
auto numberInfoOpt = cache.GetPinNumberInfo( 0 ); |
|||
PIN_LAYOUT_CACHE::TEXT_INFO numberInfo; // store for later heuristics
|
|||
if( numberInfoOpt.has_value() ) |
|||
{ |
|||
numberInfo = numberInfoOpt.value(); |
|||
data.numberPos = numberInfo.m_TextPosition; |
|||
data.isMultiline = numberInfo.m_Text.Contains( '\n' ); |
|||
} |
|||
else |
|||
{ |
|||
BOOST_FAIL( "Expected pin number text info" ); |
|||
} |
|||
|
|||
// Get name position
|
|||
auto nameInfoOpt = cache.GetPinNameInfo( 0 ); |
|||
PIN_LAYOUT_CACHE::TEXT_INFO nameInfo; // store for width/height heuristic
|
|||
if( nameInfoOpt.has_value() ) |
|||
{ |
|||
nameInfo = nameInfoOpt.value(); |
|||
data.namePos = nameInfo.m_TextPosition; |
|||
} |
|||
else |
|||
{ |
|||
BOOST_FAIL( "Expected pin name text info" ); |
|||
} |
|||
|
|||
// Calculate bottom distance (closest distance to pin along pin-text axis)
|
|||
PIN_ORIENTATION orient = pin->PinDrawOrient( DefaultTransform ); |
|||
|
|||
if( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN ) |
|||
{ |
|||
// Vertical pins: measure clearance from pin (at pin.x) to RIGHT edge of text box.
|
|||
// We approximate half width from text length heuristic.
|
|||
int textWidth = data.isMultiline ? 0 : (int)( data.pinNumber.Length() * numberInfo.m_TextSize * 0.6 ); |
|||
// (Multiline case: numberInfo.m_Text already contains \n; heuristic in earlier section)
|
|||
if( data.isMultiline ) |
|||
{ |
|||
wxArrayString lines; wxStringSplit( numberInfo.m_Text, lines, '\n' ); |
|||
int lineSpacing = numberInfo.m_TextSize * 1.3; |
|||
textWidth = lines.size() * lineSpacing; // when vertical orientation text is rotated
|
|||
} |
|||
int rightEdge = data.numberPos.x + textWidth / 2; |
|||
data.numberBottomDistance = data.pinPos.x - rightEdge; // positive gap
|
|||
int nameWidth = (int)( nameInfo.m_Text.Length() * nameInfo.m_TextSize * 0.6 ); |
|||
int nameRightEdge = data.namePos.x + nameWidth / 2; |
|||
data.nameBottomDistance = data.pinPos.x - nameRightEdge; // expect similar across pins
|
|||
} |
|||
else |
|||
{ |
|||
// Horizontal pins: we align centers at a fixed offset above the pin. Measure center gap.
|
|||
data.numberBottomDistance = data.pinPos.y - data.numberPos.y; // center gap constant
|
|||
data.nameBottomDistance = data.pinPos.y - data.namePos.y; // center gap for names
|
|||
} |
|||
|
|||
pinData.push_back( data ); |
|||
|
|||
wxLogDebug( "Rotation %s, Pin %s: pos=(%d,%d) numberPos=(%d,%d) namePos=(%d,%d) multiline=%s numberBottomDist=%d nameBottomDist=%d", |
|||
rotName, data.pinNumber, |
|||
data.pinPos.x, data.pinPos.y, |
|||
data.numberPos.x, data.numberPos.y, |
|||
data.namePos.x, data.namePos.y, |
|||
data.isMultiline ? wxT("YES") : wxT("NO"), |
|||
data.numberBottomDistance, data.nameBottomDistance ); |
|||
} |
|||
|
|||
BOOST_REQUIRE_EQUAL( pinData.size(), 2 ); |
|||
|
|||
// Check that both pins have their numbers at the same bottom distance from pin
|
|||
// Allow small tolerance for rounding differences
|
|||
const int tolerance = 100; // 100 internal units tolerance
|
|||
|
|||
int bottomDist1 = pinData[0].numberBottomDistance; |
|||
int bottomDist2 = pinData[1].numberBottomDistance; |
|||
int distanceDiff = abs( bottomDist1 - bottomDist2 ); |
|||
|
|||
BOOST_CHECK_MESSAGE( distanceDiff <= tolerance, |
|||
"At rotation " << rotName << ", pin numbers have different bottom distances from pin. " |
|||
<< "Pin " << pinData[0].pinNumber << " distance=" << bottomDist1 |
|||
<< ", Pin " << pinData[1].pinNumber << " distance=" << bottomDist2 |
|||
<< ", difference=" << distanceDiff << " (tolerance=" << tolerance << ")" ); |
|||
|
|||
// Check that both pins have their names at the same bottom distance from pin
|
|||
int nameBottomDist1 = pinData[0].nameBottomDistance; |
|||
int nameBottomDist2 = pinData[1].nameBottomDistance; |
|||
int nameDistanceDiff = abs( nameBottomDist1 - nameBottomDist2 ); |
|||
|
|||
BOOST_CHECK_MESSAGE( nameDistanceDiff <= tolerance, |
|||
"At rotation " << rotName << ", pin names have different bottom distances from pin. " |
|||
<< "Pin " << pinData[0].pinNumber << " name distance=" << nameBottomDist1 |
|||
<< ", Pin " << pinData[1].pinNumber << " name distance=" << nameBottomDist2 |
|||
<< ", difference=" << nameDistanceDiff << " (tolerance=" << tolerance << ")" ); |
|||
|
|||
// Restore original transform
|
|||
DefaultTransform = oldTransform; |
|||
} |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
@ -0,0 +1,742 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2024 KiCad Developers |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
|||
#include <sch_pin.h>
|
|||
#include <lib_symbol.h>
|
|||
#include <eeschema_test_utils.h>
|
|||
|
|||
struct STACKED_PIN_CONVERSION_FIXTURE |
|||
{ |
|||
STACKED_PIN_CONVERSION_FIXTURE() |
|||
{ |
|||
m_settingsManager = std::make_unique<SETTINGS_MANAGER>( true ); |
|||
m_symbol = std::make_unique<LIB_SYMBOL>( "TestSymbol" ); |
|||
} |
|||
|
|||
std::unique_ptr<SETTINGS_MANAGER> m_settingsManager; |
|||
std::unique_ptr<LIB_SYMBOL> m_symbol; |
|||
}; |
|||
|
|||
BOOST_FIXTURE_TEST_SUITE( StackedPinConversion, STACKED_PIN_CONVERSION_FIXTURE ) |
|||
|
|||
|
|||
/**
|
|||
* Test basic stacked pin number expansion functionality |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedPinExpansion ) |
|||
{ |
|||
// Test simple list notation
|
|||
SCH_PIN* pin = new SCH_PIN( m_symbol.get() ); |
|||
pin->SetNumber( wxT("[1,2,3]") ); |
|||
|
|||
bool isValid; |
|||
std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
|
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 3 ); |
|||
BOOST_CHECK_EQUAL( expanded[0], "1" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "2" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "3" ); |
|||
|
|||
delete pin; |
|||
|
|||
// Test range notation
|
|||
pin = new SCH_PIN( m_symbol.get() ); |
|||
pin->SetNumber( wxT("[5-7]") ); |
|||
|
|||
expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
|
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 3 ); |
|||
BOOST_CHECK_EQUAL( expanded[0], "5" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "6" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "7" ); |
|||
|
|||
delete pin; |
|||
|
|||
// Test mixed notation
|
|||
pin = new SCH_PIN( m_symbol.get() ); |
|||
pin->SetNumber( wxT("[1,3,5-7]") ); |
|||
|
|||
expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
|
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 5 ); |
|||
BOOST_CHECK_EQUAL( expanded[0], "1" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "3" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "5" ); |
|||
BOOST_CHECK_EQUAL( expanded[3], "6" ); |
|||
BOOST_CHECK_EQUAL( expanded[4], "7" ); |
|||
|
|||
delete pin; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test stacked pin validity checking |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedPinValidity ) |
|||
{ |
|||
SCH_PIN* pin = new SCH_PIN( m_symbol.get() ); |
|||
|
|||
// Test valid single pin (should not be considered stacked)
|
|||
pin->SetNumber( wxT("1") ); |
|||
bool isValid; |
|||
std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( isValid ); |
|||
BOOST_CHECK_EQUAL( expanded.size(), 1 ); |
|||
BOOST_CHECK_EQUAL( expanded[0], "1" ); |
|||
|
|||
// Test invalid notation (malformed brackets)
|
|||
pin->SetNumber( wxT("[1,2") ); |
|||
expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( !isValid ); |
|||
|
|||
// Test invalid range
|
|||
pin->SetNumber( wxT("[5-3]") ); // backwards range
|
|||
expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( !isValid ); |
|||
|
|||
// Test empty brackets
|
|||
pin->SetNumber( wxT("[]") ); |
|||
expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( !isValid ); |
|||
|
|||
delete pin; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test pin creation and positioning for conversion tests |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestPinCreation ) |
|||
{ |
|||
// Create multiple pins at the same location
|
|||
VECTOR2I position( 0, 0 ); |
|||
|
|||
SCH_PIN* pin1 = new SCH_PIN( m_symbol.get() ); |
|||
pin1->SetNumber( wxT("1") ); |
|||
pin1->SetPosition( position ); |
|||
pin1->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); |
|||
|
|||
SCH_PIN* pin2 = new SCH_PIN( m_symbol.get() ); |
|||
pin2->SetNumber( wxT("2") ); |
|||
pin2->SetPosition( position ); |
|||
pin2->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); |
|||
|
|||
SCH_PIN* pin3 = new SCH_PIN( m_symbol.get() ); |
|||
pin3->SetNumber( wxT("3") ); |
|||
pin3->SetPosition( position ); |
|||
pin3->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); |
|||
|
|||
// Verify pins are at same location
|
|||
BOOST_CHECK_EQUAL( pin1->GetPosition(), pin2->GetPosition() ); |
|||
BOOST_CHECK_EQUAL( pin2->GetPosition(), pin3->GetPosition() ); |
|||
|
|||
// Test IsStacked functionality
|
|||
BOOST_CHECK( pin1->IsStacked( pin2 ) ); |
|||
BOOST_CHECK( pin2->IsStacked( pin3 ) ); |
|||
|
|||
delete pin1; |
|||
delete pin2; |
|||
delete pin3; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test net name generation with stacked pins |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedPinNetNaming ) |
|||
{ |
|||
SCH_PIN* pin = new SCH_PIN( m_symbol.get() ); |
|||
pin->SetNumber( wxT("[8,9,10]") ); |
|||
|
|||
// This test would require a full SCH_SYMBOL context to test GetDefaultNetName
|
|||
// For now just verify the pin number expansion works
|
|||
bool isValid; |
|||
std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
|
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 3 ); |
|||
// The smallest number should be first for deterministic net naming
|
|||
BOOST_CHECK_EQUAL( expanded[0], "8" ); |
|||
|
|||
delete pin; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test conversion from multiple co-located pins to stacked notation |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestConvertMultiplePinsToStacked ) |
|||
{ |
|||
// Create multiple pins at the same location
|
|||
VECTOR2I position( 0, 0 ); |
|||
|
|||
SCH_PIN* pin1 = new SCH_PIN( m_symbol.get() ); |
|||
pin1->SetNumber( wxT("1") ); |
|||
pin1->SetPosition( position ); |
|||
pin1->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); |
|||
pin1->SetVisible( true ); |
|||
|
|||
SCH_PIN* pin2 = new SCH_PIN( m_symbol.get() ); |
|||
pin2->SetNumber( wxT("2") ); |
|||
pin2->SetPosition( position ); |
|||
pin2->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); |
|||
pin2->SetVisible( true ); |
|||
|
|||
SCH_PIN* pin3 = new SCH_PIN( m_symbol.get() ); |
|||
pin3->SetNumber( wxT("3") ); |
|||
pin3->SetPosition( position ); |
|||
pin3->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); |
|||
pin3->SetVisible( true ); |
|||
|
|||
// Test basic property access before adding to symbol
|
|||
BOOST_CHECK_EQUAL( pin1->GetNumber(), "1" ); |
|||
BOOST_CHECK_EQUAL( pin2->GetNumber(), "2" ); |
|||
BOOST_CHECK_EQUAL( pin3->GetNumber(), "3" ); |
|||
|
|||
// Just test the basic conversion logic without symbol management
|
|||
// Build the stacked notation string
|
|||
wxString stackedNotation = wxT("["); |
|||
stackedNotation += pin1->GetNumber(); |
|||
stackedNotation += wxT(","); |
|||
stackedNotation += pin2->GetNumber(); |
|||
stackedNotation += wxT(","); |
|||
stackedNotation += pin3->GetNumber(); |
|||
stackedNotation += wxT("]"); |
|||
|
|||
// Test stacked notation creation
|
|||
BOOST_CHECK_EQUAL( stackedNotation, "[1,2,3]" ); |
|||
|
|||
// Set stacked notation on one pin
|
|||
pin1->SetNumber( stackedNotation ); |
|||
BOOST_CHECK_EQUAL( pin1->GetNumber(), "[1,2,3]" ); |
|||
|
|||
// Verify the stacked pin expansion works
|
|||
bool isValid; |
|||
std::vector<wxString> expanded = pin1->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 3 ); |
|||
BOOST_CHECK_EQUAL( expanded[0], "1" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "2" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "3" ); |
|||
|
|||
// Clean up - delete pins manually since they're not in symbol
|
|||
delete pin1; |
|||
delete pin2; |
|||
delete pin3; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test range collapsing functionality in stacked pin conversion |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestRangeCollapsingConversion ) |
|||
{ |
|||
// Test range collapsing logic directly without symbol management
|
|||
|
|||
// Test consecutive pins that should collapse to a range
|
|||
std::vector<long> numbers = { 1, 2, 3, 4 }; |
|||
|
|||
// Build collapsed ranges
|
|||
wxString result; |
|||
size_t i = 0; |
|||
while( i < numbers.size() ) |
|||
{ |
|||
if( !result.IsEmpty() ) |
|||
result += wxT(","); |
|||
|
|||
long start = numbers[i]; |
|||
long end = start; |
|||
|
|||
// Find the end of consecutive sequence
|
|||
while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 ) |
|||
{ |
|||
i++; |
|||
end = numbers[i]; |
|||
} |
|||
|
|||
// Add range or single number
|
|||
if( end > start + 1 ) // Range of 3+ numbers
|
|||
result += wxString::Format( wxT("%ld-%ld"), start, end ); |
|||
else if( end == start + 1 ) // Two consecutive numbers
|
|||
result += wxString::Format( wxT("%ld,%ld"), start, end ); |
|||
else // Single number
|
|||
result += wxString::Format( wxT("%ld"), start ); |
|||
|
|||
i++; |
|||
} |
|||
|
|||
// Verify range collapsing: 1,2,3,4 should become "1-4"
|
|||
BOOST_CHECK_EQUAL( result, "1-4" ); |
|||
|
|||
// Test with mixed consecutive and non-consecutive: 1,2,3,4,7,8,9
|
|||
numbers = { 1, 2, 3, 4, 7, 8, 9 }; |
|||
result.Clear(); |
|||
i = 0; |
|||
|
|||
while( i < numbers.size() ) |
|||
{ |
|||
if( !result.IsEmpty() ) |
|||
result += wxT(","); |
|||
|
|||
long start = numbers[i]; |
|||
long end = start; |
|||
|
|||
// Find the end of consecutive sequence
|
|||
while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 ) |
|||
{ |
|||
i++; |
|||
end = numbers[i]; |
|||
} |
|||
|
|||
// Add range or single number
|
|||
if( end > start + 1 ) // Range of 3+ numbers
|
|||
result += wxString::Format( wxT("%ld-%ld"), start, end ); |
|||
else if( end == start + 1 ) // Two consecutive numbers
|
|||
result += wxString::Format( wxT("%ld,%ld"), start, end ); |
|||
else // Single number
|
|||
result += wxString::Format( wxT("%ld"), start ); |
|||
|
|||
i++; |
|||
} |
|||
|
|||
// Verify mixed ranges: 1,2,3,4,7,8,9 should become "1-4,7-9"
|
|||
BOOST_CHECK_EQUAL( result, "1-4,7-9" ); |
|||
|
|||
// Test edge cases
|
|||
numbers = { 1, 3, 5 }; // Non-consecutive
|
|||
result.Clear(); |
|||
i = 0; |
|||
|
|||
while( i < numbers.size() ) |
|||
{ |
|||
if( !result.IsEmpty() ) |
|||
result += wxT(","); |
|||
|
|||
long start = numbers[i]; |
|||
long end = start; |
|||
|
|||
// Find the end of consecutive sequence
|
|||
while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 ) |
|||
{ |
|||
i++; |
|||
end = numbers[i]; |
|||
} |
|||
|
|||
// Add range or single number
|
|||
if( end > start + 1 ) // Range of 3+ numbers
|
|||
result += wxString::Format( wxT("%ld-%ld"), start, end ); |
|||
else if( end == start + 1 ) // Two consecutive numbers
|
|||
result += wxString::Format( wxT("%ld,%ld"), start, end ); |
|||
else // Single number
|
|||
result += wxString::Format( wxT("%ld"), start ); |
|||
|
|||
i++; |
|||
} |
|||
|
|||
// Verify non-consecutive: 1,3,5 should remain "1,3,5"
|
|||
BOOST_CHECK_EQUAL( result, "1,3,5" ); |
|||
|
|||
// Test two consecutive numbers
|
|||
numbers = { 5, 6 }; |
|||
result.Clear(); |
|||
i = 0; |
|||
|
|||
while( i < numbers.size() ) |
|||
{ |
|||
if( !result.IsEmpty() ) |
|||
result += wxT(","); |
|||
|
|||
long start = numbers[i]; |
|||
long end = start; |
|||
|
|||
// Find the end of consecutive sequence
|
|||
while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 ) |
|||
{ |
|||
i++; |
|||
end = numbers[i]; |
|||
} |
|||
|
|||
// Add range or single number
|
|||
if( end > start + 1 ) // Range of 3+ numbers
|
|||
result += wxString::Format( wxT("%ld-%ld"), start, end ); |
|||
else if( end == start + 1 ) // Two consecutive numbers
|
|||
result += wxString::Format( wxT("%ld,%ld"), start, end ); |
|||
else // Single number
|
|||
result += wxString::Format( wxT("%ld"), start ); |
|||
|
|||
i++; |
|||
} |
|||
|
|||
// Verify two consecutive: 5,6 should remain "5,6" (not convert to range)
|
|||
BOOST_CHECK_EQUAL( result, "5,6" ); |
|||
|
|||
// Test complex mixed case: 1,2,4,5,6,8,9,10,11
|
|||
numbers = { 1, 2, 4, 5, 6, 8, 9, 10, 11 }; |
|||
result.Clear(); |
|||
i = 0; |
|||
|
|||
while( i < numbers.size() ) |
|||
{ |
|||
if( !result.IsEmpty() ) |
|||
result += wxT(","); |
|||
|
|||
long start = numbers[i]; |
|||
long end = start; |
|||
|
|||
// Find the end of consecutive sequence
|
|||
while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 ) |
|||
{ |
|||
i++; |
|||
end = numbers[i]; |
|||
} |
|||
|
|||
// Add range or single number
|
|||
if( end > start + 1 ) // Range of 3+ numbers
|
|||
result += wxString::Format( wxT("%ld-%ld"), start, end ); |
|||
else if( end == start + 1 ) // Two consecutive numbers
|
|||
result += wxString::Format( wxT("%ld,%ld"), start, end ); |
|||
else // Single number
|
|||
result += wxString::Format( wxT("%ld"), start ); |
|||
|
|||
i++; |
|||
} |
|||
|
|||
// Verify complex case: 1,2,4,5,6,8,9,10,11 should become "1,2,4-6,8-11"
|
|||
BOOST_CHECK_EQUAL( result, "1,2,4-6,8-11" ); |
|||
|
|||
// Test that our range notation can be expanded back correctly
|
|||
SCH_PIN* rangePin = new SCH_PIN( m_symbol.get() ); |
|||
rangePin->SetNumber( wxT("[1-4,7-9]") ); |
|||
|
|||
bool isValid; |
|||
std::vector<wxString> expanded = rangePin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 7 ); |
|||
|
|||
// Should expand to: 1,2,3,4,7,8,9
|
|||
BOOST_CHECK_EQUAL( expanded[0], "1" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "2" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "3" ); |
|||
BOOST_CHECK_EQUAL( expanded[3], "4" ); |
|||
BOOST_CHECK_EQUAL( expanded[4], "7" ); |
|||
BOOST_CHECK_EQUAL( expanded[5], "8" ); |
|||
BOOST_CHECK_EQUAL( expanded[6], "9" ); |
|||
|
|||
delete rangePin; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test round-trip conversion: multiple pins -> stacked -> multiple pins |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestRoundTripConversion ) |
|||
{ |
|||
// Create multiple pins at the same location
|
|||
VECTOR2I position( 100, 200 ); |
|||
|
|||
SCH_PIN* pin5 = new SCH_PIN( m_symbol.get() ); |
|||
pin5->SetNumber( wxT("5") ); |
|||
pin5->SetPosition( position ); |
|||
pin5->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); |
|||
pin5->SetType( ELECTRICAL_PINTYPE::PT_INPUT ); |
|||
pin5->SetName( wxT("TestPin") ); |
|||
pin5->SetVisible( true ); |
|||
|
|||
SCH_PIN* pin7 = new SCH_PIN( m_symbol.get() ); |
|||
pin7->SetNumber( wxT("7") ); |
|||
pin7->SetPosition( position ); |
|||
pin7->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); |
|||
pin7->SetType( ELECTRICAL_PINTYPE::PT_INPUT ); |
|||
pin7->SetName( wxT("TestPin") ); |
|||
pin7->SetVisible( true ); |
|||
|
|||
SCH_PIN* pin9 = new SCH_PIN( m_symbol.get() ); |
|||
pin9->SetNumber( wxT("9") ); |
|||
pin9->SetPosition( position ); |
|||
pin9->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); |
|||
pin9->SetType( ELECTRICAL_PINTYPE::PT_INPUT ); |
|||
pin9->SetName( wxT("TestPin") ); |
|||
pin9->SetVisible( true ); |
|||
|
|||
// Store original properties for comparison
|
|||
PIN_ORIENTATION originalOrientation = pin5->GetOrientation(); |
|||
ELECTRICAL_PINTYPE originalType = pin5->GetType(); |
|||
wxString originalName = pin5->GetName(); |
|||
int originalLength = pin5->GetLength(); |
|||
|
|||
// Step 1: Convert to stacked notation (simulating ConvertStackedPins)
|
|||
std::vector<SCH_PIN*> pinsToConvert = { pin5, pin7, pin9 }; |
|||
|
|||
// Sort pins numerically
|
|||
std::sort( pinsToConvert.begin(), pinsToConvert.end(), |
|||
[]( SCH_PIN* a, SCH_PIN* b ) |
|||
{ |
|||
long numA, numB; |
|||
if( a->GetNumber().ToLong( &numA ) && b->GetNumber().ToLong( &numB ) ) |
|||
return numA < numB; |
|||
return a->GetNumber() < b->GetNumber(); |
|||
}); |
|||
|
|||
// Build stacked notation
|
|||
wxString stackedNotation = wxT("[5,7,9]"); |
|||
pinsToConvert[0]->SetNumber( stackedNotation ); |
|||
|
|||
// Remove other pins (don't delete them yet for testing)
|
|||
SCH_PIN* stackedPin = pinsToConvert[0]; |
|||
|
|||
// Step 2: Verify stacked notation
|
|||
bool isValid; |
|||
std::vector<wxString> expanded = stackedPin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 3 ); |
|||
BOOST_CHECK_EQUAL( expanded[0], "5" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "7" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "9" ); |
|||
|
|||
// Step 3: Convert back to individual pins (simulating ExplodeStackedPin)
|
|||
// Sort the stacked numbers (should already be sorted in our case)
|
|||
std::sort( expanded.begin(), expanded.end(), |
|||
[]( const wxString& a, const wxString& b ) |
|||
{ |
|||
long numA, numB; |
|||
if( a.ToLong( &numA ) && b.ToLong( &numB ) ) |
|||
return numA < numB; |
|||
return a < b; |
|||
}); |
|||
|
|||
// Change the original pin to use the first (smallest) number and make it visible
|
|||
stackedPin->SetNumber( expanded[0] ); |
|||
stackedPin->SetVisible( true ); |
|||
|
|||
// Create additional pins for the remaining numbers and make them invisible
|
|||
std::vector<SCH_PIN*> explodedPins; |
|||
explodedPins.push_back( stackedPin ); |
|||
|
|||
for( size_t i = 1; i < expanded.size(); ++i ) |
|||
{ |
|||
SCH_PIN* newPin = new SCH_PIN( m_symbol.get() ); |
|||
|
|||
// Copy all properties from the original pin
|
|||
newPin->SetPosition( stackedPin->GetPosition() ); |
|||
newPin->SetOrientation( stackedPin->GetOrientation() ); |
|||
newPin->SetShape( stackedPin->GetShape() ); |
|||
newPin->SetLength( stackedPin->GetLength() ); |
|||
newPin->SetType( stackedPin->GetType() ); |
|||
newPin->SetName( stackedPin->GetName() ); |
|||
newPin->SetNumber( expanded[i] ); |
|||
newPin->SetNameTextSize( stackedPin->GetNameTextSize() ); |
|||
newPin->SetNumberTextSize( stackedPin->GetNumberTextSize() ); |
|||
newPin->SetUnit( stackedPin->GetUnit() ); |
|||
newPin->SetBodyStyle( stackedPin->GetBodyStyle() ); |
|||
newPin->SetVisible( false ); // Make all other pins invisible
|
|||
|
|||
explodedPins.push_back( newPin ); |
|||
} |
|||
|
|||
// Step 4: Verify the round-trip conversion
|
|||
BOOST_REQUIRE_EQUAL( explodedPins.size(), 3 ); |
|||
|
|||
// Check pin numbers
|
|||
BOOST_CHECK_EQUAL( explodedPins[0]->GetNumber(), "5" ); |
|||
BOOST_CHECK_EQUAL( explodedPins[1]->GetNumber(), "7" ); |
|||
BOOST_CHECK_EQUAL( explodedPins[2]->GetNumber(), "9" ); |
|||
|
|||
// Check visibility (only first pin should be visible)
|
|||
BOOST_CHECK( explodedPins[0]->IsVisible() ); |
|||
BOOST_CHECK( !explodedPins[1]->IsVisible() ); |
|||
BOOST_CHECK( !explodedPins[2]->IsVisible() ); |
|||
|
|||
// Check that properties were preserved
|
|||
for( SCH_PIN* pin : explodedPins ) |
|||
{ |
|||
BOOST_CHECK_EQUAL( pin->GetPosition(), position ); |
|||
BOOST_CHECK( pin->GetOrientation() == originalOrientation ); |
|||
BOOST_CHECK( pin->GetType() == originalType ); |
|||
BOOST_CHECK_EQUAL( pin->GetName(), originalName ); |
|||
BOOST_CHECK_EQUAL( pin->GetLength(), originalLength ); |
|||
} |
|||
|
|||
// Clean up
|
|||
for( size_t i = 1; i < explodedPins.size(); ++i ) |
|||
delete explodedPins[i]; |
|||
// Note: explodedPins[0] is the original stackedPin, don't delete twice
|
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test visibility behavior during conversions |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestVisibilityHandling ) |
|||
{ |
|||
// Create a single pin to test visibility handling
|
|||
SCH_PIN* pin = new SCH_PIN( m_symbol.get() ); |
|||
pin->SetNumber( wxT("[8,10,12]") ); |
|||
pin->SetVisible( false ); // Start invisible
|
|||
|
|||
// Test expansion of stacked notation
|
|||
bool isValid; |
|||
std::vector<wxString> expanded = pin->GetStackedPinNumbers( &isValid ); |
|||
BOOST_CHECK( isValid ); |
|||
BOOST_REQUIRE_EQUAL( expanded.size(), 3 ); |
|||
|
|||
// Sort expanded numbers
|
|||
std::sort( expanded.begin(), expanded.end(), |
|||
[]( const wxString& a, const wxString& b ) |
|||
{ |
|||
long numA, numB; |
|||
if( a.ToLong( &numA ) && b.ToLong( &numB ) ) |
|||
return numA < numB; |
|||
return a < b; |
|||
}); |
|||
|
|||
// Verify sorted order is correct
|
|||
BOOST_CHECK_EQUAL( expanded[0], "8" ); |
|||
BOOST_CHECK_EQUAL( expanded[1], "10" ); |
|||
BOOST_CHECK_EQUAL( expanded[2], "12" ); |
|||
|
|||
// Set the smallest pin number and make it visible
|
|||
pin->SetNumber( expanded[0] ); // "8"
|
|||
pin->SetVisible( true ); // Make visible
|
|||
|
|||
// Verify the smallest pin is visible and has correct number
|
|||
BOOST_CHECK_EQUAL( pin->GetNumber(), "8" ); |
|||
BOOST_CHECK( pin->IsVisible() ); |
|||
|
|||
// Clean up
|
|||
delete pin; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test alphanumeric range collapsing functionality |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestAlphanumericRangeCollapsing ) |
|||
{ |
|||
// Test the new alphanumeric prefix parsing logic
|
|||
|
|||
// Helper function to test prefix parsing
|
|||
auto testPrefixParsing = []( const wxString& pinNumber ) -> std::pair<wxString, long> |
|||
{ |
|||
wxString prefix; |
|||
long numValue = -1; |
|||
|
|||
// Find where numeric part starts (scan from end)
|
|||
size_t numStart = pinNumber.length(); |
|||
for( int i = pinNumber.length() - 1; i >= 0; i-- ) |
|||
{ |
|||
if( !wxIsdigit( pinNumber[i] ) ) |
|||
{ |
|||
numStart = i + 1; |
|||
break; |
|||
} |
|||
if( i == 0 ) // All digits
|
|||
numStart = 0; |
|||
} |
|||
|
|||
if( numStart < pinNumber.length() ) // Has numeric suffix
|
|||
{ |
|||
prefix = pinNumber.Left( numStart ); |
|||
wxString numericPart = pinNumber.Mid( numStart ); |
|||
numericPart.ToLong( &numValue ); |
|||
} |
|||
|
|||
return std::make_pair( prefix, numValue ); |
|||
}; |
|||
|
|||
// Test basic prefix parsing
|
|||
auto [prefix1, num1] = testPrefixParsing( wxT("A1") ); |
|||
BOOST_CHECK_EQUAL( prefix1, "A" ); |
|||
BOOST_CHECK_EQUAL( num1, 1 ); |
|||
|
|||
auto [prefix2, num2] = testPrefixParsing( wxT("AB12") ); |
|||
BOOST_CHECK_EQUAL( prefix2, "AB" ); |
|||
BOOST_CHECK_EQUAL( num2, 12 ); |
|||
|
|||
auto [prefix3, num3] = testPrefixParsing( wxT("123") ); |
|||
BOOST_CHECK_EQUAL( prefix3, "" ); |
|||
BOOST_CHECK_EQUAL( num3, 123 ); |
|||
|
|||
auto [prefix4, num4] = testPrefixParsing( wxT("XYZ") ); |
|||
BOOST_CHECK_EQUAL( prefix4, "" ); // No numeric suffix
|
|||
BOOST_CHECK_EQUAL( num4, -1 ); |
|||
|
|||
// Test grouping logic with example: AA1,AA2,AA3,AB4,CD12,CD13,CD14
|
|||
std::map<wxString, std::vector<long>> prefixGroups; |
|||
std::vector<wxString> testPins = { wxT("AA1"), wxT("AA2"), wxT("AA3"), wxT("AB4"), wxT("CD12"), wxT("CD13"), wxT("CD14") }; |
|||
|
|||
for( const wxString& pinNumber : testPins ) |
|||
{ |
|||
auto [prefix, numValue] = testPrefixParsing( pinNumber ); |
|||
if( numValue != -1 ) |
|||
prefixGroups[prefix].push_back( numValue ); |
|||
} |
|||
|
|||
// Verify grouping
|
|||
BOOST_CHECK_EQUAL( prefixGroups.size(), 3 ); |
|||
BOOST_CHECK_EQUAL( prefixGroups[wxT("AA")].size(), 3 ); |
|||
BOOST_CHECK_EQUAL( prefixGroups[wxT("AB")].size(), 1 ); |
|||
BOOST_CHECK_EQUAL( prefixGroups[wxT("CD")].size(), 3 ); |
|||
|
|||
// Build expected result: AA1-AA3,AB4,CD12-CD14
|
|||
wxString expectedResult; |
|||
for( auto& [prefix, numbers] : prefixGroups ) |
|||
{ |
|||
if( !expectedResult.IsEmpty() ) |
|||
expectedResult += wxT(","); |
|||
|
|||
std::sort( numbers.begin(), numbers.end() ); |
|||
|
|||
size_t i = 0; |
|||
while( i < numbers.size() ) |
|||
{ |
|||
if( i > 0 ) |
|||
expectedResult += wxT(","); |
|||
|
|||
long start = numbers[i]; |
|||
long end = start; |
|||
|
|||
// Find consecutive sequence
|
|||
while( i + 1 < numbers.size() && numbers[i + 1] == numbers[i] + 1 ) |
|||
{ |
|||
i++; |
|||
end = numbers[i]; |
|||
} |
|||
|
|||
// Format with prefix
|
|||
if( end > start + 1 ) // Range of 3+ numbers
|
|||
expectedResult += wxString::Format( wxT("%s%ld-%s%ld"), prefix, start, prefix, end ); |
|||
else if( end == start + 1 ) // Two consecutive numbers
|
|||
expectedResult += wxString::Format( wxT("%s%ld,%s%ld"), prefix, start, prefix, end ); |
|||
else // Single number
|
|||
expectedResult += wxString::Format( wxT("%s%ld"), prefix, start ); |
|||
|
|||
i++; |
|||
} |
|||
} |
|||
|
|||
// Should result in: AA1-AA3,AB4,CD12-CD14
|
|||
BOOST_CHECK_EQUAL( expectedResult, "AA1-AA3,AB4,CD12-CD14" ); |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
@ -0,0 +1,101 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright The KiCad Developers |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 3 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one at |
|||
* http://www.gnu.org/licenses/
|
|||
*/ |
|||
|
|||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
|||
#include <schematic_utils/schematic_file_util.h>
|
|||
|
|||
#include <algorithm>
|
|||
#include <vector>
|
|||
|
|||
#include <schematic.h>
|
|||
#include <sch_screen.h>
|
|||
#include <sch_symbol.h>
|
|||
#include <sch_pin.h>
|
|||
#include <settings/settings_manager.h>
|
|||
#include <locale_io.h>
|
|||
|
|||
struct STACKED_PIN_FIXTURE |
|||
{ |
|||
STACKED_PIN_FIXTURE() : m_settingsManager( true /* headless */ ) {} |
|||
|
|||
SETTINGS_MANAGER m_settingsManager; |
|||
std::unique_ptr<SCHEMATIC> m_schematic; |
|||
}; |
|||
|
|||
static std::vector<wxString> ToVector( const std::initializer_list<const char*>& init ) |
|||
{ |
|||
std::vector<wxString> out; |
|||
for( const char* s : init ) |
|||
out.emplace_back( wxString::FromUTF8( s ) ); |
|||
return out; |
|||
} |
|||
|
|||
BOOST_FIXTURE_TEST_CASE( StackedPinNomenclature_ExpandsCorrectly, STACKED_PIN_FIXTURE ) |
|||
{ |
|||
LOCALE_IO dummy; |
|||
|
|||
// Load the custom schematic with bracketed pin numbers
|
|||
KI_TEST::LoadSchematic( m_settingsManager, "stacked_pin_nomenclature", m_schematic ); |
|||
|
|||
SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers(); |
|||
BOOST_REQUIRE( sheets.size() >= 1 ); |
|||
SCH_SCREEN* screen = sheets.at( 0 ).LastScreen(); |
|||
|
|||
// Find the Device:R symbol on the sheet
|
|||
SCH_SYMBOL* resistor = nullptr; |
|||
for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) ) |
|||
{ |
|||
SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item ); |
|||
if( sym->GetSchSymbolLibraryName() == wxT( "Device:R" ) ) |
|||
{ |
|||
resistor = sym; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
BOOST_REQUIRE_MESSAGE( resistor, "Resistor symbol not found in test schematic" ); |
|||
|
|||
// Collect both pins (each with bracketed numbers)
|
|||
std::vector<SCH_PIN*> rpins = resistor->GetPins( &sheets.at( 0 ) ); |
|||
BOOST_REQUIRE_EQUAL( rpins.size(), 2 ); |
|||
|
|||
// For determinism, sort by local Y position
|
|||
std::sort( rpins.begin(), rpins.end(), []( SCH_PIN* a, SCH_PIN* b ) { |
|||
return a->GetLocalPosition().y < b->GetLocalPosition().y; |
|||
} ); |
|||
|
|||
// Top pin is [1-5]
|
|||
bool validTop = false; |
|||
std::vector<wxString> top = rpins[0]->GetStackedPinNumbers( &validTop ); |
|||
BOOST_CHECK( validTop ); |
|||
std::vector<wxString> expectedTop = ToVector( { "1", "2", "3", "4", "5" } ); |
|||
BOOST_CHECK_EQUAL_COLLECTIONS( top.begin(), top.end(), expectedTop.begin(), expectedTop.end() ); |
|||
|
|||
// Bottom pin is [6,7,9-11]
|
|||
bool validBot = false; |
|||
std::vector<wxString> bot = rpins[1]->GetStackedPinNumbers( &validBot ); |
|||
BOOST_CHECK( validBot ); |
|||
std::vector<wxString> expectedBot = ToVector( { "6", "7", "9", "10", "11" } ); |
|||
BOOST_CHECK_EQUAL_COLLECTIONS( bot.begin(), bot.end(), expectedBot.begin(), expectedBot.end() ); |
|||
|
|||
// Total expanded count across both pins should be 10
|
|||
size_t total = top.size() + bot.size(); |
|||
BOOST_CHECK_EQUAL( total, 10 ); |
|||
} |
@ -0,0 +1,266 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <boost/test/unit_test.hpp>
|
|||
|
|||
#include <netlist_reader/pcb_netlist.h>
|
|||
#include <lib_id.h>
|
|||
#include <board.h>
|
|||
#include <footprint.h>
|
|||
#include <pad.h>
|
|||
#include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
|
|||
#include <project.h>
|
|||
#include <settings/settings_manager.h>
|
|||
#include <wx/log.h>
|
|||
#include <qa_utils/utility_registry.h>
|
|||
#include <reporter.h>
|
|||
|
|||
BOOST_AUTO_TEST_SUITE( StackedPinNetlist ) |
|||
|
|||
|
|||
/**
|
|||
* Test that COMPONENT::GetNet properly handles stacked pin notation like [8,9,10] |
|||
* and finds individual pin numbers within the stacked group. |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedPinNetMatch ) |
|||
{ |
|||
// Create a component with a stacked pin notation
|
|||
LIB_ID fpid( wxT( "TestLib" ), wxT( "TestFootprint" ) ); |
|||
wxString reference = wxT( "U1" ); |
|||
wxString value = wxT( "TestIC" ); |
|||
KIID_PATH path; |
|||
std::vector<KIID> kiids; |
|||
|
|||
COMPONENT component( fpid, reference, value, path, kiids ); |
|||
|
|||
// Add a net with stacked pin notation [8,9,10]
|
|||
component.AddNet( wxT( "[8,9,10]" ), wxT( "DATA_BUS" ), wxT( "bidirectional" ), wxT( "bidirectional" ) ); |
|||
|
|||
// Test that individual pins within the stack are found
|
|||
const COMPONENT_NET& net8 = component.GetNet( wxT( "8" ) ); |
|||
const COMPONENT_NET& net9 = component.GetNet( wxT( "9" ) ); |
|||
const COMPONENT_NET& net10 = component.GetNet( wxT( "10" ) ); |
|||
|
|||
BOOST_CHECK( net8.IsValid() ); |
|||
BOOST_CHECK( net9.IsValid() ); |
|||
BOOST_CHECK( net10.IsValid() ); |
|||
|
|||
BOOST_CHECK_EQUAL( net8.GetNetName(), wxString( "DATA_BUS" ) ); |
|||
BOOST_CHECK_EQUAL( net9.GetNetName(), wxString( "DATA_BUS" ) ); |
|||
BOOST_CHECK_EQUAL( net10.GetNetName(), wxString( "DATA_BUS" ) ); |
|||
|
|||
// Test that pins outside the stack are not found
|
|||
const COMPONENT_NET& net7 = component.GetNet( wxT( "7" ) ); |
|||
const COMPONENT_NET& net11 = component.GetNet( wxT( "11" ) ); |
|||
|
|||
BOOST_CHECK( !net7.IsValid() ); |
|||
BOOST_CHECK( !net11.IsValid() ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test stacked pin notation with range syntax [1-4] |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedPinRangeMatch ) |
|||
{ |
|||
LIB_ID fpid( wxT( "TestLib" ), wxT( "TestFootprint" ) ); |
|||
wxString reference = wxT( "U2" ); |
|||
wxString value = wxT( "TestIC" ); |
|||
KIID_PATH path; |
|||
std::vector<KIID> kiids; |
|||
|
|||
COMPONENT component( fpid, reference, value, path, kiids ); |
|||
|
|||
// Add a net with range notation [1-4]
|
|||
component.AddNet( wxT( "[1-4]" ), wxT( "POWER_BUS" ), wxT( "power_in" ), wxT( "power_in" ) ); |
|||
|
|||
// Test that all pins in the range are found
|
|||
for( int i = 1; i <= 4; i++ ) |
|||
{ |
|||
const COMPONENT_NET& net = component.GetNet( wxString::Format( wxT( "%d" ), i ) ); |
|||
BOOST_CHECK( net.IsValid() ); |
|||
BOOST_CHECK_EQUAL( net.GetNetName(), wxString( "POWER_BUS" ) ); |
|||
} |
|||
|
|||
// Test pins outside the range
|
|||
const COMPONENT_NET& net0 = component.GetNet( wxT( "0" ) ); |
|||
const COMPONENT_NET& net5 = component.GetNet( wxT( "5" ) ); |
|||
|
|||
BOOST_CHECK( !net0.IsValid() ); |
|||
BOOST_CHECK( !net5.IsValid() ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test mixed notation [1,3,5-7] |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedPinMixedMatch ) |
|||
{ |
|||
LIB_ID fpid( wxT( "TestLib" ), wxT( "TestFootprint" ) ); |
|||
wxString reference = wxT( "U3" ); |
|||
wxString value = wxT( "TestIC" ); |
|||
KIID_PATH path; |
|||
std::vector<KIID> kiids; |
|||
|
|||
COMPONENT component( fpid, reference, value, path, kiids ); |
|||
|
|||
// Add a net with mixed notation [1,3,5-7]
|
|||
component.AddNet( wxT( "[1,3,5-7]" ), wxT( "CONTROL_BUS" ), wxT( "output" ), wxT( "output" ) ); |
|||
|
|||
// Test individual pins and ranges
|
|||
std::vector<int> expectedPins = { 1, 3, 5, 6, 7 }; |
|||
for( int pin : expectedPins ) |
|||
{ |
|||
const COMPONENT_NET& net = component.GetNet( wxString::Format( wxT( "%d" ), pin ) ); |
|||
BOOST_CHECK( net.IsValid() ); |
|||
BOOST_CHECK_EQUAL( net.GetNetName(), wxString( "CONTROL_BUS" ) ); |
|||
} |
|||
|
|||
// Test pins that should not be found
|
|||
std::vector<int> unexpectedPins = { 2, 4, 8 }; |
|||
for( int pin : unexpectedPins ) |
|||
{ |
|||
const COMPONENT_NET& net = component.GetNet( wxString::Format( wxT( "%d" ), pin ) ); |
|||
BOOST_CHECK( !net.IsValid() ); |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Test that regular (non-stacked) pin names still work |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestRegularPinMatch ) |
|||
{ |
|||
LIB_ID fpid( wxT( "TestLib" ), wxT( "TestFootprint" ) ); |
|||
wxString reference = wxT( "R1" ); |
|||
wxString value = wxT( "1k" ); |
|||
KIID_PATH path; |
|||
std::vector<KIID> kiids; |
|||
|
|||
COMPONENT component( fpid, reference, value, path, kiids ); |
|||
|
|||
// Add regular pins
|
|||
component.AddNet( wxT( "1" ), wxT( "VCC" ), wxT( "passive" ), wxT( "passive" ) ); |
|||
component.AddNet( wxT( "2" ), wxT( "GND" ), wxT( "passive" ), wxT( "passive" ) ); |
|||
|
|||
const COMPONENT_NET& net1 = component.GetNet( wxT( "1" ) ); |
|||
const COMPONENT_NET& net2 = component.GetNet( wxT( "2" ) ); |
|||
|
|||
BOOST_CHECK( net1.IsValid() ); |
|||
BOOST_CHECK( net2.IsValid() ); |
|||
BOOST_CHECK_EQUAL( net1.GetNetName(), wxString( "VCC" ) ); |
|||
BOOST_CHECK_EQUAL( net2.GetNetName(), wxString( "GND" ) ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* This test creates a mock netlist that matches the stacked project structure and |
|||
* validates that PCB pad lookups work correctly with stacked pin notation. |
|||
*/ |
|||
BOOST_AUTO_TEST_CASE( TestStackedProjectNetlistUpdate ) |
|||
{ |
|||
BOOST_TEST_MESSAGE( "Testing stacked pin project netlist functionality" ); |
|||
|
|||
// Create a component that matches the R1 component from the stacked project
|
|||
LIB_ID fpid( wxT( "Connector" ), wxT( "Tag-Connect_TC2050-IDC-FP_2x05_P1.27mm_Vertical" ) ); |
|||
wxString reference = wxT( "R1" ); |
|||
wxString value = wxT( "R" ); |
|||
KIID_PATH path; |
|||
std::vector<KIID> kiids; |
|||
|
|||
COMPONENT component( fpid, reference, value, path, kiids ); |
|||
|
|||
// Add nets matching the stacked project
|
|||
// The schematic has two stacked pin groups: [1-5] and [6,7,9-11]
|
|||
component.AddNet( wxT( "[1-5]" ), wxT( "Net-(R1-Pad1)" ), wxT( "passive" ), wxT( "passive" ) ); |
|||
component.AddNet( wxT( "[6,7,9-11]" ), wxT( "Net-(R1-Pad6)" ), wxT( "passive" ), wxT( "passive" ) ); |
|||
|
|||
BOOST_TEST_MESSAGE( "Created R1 component with stacked pins [1-5] and [6,7,9-11]" ); |
|||
|
|||
// Log all nets for the component
|
|||
BOOST_TEST_MESSAGE( "R1 component nets:" ); |
|||
for( unsigned i = 0; i < component.GetNetCount(); i++ ) |
|||
{ |
|||
const COMPONENT_NET& net = component.GetNet( i ); |
|||
BOOST_TEST_MESSAGE( " Pin: " + net.GetPinName() + " -> Net: " + net.GetNetName() ); |
|||
} |
|||
|
|||
// Test individual pin lookups
|
|||
// Pins 1-5 should be found (they're in the [1-5] stacked group)
|
|||
for( int pin = 1; pin <= 5; pin++ ) |
|||
{ |
|||
wxString pinStr = wxString::Format( wxT( "%d" ), pin ); |
|||
const COMPONENT_NET& net = component.GetNet( pinStr ); |
|||
|
|||
BOOST_CHECK_MESSAGE( net.IsValid(), |
|||
"Pin " + pinStr + " should be found in stacked group [1-5]" ); |
|||
|
|||
if( net.IsValid() ) |
|||
{ |
|||
BOOST_CHECK_EQUAL( net.GetNetName(), wxString( "Net-(R1-Pad1)" ) ); |
|||
BOOST_TEST_MESSAGE( "Pin " + pinStr + " found with net: " + net.GetNetName() ); |
|||
} |
|||
else |
|||
{ |
|||
BOOST_TEST_MESSAGE( "Pin " + pinStr + " NOT found (should be in [1-5])" ); |
|||
} |
|||
} |
|||
|
|||
// Pins 6,7,9,10,11 should be found (they're in the [6,7,9-11] stacked group)
|
|||
std::vector<int> groupTwoPins = { 6, 7, 9, 10, 11 }; |
|||
for( int pin : groupTwoPins ) |
|||
{ |
|||
wxString pinStr = wxString::Format( wxT( "%d" ), pin ); |
|||
const COMPONENT_NET& net = component.GetNet( pinStr ); |
|||
|
|||
BOOST_CHECK_MESSAGE( net.IsValid(), |
|||
"Pin " + pinStr + " should be found in stacked group [6,7,9-11]" ); |
|||
|
|||
if( net.IsValid() ) |
|||
{ |
|||
BOOST_CHECK_EQUAL( net.GetNetName(), wxString( "Net-(R1-Pad6)" ) ); |
|||
BOOST_TEST_MESSAGE( "Pin " + pinStr + " found with net: " + net.GetNetName() ); |
|||
} |
|||
else |
|||
{ |
|||
BOOST_TEST_MESSAGE( "Pin " + pinStr + " NOT found (should be in [6,7,9-11])" ); |
|||
} |
|||
} |
|||
|
|||
// Pin 8 should NOT be found (it's not in either stacked group)
|
|||
const COMPONENT_NET& net8 = component.GetNet( wxT( "8" ) ); |
|||
BOOST_CHECK_MESSAGE( !net8.IsValid(), |
|||
"Pin 8 should NOT be found (not in any stacked group)" ); |
|||
|
|||
if( net8.IsValid() ) |
|||
{ |
|||
BOOST_TEST_MESSAGE( "Pin 8 unexpectedly found with net: " + net8.GetNetName() ); |
|||
} |
|||
else |
|||
{ |
|||
BOOST_TEST_MESSAGE( "Pin 8 correctly NOT found (expected behavior)" ); |
|||
} |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
Write
Preview
Loading…
Cancel
Save
Reference in new issue