KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_chain_topology.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 2
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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
23
24#include <set>
25
26#include <board.h>
29#include <footprint.h>
30#include <netinfo.h>
31#include <pad.h>
32#include <pcb_track.h>
34
35
36namespace
37{
38
39constexpr int MM = 1000000;
40
41
42std::unique_ptr<BOARD> loadBoard( const char* aBoardFile )
43{
44 PCB_IO_KICAD_SEXPR plugin;
45 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
46 plugin.LoadBoard( KI_TEST::GetPcbnewTestDataDir() + aBoardFile, board.get() );
47 board->BuildConnectivity();
48
49 return board;
50}
51
52
53std::set<BOARD_CONNECTED_ITEM*> chainItems( BOARD* aBoard, const wxString& aChain )
54{
55 std::set<BOARD_CONNECTED_ITEM*> items;
56
57 for( PCB_TRACK* t : aBoard->Tracks() )
58 {
59 if( t->GetNet() && t->GetNet()->GetNetChain() == aChain )
60 items.insert( t );
61 }
62
63 for( FOOTPRINT* fp : aBoard->Footprints() )
64 {
65 for( PAD* p : fp->Pads() )
66 {
67 if( p->GetNet() && p->GetNet()->GetNetChain() == aChain )
68 items.insert( p );
69 }
70 }
71
72 return items;
73}
74
75
76// Tag every net whose name starts with "/NET_" into the named chain, and set
77// terminal pads to the first/last footprint's first pad on that chain. fp1
78// pad-1 is terminal[0], lastFp pad-1 is terminal[1].
79void tagChain( BOARD* aBoard, const wxString& aChain, const wxString& aFirstFpRef,
80 const wxString& aLastFpRef )
81{
82 for( NETINFO_ITEM* n : aBoard->GetNetInfo() )
83 {
84 if( n && n->GetNetname().StartsWith( wxS( "/NET_" ) ) )
85 n->SetNetChain( aChain );
86 }
87
88 PAD* termA = nullptr;
89 PAD* termB = nullptr;
90
91 for( FOOTPRINT* fp : aBoard->Footprints() )
92 {
93 if( fp->GetReference() == aFirstFpRef && !fp->Pads().empty() )
94 termA = fp->Pads().front();
95
96 if( fp->GetReference() == aLastFpRef && !fp->Pads().empty() )
97 termB = fp->Pads().back();
98 }
99
100 if( termA )
101 {
102 for( NETINFO_ITEM* n : aBoard->GetNetInfo() )
103 if( n && n->GetNetChain() == aChain )
104 n->SetTerminalPad( 0, termA );
105 }
106
107 if( termB )
108 {
109 for( NETINFO_ITEM* n : aBoard->GetNetInfo() )
110 if( n && n->GetNetChain() == aChain )
111 n->SetTerminalPad( 1, termB );
112 }
113}
114
115
116} // namespace
117
118
119BOOST_AUTO_TEST_SUITE( DRCChainTopology )
120
121
122// Three-net trunk through two passives.
123// pad@(0,0) — track 30mm — bridge 5mm — track 30mm — bridge 5mm — track 30mm — pad@(100,0).
124// Trunk = 100 mm, zero stubs.
125BOOST_AUTO_TEST_CASE( TopologyTreeOnSimpleTrunk )
126{
127 auto board = loadBoard( "net_chains/chain_topology_trunk.kicad_pcb" );
128 tagChain( board.get(), wxS( "SIG" ), wxS( "FP_START" ), wxS( "FP_END" ) );
129
130 auto items = chainItems( board.get(), wxS( "SIG" ) );
131 CHAIN_TOPOLOGY topo( board.get(), wxS( "SIG" ), items );
132
133 BOOST_CHECK_EQUAL( static_cast<int>( topo.GetStatus() ),
134 static_cast<int>( CHAIN_TOPOLOGY::STATUS::OK ) );
135 BOOST_CHECK( topo.IsValid() );
136 BOOST_CHECK_CLOSE( topo.TrunkLength(), 100.0 * MM, 5.0 );
137 BOOST_CHECK( topo.Stubs().empty() );
138}
139
140
141// Single-net trunk plus a perpendicular T-stub of 5 mm at the midpoint.
142BOOST_AUTO_TEST_CASE( TopologyDetectsTStub )
143{
144 auto board = loadBoard( "net_chains/chain_topology_t_stub.kicad_pcb" );
145 tagChain( board.get(), wxS( "TSIG" ), wxS( "FP_START" ), wxS( "FP_END" ) );
146
147 auto items = chainItems( board.get(), wxS( "TSIG" ) );
148 CHAIN_TOPOLOGY topo( board.get(), wxS( "TSIG" ), items );
149
150 BOOST_CHECK( topo.IsValid() );
151 BOOST_REQUIRE_EQUAL( topo.Stubs().size(), 1u );
152
153 // Fixture trunk runs (32,49) to (82,49); the stub taps it at the midpoint
154 const CHAIN_TOPOLOGY::STUB& stub = topo.Stubs().front();
155 BOOST_CHECK_LE( std::abs( stub.branchPoint.x - 57 * MM ), 100 );
156 BOOST_CHECK_LE( std::abs( stub.branchPoint.y - 49 * MM ), 100 );
157 BOOST_CHECK_CLOSE( stub.length, 5.0 * MM, 5.0 );
158
159 // Trunk length is 50 mm and the stub does not contribute.
160 BOOST_CHECK_CLOSE( topo.TrunkLength(), 50.0 * MM, 5.0 );
161}
162
163
164// Only one terminal pad set: NO_TERMINAL_PADS.
165BOOST_AUTO_TEST_CASE( TopologyMissingTerminalPad )
166{
167 auto board = loadBoard( "net_chains/chain_topology_missing_terminal.kicad_pcb" );
168 // Only set chain — no second terminal-pad anchor footprint.
169 for( NETINFO_ITEM* n : board->GetNetInfo() )
170 {
171 if( n && n->GetNetname().StartsWith( wxS( "/NET_" ) ) )
172 n->SetNetChain( wxS( "MISSING" ) );
173 }
174
175 PAD* termA = board->Footprints().empty()
176 ? nullptr
177 : board->Footprints().front()->Pads().empty()
178 ? nullptr
179 : board->Footprints().front()->Pads().front();
180
181 if( termA )
182 {
183 for( NETINFO_ITEM* n : board->GetNetInfo() )
184 if( n && n->GetNetChain() == wxS( "MISSING" ) )
185 n->SetTerminalPad( 0, termA );
186 }
187
188 auto items = chainItems( board.get(), wxS( "MISSING" ) );
189 CHAIN_TOPOLOGY topo( board.get(), wxS( "MISSING" ), items );
190
191 BOOST_CHECK_EQUAL( static_cast<int>( topo.GetStatus() ),
192 static_cast<int>( CHAIN_TOPOLOGY::STATUS::NO_TERMINAL_PADS ) );
193}
194
195
196// Both terminals set, no track between them: DISCONNECTED.
197BOOST_AUTO_TEST_CASE( TopologyDisconnected )
198{
199 auto board = loadBoard( "net_chains/chain_topology_disconnected.kicad_pcb" );
200 tagChain( board.get(), wxS( "DISC" ), wxS( "FP_START" ), wxS( "FP_END" ) );
201
202 auto items = chainItems( board.get(), wxS( "DISC" ) );
203 CHAIN_TOPOLOGY topo( board.get(), wxS( "DISC" ), items );
204
205 BOOST_CHECK_EQUAL( static_cast<int>( topo.GetStatus() ),
206 static_cast<int>( CHAIN_TOPOLOGY::STATUS::DISCONNECTED ) );
207}
208
209
210// Two parallel paths between terminals → CYCLE_DETECTED.
211BOOST_AUTO_TEST_CASE( TopologyCycleDetected )
212{
213 auto board = loadBoard( "net_chains/chain_topology_cycle.kicad_pcb" );
214 tagChain( board.get(), wxS( "LOOP" ), wxS( "FP_START" ), wxS( "FP_END" ) );
215
216 auto items = chainItems( board.get(), wxS( "LOOP" ) );
217 CHAIN_TOPOLOGY topo( board.get(), wxS( "LOOP" ), items );
218
219 BOOST_CHECK_EQUAL( static_cast<int>( topo.GetStatus() ),
220 static_cast<int>( CHAIN_TOPOLOGY::STATUS::CYCLE_DETECTED ) );
221}
222
223
General utilities for PCB file IO for QA programs.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1098
const FOOTPRINTS & Footprints() const
Definition board.h:421
const TRACKS & Tracks() const
Definition board.h:419
Build a topological view of a single named net chain's routed copper.
const std::vector< STUB > & Stubs() const
STATUS GetStatus() const
double TrunkLength() const
bool IsValid() const
Handle the data for a net.
Definition netinfo.h:46
Definition pad.h:61
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(TopologyTreeOnSimpleTrunk)
BOOST_AUTO_TEST_SUITE_END()
static const long long MM
BOOST_CHECK_EQUAL(result, "25.4")