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