KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_diff_pair_coupling.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
21#include <advanced_config.h>
22#include <board.h>
24#include <pcb_track.h>
25#include <pcb_generator.h>
26#include <drc/drc_item.h>
28#include <drc/drc_rtree.h>
32
33#include <view/view_overlay.h>
34
35/*
36 Differential pair gap/coupling test.
37 Errors generated:
38 - DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE
39 - DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG
40 - DRCE_TOO_MANY_VIAS
41*/
42
43namespace test {
44
46{
47public:
49 m_board( nullptr )
50 {}
51
53
54 virtual bool Run() override;
55
56 virtual const wxString GetName() const override { return wxT( "diff_pair_coupling" ); };
57
58private:
60};
61
62};
63
64
65static bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip )
66{
67 SEG n_proj_p( p.LineProject( n.A ), p.LineProject( n.B ) );
68
69 int64_t t_a = 0;
70 int64_t t_b = p.TCoef( p.B );
71
72 int64_t tproj_a = p.TCoef( n_proj_p.A );
73 int64_t tproj_b = p.TCoef( n_proj_p.B );
74
75 if( t_b < t_a )
76 std::swap( t_b, t_a );
77
78 if( tproj_b < tproj_a )
79 std::swap( tproj_b, tproj_a );
80
81 if( t_b <= tproj_a )
82 return false;
83
84 if( t_a >= tproj_b )
85 return false;
86
87 int64_t t[4] = { 0, p.TCoef( p.B ), p.TCoef( n_proj_p.A ), p.TCoef( n_proj_p.B ) };
88 std::vector<int64_t> tv( t, t + 4 );
89 std::sort( tv.begin(), tv.end() ); // fixme: awful and disgusting way of finding 2 midpoints
90
91 int64_t pLenSq = p.SquaredLength();
92
93 VECTOR2I dp = p.B - p.A;
94 pClip.A.x = p.A.x + rescale( (int64_t)dp.x, tv[1], pLenSq );
95 pClip.A.y = p.A.y + rescale( (int64_t)dp.y, tv[1], pLenSq );
96
97 pClip.B.x = p.A.x + rescale( (int64_t)dp.x, tv[2], pLenSq );
98 pClip.B.y = p.A.y + rescale( (int64_t)dp.y, tv[2], pLenSq );
99
100 nClip.A = n.LineProject( pClip.A );
101 nClip.B = n.LineProject( pClip.B );
102
103 return true;
104}
105
106
107static bool commonParallelProjection( const PCB_ARC& p, const PCB_ARC& n, SHAPE_ARC &pClip, SHAPE_ARC& nClip )
108{
109 VECTOR2I p_center = p.GetCenter();
110 VECTOR2I n_center = n.GetCenter();
111 double p_radius = p.GetRadius();
112 double n_radius = n.GetRadius();
113 bool p_is_ccw = p.IsCCW();
114 bool n_is_ccw = n.IsCCW();
115
116 // Quick check to ensure arcs are of similar size and close enough to be considered coupled
117 double radiusDiffRatio = std::abs(p_radius - n_radius) / std::max(p_radius, n_radius);
118 double centerDistance = (p_center - n_center).EuclideanNorm();
119
120 if (radiusDiffRatio > 0.5 || centerDistance > std::max(p_radius, n_radius) * 0.5)
121 return false;
122
123 VECTOR2I p_start( p.GetStart() );
124 VECTOR2I p_end( p.GetEnd() );
125
126 if( !p_is_ccw )
127 std::swap( p_start, p_end );
128
129 VECTOR2I n_start( n.GetStart() );
130 VECTOR2I n_end( n.GetEnd() );
131
132 if( !n_is_ccw )
133 std::swap( n_start, n_end );
134
135 SHAPE_ARC p_arc( p_start, p.GetMid(), p_end, 0 );
136 SHAPE_ARC n_arc( n_start, n.GetMid(), n_end, 0 );
137
138 EDA_ANGLE p_start_angle = p_arc.GetStartAngle();
139
140 // Rotate the arcs to a common 0 starting angle
141 p_arc.Rotate( p_start_angle, p_center );
142 n_arc.Rotate( p_start_angle, n_center );
143
144 EDA_ANGLE p_end_angle = p_arc.GetEndAngle();
145 EDA_ANGLE n_start_angle = n_arc.GetStartAngle();
146 EDA_ANGLE n_end_angle = n_arc.GetEndAngle();
147
148 // Determine overlap region
149 EDA_ANGLE clip_start_angle, clip_end_angle;
150
151 // No overlap when n starts after p ends or n ends before p starts
152 if( n_start_angle >= p_end_angle || n_end_angle <= EDA_ANGLE(0) )
153 return false;
154
155 // Calculate the start and end angles of the overlap
156 clip_start_angle = std::max( EDA_ANGLE(0), n_start_angle );
157 clip_end_angle = std::min( p_end_angle, n_end_angle );
158
159 // Calculate the total angle of the overlap
160 EDA_ANGLE clip_total_angle = clip_end_angle - clip_start_angle;
161
162 // Now we reset the angles. However, note that the convention here for adding angles
163 // is OPPOSITE the Rotate convention above. So this undoes the rotation.
164 clip_start_angle += p_start_angle;
165 clip_end_angle += p_start_angle;
166 clip_start_angle.Normalize();
167 clip_end_angle.Normalize();
168
169 // One arc starts approximately where the other ends or overlap is too small
170 if( clip_total_angle <= EDA_ANGLE( ADVANCED_CFG::GetCfg().m_MinParallelAngle ) )
171 return false;
172
173 // For CCW arcs, we start at clip_start_angle and sweep through clip_total_angle
174 // For CW arcs, we start at clip_end_angle and sweep through -clip_total_angle
175 VECTOR2I p_clip_point, n_clip_point;
176
177 if( p_is_ccw )
178 {
179 p_clip_point = p_center + VECTOR2I( KiROUND( p_radius * clip_start_angle.Cos() ),
180 KiROUND( p_radius * clip_start_angle.Sin() ) );
181 pClip = SHAPE_ARC( p_center, p_clip_point, clip_total_angle );
182 }
183 else
184 {
185 p_clip_point = p_center + VECTOR2I( KiROUND( p_radius * clip_end_angle.Cos() ),
186 KiROUND( p_radius * clip_end_angle.Sin() ) );
187 pClip = SHAPE_ARC( p_center, p_clip_point, -clip_total_angle );
188 }
189
190 if( n_is_ccw )
191 {
192 n_clip_point = n_center + VECTOR2I( KiROUND( n_radius * clip_start_angle.Cos() ),
193 KiROUND( n_radius * clip_start_angle.Sin() ) );
194 nClip = SHAPE_ARC( n_center, n_clip_point, clip_total_angle );
195 }
196 else
197 {
198 n_clip_point = n_center + VECTOR2I( KiROUND( n_radius * clip_end_angle.Cos() ),
199 KiROUND( n_radius * clip_end_angle.Sin() ) );
200 nClip = SHAPE_ARC( n_center, n_clip_point, -clip_total_angle );
201 }
202
203 // Ensure the resulting arcs are not degenerate
204 if( pClip.GetLength() < 1.0 || nClip.GetLength() < 1.0 )
205 return false;
206
207 return true;
208}
209
210
212{
213 bool operator<( const DIFF_PAIR_KEY& b ) const
214 {
215 if( netP < b.netP )
216 {
217 return true;
218 }
219 else if( netP > b.netP )
220 {
221 return false;
222 }
223 else // netP == b.netP
224 {
225 if( netN < b.netN )
226 return true;
227 else if( netN > b.netN )
228 return false;
229 else if( gapRuleName.IsEmpty() )
230 return gapRuleName < b.gapRuleName;
231 else
233 }
234 }
235
236 int netP, netN;
237 wxString gapRuleName;
239 std::optional<MINOPTMAX<int>> gapConstraint;
241 std::optional<MINOPTMAX<int>> uncoupledConstraint;
243};
244
246{
249 bool isArc;
254 int64_t computedGap;
260
262 isArc( false ),
263 parentN( nullptr ),
264 parentP( nullptr ),
265 computedGap( 0 ),
267 couplingFailMin( false ),
268 couplingFailMax( false )
269 {}
270};
271
272
274{
275 std::set<BOARD_CONNECTED_ITEM*> itemsP, itemsN;
276 std::vector<DIFF_PAIR_COUPLED_SEGMENTS> coupled;
280};
281
282
284{
285 auto isInTuningPattern =
286 []( BOARD_ITEM* track )
287 {
288 if( EDA_GROUP* parent = track->GetParentGroup() )
289 {
290 if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( parent ) )
291 {
292 if( generator->GetGeneratorType() == wxS( "tuning_pattern" ) )
293 return true;
294 }
295 }
296
297 return false;
298 };
299
300 for( BOARD_CONNECTED_ITEM* itemP : aDp.itemsP )
301 {
302 if( !itemP || itemP->Type() != PCB_TRACE_T || isInTuningPattern( itemP ) )
303 continue;
304
305 PCB_TRACK* sp = static_cast<PCB_TRACK*>( itemP );
306 std::vector<std::optional<DIFF_PAIR_COUPLED_SEGMENTS>> coupled_vec;
307
308 for( BOARD_CONNECTED_ITEM* itemN : aDp.itemsN )
309 {
310 if( !itemN || itemN->Type() != PCB_TRACE_T || isInTuningPattern( itemN ) )
311 continue;
312
313 PCB_TRACK* sn = static_cast<PCB_TRACK*>( itemN );
314
315 if( ( sn->GetLayerSet() & sp->GetLayerSet() ).none() )
316 continue;
317
318 SEG ssp( sp->GetStart(), sp->GetEnd() );
319 SEG ssn( sn->GetStart(), sn->GetEnd() );
320
321 // Segments that are ~ 1 IU in length per side are approximately parallel (tolerance is 1 IU)
322 // with everything and their parallel projection is < 1 IU, leading to bad distance calculations
323 if( ssp.SquaredLength() > 2 && ssn.SquaredLength() > 2 && !ssp.Intersect( ssn, false, true ) )
324 {
326 bool coupled = commonParallelProjection( ssp, ssn, cpair.coupledP, cpair.coupledN );
327
328 if( coupled )
329 {
330 cpair.parentP = sp;
331 cpair.parentN = sn;
332 cpair.layer = sp->GetLayer();
333 cpair.coupledP.NearestPoints( cpair.coupledN, cpair.nearestP, cpair.nearestN,
334 cpair.computedGap );
335 cpair.computedGap = std::sqrt( cpair.computedGap ); // NearestPoints returns squared distance
336 cpair.computedGap -= ( sp->GetWidth() + sn->GetWidth() ) / 2;
337 coupled_vec.push_back( cpair );
338 }
339
340 }
341 }
342
343 for( const std::optional<DIFF_PAIR_COUPLED_SEGMENTS>& coupled : coupled_vec )
344 {
345 auto excludeSelf =
346 [&]( BOARD_ITEM* aItem )
347 {
348 if( aItem == coupled->parentN || aItem == coupled->parentP )
349 return false;
350
351 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_ARC_T )
352 {
353 PCB_TRACK* bci = static_cast<PCB_TRACK*>( aItem );
354
355 // Directly connected items don't count
356 if( bci->HitTest( coupled->coupledN.A, 0 )
357 || bci->HitTest( coupled->coupledN.B, 0 )
358 || bci->HitTest( coupled->coupledP.A, 0 )
359 || bci->HitTest( coupled->coupledP.B, 0 ) )
360 {
361 return false;
362 }
363 }
364
365 return true;
366 };
367
368 SHAPE_SEGMENT checkSeg( coupled->nearestN, coupled->nearestP );
369 DRC_RTREE* tree = coupled->parentP->GetBoard()->m_CopperItemRTreeCache.get();
370
371 // check if there's anything in between the segments suspected to be coupled. If
372 // there's nothing, assume they are really coupled.
373
374 if( !tree->CheckColliding( &checkSeg, sp->GetLayer(), 0, excludeSelf ) )
375 aDp.coupled.push_back( *coupled );
376 }
377 }
378
379 for( BOARD_CONNECTED_ITEM* itemP : aDp.itemsP )
380 {
381 if( !itemP || itemP->Type() != PCB_ARC_T || isInTuningPattern( itemP ) )
382 continue;
383
384 PCB_ARC* sp = static_cast<PCB_ARC*>( itemP );
385 std::vector<std::optional<DIFF_PAIR_COUPLED_SEGMENTS>> coupled_vec;
386
387 for( BOARD_CONNECTED_ITEM* itemN : aDp.itemsN )
388 {
389 if( !itemN || itemN->Type() != PCB_ARC_T || isInTuningPattern( itemN ) )
390 continue;
391
392 PCB_ARC* sn = static_cast<PCB_ARC*>( itemN );
393
394 if( ( sn->GetLayerSet() & sp->GetLayerSet() ).none() )
395 continue;
396
397 // Segments that are ~ 1 IU in length per side are approximately parallel (tolerance is 1 IU)
398 // with everything and their parallel projection is < 1 IU, leading to bad distance calculations
399 int64_t sqWidth = static_cast<int64_t>( sp->GetWidth() ) * sp->GetWidth();
400
401 if( sp->GetLength() > 2
402 && sn->GetLength() > 2
403 && sp->GetCenter().SquaredDistance( sn->GetCenter() ) < sqWidth )
404 {
406 cpair.isArc = true;
407 bool coupled = commonParallelProjection( *sp, *sn, cpair.coupledArcP, cpair.coupledArcN );
408
409 if( coupled )
410 {
411 cpair.parentP = sp;
412 cpair.parentN = sn;
413 cpair.layer = sp->GetLayer();
414 cpair.coupledArcP.NearestPoints( cpair.coupledArcN, cpair.nearestP, cpair.nearestN,
415 cpair.computedGap );
416 cpair.computedGap = std::sqrt( cpair.computedGap ); // NearestPoints returns squared distance
417 cpair.computedGap -= ( sp->GetWidth() + sn->GetWidth() ) / 2;
418 coupled_vec.push_back( cpair );
419 }
420 }
421 }
422
423 for( const std::optional<DIFF_PAIR_COUPLED_SEGMENTS>& coupled : coupled_vec )
424 {
425 auto excludeSelf =
426 [&] ( BOARD_ITEM *aItem )
427 {
428 if( aItem == coupled->parentN || aItem == coupled->parentP )
429 return false;
430
431 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_ARC_T )
432 {
433 BOARD_CONNECTED_ITEM* bci = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
434
435 if( bci->GetNetCode() == coupled->parentN->GetNetCode()
436 || bci->GetNetCode() == coupled->parentP->GetNetCode() )
437 {
438 return false;
439 }
440 }
441
442 return true;
443 };
444
445 SHAPE_SEGMENT checkArcMid( coupled->coupledArcN.GetArcMid(), coupled->coupledArcP.GetArcMid() );
446 DRC_RTREE* tree = coupled->parentP->GetBoard()->m_CopperItemRTreeCache.get();
447
448 // check if there's anything in between the segments suspected to be coupled. If
449 // there's nothing, assume they are really coupled.
450
451 if( !tree->CheckColliding( &checkArcMid, sp->GetLayer(), 0, excludeSelf ) )
452 aDp.coupled.push_back( *coupled );
453 }
454 }
455}
456
457
458
460{
462
464
465 std::map<DIFF_PAIR_KEY, DIFF_PAIR_ITEMS> dpRuleMatches;
466
467 auto evaluateDpConstraints =
468 [&]( BOARD_ITEM *item ) -> bool
469 {
470 DIFF_PAIR_KEY key;
471 BOARD_CONNECTED_ITEM* citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
472 NETINFO_ITEM* refNet = citem->GetNet();
473
474 if( refNet && DRC_ENGINE::IsNetADiffPair( m_board, refNet, key.netP, key.netN ) )
475 {
476 // drc_dbg( 10, wxT( "eval dp %p\n" ), item );
477
478 for( DRC_CONSTRAINT_T constraintType : { DIFF_PAIR_GAP_CONSTRAINT,
480 {
481 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( constraintType,
482 item, nullptr,
483 item->GetLayer() );
484
485 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
486 continue;
487
488 // drc_dbg( 10, wxT( "cns %d item %p\n" ), (int) constraintType, item );
489
490 DRC_RULE* parentRule = constraint.GetParentRule();
491 wxString ruleName = parentRule ? parentRule->m_Name : constraint.GetName();
492
493 switch( constraintType )
494 {
496 key.gapConstraint = constraint.GetValue();
497 key.gapRule = parentRule;
498 key.gapRuleName = std::move( ruleName );
499 break;
500
502 key.uncoupledConstraint = constraint.GetValue();
503 key.uncoupledRule = parentRule;
504 key.uncoupledRuleName = std::move( ruleName );
505 break;
506
507 default:
508 break;
509 }
510
511 if( refNet->GetNetCode() == key.netN )
512 dpRuleMatches[key].itemsN.insert( citem );
513 else
514 dpRuleMatches[key].itemsP.insert( citem );
515 }
516 }
517
518 return true;
519 };
520
521 m_board->GetConnectivity()->GetFromToCache()->Rebuild( m_board );
522
524 evaluateDpConstraints );
525
526 // drc_dbg( 10, wxT( "dp rule matches %d\n" ), (int) dpRuleMatches.size() );
527
528 REPORT_AUX( wxT( "DPs evaluated:" ) );
529
530 for( auto& [ key, itemSet ] : dpRuleMatches )
531 {
532 NETINFO_ITEM *niP = m_board->GetNetInfo().GetNetItem( key.netP );
533 NETINFO_ITEM *niN = m_board->GetNetInfo().GetNetItem( key.netN );
534
535 assert( niP );
536 assert( niN );
537
538 wxString nameP = niP->GetNetname();
539 wxString nameN = niN->GetNetname();
540
541 REPORT_AUX( wxString::Format( wxT( "Rule '%s', DP: (+) %s - (-) %s" ),
542 key.gapRuleName, nameP, nameN ) );
543
545
546 itemSet.totalCoupled = 0;
547 itemSet.totalLengthN = 0;
548 itemSet.totalLengthP = 0;
549
550 // drc_dbg( 10, wxT( " coupled prims : %d\n" ), (int) itemSet.coupled.size() );
551
552 std::set<BOARD_CONNECTED_ITEM*> allItems;
553
554 for( BOARD_CONNECTED_ITEM* item : itemSet.itemsN )
555 {
556 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
557 {
558 if( allItems.insert( item ).second)
559 itemSet.totalLengthN += track->GetLength();
560 }
561 }
562
563 for( BOARD_CONNECTED_ITEM* item : itemSet.itemsP )
564 {
565 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
566 {
567 if( allItems.insert( item ).second)
568 itemSet.totalLengthP += track->GetLength();
569 }
570 }
571
572 for( DIFF_PAIR_COUPLED_SEGMENTS& dp : itemSet.coupled )
573 {
574 int length = dp.isArc ? dp.coupledArcN.GetLength() : dp.coupledN.Length();
575 wxCHECK2( dp.parentN && dp.parentP, continue );
576
577 std::shared_ptr<KIGFX::VIEW_OVERLAY> overlay = m_drcEngine->GetDebugOverlay();
578
579 if( overlay )
580 {
581 overlay->SetIsFill(false);
582 overlay->SetIsStroke(true);
583 overlay->SetStrokeColor( RED );
584 overlay->SetLineWidth( 100000 );
585 overlay->Line( dp.coupledP );
586 overlay->SetStrokeColor( BLUE );
587 overlay->Line( dp.coupledN );
588 }
589
590 // drc_dbg( 10, wxT( " len %d gap %ld l %d\n" ),
591 // length,
592 // (long int) dp.computedGap,
593 // (int) dp.parentP->GetLayer() );
594
595 if( key.gapConstraint )
596 {
597 if( key.gapConstraint->HasMin()
598 && key.gapConstraint->Min() >= 0
599 && ( dp.computedGap < key.gapConstraint->Min() - epsilon ) )
600 {
601 dp.couplingFailMin = true;
602 }
603
604 if( key.gapConstraint->HasMax()
605 && key.gapConstraint->Max() >= 0
606 && ( dp.computedGap > key.gapConstraint->Max() + epsilon ) )
607 {
608 dp.couplingFailMax = true;
609 }
610 }
611
612 if( !dp.couplingFailMin && !dp.couplingFailMax )
613 itemSet.totalCoupled += length;
614 }
615
616 int totalLen = std::max( itemSet.totalLengthN, itemSet.totalLengthP );
617
618 REPORT_AUX( wxString::Format( wxT( " - coupled length: %s, total length: %s" ),
619 MessageTextFromValue( itemSet.totalCoupled ),
620 MessageTextFromValue( totalLen ) ) );
621
622 int totalUncoupled = totalLen - itemSet.totalCoupled;
623 bool uncoupledViolation = false;
624
625 if( key.uncoupledConstraint && ( !itemSet.itemsP.empty() || !itemSet.itemsN.empty() ) )
626 {
627 const MINOPTMAX<int>& val = *key.uncoupledConstraint;
628
629 if( val.HasMax() && val.Max() >= 0 && totalUncoupled > val.Max() )
630 {
632 wxString msg = formatMsg( _( "(%s maximum uncoupled length %s; actual %s)" ),
633 key.uncoupledRuleName,
634 val.Max(),
635 totalUncoupled );
636
637 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
638
639 BOARD_CONNECTED_ITEM* item = nullptr;
640 auto p_it = itemSet.itemsP.begin();
641 auto n_it = itemSet.itemsN.begin();
642
643 if( p_it != itemSet.itemsP.end() )
644 {
645 item = *p_it;
646 drce->AddItem( *p_it );
647 p_it++;
648 }
649
650 if( n_it != itemSet.itemsN.end() )
651 {
652 item = *n_it;
653 drce->AddItem( *n_it );
654 n_it++;
655 }
656
657 while( p_it != itemSet.itemsP.end() )
658 drce->AddItem( *p_it++ );
659
660 while( n_it != itemSet.itemsN.end() )
661 drce->AddItem( *n_it++ );
662
663 uncoupledViolation = true;
664
665 drce->SetViolatingRule( key.uncoupledRule );
666
667 reportViolation( drce, item->GetPosition(), item->GetLayer() );
668 }
669 }
670
671 if( key.gapConstraint && ( uncoupledViolation || !key.uncoupledConstraint ) )
672 {
673 for( DIFF_PAIR_COUPLED_SEGMENTS& dp : itemSet.coupled )
674 {
675 wxCHECK2( dp.parentP && dp.parentN, continue );
676
677 if( ( dp.couplingFailMin || dp.couplingFailMax ) )
678 {
679 // We have a candidate violation, now we need to re-query for a constraint
680 // given the actual items, because there may be a location-based rule in play.
682 dp.parentP, dp.parentN,
683 dp.parentP->GetLayer() );
684 MINOPTMAX<int> val = constraint.GetValue();
685
686 if( !val.HasMin() || val.Min() < 0 || dp.computedGap >= val.Min() )
687 dp.couplingFailMin = false;
688
689 if( !val.HasMax() || val.Max() < 0 || dp.computedGap <= val.Max() )
690 dp.couplingFailMax = false;
691
692 if( !dp.couplingFailMin && !dp.couplingFailMax )
693 continue;
694
695 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE );
696 wxString msg;
697
698 if( dp.couplingFailMin )
699 {
700 msg = formatMsg( _( "(%s minimum gap %s; actual %s)" ),
701 key.gapRuleName,
702 val.Min(),
703 (double) dp.computedGap );
704 }
705 else if( dp.couplingFailMax )
706 {
707 msg = formatMsg( _( "(%s maximum gap %s; actual %s)" ),
708 key.gapRuleName,
709 val.Max(),
710 (double) dp.computedGap );
711 }
712
713 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
714 drcItem->SetViolatingRule( key.gapRule );
715
716 BOARD_CONNECTED_ITEM* item = nullptr;
717
718 if( dp.parentP )
719 {
720 item = dp.parentP;
721 drcItem->AddItem( dp.parentP );
722 }
723
724 if( dp.parentN )
725 {
726 item = dp.parentN;
727 drcItem->AddItem( dp.parentN );
728 }
729
730 if( item )
731 reportViolation( drcItem, item->GetFocusPosition(), item->GetLayer() );
732 }
733 }
734 }
735 }
736
737 return true;
738}
739
740
741namespace detail
742{
744}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:232
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:922
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1011
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:520
wxString GetName() const
Definition: drc_rule.h:168
SEVERITY GetSeverity() const
Definition: drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:160
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:164
bool IsNull() const
Definition: drc_rule.h:155
BOARD * GetBoard() const
Definition: drc_engine.h:95
std::shared_ptr< KIGFX::VIEW_OVERLAY > GetDebugOverlay() const
Definition: drc_engine.h:111
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:706
static bool IsNetADiffPair(BOARD *aBoard, NETINFO_ITEM *aNet, int &aNetP, int &aNetN)
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:393
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:48
bool CheckColliding(SHAPE *aRefShape, PCB_LAYER_ID aTargetLayer, int aClearance=0, std::function< bool(BOARD_ITEM *)> aFilter=nullptr) const
Definition: drc_rtree.h:175
wxString m_Name
Definition: drc_rule.h:120
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out DRC_ITEM and posi...
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
DRC_ENGINE * m_drcEngine
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
EDA_ANGLE Normalize()
Definition: eda_angle.h:226
double Sin() const
Definition: eda_angle.h:175
double Cos() const
Definition: eda_angle.h:194
A set of EDA_ITEMs (i.e., without duplicates).
Definition: eda_group.h:46
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:259
virtual const VECTOR2I GetFocusPosition() const
Similar to GetPosition() but allows items to return their visual center rather than their anchor.
Definition: eda_item.h:266
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:109
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:582
T Min() const
Definition: minoptmax.h:33
bool HasMax() const
Definition: minoptmax.h:38
bool HasMin() const
Definition: minoptmax.h:37
T Max() const
Definition: minoptmax.h:34
Handle the data for a net.
Definition: netinfo.h:56
const wxString & GetNetname() const
Definition: netinfo.h:114
int GetNetCode() const
Definition: netinfo.h:108
NETINFO_ITEM * GetNetItem(int aNetCode) const
bool IsCCW() const
Definition: pcb_track.cpp:855
virtual double GetLength() const override
Return the length of the arc track.
Definition: pcb_track.h:378
double GetRadius() const
Definition: pcb_track.cpp:2132
const VECTOR2I & GetMid() const
Definition: pcb_track.h:344
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:351
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:1243
const VECTOR2I & GetStart() const
Definition: pcb_track.h:152
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: pcb_track.cpp:1967
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:149
virtual int GetWidth() const
Definition: pcb_track.h:146
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
int Length() const
Return the length (this).
Definition: seg.h:333
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition: seg.cpp:259
ecoord TCoef(const VECTOR2I &aP) const
Definition: seg.h:395
ecoord SquaredLength() const
Definition: seg.h:338
bool NearestPoints(const SEG &aSeg, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this segment and aSeg.
Definition: seg.cpp:155
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:422
EDA_ANGLE GetEndAngle() const
Definition: shape_arc.cpp:841
double GetLength() const
Definition: shape_arc.cpp:855
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter) override
Rotate the arc by a given angle about a point.
Definition: shape_arc.cpp:974
EDA_ANGLE GetStartAngle() const
Definition: shape_arc.cpp:833
bool NearestPoints(const SHAPE_ARC &aArc, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this arc and aArc.
Definition: shape_arc.cpp:629
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition: vector2d.h:569
virtual ~DRC_TEST_PROVIDER_DIFF_PAIR_COUPLING()=default
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
@ BLUE
Definition: color4d.h:56
@ RED
Definition: color4d.h:59
@ DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE
Definition: drc_item.h:106
@ DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG
Definition: drc_item.h:107
DRC_CONSTRAINT_T
Definition: drc_rule.h:47
@ DIFF_PAIR_GAP_CONSTRAINT
Definition: drc_rule.h:73
@ MAX_UNCOUPLED_CONSTRAINT
Definition: drc_rule.h:74
#define REPORT_AUX(s)
static bool commonParallelProjection(SEG p, SEG n, SEG &pClip, SEG &nClip)
static void extractDiffPairCoupledItems(DIFF_PAIR_ITEMS &aDp)
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:395
std::shared_ptr< PNS_LOG_VIEWER_OVERLAY > overlay
Definition: playground.cpp:46
@ RPT_SEVERITY_IGNORE
const double epsilon
std::vector< FAB_LAYER_COLOR > dummy
std::set< BOARD_CONNECTED_ITEM * > itemsN
std::vector< DIFF_PAIR_COUPLED_SEGMENTS > coupled
std::set< BOARD_CONNECTED_ITEM * > itemsP
std::optional< MINOPTMAX< int > > uncoupledConstraint
std::optional< MINOPTMAX< int > > gapConstraint
bool operator<(const DIFF_PAIR_KEY &b) const
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition: util.h:153
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695