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 + VECTOR2I( KiROUND( p_radius * clip_start_angle.Cos() ),
181 KiROUND( p_radius * clip_start_angle.Sin() ) );
182 pClip = SHAPE_ARC( p_center, p_clip_point, clip_total_angle );
183 }
184 else
185 {
186 p_clip_point = p_center + VECTOR2I( KiROUND( p_radius * clip_end_angle.Cos() ),
187 KiROUND( p_radius * clip_end_angle.Sin() ) );
188 pClip = SHAPE_ARC( p_center, p_clip_point, -clip_total_angle );
189 }
190
191 if( n_is_ccw )
192 {
193 n_clip_point = n_center + VECTOR2I( KiROUND( n_radius * clip_start_angle.Cos() ),
194 KiROUND( n_radius * clip_start_angle.Sin() ) );
195 nClip = SHAPE_ARC( n_center, n_clip_point, clip_total_angle );
196 }
197 else
198 {
199 n_clip_point = n_center + VECTOR2I( KiROUND( n_radius * clip_end_angle.Cos() ),
200 KiROUND( n_radius * clip_end_angle.Sin() ) );
201 nClip = SHAPE_ARC( n_center, n_clip_point, -clip_total_angle );
202 }
203
204 // Ensure the resulting arcs are not degenerate
205 if( pClip.GetLength() < 1.0 || nClip.GetLength() < 1.0 )
206 return false;
207
208 return true;
209}
210
211
213{
214 bool operator<( const DIFF_PAIR_KEY& b ) const
215 {
216 if( netP < b.netP )
217 {
218 return true;
219 }
220 else if( netP > b.netP )
221 {
222 return false;
223 }
224 else // netP == b.netP
225 {
226 if( netN < b.netN )
227 return true;
228 else if( netN > b.netN )
229 return false;
230 else if( gapRuleName.IsEmpty() )
231 return gapRuleName < b.gapRuleName;
232 else
234 }
235 }
236
237 int netP, netN;
238 wxString gapRuleName;
240 std::optional<MINOPTMAX<int>> gapConstraint;
242 std::optional<MINOPTMAX<int>> uncoupledConstraint;
244};
245
272
273
275{
276 std::set<BOARD_CONNECTED_ITEM*> itemsP, itemsN;
277 std::vector<DIFF_PAIR_COUPLED_SEGMENTS> coupled;
281};
282
283
285{
286 auto isInTuningPattern =
287 []( BOARD_ITEM* track )
288 {
289 if( EDA_GROUP* parent = track->GetParentGroup() )
290 {
291 if( PCB_GENERATOR* generator = dynamic_cast<PCB_GENERATOR*>( parent ) )
292 {
293 if( generator->GetGeneratorType() == wxS( "tuning_pattern" ) )
294 return true;
295 }
296 }
297
298 return false;
299 };
300
301 for( BOARD_CONNECTED_ITEM* itemP : aDp.itemsP )
302 {
303 if( !itemP || itemP->Type() != PCB_TRACE_T || isInTuningPattern( itemP ) )
304 continue;
305
306 PCB_TRACK* sp = static_cast<PCB_TRACK*>( itemP );
307 std::vector<std::optional<DIFF_PAIR_COUPLED_SEGMENTS>> coupled_vec;
308
309 for( BOARD_CONNECTED_ITEM* itemN : aDp.itemsN )
310 {
311 if( !itemN || itemN->Type() != PCB_TRACE_T || isInTuningPattern( itemN ) )
312 continue;
313
314 PCB_TRACK* sn = static_cast<PCB_TRACK*>( itemN );
315
316 if( ( sn->GetLayerSet() & sp->GetLayerSet() ).none() )
317 continue;
318
319 SEG ssp( sp->GetStart(), sp->GetEnd() );
320 SEG ssn( sn->GetStart(), sn->GetEnd() );
321
322 // Segments that are ~ 1 IU in length per side are approximately parallel (tolerance is 1 IU)
323 // with everything and their parallel projection is < 1 IU, leading to bad distance calculations
324 if( ssp.SquaredLength() > 2 && ssn.SquaredLength() > 2 && !ssp.Intersect( ssn, false, true ) )
325 {
327 bool coupled = commonParallelProjection( ssp, ssn, cpair.coupledP, cpair.coupledN );
328
329 if( coupled )
330 {
331 cpair.parentP = sp;
332 cpair.parentN = sn;
333 cpair.layer = sp->GetLayer();
334 cpair.coupledP.NearestPoints( cpair.coupledN, cpair.nearestP, cpair.nearestN,
335 cpair.computedGap );
336 cpair.computedGap = std::sqrt( cpair.computedGap ); // NearestPoints returns squared distance
337 cpair.computedGap -= ( sp->GetWidth() + sn->GetWidth() ) / 2;
338 coupled_vec.push_back( cpair );
339 }
340
341 }
342 }
343
344 for( const std::optional<DIFF_PAIR_COUPLED_SEGMENTS>& coupled : coupled_vec )
345 {
346 auto excludeSelf =
347 [&]( BOARD_ITEM* aItem )
348 {
349 if( aItem == coupled->parentN || aItem == coupled->parentP )
350 return false;
351
352 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_ARC_T )
353 {
354 PCB_TRACK* bci = static_cast<PCB_TRACK*>( aItem );
355
356 // Directly connected items don't count
357 if( bci->HitTest( coupled->coupledN.A, 0 )
358 || bci->HitTest( coupled->coupledN.B, 0 )
359 || bci->HitTest( coupled->coupledP.A, 0 )
360 || bci->HitTest( coupled->coupledP.B, 0 ) )
361 {
362 return false;
363 }
364 }
365 else if( aItem->Type() == PCB_PAD_T )
366 {
367 PAD* pad = static_cast<PAD*>( aItem );
368
369 auto trackExitsPad = [&]( PCB_TRACK* track )
370 {
371 bool startIn = pad->HitTest( track->GetStart(), 0 );
372 bool endIn = pad->HitTest( track->GetEnd(), 0 );
373
374 return startIn ^ endIn;
375 };
376
377 if( trackExitsPad( static_cast<PCB_TRACK*>( coupled->parentP ) )
378 || trackExitsPad( static_cast<PCB_TRACK*>( coupled->parentN ) ) )
379 {
380 return false;
381 }
382 }
383
384 return true;
385 };
386
387 SHAPE_SEGMENT checkSeg( coupled->nearestN, coupled->nearestP );
388 DRC_RTREE* tree = coupled->parentP->GetBoard()->m_CopperItemRTreeCache.get();
389
390 // check if there's anything in between the segments suspected to be coupled. If
391 // there's nothing, assume they are really coupled.
392
393 if( !tree->CheckColliding( &checkSeg, sp->GetLayer(), 0, excludeSelf ) )
394 aDp.coupled.push_back( *coupled );
395 }
396 }
397
398 for( BOARD_CONNECTED_ITEM* itemP : aDp.itemsP )
399 {
400 if( !itemP || itemP->Type() != PCB_ARC_T || isInTuningPattern( itemP ) )
401 continue;
402
403 PCB_ARC* sp = static_cast<PCB_ARC*>( itemP );
404 std::vector<std::optional<DIFF_PAIR_COUPLED_SEGMENTS>> coupled_vec;
405
406 for( BOARD_CONNECTED_ITEM* itemN : aDp.itemsN )
407 {
408 if( !itemN || itemN->Type() != PCB_ARC_T || isInTuningPattern( itemN ) )
409 continue;
410
411 PCB_ARC* sn = static_cast<PCB_ARC*>( itemN );
412
413 if( ( sn->GetLayerSet() & sp->GetLayerSet() ).none() )
414 continue;
415
416 // Segments that are ~ 1 IU in length per side are approximately parallel (tolerance is 1 IU)
417 // with everything and their parallel projection is < 1 IU, leading to bad distance calculations
418 int64_t sqWidth = static_cast<int64_t>( sp->GetWidth() ) * sp->GetWidth();
419
420 if( sp->GetLength() > 2
421 && sn->GetLength() > 2
422 && sp->GetCenter().SquaredDistance( sn->GetCenter() ) < sqWidth )
423 {
425 cpair.isArc = true;
426 bool coupled = commonParallelProjection( *sp, *sn, cpair.coupledArcP, cpair.coupledArcN );
427
428 if( coupled )
429 {
430 cpair.parentP = sp;
431 cpair.parentN = sn;
432 cpair.layer = sp->GetLayer();
433 cpair.coupledArcP.NearestPoints( cpair.coupledArcN, cpair.nearestP, cpair.nearestN,
434 cpair.computedGap );
435 cpair.computedGap = std::sqrt( cpair.computedGap ); // NearestPoints returns squared distance
436 cpair.computedGap -= ( sp->GetWidth() + sn->GetWidth() ) / 2;
437 coupled_vec.push_back( cpair );
438 }
439 }
440 }
441
442 for( const std::optional<DIFF_PAIR_COUPLED_SEGMENTS>& coupled : coupled_vec )
443 {
444 auto excludeSelf =
445 [&] ( BOARD_ITEM *aItem )
446 {
447 if( aItem == coupled->parentN || aItem == coupled->parentP )
448 return false;
449
450 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_ARC_T )
451 {
452 BOARD_CONNECTED_ITEM* bci = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
453
454 if( bci->GetNetCode() == coupled->parentN->GetNetCode()
455 || bci->GetNetCode() == coupled->parentP->GetNetCode() )
456 {
457 return false;
458 }
459 }
460 else if( aItem->Type() == PCB_PAD_T )
461 {
462 PAD* pad = static_cast<PAD*>( aItem );
463
464 auto arcExitsPad = [&]( PCB_ARC* arc )
465 {
466 bool startIn = pad->HitTest( arc->GetStart(), 0 );
467 bool endIn = pad->HitTest( arc->GetEnd(), 0 );
468
469 return startIn ^ endIn;
470 };
471
472 if( arcExitsPad( static_cast<PCB_ARC*>( coupled->parentP ) )
473 || arcExitsPad( static_cast<PCB_ARC*>( coupled->parentN ) ) )
474 {
475 return false;
476 }
477 }
478
479 return true;
480 };
481
482 SHAPE_SEGMENT checkArcMid( coupled->coupledArcN.GetArcMid(), coupled->coupledArcP.GetArcMid() );
483 DRC_RTREE* tree = coupled->parentP->GetBoard()->m_CopperItemRTreeCache.get();
484
485 // check if there's anything in between the segments suspected to be coupled. If
486 // there's nothing, assume they are really coupled.
487
488 if( !tree->CheckColliding( &checkArcMid, sp->GetLayer(), 0, excludeSelf ) )
489 aDp.coupled.push_back( *coupled );
490 }
491 }
492}
493
494
495
497{
498 m_board = m_drcEngine->GetBoard();
499
500 int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
501 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
502
503 std::map<DIFF_PAIR_KEY, DIFF_PAIR_ITEMS> dpRuleMatches;
504
505 auto evaluateDpConstraints =
506 [&]( BOARD_ITEM *item ) -> bool
507 {
508 DIFF_PAIR_KEY key;
509 BOARD_CONNECTED_ITEM* citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
510 NETINFO_ITEM* refNet = citem->GetNet();
511
512 if( refNet && DRC_ENGINE::IsNetADiffPair( m_board, refNet, key.netP, key.netN ) )
513 {
514 // drc_dbg( 10, wxT( "eval dp %p\n" ), item );
515
516 for( DRC_CONSTRAINT_T constraintType : { DIFF_PAIR_GAP_CONSTRAINT,
518 {
519 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( constraintType,
520 item, nullptr,
521 item->GetLayer() );
522
523 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
524 continue;
525
526 // drc_dbg( 10, wxT( "cns %d item %p\n" ), (int) constraintType, item );
527
528 DRC_RULE* parentRule = constraint.GetParentRule();
529 wxString ruleName = parentRule ? parentRule->m_Name : constraint.GetName();
530
531 switch( constraintType )
532 {
534 key.gapConstraint = constraint.GetValue();
535 key.gapRule = parentRule;
536 key.gapRuleName = std::move( ruleName );
537 break;
538
540 key.uncoupledConstraint = constraint.GetValue();
541 key.uncoupledRule = parentRule;
542 key.uncoupledRuleName = std::move( ruleName );
543 break;
544
545 default:
546 break;
547 }
548
549 if( refNet->GetNetCode() == key.netN )
550 dpRuleMatches[key].itemsN.insert( citem );
551 else
552 dpRuleMatches[key].itemsP.insert( citem );
553 }
554 }
555
556 return true;
557 };
558
559 m_board->GetConnectivity()->GetFromToCache()->Rebuild( m_board );
560
561 forEachGeometryItem( { PCB_TRACE_T, PCB_VIA_T, PCB_ARC_T }, boardCopperLayers, evaluateDpConstraints );
562
563 // drc_dbg( 10, wxT( "dp rule matches %d\n" ), (int) dpRuleMatches.size() );
564
565 REPORT_AUX( wxT( "DPs evaluated:" ) );
566
567 for( auto& [ key, itemSet ] : dpRuleMatches )
568 {
569 NETINFO_ITEM *niP = m_board->GetNetInfo().GetNetItem( key.netP );
570 NETINFO_ITEM *niN = m_board->GetNetInfo().GetNetItem( key.netN );
571
572 assert( niP );
573 assert( niN );
574
575 wxString nameP = niP->GetNetname();
576 wxString nameN = niN->GetNetname();
577
578 REPORT_AUX( wxString::Format( wxT( "Rule '%s', DP: (+) %s - (-) %s" ),
579 key.gapRuleName, nameP, nameN ) );
580
582
583 itemSet.totalCoupled = 0;
584 itemSet.totalLengthN = 0;
585 itemSet.totalLengthP = 0;
586
587 // drc_dbg( 10, wxT( " coupled prims : %d\n" ), (int) itemSet.coupled.size() );
588
589 std::set<BOARD_CONNECTED_ITEM*> allItems;
590
591 for( BOARD_CONNECTED_ITEM* item : itemSet.itemsN )
592 {
593 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
594 {
595 if( allItems.insert( item ).second)
596 itemSet.totalLengthN += track->GetLength();
597 }
598 }
599
600 for( BOARD_CONNECTED_ITEM* item : itemSet.itemsP )
601 {
602 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
603 {
604 if( allItems.insert( item ).second)
605 itemSet.totalLengthP += track->GetLength();
606 }
607 }
608
609 for( DIFF_PAIR_COUPLED_SEGMENTS& dp : itemSet.coupled )
610 {
611 int length = dp.isArc ? dp.coupledArcN.GetLength() : dp.coupledN.Length();
612 wxCHECK2( dp.parentN && dp.parentP, continue );
613
614 std::shared_ptr<KIGFX::VIEW_OVERLAY> overlay = m_drcEngine->GetDebugOverlay();
615
616 if( overlay )
617 {
618 overlay->SetIsFill(false);
619 overlay->SetIsStroke(true);
620 overlay->SetStrokeColor( RED );
621 overlay->SetLineWidth( 100000 );
622 overlay->Line( dp.coupledP );
623 overlay->SetStrokeColor( BLUE );
624 overlay->Line( dp.coupledN );
625 }
626
627 // drc_dbg( 10, wxT( " len %d gap %ld l %d\n" ),
628 // length,
629 // (long int) dp.computedGap,
630 // (int) dp.parentP->GetLayer() );
631
632 if( key.gapConstraint )
633 {
634 if( key.gapConstraint->HasMin()
635 && key.gapConstraint->Min() >= 0
636 && ( dp.computedGap < key.gapConstraint->Min() - epsilon ) )
637 {
638 dp.couplingFailMin = true;
639 }
640
641 if( key.gapConstraint->HasMax()
642 && key.gapConstraint->Max() >= 0
643 && ( dp.computedGap > key.gapConstraint->Max() + epsilon ) )
644 {
645 dp.couplingFailMax = true;
646 }
647 }
648
649 if( !dp.couplingFailMin && !dp.couplingFailMax )
650 itemSet.totalCoupled += length;
651 }
652
653 int totalLen = std::max( itemSet.totalLengthN, itemSet.totalLengthP );
654
655 REPORT_AUX( wxString::Format( wxT( " - coupled length: %s, total length: %s" ),
656 MessageTextFromValue( itemSet.totalCoupled ),
657 MessageTextFromValue( totalLen ) ) );
658
659 int totalUncoupled = totalLen - itemSet.totalCoupled;
660 bool uncoupledViolation = false;
661
662 if( key.uncoupledConstraint && ( !itemSet.itemsP.empty() || !itemSet.itemsN.empty() ) )
663 {
664 const MINOPTMAX<int>& val = *key.uncoupledConstraint;
665
666 if( val.HasMax() && val.Max() >= 0 && totalUncoupled > val.Max() )
667 {
669 wxString msg = formatMsg( _( "(%s maximum uncoupled length %s; actual %s)" ),
670 key.uncoupledRuleName,
671 val.Max(),
672 totalUncoupled );
673
674 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
675
676 BOARD_CONNECTED_ITEM* item = nullptr;
677 auto p_it = itemSet.itemsP.begin();
678 auto n_it = itemSet.itemsN.begin();
679
680 if( p_it != itemSet.itemsP.end() )
681 {
682 item = *p_it;
683 drce->AddItem( *p_it );
684 p_it++;
685 }
686
687 if( n_it != itemSet.itemsN.end() )
688 {
689 item = *n_it;
690 drce->AddItem( *n_it );
691 n_it++;
692 }
693
694 while( p_it != itemSet.itemsP.end() )
695 drce->AddItem( *p_it++ );
696
697 while( n_it != itemSet.itemsN.end() )
698 drce->AddItem( *n_it++ );
699
700 uncoupledViolation = true;
701
702 drce->SetViolatingRule( key.uncoupledRule );
703
704 reportViolation( drce, item->GetPosition(), item->GetLayer() );
705 }
706 }
707
708 if( key.gapConstraint && ( uncoupledViolation || !key.uncoupledConstraint ) )
709 {
710 for( DIFF_PAIR_COUPLED_SEGMENTS& dp : itemSet.coupled )
711 {
712 wxCHECK2( dp.parentP && dp.parentN, continue );
713
714 if( ( dp.couplingFailMin || dp.couplingFailMax ) )
715 {
716 // We have a candidate violation, now we need to re-query for a constraint
717 // given the actual items, because there may be a location-based rule in play.
718 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT,
719 dp.parentP, dp.parentN,
720 dp.parentP->GetLayer() );
721 MINOPTMAX<int> val = constraint.GetValue();
722
723 if( !val.HasMin() || val.Min() < 0 || dp.computedGap >= val.Min() )
724 dp.couplingFailMin = false;
725
726 if( !val.HasMax() || val.Max() < 0 || dp.computedGap <= val.Max() )
727 dp.couplingFailMax = false;
728
729 if( !dp.couplingFailMin && !dp.couplingFailMax )
730 continue;
731
732 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE );
733 wxString msg;
734
735 if( dp.couplingFailMin )
736 {
737 msg = formatMsg( _( "(%s minimum gap %s; actual %s)" ),
738 key.gapRuleName,
739 val.Min(),
740 (double) dp.computedGap );
741 }
742 else if( dp.couplingFailMax )
743 {
744 msg = formatMsg( _( "(%s maximum gap %s; actual %s)" ),
745 key.gapRuleName,
746 val.Max(),
747 (double) dp.computedGap );
748 }
749
750 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
751 drcItem->SetViolatingRule( key.gapRule );
752
753 BOARD_CONNECTED_ITEM* item = nullptr;
754
755 if( dp.parentP )
756 {
757 item = dp.parentP;
758 drcItem->AddItem( dp.parentP );
759 }
760
761 if( dp.parentN )
762 {
763 item = dp.parentN;
764 drcItem->AddItem( dp.parentN );
765 }
766
767 if( item )
768 reportViolation( drcItem, item->GetFocusPosition(), item->GetLayer() );
769 }
770 }
771 }
772 }
773
774 return true;
775}
776
777
778namespace detail
779{
781}
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: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
wxString GetName() const
Definition drc_rule.h:170
SEVERITY GetSeverity() const
Definition drc_rule.h:183
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:162
DRC_RULE * GetParentRule() const
Definition drc_rule.h:166
bool IsNull() const
Definition drc_rule.h:157
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:381
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:122
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::vector< PCB_SHAPE > &aShapes={})
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:54
bool IsCCW() const
virtual double GetLength() const override
Return the length of the arc track.
Definition pcb_track.h:379
double GetRadius() const
const VECTOR2I & GetMid() const
Definition pcb_track.h:345
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:352
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:152
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: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: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:656
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: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
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