KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_total_length_sexpr.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.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
22#include <board.h>
23#include <pcb_track.h>
24#include <netinfo.h>
25#include <pad.h>
26#include <footprint.h>
28#include <filesystem>
29#include <fstream>
30
31static const long long MM = 1000000LL; // internal units
32
33static const char* BOARD_TEXT = R"KICAD(
34(kicad_pcb
35 (version 20250904)
36 (generator "pcbnew")
37 (generator_version "9.99")
38 (layers
39 (0 "F.Cu" signal)
40 (2 "B.Cu" signal)
41 )
42 (net 0 "")
43 (net 1 "Net-(R1-Pad1)")
44 (net 2 "Net-(R1-Pad2)")
45 (net 3 "Net-(R2-Pad2)")
46 (net 4 "Net-(R3-Pad2)")
47 (segment (start 17.015 0.005) (end 17.005 -0.005) (width 0.2) (layer "F.Cu") (net 3))
48 (segment (start 15.355 -0.005) (end 15.345 0.005) (width 0.2) (layer "F.Cu") (net 2))
49 (segment (start 30 0.125) (end 29.75 -0.125) (width 0.2) (layer "F.Cu") (net 4))
50 (segment (start 22.15 0) (end 30 0) (width 0.2) (layer "F.Cu") (net 4))
51 (segment (start 8 -0.1) (end 7.8 0.1) (width 0.2) (layer "F.Cu") (net 1))
52 (segment (start 9.65 0) (end 15.355 0) (width 0.2) (layer "F.Cu") (net 2))
53 (footprint "MountingHole:MountingHole_2.2mm_M2_DIN965_Pad" (layer "F.Cu") (at 0 0)
54 (property "Reference" "TP2")
55 (pad "1" thru_hole circle (at 0 0) (size 3.8 3.8) (drill 2.2) (layers "*.Cu" "*.Mask") (net 1 "Net-(R1-Pad1)"))
56 )
57 (segment (start 17.005 0) (end 20.5 0) (width 0.2) (layer "F.Cu") (net 3))
58 (footprint "MountingHole:MountingHole_2.2mm_M2_DIN965_Pad" (layer "F.Cu") (at 30 0)
59 (property "Reference" "TP1")
60 (pad "1" thru_hole circle (at 0 0) (size 3.8 3.8) (drill 2.2) (layers "*.Cu" "*.Mask") (net 4 "Net-(R3-Pad2)"))
61 )
62 (segment (start 0 0) (end 8 0) (width 0.2) (layer "F.Cu") (net 1))
63 (footprint "Resistor_SMD:R_0603_1608Metric" (layer "F.Cu") (at 8.825 0)
64 (property "Reference" "R1")
65 (pad "1" smd roundrect (at -0.825 0) (size 0.8 0.95) (layers "F.Cu" "F.Mask" "F.Paste") (net 1 "Net-(R1-Pad1)"))
66 (pad "2" smd roundrect (at 0.825 0) (size 0.8 0.95) (layers "F.Cu" "F.Mask" "F.Paste") (net 2 "Net-(R1-Pad2)"))
67 )
68 (footprint "Resistor_SMD:R_0603_1608Metric" (layer "F.Cu") (at 16.18 0)
69 (property "Reference" "R2")
70 (pad "1" smd roundrect (at -0.825 0) (size 0.8 0.95) (layers "F.Cu" "F.Mask" "F.Paste") (net 2 "Net-(R1-Pad2)"))
71 (pad "2" smd roundrect (at 0.825 0) (size 0.8 0.95) (layers "F.Cu" "F.Mask" "F.Paste") (net 3 "Net-(R2-Pad2)"))
72 )
73 (footprint "Resistor_SMD:R_0603_1608Metric" (layer "F.Cu") (at 21.325 0)
74 (property "Reference" "R3")
75 (pad "1" smd roundrect (at -0.825 0) (size 0.8 0.95) (layers "F.Cu" "F.Mask" "F.Paste") (net 3 "Net-(R2-Pad2)"))
76 (pad "2" smd roundrect (at 0.825 0) (size 0.8 0.95) (layers "F.Cu" "F.Mask" "F.Paste") (net 4 "Net-(R3-Pad2)"))
77 )
78)
79)KICAD";
80
81BOOST_AUTO_TEST_SUITE( SignalTotalLengthSexpr )
82
83BOOST_AUTO_TEST_CASE( SignalAggregateMatchesPadSpacing )
84{
85 PCB_IO_KICAD_SEXPR plugin;
86 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
87 auto tmpFile = std::filesystem::temp_directory_path() / "net_chain_total_length_sexpr.kicad_pcb";
88 {
89 std::ofstream ofs( tmpFile );
90 ofs << BOARD_TEXT;
91 }
92 plugin.LoadBoard( tmpFile.string(), board.get() );
93 board->BuildConnectivity();
94
95 // Assign signal name
96 for( NETINFO_ITEM* net : board->GetNetInfo() )
97 if( net->GetNetCode() > 0 ) net->SetNetChain( wxS("Signal1") );
98
99 // Map net -> two pads (terminal pads) using pad net codes
100 std::map<int, std::vector<PAD*>> netPads;
101 for( FOOTPRINT* fp : board->Footprints() )
102 for( PAD* pad : fp->Pads() )
103 if( pad->GetNetCode() > 0 ) netPads[ pad->GetNetCode() ].push_back( pad );
104
105 for( auto& [code, pads] : netPads )
106 {
107 if( pads.size() >= 2 )
108 {
109 NETINFO_ITEM* net = board->FindNet( code );
110 net->SetTerminalPad( 0, pads[0] );
111 net->SetTerminalPad( 1, pads[1] );
112 }
113 }
114
115 NETINFO_ITEM* n1 = board->FindNet( 1 );
116 BOOST_REQUIRE( n1 );
117
118 PNS_KICAD_IFACE_BASE ifaceBase; ifaceBase.SetBoard( board.get() );
119 long long extraLen = 0, extraDelay = 0;
120 bool ok = ifaceBase.GetSignalAggregate( n1, n1, extraLen, extraDelay );
121 BOOST_CHECK( ok );
122
123 // Compute actual routed length (with wiggles) and linear span for net1
124 long long net1TrackLen = 0;
125 bool havePoint = false; long long minX = 0, maxX = 0;
126 for( BOARD_ITEM* bi : board->Tracks() )
127 if( auto tr = dynamic_cast<PCB_TRACK*>( bi ) )
128 if( tr->GetNetCode() == 1 )
129 {
130 net1TrackLen += ( tr->GetStart() - tr->GetEnd() ).EuclideanNorm();
131 long long sx = tr->GetStart().x; long long ex = tr->GetEnd().x;
132 if( !havePoint ) { minX = std::min( sx, ex ); maxX = std::max( sx, ex ); havePoint = true; }
133 else { minX = std::min( minX, std::min( sx, ex ) ); maxX = std::max( maxX, std::max( sx, ex ) ); }
134 }
135 long long net1Span = havePoint ? ( maxX - minX ) : 0;
136 BOOST_CHECK( net1Span > 0 );
137 BOOST_CHECK( net1TrackLen >= net1Span ); // jog increases physical path
138
139 // Chain layout: TP2(0,0) — net1 — R1 — net2 — R2 — net3 — R3 — net4 — TP1(30,0)
140 //
141 // Copper per net (tracks + jog segments):
142 // net1: 8.000 + 0.283 = 8.283 mm
143 // net2: 5.705 + 0.014 = 5.719 mm
144 // net3: 3.495 + 0.014 = 3.509 mm
145 // net4: 7.850 + 0.354 = 8.204 mm
146 // Total copper: 25.715 mm
147 //
148 // Bridging through resistors (pad-to-pad, not copper):
149 // R1: 1.65 mm, R2: 1.65 mm, R3: 1.65 mm = 4.95 mm
150 //
151 // Full chain: 25.715 + 4.95 = 30.665 mm
152 //
153 // GetSignalAggregate returns copper only (no bridging).
154 // extraLen = net2 + net3 + net4 copper = ~17.432 mm
155 long long totalCopper = net1TrackLen + extraLen;
156 long long expectedCopper = 25 * MM;
157 long long padSpacing = 30 * MM;
158 BOOST_CHECK_MESSAGE( totalCopper >= expectedCopper,
159 "RoutedTotal=" << totalCopper << " expected>=" << expectedCopper
160 << " net1Track=" << net1TrackLen << " extra=" << extraLen );
161 BOOST_CHECK( totalCopper < padSpacing ); // copper alone is less than pad spacing
162
163 // Compute bridging: pad-to-pad distance through each 2-net series component
164 long long bridging = 0;
165 for( FOOTPRINT* fp : board->Footprints() )
166 {
167 std::map<int, PAD*> chainPads;
168 for( PAD* pad : fp->Pads() )
169 {
170 NETINFO_ITEM* pn = pad->GetNet();
171 if( pn && !pn->GetNetChain().IsEmpty() )
172 chainPads.emplace( pn->GetNetCode(), pad );
173 }
174 if( chainPads.size() == 2 )
175 {
176 auto it = chainPads.begin();
177 PAD* p1 = it->second; ++it; PAD* p2 = it->second;
178 bridging += ( p1->GetCenter() - p2->GetCenter() ).EuclideanNorm();
179 }
180 }
181 BOOST_CHECK( bridging > 0 );
182
183 // Full chain length (copper + bridging) must cover the pad spacing
184 long long fullChain = totalCopper + bridging;
185 long long tol = 1 * MM;
186 BOOST_CHECK_MESSAGE( fullChain >= padSpacing && fullChain < padSpacing + tol,
187 "FullChain=" << fullChain << " padSpacing=" << padSpacing
188 << " copper=" << totalCopper << " bridging=" << bridging );
189}
190
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:115
int GetNetCode() const
Definition netinfo.h:97
void SetTerminalPad(int aIndex, PAD *aPad)
Definition netinfo.h:119
Definition pad.h:55
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pad.h:331
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 ...
void SetBoard(BOARD *aBoard)
bool GetSignalAggregate(PNS::NET_HANDLE aNetP, PNS::NET_HANDLE aNetN, long long &aExtraLength, long long &aExtraDelay) const override
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
static const char * BOARD_TEXT
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static const long long MM
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")