KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_odbpp_netlist.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
31
32#include <boost/test/unit_test.hpp>
33
34#include <board.h>
36#include <footprint.h>
37#include <pad.h>
38#include <pcb_track.h>
39#include <base_units.h>
40#include <core/utf8.h>
41
43
44#include <wx/dir.h>
45#include <wx/ffile.h>
46#include <wx/filename.h>
47#include <wx/utils.h>
48#include <wx/regex.h>
49
50#include "odb_test_utils.h"
51
52
53namespace
54{
55
56wxFileName MakeTempDir( const wxString& aPrefix )
57{
58 wxFileName tempDir( wxFileName::CreateTempFileName( wxString::Format( wxT( "kicad-%s-" ), aPrefix ) ) );
59
60 BOOST_REQUIRE( tempDir.FileExists() );
61 BOOST_REQUIRE( wxRemoveFile( tempDir.GetFullPath() ) );
62 BOOST_REQUIRE( tempDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
63
64 return tempDir;
65}
66
67
68wxString ReadFile( const wxFileName& aPath )
69{
70 wxFFile stream( aPath.GetFullPath(), wxT( "rb" ) );
71 wxString contents;
72 BOOST_REQUIRE( stream.ReadAll( &contents ) );
73 stream.Close();
74 return contents;
75}
76
77} // anonymous namespace
78
79
80BOOST_AUTO_TEST_CASE( ODBNetlistCoordinatesMatchFeatures )
81{
82 wxFileName tempDir = MakeTempDir( wxT( "odb-netlist" ) );
83
84 BOARD board;
85 board.SetCopperLayerCount( 2 );
86
87 // Set a non-zero aux origin to trigger the bug
88 VECTOR2I auxOrigin( pcbIUScale.mmToIU( 50.0 ), pcbIUScale.mmToIU( 30.0 ) );
89 board.GetDesignSettings().SetAuxOrigin( auxOrigin );
90
91 // Add a footprint with a pad at a known position
92 FOOTPRINT* fp = new FOOTPRINT( &board );
93 fp->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 100.0 ), pcbIUScale.mmToIU( 80.0 ) ) );
94 fp->SetReference( wxT( "U1" ) );
95
96 PAD* pad = new PAD( fp );
97 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
98 pad->SetAttribute( PAD_ATTRIB::SMD );
100 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 0.5 ) ) );
101 pad->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 100.0 ), pcbIUScale.mmToIU( 80.0 ) ) );
102 pad->SetLayerSet( LSET( { F_Cu, F_Mask, F_Paste } ) );
103 pad->SetNumber( wxT( "1" ) );
104
105 NETINFO_ITEM* net1 = new NETINFO_ITEM( &board, wxT( "TestNet" ), 1 );
106 board.Add( net1 );
107 pad->SetNet( net1 );
108
109 fp->Add( pad );
110 board.Add( fp );
111
112 // Add a via at a known position
113 PCB_VIA* via = new PCB_VIA( &board );
114 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
115 via->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 120.0 ), pcbIUScale.mmToIU( 60.0 ) ) );
116 via->SetLayerPair( F_Cu, B_Cu );
117 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
118 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
119 via->SetNet( net1 );
120 board.Add( via );
121
122 // Export ODB++
123 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
124 odbRoot.AppendDir( wxT( "odb_out" ) );
125 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
126
127 PCB_IO_ODBPP odbExporter;
128 std::map<std::string, UTF8> props;
129 props["units"] = "mm";
130 props["sigfig"] = "6";
131 BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), board, &props ) );
132
133 // Read the netlist file
134 wxFileName netlistFile( odbRoot.GetFullPath(), wxEmptyString );
135 netlistFile.AppendDir( wxT( "steps" ) );
136 netlistFile.AppendDir( wxT( "pcb" ) );
137 netlistFile.AppendDir( wxT( "netlists" ) );
138 netlistFile.AppendDir( wxT( "cadnet" ) );
139 netlistFile.SetFullName( wxT( "netlist" ) );
140 BOOST_REQUIRE( netlistFile.FileExists() );
141
142 wxString netlistContent = ReadFile( netlistFile );
143
144 // Read a features file that has the toeprint/component position to compare
145 wxFileName compFile( odbRoot.GetFullPath(), wxEmptyString );
146 compFile.AppendDir( wxT( "steps" ) );
147 compFile.AppendDir( wxT( "pcb" ) );
148 compFile.AppendDir( wxT( "layers" ) );
149 compFile.AppendDir( wxT( "comp_+_top" ) );
150 compFile.SetFullName( wxT( "components" ) );
151 BOOST_REQUIRE( compFile.FileExists() );
152
153 wxString compContent = ReadFile( compFile );
154
155 // The pad is at board position (100, 80) mm.
156 // ODB::AddXY applies m_scale and negates Y, giving (100.0, -80.0) in mm.
157 // The toeprint (TOP record) should have x=100.0, y=-80.0 if in mm.
158 // The netlist net-point should have the same coordinates.
159
160 // Verify the toeprint has the pad position (component file uses ODB::AddXY)
161 BOOST_CHECK_MESSAGE( compContent.Contains( wxT( "100" ) ),
162 "Component file should contain the pad X coordinate" );
163
164 // The critical check: the netlist should NOT be offset by the aux origin.
165 // With the bug, x would be (100 - 50) = 50 and y would be (30 - 80) = -50 in mm.
166 // With the fix, x should be 100 and y should be -80 in mm.
167 // We look for "100" and "-80" appearing in the netlist net-point data.
168 BOOST_CHECK_MESSAGE( !netlistContent.Contains( wxT( "50.0" ) ),
169 "Netlist should not contain aux-origin-shifted X coordinate 50.0" );
170 BOOST_CHECK_MESSAGE( netlistContent.Contains( wxT( "100.0" ) ),
171 "Netlist should contain the raw pad X coordinate 100.0" );
172
173 // Clean up
174 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
175 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
176}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
void SetAuxOrigin(const VECTOR2I &aOrigin)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
void SetCopperLayerCount(int aCount)
Definition board.cpp:1144
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
void SetPosition(const VECTOR2I &aPos) override
void SetReference(const wxString &aReference)
Definition footprint.h:915
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:50
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
void SaveBoard(const wxString &aFileName, BOARD &aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
@ F_Paste
Definition layer_ids.h:100
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ F_Cu
Definition layer_ids.h:60
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ RECTANGLE
Definition padstack.h:53
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_CASE(ODBNetlistCoordinatesMatchFeatures)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708