KiCad PCB EDA Suite
Loading...
Searching...
No Matches
diff_phase_skew_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author James Jackson
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
26
27#include <router/pns_arc.h>
29#include <router/pns_helpers.h>
31#include <router/pns_router.h>
32#include <router/pns_topology.h>
33
34#include <advanced_config.h>
35#include <board.h>
36#include <collectors.h>
38#include <drc/drc_engine.h>
39#include <gal/painter.h>
41#include <pcb_edit_frame.h>
42#include <pad.h>
43#include <pcb_track.h>
46#include <tools/drc_tool.h>
47#include <tools/pcb_actions.h>
49#include <tool/tool_manager.h>
50#include <view/view.h>
51#include <view/view_controls.h>
52
53
54#define INITIAL_HOVER_HITTEST_THRESHOLD_PIXELS 5
55#define DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS 20
56
57
68
69
73
74
76{
77 return true;
78}
79
80
82{
83 delete m_router;
84 delete m_iface; // Delete after m_router because PNS::NODE dtor needs m_ruleResolver
85
86 if( aReason == RESET_REASON::SHUTDOWN )
87 {
88 m_router = nullptr;
89 m_iface = nullptr;
90 return;
91 }
92
93 // Get core objects
94 m_view = getView();
97 m_frame = frame();
98 DRC_TOOL* drcTool = m_toolMgr->GetTool<DRC_TOOL>();
99 m_drcEngine = drcTool->GetDRCEngine();
100
101 // Initialise a router instance
103 m_iface->SetBoard( m_board );
104 m_iface->SetView( m_view );
105 m_iface->SetHostTool( this );
106
107 m_router = new PNS::ROUTER;
108 m_router->SetInterface( m_iface );
109 m_router->ClearWorld();
110 m_router->SyncWorld();
111 m_router->UpdateSizes( m_savedSizes );
112
113 PCBNEW_SETTINGS* settings = m_frame->GetPcbNewSettings();
114
115 if( !settings->m_PnsSettings )
116 settings->m_PnsSettings = std::make_unique<PNS::ROUTING_SETTINGS>( settings, "tools.pns" );
117
118 m_router->LoadSettings( settings->m_PnsSettings.get() );
119
121}
122
123
125{
127 return 0;
128
130
131 TOOL_EVENT pushedEvent = aEvent;
132 m_frame->PushTool( aEvent );
133 Activate();
134
135 // Must be done after Activate() so that it gets set into the correct context
137 // controls->ShowCursor( true );
138 // controls->ForceCursorPosition( false );
139
140 // Set initial cursor
141 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TUNE );
142
143 // Get the required tools and helpers
144 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
145 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
146
147 // Create the VIEW_OVERLAY
148 getOverlay();
149
150 m_pickerItemFirst = nullptr;
151
152 if( aEvent.HasPosition() )
153 m_toolMgr->PrimeTool( aEvent.Position() );
154
155 // Main loop: keep receiving events
156 while( TOOL_EVENT* evt = Wait() )
157 {
158 m_cursorPos = controls->GetMousePosition();
159
160 if( evt->IsCancelInteractive() || evt->IsActivate() )
161 {
162 // Roll back our mode, or exit if we are in the initial hover mode
164 {
165 m_pickerItemFirst = nullptr;
167 m_pickerItemSecond = nullptr;
168 clearOverlay();
170 m_maxSkew.reset();
172 }
173 else
174 {
176 break;
177 }
178 }
179
180 if( evt->IsMotion() )
181 {
182 if( GetMode() == MODE::HOVER )
183 {
184 m_pickerItemFirst = nullptr;
185 m_pickerItemSecond = nullptr;
187 doInitialHover( selectionTool, guide );
189 }
190 else if( GetMode() == MODE::SELECTED_FIRST )
191 {
192 m_pickerItemSecond = nullptr;
193 doInitialHover( selectionTool, guide );
195 }
196 else if( GetMode() == MODE::FIXED )
197 {
200 }
201 }
202 else if( evt->IsClick( BUT_LEFT ) && GetMode() == MODE::HOVER && m_pickerItemFirst )
203 {
205
207 {
210 }
211 else
212 {
214 }
215
217 }
218 else if( evt->IsClick( BUT_LEFT ) && GetMode() == MODE::SELECTED_FIRST && m_pickerItemSecond )
219 {
220 // First click to select the diff pair for inspection
225 }
226 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
227 {
229 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
230 settings = cfg->m_DiffPhaseSkewSettings;
231
233
234 if( dlg.ShowModal() == wxID_OK )
235 {
236 cfg->m_DiffPhaseSkewSettings = settings;
237
238 if( GetMode() == MODE::FIXED )
240 }
241 }
242 }
243
244 // Restore UI state
245 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
246
247 // Reset tool state
250
251 updateNetHighlights( false );
252 m_frame->GetCanvas()->Refresh();
253
254 // Done
255 m_frame->PopTool( aEvent );
256
257 return 0;
258}
259
260
262{
263 GENERAL_COLLECTOR collector;
265
266 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
267 aGuide.SetIncludeSecondary( false );
268 else
269 aGuide.SetIncludeSecondary( true );
270
271 aGuide.SetPreferredLayer( m_frame->GetActiveLayer() );
272 collector.Collect( m_board, { PCB_TRACE_T, PCB_ARC_T }, m_cursorPos, aGuide );
273
274 if( collector.GetCount() > 1 )
275 aSelectionTool->GuessSelectionCandidates( collector, m_cursorPos );
276
277 if( collector.GetCount() > 0 )
278 {
279 double min_dist_sq = std::numeric_limits<double>::max();
280
281 for( EDA_ITEM* candidate : collector )
282 {
283 VECTOR2I candidatePos;
284
285 if( candidate->Type() == PCB_TRACE_T )
286 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
287 else if( candidate->Type() == PCB_ARC_T )
288 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
289
290 const double dist_sq = ( m_cursorPos - candidatePos ).SquaredEuclideanNorm();
291
292 if( dist_sq < min_dist_sq )
293 {
294 const auto bci = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
295 const NETINFO_ITEM* candidateNet = bci->GetNet();
296
297 if( GetMode() == MODE::HOVER )
298 {
299 min_dist_sq = dist_sq;
300
301 // We only accept diff pairs in initial hover mode
302 const bool isDiffPairItem =
303 m_drcEngine->IsNetADiffPair( m_board, candidateNet, m_netcodeP, m_netcodeN );
304 m_pickerItemFirst = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
305
306 if( isDiffPairItem )
307 {
309 }
310 else
311 {
313 m_netcodeP = candidateNet->GetNetCode();
314 }
315 }
316 else if( GetMode() == MODE::SELECTED_FIRST )
317 {
318 int fakeNCP, fakeNCN;
319
320 // Reject diff pairs here as we have a not-diff-pair selected
321 const bool isDiffPairItem = m_drcEngine->IsNetADiffPair( m_board, candidateNet, fakeNCP, fakeNCN );
322
323 auto existingBci = static_cast<BOARD_CONNECTED_ITEM*>( m_pickerItemFirst );
324 const NETINFO_ITEM* existingNet = existingBci->GetNet();
325
326 if( !isDiffPairItem && candidateNet != existingNet )
327 {
328 min_dist_sq = dist_sq;
329 m_pickerItemSecond = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
330 m_netcodeN = candidateNet->GetNetCode();
331 }
332 }
333 }
334 }
335 }
336
338}
339
340
342 const PNS::SOLID* aEndPad, const NETINFO_ITEM* aNet,
343 std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
344 LENGTH_DELAY_ITEM_DETAILS& aItemDetails ) const
345{
346 if( aPath.Size() == 0 )
347 return;
348
349 // Convert path to length / delay interface types
350 aItems = m_iface->GetLengthDelayCalculationItems( aPath, aNet->GetNetClass() );
351 wxASSERT( aItems.size() == static_cast<size_t>( aPath.Size() ) );
352
353 // The router returns compound lines - we need to split them in to their constituent segments / arcs
354 splitLengthItems( aItems );
355
356 // Get the per-item length / delay statistics
357 constexpr PATH_OPTIMISATIONS opts = {
358 .OptimiseVias = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
359 };
360
361 const PAD* startPad = dynamic_cast<PAD*>( aStartPad->BoardItem() );
362 const PAD* endPad = dynamic_cast<PAD*>( aEndPad->BoardItem() );
363 aItemDetails = LENGTH_DELAY_ITEM_DETAILS{};
364 m_board->GetLengthCalculation()->CalculateLengthDetails(
365 aItems, opts, startPad, endPad, LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
367 wxASSERT( aItemDetails.LengthsAndDelays.size() == aItems.size() );
368}
369
370
372{
373 // Extract the diff pair net paths
374 getNetPaths();
375
376 // Determine what we are defining as the 'start' of the net
378
379 if( reportValidityErrors( direction ) )
380 {
382 return;
383 }
384
385 m_timeDomain = false;
386
387 // Check if both nets have time domain parameters
388 if( m_selectedNetinfo->GetNetClass()->HasTuningProfile() && m_coupledNetinfo->GetNetClass()->HasTuningProfile() )
389 {
390 wxString selectedTuningProfileName = m_selectedNetinfo->GetNetClass()->GetTuningProfile();
391 wxString coupledTuningProfileName = m_coupledNetinfo->GetNetClass()->GetTuningProfile();
392
393 std::shared_ptr<TUNING_PROFILES> tuningParams = m_frame->Prj().GetProjectFile().TuningProfileParameters();
394 const TUNING_PROFILE& selectedTuningProfile = tuningParams->GetTuningProfile( selectedTuningProfileName );
395 const TUNING_PROFILE& coupledTuningProfile = tuningParams->GetTuningProfile( coupledTuningProfileName );
396
397 if( selectedTuningProfile.m_EnableTimeDomainTuning && coupledTuningProfile.m_EnableTimeDomainTuning )
398 m_timeDomain = true;
399 }
400
401 // Construct the length / delay calculation items
404
407
408 // Build the cumulative length / delay structures
409 const std::vector<CUMULATIVE_ENTRY> selectedCumulative =
412 const std::vector<CUMULATIVE_ENTRY> coupledCumulative =
415
416 // Walk the two tracks and construct the localised phase differences
417 const std::vector<PARALLEL_RUN> parallelRuns = findParallelRuns( selectedCumulative, coupledCumulative );
418
419 m_maxSkew.reset();
420 m_selectedDiffs = buildDiffOverlaySegments( selectedCumulative, m_selectedLengthDelayDetails, parallelRuns,
422 m_coupledDiffs = buildDiffOverlaySegments( coupledCumulative, m_coupledLengthDelayDetails, parallelRuns,
424
425 // Finally draw the overlay
427}
428
429
430std::vector<KNOWN_RELATIVE_POINT> DIFF_PHASE_SKEW_TOOL::buildKnownRelativePoints(
431 const std::vector<CUMULATIVE_ENTRY>& aSegments, const LENGTH_DELAY_ITEM_DETAILS& aSourceItemDetails,
432 const std::vector<PARALLEL_RUN>& aKnownRuns, const bool isCoupledTrack ) const
433{
434 std::vector<KNOWN_RELATIVE_POINT> pts;
435 pts.reserve( aKnownRuns.size() * 2 );
436
437 for( const auto& r : aKnownRuns )
438 {
439 const std::size_t segIdx = isCoupledTrack ? r.segB : r.segA;
440 const double segStart = segIdx == 0 ? 0.0 : static_cast<double>( aSegments[segIdx - 1].m_Length );
441 const double segLen = static_cast<double>( aSourceItemDetails.LengthsAndDelays[segIdx].first );
442
443 double s0;
444 double s1;
445
446 if( isCoupledTrack )
447 {
448 s0 = segStart + r.tb0 * segLen;
449 s1 = segStart + r.tb1 * segLen;
450 }
451 else
452 {
453 s0 = segStart + r.ta0 * segLen;
454 s1 = segStart + r.ta1 * segLen;
455 }
456
457 const double startPadsLengthDiff =
458 isCoupledTrack ? m_coupledStartEndDetails.StartPadLength - m_selectedStartEndDetails.StartPadLength
459 : m_selectedStartEndDetails.StartPadLength - m_coupledStartEndDetails.StartPadLength;
460 const double startPadsDelayDiff =
461 isCoupledTrack ? m_coupledStartEndDetails.StartPadDelay - m_selectedStartEndDetails.StartPadDelay
462 : m_selectedStartEndDetails.StartPadDelay - m_coupledStartEndDetails.StartPadDelay;
463
464 double startLen = isCoupledTrack ? r.startLenB - r.startLenA : r.startLenA - r.startLenB;
465 startLen += startPadsLengthDiff;
466
467 double endLen = isCoupledTrack ? r.endLenB - r.endLenA : r.endLenA - r.endLenB;
468 endLen += startPadsLengthDiff;
469
470 double startDelay = isCoupledTrack ? r.startDelayB - r.startDelayA : r.startDelayA - r.startDelayB;
471 startDelay += startPadsDelayDiff;
472
473 double endDelay = isCoupledTrack ? r.endDelayB - r.endDelayA : r.endDelayA - r.endDelayB;
474 endDelay += startPadsDelayDiff;
475
476 pts.push_back( { s0, startLen, startDelay } );
477 pts.push_back( { s1, endLen, endDelay } );
478 }
479
480 std::ranges::sort( pts,
481 []( const auto& a, const auto& b )
482 {
483 return a.LinearDistance < b.LinearDistance;
484 } );
485
486 return pts;
487}
488
489
490std::vector<double> DIFF_PHASE_SKEW_TOOL::buildSplitPositions( const std::vector<CUMULATIVE_ENTRY>& aSegments,
491 const double aTargetSubsegmentSize )
492{
493 if( aSegments.empty() )
494 return {};
495
496 std::vector<double> splits;
497
498 // Start of line
499 if( aSegments[0].m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
500 splits.push_back( 0.0 );
501
502 double currentDistance = 0;
503
504 for( const CUMULATIVE_ENTRY& seg : aSegments )
505 {
506 const double segStart = currentDistance;
507 const double segEnd = seg.m_Length;
508 currentDistance = segEnd;
509
510 // Only emit segments for lines
511 if( seg.m_SourceType != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
512 continue;
513
514 // Fixed subdivision spacing
515 for( double s = segStart; s < segEnd; s += aTargetSubsegmentSize )
516 {
517 splits.push_back( s );
518 }
519
520 splits.push_back( segEnd );
521 }
522
523 // Sort and make unique
524 std::ranges::sort( splits );
525
526 splits.erase( std::ranges::unique( splits,
527 []( const double a, const double b )
528 {
529 return std::abs( a - b ) < EPS;
530 } )
531 .begin(),
532 splits.end() );
533
534 return splits;
535}
536
537
538std::pair<VECTOR2D, std::size_t>
539DIFF_PHASE_SKEW_TOOL::pointAtDistance( const std::vector<CUMULATIVE_ENTRY>& aSegments,
540 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
541 const double aDist )
542{
543 for( std::size_t i = 0; i < aSegments.size(); ++i )
544 {
545 const double segStart = i == 0 ? 0.0 : static_cast<double>( aSegments[i - 1].m_Length );
546 const double segEnd = static_cast<double>( aSegments[i].m_Length );
547
548 if( aDist <= segEnd + EPS )
549 {
550 const double segLen = segEnd - segStart;
551
552 double t = 0.0;
553
554 if( segLen > EPS )
555 t = ( aDist - segStart ) / segLen;
556
557 t = std::clamp( t, 0.0, 1.0 );
558
559 if( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
560 {
561 // We've hit a via - use the end point from the previous line
562 wxASSERT( i > 0 );
563 wxASSERT( aSourceItemDetails[i - 1].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
564 wxASSERT( aSourceItemDetails[i - 1].GetLine().CPoints().size() == 2 );
565 return { aSourceItemDetails[i - 1].GetLine().CPoints()[1], i - 1 };
566 }
567
568 wxASSERT( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
569 wxASSERT( aSourceItemDetails[i].GetLine().CPoints().size() == 2 );
570
571 return {
572 lerp( aSourceItemDetails[i].GetLine().CPoints()[0], aSourceItemDetails[i].GetLine().CPoints()[1], t ), i
573 };
574 }
575 }
576
577 // We shouldn't reach this point...
578 wxASSERT( false );
579 return { { 0.0, 0.0 }, 0 };
580}
581
582
583COLOR4D DIFF_PHASE_SKEW_TOOL::interpolateColours( const COLOR4D& aColour1, const COLOR4D& aColour2, double aS,
584 const bool aUseLogScale ) const
585{
586 auto lerp = []( const double d1, const double d2, const double s )
587 {
588 return d1 + s * ( d2 - d1 );
589 };
590
591 if( aUseLogScale )
592 aS = std::log( 1.0 + m_colourInterpolationLogStrength * aS )
593 / std::log( 1.0 + m_colourInterpolationLogStrength );
594
595 const double r = std::clamp( lerp( aColour1.r, aColour2.r, aS ), 0.0, 1.0 );
596 const double g = std::clamp( lerp( aColour1.g, aColour2.g, aS ), 0.0, 1.0 );
597 const double b = std::clamp( lerp( aColour1.b, aColour2.b, aS ), 0.0, 1.0 );
598 const double a = std::clamp( lerp( aColour1.a, aColour2.a, aS ), 0.0, 1.0 );
599
600 return COLOR4D( r, g, b, a );
601}
602
603
604std::vector<DIFF_PHASE_SKEW_TOOL::OUTPUT_SEGMENT> DIFF_PHASE_SKEW_TOOL::buildDiffOverlaySegments(
605 const std::vector<CUMULATIVE_ENTRY>& aSegments, const LENGTH_DELAY_ITEM_DETAILS& aSourceItemDetails,
606 const std::vector<PARALLEL_RUN>& aKnownRuns, const double aTargetSubsegmentSize, const bool isCoupledTrack )
607{
608 std::vector<OUTPUT_SEGMENT> result;
609
610 if( aSegments.empty() )
611 return result;
612
613 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
615
616 // Build ordered known value control points
617 const std::vector<KNOWN_RELATIVE_POINT> knownPoints =
618 buildKnownRelativePoints( aSegments, aSourceItemDetails, aKnownRuns, isCoupledTrack );
619
620 // Get min and max values
621 double minLen = 0.0;
622 double maxLen = 0.0;
623 double minDelay = 0.0;
624 double maxDelay = 0.0;
625
626 for( const auto& [_, relLen, relDelay] : knownPoints )
627 {
628 minLen = std::min( minLen, relLen );
629 maxLen = std::max( maxLen, relLen );
630 minDelay = std::min( minDelay, relDelay );
631 maxDelay = std::max( maxDelay, relDelay );
632 }
633
634 int maxSkew = m_maxSkew.value_or( 0 );
635
636 if( m_timeDomain )
637 m_maxSkew = std::max( static_cast<int>( std::round( maxDelay / 10 ) * 10 ), maxSkew );
638 else
639 m_maxSkew = std::max( static_cast<int>( std::round( maxLen / 10 ) * 10 ), maxSkew );
640
641 // Build all subdivision boundaries
642 const auto splits = buildSplitPositions( aSegments, aTargetSubsegmentSize );
643
644 KnownValueInterpolator interp( knownPoints );
645
646 // Emit subdivided segments
647 for( std::size_t i = 0; i + 1 < splits.size(); ++i )
648 {
649 const double s0 = splits[i];
650 const double s1 = splits[i + 1];
651
652 // Skip degenerate intervals
653 if( s1 - s0 <= EPS )
654 continue;
655
656 OUTPUT_SEGMENT out;
657
658 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& items =
660
661 auto [startPoint, segIdx] = pointAtDistance( aSegments, items, s0 );
662 out.Width = items[segIdx].GetWidth() * m_overlayTrackInflation;
663 out.Start = startPoint;
664 auto [endPoint, _] = pointAtDistance( aSegments, items, s1 );
665 out.End = endPoint;
666
667 const double sMid = ( s0 + s1 ) / 2.0;
668
669 const std::optional<std::pair<double, double>> knownInterp = interp.ValueAt( sMid );
670 out.RelativeValueKnown = knownInterp.has_value();
671
672 double min = 0.0;
673 double max = 0.0;
674
675 if( m_timeDomain )
676 {
677 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).second;
678 min = minDelay;
679 max = maxDelay;
680 }
681 else
682 {
683 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).first;
684 min = minLen;
685 max = maxLen;
686 }
687
688 // Round value to nearest 10 IU to reduce low-level colour jitter on equal tracks
689 out.RelativeValueAtMid = std::round( out.RelativeValueAtMid / 10 ) * 10;
690
691 // Calculate colour value
692 if( !out.RelativeValueKnown )
693 {
694 out.Colour = settings.m_UnknownSkewColor;
695 }
696 else if( out.RelativeValueAtMid < 0.0 )
697 {
698 const double frac = fabs( out.RelativeValueAtMid / min );
699 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_NegativeSkewColor, frac,
700 settings.m_UseLogScale );
701 }
702 else if( out.RelativeValueAtMid > 0.0 )
703 {
704 const double frac = fabs( out.RelativeValueAtMid / max );
705 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_PositiveSkewColor, frac,
706 settings.m_UseLogScale );
707 }
708 else
709 {
710 out.Colour = COLOR4D( 1.0, 1.0, 1.0, 1.0 );
711 }
712
713 result.push_back( out );
714 }
715
716 return result;
717}
718
719
721{
722 clearOverlay();
723
724 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
725 const bool isSelected = m_segmentForStatisticsDisplay.second;
726 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
727
728 m_viewOverlay->SetIsStroke( true );
729 m_viewOverlay->SetIsFill( false );
730
731 for( std::size_t i = 0; i < m_selectedDiffs.size(); ++i )
732 {
733 const OUTPUT_SEGMENT& segment = m_selectedDiffs[i];
734
735 if( drawHighlight && isSelected && i == selIdx )
736 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
737 else
738 m_viewOverlay->SetStrokeColor( segment.Colour );
739
740 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
741 }
742
743 for( std::size_t i = 0; i < m_coupledDiffs.size(); ++i )
744 {
745 const OUTPUT_SEGMENT& segment = m_coupledDiffs[i];
746
747 if( drawHighlight && !isSelected && i == selIdx )
748 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
749 else
750 m_viewOverlay->SetStrokeColor( segment.Colour );
751
752 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
753 }
754
755 std::vector<MSG_PANEL_ITEM> items;
756 wxString description, value;
757
759}
760
761
763{
764 std::vector<MSG_PANEL_ITEM> items;
765
766 if( !m_pickerItemFirst )
767 {
768 frame()->SetMsgPanel( m_board );
769 return;
770 }
771
773 {
774 wxString description = wxString::Format( _( "Net A Name" ) );
775 wxString netName = m_pickerItemFirst->GetNet()->GetDisplayNetname();
776 items.emplace_back( description, netName );
777
779 {
780 description = wxString::Format( _( "Net B Name" ) );
781 netName = m_pickerItemSecond->GetNet()->GetDisplayNetname();
782 items.emplace_back( description, netName );
783 }
784 }
785 else
786 {
787 wxString description = wxString::Format( _( "Net P Name" ) );
788 wxString netName = m_board->GetNetInfo().GetNetItem( m_netcodeP )->GetDisplayNetname();
789 items.emplace_back( description, netName );
790
791 description = wxString::Format( _( "Net N Name" ) );
792 netName = m_board->GetNetInfo().GetNetItem( m_netcodeN )->GetDisplayNetname();
793 items.emplace_back( description, netName );
794 }
795
796 if( m_maxSkew.has_value() )
797 {
798 wxString description = wxString::Format( _( "Max Skew" ) );
799 wxString value;
800
801 if( m_timeDomain )
802 value = m_frame->MessageTextFromValue( m_maxSkew.value(), true, EDA_DATA_TYPE::TIME );
803 else
804 value = m_frame->MessageTextFromValue( m_maxSkew.value(), true, EDA_DATA_TYPE::DISTANCE );
805
806 items.emplace_back( description, value );
807 }
808
809 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
810 const bool isSelected = m_segmentForStatisticsDisplay.second;
811 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
812
813 if( drawHighlight )
814 {
815 const OUTPUT_SEGMENT& segment = isSelected ? m_selectedDiffs[selIdx] : m_coupledDiffs[selIdx];
816 double normalisedValue = std::round( segment.RelativeValueAtMid );
817 normalisedValue = ( normalisedValue == 0.0 ) ? 0.0 : normalisedValue;
818
819 wxString description = _( "Local Skew" );
820 wxString value = _( "Unknown" );
821
822 if( segment.RelativeValueKnown )
823 {
824 value = m_frame->MessageTextFromValue( normalisedValue, true,
826 }
827
828
829 items.emplace_back( description, value );
830 }
831
832 frame()->SetMsgPanel( items );
833}
834
835
837{
839 {
840 // TODO: This assumes the gap is the same across all traces, but actually this can vary
841 // TODO: by layer. We should get a representative segment for each layer from the
842 // TODO: two tracks and find the max constraint across all of them.
843 const DRC_CONSTRAINT constraint =
844 m_drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, aItem, nullptr, aItem->GetLayer() );
845
846 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
847 return std::numeric_limits<int>::max();
848
849 const MINOPTMAX<int>& val = constraint.GetValue();
850
851 if( val.HasOpt() && val.HasMax() )
852 return std::max( val.Max(), val.Opt() );
853 else if( val.HasMax() )
854 return val.Max();
855 else if( val.HasOpt() )
856 return val.Opt();
857
858 return std::numeric_limits<int>::max();
859 }
860 else
861 {
863 return dist.EuclideanNorm();
864 }
865}
866
867
868void DIFF_PHASE_SKEW_TOOL::findParallelRunsImpl( const std::vector<CUMULATIVE_ENTRY>& aSelectedCumulative,
869 const std::vector<CUMULATIVE_ENTRY>& aCoupledCumulative,
870 std::pair<std::size_t, std::size_t> aRangeA,
871 std::pair<std::size_t, std::size_t> aRangeB, double aMaxSpacing,
872 std::vector<PARALLEL_RUN>& aRuns ) const
873{
874 for( size_t ia = aRangeA.first; ia < aRangeA.second; ++ia )
875 {
877
878 if( selectedItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
879 continue;
880
881 const SHAPE_LINE_CHAIN& lineA = selectedItem.GetLine();
882 wxASSERT( lineA.SegmentCount() == 1 );
883 const SEG segA = lineA.Segment( 0 );
884
885 VECTOR2D A0 = segA.A;
886 VECTOR2D A1 = segA.B;
887
888 VECTOR2D dA = A1 - A0;
889 const double lenA = dA.EuclideanNorm();
890 VECTOR2D nA{ dA.x / lenA, dA.y / lenA };
891
892 for( size_t ib = aRangeB.first; ib < aRangeB.second; ++ib )
893 {
895
896 if( coupledItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
897 continue;
898
899 const SHAPE_LINE_CHAIN& lineB = coupledItem.GetLine();
900 wxASSERT( lineB.SegmentCount() == 1 );
901 const SEG segB = lineB.Segment( 0 );
902
903 VECTOR2D B0 = segB.A;
904 VECTOR2D B1 = segB.B;
905
906 VECTOR2D dB = B1 - B0;
907 const double lenB = dB.EuclideanNorm();
908 VECTOR2D nB{ dB.x / lenB, dB.y / lenB };
909
910 // Test for parallel line segments
911 const double dp = nA.Dot( nB );
912
913 // Note that this test is explicitly signed (not fabs(dp)) to ensure anti-parallel tracks are rejected
915 continue;
916
917 // Test for perpendicular distance
918 VECTOR2D midB = ( B0 + B1 ) * 0.5;
919
920 // Perpendicular distance from midpoint of B to infinite line through A
921 const double perpDistance = std::fabs( nA.Cross( midB - A0 ) );
922
923 const double maxItemGap =
924 ( aMaxSpacing + ( selectedItem.GetWidth() + coupledItem.GetWidth() ) / 2.0 ) * m_trackGapInflation;
925
926 if( perpDistance > maxItemGap )
927 continue;
928
929 // Project on to common axis
930 double a0 = A0.Dot( nA );
931 double a1 = A1.Dot( nA );
932 double b0 = B0.Dot( nA );
933 double b1 = B1.Dot( nA );
934
935 bool aReversed = false;
936 bool bReversed = false;
937
938 if( a0 > a1 )
939 {
940 std::swap( a0, a1 );
941 aReversed = true;
942 }
943
944 if( b0 > b1 )
945 {
946 std::swap( b0, b1 );
947 bReversed = true;
948 }
949
950 // Compute overlap interval
951 double overlap0 = std::max( a0, b0 );
952 double overlap1 = std::min( a1, b1 );
953
954 // Test for overlap
955 if( overlap1 <= overlap0 )
956 continue;
957
958 // Convert overlap to segment parameters
959 double tA0 = ( overlap0 - a0 ) / ( a1 - a0 );
960 double tA1 = ( overlap1 - a0 ) / ( a1 - a0 );
961 double tB0 = ( overlap0 - b0 ) / ( b1 - b0 );
962 double tB1 = ( overlap1 - b0 ) / ( b1 - b0 );
963
964 // Handle reversed parameterization
965 if( aReversed )
966 {
967 tA0 = 1.0 - tA0;
968 tA1 = 1.0 - tA1;
969 }
970
971 if( bReversed )
972 {
973 tB0 = 1.0 - tB0;
974 tB1 = 1.0 - tB1;
975 }
976
977 // Normalize parameter ordering
978 if( tA0 > tA1 )
979 std::swap( tA0, tA1 );
980
981 if( tB0 > tB1 )
982 std::swap( tB0, tB1 );
983
984 // Clamp to physical range
985 tA0 = std::clamp( tA0, 0.0, 1.0 );
986 tA1 = std::clamp( tA1, 0.0, 1.0 );
987
988 tB0 = std::clamp( tB0, 0.0, 1.0 );
989 tB1 = std::clamp( tB1, 0.0, 1.0 );
990
991 // Emit the parallel run
992 PARALLEL_RUN run;
993
994 run.ta0 = tA0;
995 run.ta1 = tA1;
996 run.tb0 = tB0;
997 run.tb1 = tB1;
998
999 run.segA = ia;
1000 run.segB = ib;
1001
1002 // Calculate start and end points as linear interpolations along segments
1003 run.startA = lerp( A0, A1, tA0 );
1004 run.endA = lerp( A0, A1, tA1 );
1005 run.startB = lerp( B0, B1, tB0 );
1006 run.endB = lerp( B0, B1, tB1 );
1007
1008 // Calculate cumulative values for deltas
1009 auto [length1, delay1] = getCumulativeLengthAndDelayAt(
1010 m_selectedLengthDelayDetails, m_selectedStartEndDetails, aSelectedCumulative, ia, run.ta0 );
1011 run.startLenA = length1;
1012 run.startDelayA = delay1;
1013
1014 auto [length2, delay2] = getCumulativeLengthAndDelayAt(
1015 m_selectedLengthDelayDetails, m_selectedStartEndDetails, aSelectedCumulative, ia, run.ta1 );
1016 run.endLenA = length2;
1017 run.endDelayA = delay2;
1018
1019 auto [length3, delay3] = getCumulativeLengthAndDelayAt(
1020 m_coupledLengthDelayDetails, m_coupledStartEndDetails, aCoupledCumulative, ib, run.tb0 );
1021 run.startLenB = length3;
1022 run.startDelayB = delay3;
1023
1024 auto [length4, delay4] = getCumulativeLengthAndDelayAt(
1025 m_coupledLengthDelayDetails, m_coupledStartEndDetails, aCoupledCumulative, ib, run.tb1 );
1026 run.endLenB = length4;
1027 run.endDelayB = delay4;
1028
1029 aRuns.push_back( run );
1030 }
1031 }
1032}
1033
1034
1035std::vector<PARALLEL_RUN>
1036DIFF_PHASE_SKEW_TOOL::findParallelRuns( const std::vector<CUMULATIVE_ENTRY>& aSelectedCumulative,
1037 const std::vector<CUMULATIVE_ENTRY>& aCoupledCumulative ) const
1038{
1039 std::vector<PARALLEL_RUN> runs;
1040 const double maxGap = getMaxDiffPairGap( m_pickerItemFirst );
1041
1042 // First find runs with regular spacing
1043 findParallelRunsImpl( aSelectedCumulative, aCoupledCumulative, { 0, aSelectedCumulative.size() },
1044 { 0, aCoupledCumulative.size() }, maxGap, runs );
1045
1046 if( runs.empty() )
1047 return {};
1048
1049 // Check what the min and max segment IDs of each track are
1050 const auto [minSelected, maxSelected] = std::ranges::minmax( runs, {},
1051 []( const PARALLEL_RUN& a )
1052 {
1053 return a.segA;
1054 } );
1055
1056 const auto [minCoupled, maxCoupled] = std::ranges::minmax( runs, {},
1057 []( const PARALLEL_RUN& a )
1058 {
1059 return a.segB;
1060 } );
1061
1062 // Find parallel segments with start separation if needed
1063 const std::size_t firstSegA = minSelected.segA;
1064 const std::size_t lastSegA = maxSelected.segA;
1065 const std::size_t firstSegB = minCoupled.segA;
1066 const std::size_t lastSegB = maxCoupled.segA;
1067
1068 // Assume that tracks start in parallel from pads that are wider than the diff pair spacing. Use the pad separation
1069 // to search for start tracks up to the start of the identified existing parallel segments
1070 if( firstSegA > 0 || firstSegB > 0 )
1071 {
1072 const VECTOR2I selPadLocn = m_selectedStartPad->Pos();
1073 const VECTOR2I coupledPadLocn = m_coupledStartPad->Pos();
1074 const VECTOR2D distVec = selPadLocn - coupledPadLocn;
1075 const double padSeparation = distVec.EuclideanNorm();
1076
1077 findParallelRunsImpl( aSelectedCumulative, aCoupledCumulative, { 0, firstSegA }, { 0, firstSegB },
1078 padSeparation, runs );
1079 }
1080
1081 // Assume that tracks end in parallel from pads that are wider than the diff pair spacing. Use the pad separation
1082 // to search for end tracks from the end of the identified existing parallel segments
1083 if( lastSegA < aSelectedCumulative.size() || lastSegB > aCoupledCumulative.size() )
1084 {
1085 const VECTOR2I selPadLocn = m_selectedEndPad->Pos();
1086 const VECTOR2I coupledPadLocn = m_coupledEndPad->Pos();
1087 const VECTOR2D distVec = selPadLocn - coupledPadLocn;
1088 const double padSeparation = distVec.EuclideanNorm();
1089
1090 findParallelRunsImpl( aSelectedCumulative, aCoupledCumulative, { lastSegA, aSelectedCumulative.size() },
1091 { lastSegB, aCoupledCumulative.size() }, padSeparation, runs );
1092 }
1093
1094 std::ranges::sort( runs,
1095 []( const PARALLEL_RUN& a, const PARALLEL_RUN& b )
1096 {
1097 if( a.startLenA != b.startLenA )
1098 {
1099 return a.startLenA < b.startLenA;
1100 }
1101
1102 return a.startLenB < b.startLenB;
1103 } );
1104
1105 return runs;
1106}
1107
1108
1110 const LENGTH_DELAY_ITEM_DETAILS& aLengthDelayDetails, const START_END_DETAILS& aPadDetails,
1111 const std::vector<CUMULATIVE_ENTRY>& aCumulative, const std::size_t aSegIdx, const double aT )
1112{
1113 const double segLength = static_cast<double>( aLengthDelayDetails.LengthsAndDelays[aSegIdx].first );
1114 const double segDelay = static_cast<double>( aLengthDelayDetails.LengthsAndDelays[aSegIdx].second );
1115
1116 // cumulative[i] is the cumulative length / delay at the end of the given segment index. Therefore, subtract
1117 // the not-included fraction of the track from the cumulative value at the segment end to get the length or
1118 // delay at the required fractional distance on the source segment
1119 const double partLen = segLength * ( 1.0 - aT );
1120 const double partDelay = segDelay * ( 1.0 - aT );
1121 return { aCumulative[aSegIdx].m_Length - static_cast<int64_t>( partLen ) + aPadDetails.StartPadLength,
1122 aCumulative[aSegIdx].m_Delay - static_cast<int64_t>( partDelay ) + aPadDetails.StartPadDelay };
1123}
1124
1125
1126std::vector<DIFF_PHASE_SKEW_TOOL::CUMULATIVE_ENTRY> DIFF_PHASE_SKEW_TOOL::buildCumulativeLengthsAndDelays(
1127 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems, const LENGTH_DELAY_ITEM_DETAILS& aLengthDelayDetails,
1128 const PNS::SOLID* aStartPad, const PNS::SOLID* aEndPad, START_END_DETAILS& aStartEndDetails )
1129{
1130 wxASSERT( aItems.size() == aLengthDelayDetails.LengthsAndDelays.size() );
1131
1132 if( aLengthDelayDetails.LengthsAndDelays.empty() )
1133 return {};
1134
1135 std::vector<CUMULATIVE_ENTRY> cumulative;
1136 cumulative.reserve( aLengthDelayDetails.LengthsAndDelays.size() );
1137
1138 // Calculate start and end pad details
1139 aStartEndDetails.StartPadLength = aStartPad->GetPadToDie() + aLengthDelayDetails.InferredStartViaLength;
1140 aStartEndDetails.StartPadDelay = aStartPad->GetPadToDieDelay() + aLengthDelayDetails.InferredStartViaDelay;
1141 aStartEndDetails.EndPadLength = aEndPad->GetPadToDie() + aLengthDelayDetails.InferredEndViaLength;
1142 aStartEndDetails.EndPadDelay = aEndPad->GetPadToDieDelay() + aLengthDelayDetails.InferredEndViaDelay;
1143
1144 int64_t totalLength = 0;
1145 int64_t totalDelay = 0;
1146
1147 // Add track element details. Note that this adds the cumulative length at the *end*
1148 // of each track element to the cumulative vector.
1149 for( std::size_t i = 0; i < aItems.size(); ++i )
1150 {
1151 const auto [itemLen, itemDly] = aLengthDelayDetails.LengthsAndDelays[i];
1152 const LENGTH_DELAY_CALCULATION_ITEM& item = aItems[i];
1153
1154 totalLength += itemLen;
1155 totalDelay += itemDly;
1156 cumulative.emplace_back( totalLength, totalDelay, item.Type() );
1157 }
1158
1159 return cumulative;
1160}
1161
1162
1163void DIFF_PHASE_SKEW_TOOL::splitLengthItems( std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems )
1164{
1165 std::vector<LENGTH_DELAY_CALCULATION_ITEM> splitItems;
1166
1167 auto makeLengthDelayItem = [&splitItems]( const SEG& aSeg, const LENGTH_DELAY_CALCULATION_ITEM& aSourceItem )
1168 {
1169 SHAPE_LINE_CHAIN newLine;
1170 newLine.Append( aSeg.A );
1171 newLine.Append( aSeg.B );
1172
1174 newItem.SetLine( newLine );
1175 newItem.SetWidth( aSourceItem.GetWidth() );
1176 newItem.SetLayers( aSourceItem.GetStartLayer() );
1177 newItem.SetEffectiveNetClass( aSourceItem.GetEffectiveNetClass() );
1178 splitItems.emplace_back( std::move( newItem ) );
1179 };
1180
1181 for( const auto& sourceItem : aItems )
1182 {
1183 // Only process lines
1184 if( sourceItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
1185 {
1186 splitItems.emplace_back( sourceItem );
1187 continue;
1188 }
1189
1190 SHAPE_LINE_CHAIN& line = sourceItem.GetLine();
1191
1192 for( int segIdx = 0; segIdx < line.SegmentCount(); ++segIdx )
1193 {
1194 SEG seg = line.GetSegment( segIdx );
1195 makeLengthDelayItem( seg, sourceItem );
1196 }
1197 }
1198
1199 aItems = std::move( splitItems );
1200}
1201
1202
1204{
1205 wxString message;
1206 bool error = true;
1207
1208 switch( aDirection )
1209 {
1211 message = wxString::Format( _( "Net %s has multiple simulation electrical source pads" ),
1212 m_selectedNetinfo->GetShortNetname() );
1213 break;
1215 message = wxString::Format( _( "Net %s has multiple simulation electrical source pads" ),
1216 m_coupledNetinfo->GetShortNetname() );
1217 break;
1219 message = wxString::Format( _( "Differential pair %s / %s is missing start and / or end pads" ),
1220 m_selectedNetinfo->GetShortNetname(), m_coupledNetinfo->GetShortNetname() );
1221 break;
1223 message = wxString::Format( _( "Net %s is missing electrical simulation source pad" ),
1224 m_selectedNetinfo->GetShortNetname() );
1225 break;
1227 message = wxString::Format( _( "Net %s is missing electrical simulation source pad" ),
1228 m_coupledNetinfo->GetShortNetname() );
1229 break;
1230 default: error = false; break;
1231 }
1232
1233 if( error )
1234 m_frame->ShowInfoBarError( message, true );
1235
1236 return error;
1237}
1238
1239
1241{
1243 {
1244 const int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemFirst->GetLayer() );
1245
1246 PCB_TRACK* track = nullptr;
1248 wxCHECK( track, /* void */ );
1249
1250 // Determine primary and secondary net codes
1251 m_selectedNetcode = track->GetNetCode();
1253
1254 // Get the netcodes
1255 m_selectedNetinfo = m_board->GetNetInfo().GetNetItem( m_selectedNetcode );
1256 m_coupledNetinfo = m_board->GetNetInfo().GetNetItem( m_coupledNetcode );
1257
1258 VECTOR2I startSnapPoint;
1259 PNS::LINKED_ITEM* startItem =
1260 PNS::HELPERS::PickSegment( m_router, m_originFirst, pnsLayer, startSnapPoint, SHAPE_LINE_CHAIN() );
1261
1262 if( !startItem || !startItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
1263 {
1264 m_frame->ShowInfoBarError( _( "Phase skew initial selection failed" ), true );
1265 return;
1266 }
1267
1268 PNS::NODE* world = m_router->GetWorld()->Branch();
1269 PNS::TOPOLOGY topo( world );
1270 PNS::DIFF_PAIR originPair;
1271
1272 m_selectedStartPad = nullptr;
1273 m_selectedEndPad = nullptr;
1274 m_coupledStartPad = nullptr;
1275 m_coupledEndPad = nullptr;
1276
1277 if( !topo.AssembleDiffPair( startItem, originPair ) )
1278 {
1279 m_frame->ShowInfoBarError( _( "Differential pair identification failed" ), true );
1280 return;
1281 }
1282
1283 if( !originPair.PLine().SegmentCount() || !originPair.NLine().SegmentCount() )
1284 return;
1285
1287 {
1291 &m_coupledEndPad );
1292 }
1293 else
1294 {
1296 &m_coupledEndPad );
1299 }
1300 }
1301 else
1302 {
1303 const int pnsLayerFirst = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemFirst->GetLayer() );
1304 const int pnsLayerSecond = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemSecond->GetLayer() );
1305
1306 PCB_TRACK* trackFirst = nullptr;
1308 wxCHECK( trackFirst, /* void */ );
1309
1310 PCB_TRACK* trackSecond = nullptr;
1312 wxCHECK( trackSecond, /* void */ );
1313
1314 // Determine primary and secondary net codes
1315 m_selectedNetcode = trackFirst->GetNetCode();
1316 m_coupledNetcode = trackSecond->GetNetCode();
1317
1318 // Get the netcodes
1319 m_selectedNetinfo = m_board->GetNetInfo().GetNetItem( m_selectedNetcode );
1320 m_coupledNetinfo = m_board->GetNetInfo().GetNetItem( m_coupledNetcode );
1321
1322 VECTOR2I startSnapPointFirst, startSnapPointSecond;
1323 PNS::LINKED_ITEM* startItemFirst = PNS::HELPERS::PickSegment( m_router, m_originFirst, pnsLayerFirst,
1324 startSnapPointFirst, SHAPE_LINE_CHAIN() );
1325 PNS::LINKED_ITEM* startItemSecond = PNS::HELPERS::PickSegment( m_router, m_originSecond, pnsLayerSecond,
1326 startSnapPointSecond, SHAPE_LINE_CHAIN() );
1327
1328 if( !startItemFirst || !startItemFirst->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) || !startItemSecond
1329 || !startItemSecond->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
1330 {
1331 m_frame->ShowInfoBarError( _( "Phase skew initial selection failed" ), true );
1332 return;
1333 }
1334
1335 PNS::NODE* world = m_router->GetWorld()->Branch();
1336 PNS::TOPOLOGY topo( world );
1337
1338 m_selectedStartPad = nullptr;
1339 m_selectedEndPad = nullptr;
1340 m_coupledStartPad = nullptr;
1341 m_coupledEndPad = nullptr;
1342
1345 }
1346
1347 // The router can return SHAPE_LINE_CHAINS that are in a reversed order. This doesn't play nicely
1348 // with our parallel / antiparallel rejection tests, therfore we need to ensure the SHAPE_LINE_CHAIN
1349 // points are in line order here
1352}
1353
1355{
1356 if( !aStartPad )
1357 return;
1358
1359 VECTOR2I pathSearchLoc = aStartPad->Pos();
1360
1361 for( int i = 0; i < aPath.Size(); ++i )
1362 {
1363 PNS::ITEM* curItem = aPath[i];
1364
1365 if( curItem->Kind() == PNS::ITEM::LINE_T )
1366 {
1367 PNS::LINE* lineItem = dynamic_cast<PNS::LINE*>( curItem );
1368
1369 if( !lineItem )
1370 continue;
1371
1372 SHAPE_LINE_CHAIN& line = lineItem->Line();
1373 const std::size_t numPoints = line.GetPointCount();
1374 wxASSERT( numPoints >= 2 );
1375
1376 if( line.GetPoint( numPoints - 1 ) == pathSearchLoc )
1377 line = line.Reverse();
1378
1379 pathSearchLoc = line.GetPoint( numPoints - 1 );
1380 }
1381 else if( curItem->Kind() == PNS::ITEM::VIA_T )
1382 {
1383 const PNS::VIA* viaItem = dynamic_cast<PNS::VIA*>( curItem );
1384 wxASSERT( viaItem->Pos() == pathSearchLoc );
1385 }
1386 }
1387}
1388
1389
1391{
1392 // This condition can occur if the route contains items that are not tracks (e.g. unconverted arcs)
1395
1396 PAD* selectedStartPad = static_cast<PAD*>( m_selectedStartPad->BoardItem() );
1397 PAD* selectedEndPad = static_cast<PAD*>( m_selectedEndPad->BoardItem() );
1398 PAD* coupledStartPad = static_cast<PAD*>( m_coupledStartPad->BoardItem() );
1399 PAD* coupledEndPad = static_cast<PAD*>( m_coupledEndPad->BoardItem() );
1400
1401 if( !selectedStartPad || !selectedEndPad || !coupledStartPad || !coupledEndPad )
1403
1404 // Normalise directions if possible
1405 if( selectedStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE
1407 {
1409 std::swap( selectedStartPad, selectedEndPad );
1410 }
1411
1414 {
1416 std::swap( coupledStartPad, coupledEndPad );
1417 }
1418
1419 // Both start pads must be sources
1420 if( selectedStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1421 {
1423 }
1424
1425 if( coupledStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1426 {
1428 }
1429
1430 // End pads can't be a source
1431 if( selectedEndPad->GetSimElectricalType() == PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1433
1436
1438}
1439
1440
1442{
1443 PNS::ITEM_SET reversed;
1444
1445 for( auto itr = aPath.rbegin(); itr != aPath.rend(); ++itr )
1446 {
1447 if( ( *itr )->Kind() == PNS::ITEM::LINE_T )
1448 {
1449 PNS::LINE* l = dyn_cast<PNS::LINE*>( *itr );
1450 wxASSERT( l != nullptr );
1451
1452 if( l != nullptr )
1453 l->Reverse();
1454 }
1455
1456 reversed.Add( *itr );
1457 }
1458
1459 aPath = std::move( reversed );
1460
1461 // Finally reverse the start / end pads
1462 std::swap( *aStartPad, *aEndPad );
1463}
1464
1465
1467{
1468 constexpr std::size_t maxIdx = std::numeric_limits<std::size_t>::max();
1469 const double hitTestDistance = m_view->ToWorld( DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS );
1470 const auto [selectedIdx, isSelectedTrack] = getNearestDiffSegments( m_cursorPos, hitTestDistance );
1471
1472 if( selectedIdx < maxIdx )
1473 {
1474 if( isSelectedTrack )
1475 m_segmentForStatisticsDisplay = { selectedIdx, true };
1476 else
1477 m_segmentForStatisticsDisplay = { selectedIdx, false };
1478 }
1479 else
1480 {
1481 m_segmentForStatisticsDisplay = { maxIdx, false };
1482 }
1483
1485}
1486
1487
1488std::pair<std::size_t, bool> DIFF_PHASE_SKEW_TOOL::getNearestDiffSegments( const VECTOR2D& aCursorPos,
1489 const double aHitTestDistance ) const
1490{
1491 auto getNearestSegment = [&aCursorPos, aHitTestDistance]( const std::vector<OUTPUT_SEGMENT>& segments )
1492 {
1493 const double maxDistSq = aHitTestDistance * aHitTestDistance;
1494
1495 std::size_t bestIndex = std::numeric_limits<std::size_t>::max();
1496 double bestDistSq = maxDistSq;
1497
1498 for( size_t i = 0; i < segments.size(); ++i )
1499 {
1500 const auto& seg = segments[i];
1501
1502 VECTOR2D mid = ( seg.Start + seg.End ) / 2.0;
1503 const double distSq = ( aCursorPos - mid ).SquaredEuclideanNorm();
1504
1505 if( distSq <= bestDistSq )
1506 {
1507 bestDistSq = distSq;
1508 bestIndex = i;
1509 }
1510 }
1511
1512 return std::pair<std::size_t, double>( bestIndex, bestDistSq );
1513 };
1514
1515 const auto [selectedIdx, selectedDist] = getNearestSegment( m_selectedDiffs );
1516 const auto [coupledIdx, coupledDist] = getNearestSegment( m_coupledDiffs );
1517
1518 constexpr std::size_t maxIdx = std::numeric_limits<std::size_t>::max();
1519
1520 if( selectedIdx != maxIdx && coupledIdx != maxIdx )
1521 {
1522 if( selectedDist <= coupledDist )
1523 return { selectedIdx, true };
1524
1525 return { coupledIdx, false };
1526 }
1527
1528 if( selectedIdx != maxIdx )
1529 return { selectedIdx, true };
1530
1531 if( coupledIdx != maxIdx )
1532 return { coupledIdx, false };
1533
1534 return { maxIdx, true };
1535}
1536
1537
1539{
1540 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1541 renderSettings->SetHighlight( false );
1542
1543 if( m_pickerItemFirst )
1544 {
1545 renderSettings->SetHighlight( true, m_netcodeP, true );
1546
1548 renderSettings->SetHighlight( true, m_netcodeN, true );
1549 }
1550
1551 if( m_pickerItemSecond )
1552 renderSettings->SetHighlight( true, m_netcodeN, true );
1553
1554 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1555
1556 if( aRefresh )
1557 m_frame->GetCanvas()->Refresh();
1558}
1559
1560
1562{
1563 // clang-format off
1565 // clang-format on
1566}
1567
1568
1570{
1571 if( !m_viewOverlay )
1572 {
1573 m_viewOverlay = m_view->MakeOverlay();
1574 m_view->Add( m_viewOverlay.get() );
1575 }
1576}
1577
1578
1580{
1581 if( m_viewOverlay )
1582 {
1583 m_viewOverlay->Clear();
1584 updateOverlay();
1585 }
1586}
1587
1588
1590{
1591 if( m_viewOverlay )
1592 {
1593 m_view->Update( m_viewOverlay.get() );
1594 }
1595}
1596
1597
1599{
1600 m_pickerItemFirst = nullptr;
1601 m_pickerItemSecond = nullptr;
1603 m_netcodeP = 0;
1604 m_netcodeN = 0;
1605 m_originFirst = { 0, 0 };
1606 m_originSecond = { 0, 0 };
1607 m_timeDomain = false;
1609 m_coupledNetcode = 0;
1610 m_selectedNetinfo = nullptr;
1611 m_coupledNetinfo = nullptr;
1612 m_selectedPath.Clear();
1613 m_coupledPath.Clear();
1614 m_selectedStartPad = nullptr;
1615 m_selectedEndPad = nullptr;
1616 m_coupledStartPad = nullptr;
1617 m_coupledEndPad = nullptr;
1620 m_selectedLengthDelayDetails.LengthsAndDelays.clear();
1621 m_coupledLengthDelayDetails.LengthsAndDelays.clear();
1622 m_selectedDiffs.clear();
1623 m_coupledDiffs.clear();
1624 m_segmentForStatisticsDisplay = { std::numeric_limits<std::size_t>::max(), false };
1625 m_maxSkew.reset();
1626}
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
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,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
int GetCount() const
Return the number of objects in the list.
Definition collector.h:83
int m_Threshold
Definition collector.h:236
int ShowModal() override
std::vector< OUTPUT_SEGMENT > buildDiffOverlaySegments(const std::vector< CUMULATIVE_ENTRY > &aSegments, const LENGTH_DELAY_ITEM_DETAILS &aSourceItemDetails, const std::vector< PARALLEL_RUN > &aKnownRuns, double aTargetSubsegmentSize, bool isCoupledTrack)
Builds a vector of known relative points for the given track and identified parallel segments.
void SetMode(const MODE aMode)
The tool entry point.
void setTransitions() override
< Set up handlers for tool events
std::vector< KNOWN_RELATIVE_POINT > buildKnownRelativePoints(const std::vector< CUMULATIVE_ENTRY > &aSegments, const LENGTH_DELAY_ITEM_DETAILS &aSourceItemDetails, const std::vector< PARALLEL_RUN > &aKnownRuns, bool isCoupledTrack) const
Determines where to apply overlay segment subsections on the source segments.
std::vector< PARALLEL_RUN > findParallelRuns(const std::vector< CUMULATIVE_ENTRY > &aSelectedCumulative, const std::vector< CUMULATIVE_ENTRY > &aCoupledCumulative) const
Finds all parallel segment runs in the selected and coupled tracks within the given segment ranges an...
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
DIFF_PAIR_VALIDITY determinePathDirections()
Reverses the direction of the selected and coupled paths (including swapping start / end pads.
bool reportValidityErrors(DIFF_PAIR_VALIDITY aDirection) const
Struct to represent one cumulative length and delay point.
START_END_DETAILS m_coupledStartEndDetails
LENGTH_DELAY_ITEM_DETAILS m_coupledLengthDelayDetails
static void reversePath(PNS::ITEM_SET &aPath, PNS::SOLID **aStartPad, PNS::SOLID **aEndPad)
Report to the user any errors after determining the signal direction.
static VECTOR2D lerp(const VECTOR2D aA, const VECTOR2D aB, const double aT)
Gets the cumulative length and delay at the given fractional coordinate in the given segment.
std::pair< std::size_t, bool > m_segmentForStatisticsDisplay
static void normalisePathItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad)
Determine which end of the extracted paths we are defining as the signal start point.
int getMaxDiffPairGap(const BOARD_CONNECTED_ITEM *aItem) const
Struct containing a final computed output diff segment.
static std::vector< double > buildSplitPositions(const std::vector< CUMULATIVE_ENTRY > &aSegments, double aTargetSubsegmentSize)
Returns the coordinate at the given linear distance along the line, along with the segment index the ...
MODE GetMode() const
Set the current mode of the tool.
void clearOverlay() const
Resets all select-specific variables.
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_selectedLengthDelayItems
void doDisplayOverlay()
Use the router to get the +ve and -ve paths from the selected item.
bool Init() override
Init() is called once upon a registration of the tool.
void getNetPaths()
Normalises the path to ensure SHAPE_LINE_CHAIN points are in overall path walk order.
BOARD_CONNECTED_ITEM * m_pickerItemFirst
KIGFX::VIEW_CONTROLS * m_controls
static void splitLengthItems(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems)
Finds all parallel segment runs in the selected and coupled tracks.
std::vector< OUTPUT_SEGMENT > m_selectedDiffs
void buildLengthDelayItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad, const PNS::SOLID *aEndPad, const NETINFO_ITEM *aNet, std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, LENGTH_DELAY_ITEM_DETAILS &aItemDetails) const
Start and end pad lengths and delays (pad-to-die + infered via-in-pad)
void doInitialHover(const PCB_SELECTION_TOOL *aSelectionTool, GENERAL_COLLECTORS_GUIDE aGuide)
Display the phase overlay for the current hover item.
PCB_BASE_EDIT_FRAME * m_frame
int ShowDiffPhaseSkew(const TOOL_EVENT &aEvent)
Flags for the analysis state of the selected tracks.
void doShowStatsAtCursor()
Determines the nearest points to the cursor from the diff segments.
void findParallelRunsImpl(const std::vector< CUMULATIVE_ENTRY > &aSelectedCumulative, const std::vector< CUMULATIVE_ENTRY > &aCoupledCumulative, std::pair< std::size_t, std::size_t > aRangeA, std::pair< std::size_t, std::size_t > aRangeB, double aMaxSpacing, std::vector< PARALLEL_RUN > &aRuns) const
Linear interpolate from point A to B at line fraction T.
std::vector< OUTPUT_SEGMENT > m_coupledDiffs
COLOR4D interpolateColours(const COLOR4D &aColour1, const COLOR4D &aColour2, double aS, bool aUseLogScale) const
Draws the visual skew overlay.
static std::vector< CUMULATIVE_ENTRY > buildCumulativeLengthsAndDelays(const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, const LENGTH_DELAY_ITEM_DETAILS &aLengthDelayDetails, const PNS::SOLID *aStartPad, const PNS::SOLID *aEndPad, START_END_DETAILS &aStartEndDetails)
Splits the calculation items from compound segments in to individual items.
std::pair< std::size_t, bool > getNearestDiffSegments(const VECTOR2D &aCursorPos, double aHitTestDistance) const
Ensures we have an active VIEW_OVERLAY to display the diff graphics.
static std::pair< int64_t, int64_t > getCumulativeLengthAndDelayAt(const LENGTH_DELAY_ITEM_DETAILS &aLengthDelayDetails, const START_END_DETAILS &aPadDetails, const std::vector< CUMULATIVE_ENTRY > &aCumulative, std::size_t aSegIdx, double aT)
Gets the maximum diff pair gap for the given item, taken from DRC rules.
static std::pair< VECTOR2D, std::size_t > pointAtDistance(const std::vector< CUMULATIVE_ENTRY > &aSegments, const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aSourceItemDetails, double aDist)
Linearly interpolates between colour1 and colour2, with interpolation point given by aS [0-1].
void updateOverlay() const
Clears the VIEW_OVERLAY.
std::optional< int > m_maxSkew
std::shared_ptr< KIGFX::VIEW_OVERLAY > m_viewOverlay
PNS::SIZES_SETTINGS m_savedSizes
void getOverlay()
Refreshes the VIEW_OVERLAY in the active VIEW.
BOARD_CONNECTED_ITEM * m_pickerItemSecond
void updateNetHighlights(bool aRefresh=true) const
Handle hover events before a DP pair is selected.
void resetStateVariables()
Updates the message panel.
LENGTH_DELAY_ITEM_DETAILS m_selectedLengthDelayDetails
START_END_DETAILS m_selectedStartEndDetails
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_coupledLengthDelayItems
void drawDiffOverlay() const
Shows the diff stats nearest the cursor.
std::shared_ptr< DRC_ENGINE > m_drcEngine
SEVERITY GetSeverity() const
Definition drc_rule.h:221
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
bool IsNull() const
Definition drc_rule.h:195
std::shared_ptr< DRC_ENGINE > GetDRCEngine()
Definition drc_tool.h:87
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:324
void SetPreferredLayer(PCB_LAYER_ID aLayer)
Definition collectors.h:390
void SetIncludeSecondary(bool include)
Definition collectors.h:404
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
double r
Red component.
Definition color4d.h:393
double g
Green component.
Definition color4d.h:394
double a
Alpha component.
Definition color4d.h:396
double b
Blue component.
Definition color4d.h:395
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An interface for classes handling user events controlling the view behavior such as zooming,...
Interpolates known relative points along a track using linear distance.
std::optional< std::pair< double, double > > ValueAt(const double s)
Lightweight class which holds a pad, via, or a routed trace outline.
void SetLine(const SHAPE_LINE_CHAIN &aLine)
Sets the source SHAPE_LINE_CHAIN of this item.
TYPE Type() const
Gets the routing item type.
const int GetWidth() const
Gets the line width.
void SetWidth(const int aWidth)
Sets the line width.
SHAPE_LINE_CHAIN & GetLine() const
Gets the SHAPE_LINE_CHAIN associated with this item.
void SetEffectiveNetClass(const NETCLASS *aNetClass)
Sets the effective net class for the item.
void SetLayers(const PCB_LAYER_ID aStart, const PCB_LAYER_ID aEnd=PCB_LAYER_ID::UNDEFINED_LAYER)
Sets the first and last layers associated with this item.
bool HasMax() const
Definition minoptmax.h:38
T Max() const
Definition minoptmax.h:34
T Opt() const
Definition minoptmax.h:35
bool HasOpt() const
Definition minoptmax.h:39
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:95
int GetNetCode() const
Definition netinfo.h:98
Definition pad.h:65
PAD_SIM_ELECTRICAL_TYPE GetSimElectricalType() const
Definition pad.h:596
DIFF_PHASE_SKEW_SETTINGS m_DiffPhaseSkewSettings
std::unique_ptr< PNS::ROUTING_SETTINGS > m_PnsSettings
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION showDiffPhaseSkew
Display of phase skew between differential pair tracks.
The selection tool: currently supports:
void GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
T * frame() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
Basic class for a differential pair.
int Size() const
std::vector< ITEM * >::reverse_iterator rbegin()
void Add(const LINE &aLine)
std::vector< ITEM * >::reverse_iterator rend()
Base class for PNS router board items.
Definition pns_item.h:98
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual BOARD_ITEM * BoardItem() const
Definition pns_item.h:207
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:141
int SegmentCount() const
Definition pns_line.h:144
void Reverse()
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
Keep the router "world" - i.e.
Definition pns_node.h:242
int GetPadToDie() const
Definition pns_solid.h:122
int GetPadToDieDelay() const
Definition pns_solid.h:125
const VECTOR2I & Pos() const
Definition pns_solid.h:119
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
const ITEM_SET AssembleTuningPath(ROUTER_IFACE *aRouterIface, ITEM *aStart, SOLID **aStartPad=nullptr, SOLID **aEndPad=nullptr)
Like AssembleTrivialPath, but follows the track length algorithm, which discards segments that are fu...
const VECTOR2I & Pos() const
Definition pns_via.h:206
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
virtual const VECTOR2I GetPoint(int aIndex) const override
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
int SegmentCount() const
Return the number of segments in this line chain.
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:199
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:224
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:84
Generic, UI-independent tool event.
Definition tool_event.h:171
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:260
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:546
@ ARROW
Definition cursors.h:46
#define INITIAL_HOVER_HITTEST_THRESHOLD_PIXELS
#define DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS
constexpr double EPS
Floating point comparison epsilon.
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:82
#define _(s)
double m_DiffSkewColourInterpolationLogStrength
The logarithmic weighting factor to apply to colour interpolation in the diff phase overlay tool.
double m_DiffSkewTrackGapInflation
The multiplier of constraint diff pair gap to allow identification of coupled track segments in the d...
double m_DiffSkewTargetDiffSegmentSize
The target size (in PCB IU) of diff phase skew gradient overlay segments.
double m_DiffSkewCosThetaParallelTestValue
The value of cos(theta) between two tracks used to test for parallelism in the diff phase skew overla...
double m_DiffSkewOverlayTrackInflation
The multiplier of underlying track size applied to the diff phase skew overlay.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ RPT_SEVERITY_IGNORE
char * GetLine(FILE *File, char *Line, int *LineNum, int SizeLine)
Read one line line from aFile.
Builds the length / delay calculation items from a given path.
Builds the final overlay output segments for plotting.
VECTOR2D Start
The start point of the segment.
VECTOR2D End
The end point of the segment.
bool RelativeValueKnown
Flag whether the diff value is valid at this segment.
double RelativeValueAtMid
The value of the diff at the beginning of this segment.
Builds a vector in which each entry represents the cumulative length and delay at the start of a give...
Used to represent the results of a call to CalculateLengthDetails, including inferred via-in-pad deta...
int64_t InferredEndViaLength
The length of an inferred end via-in-pad.
int64_t InferredStartViaLength
The length of an inferred start via-in-pad.
int64_t InferredEndViaDelay
The delay of an inferred end via-in-pad.
std::vector< std::pair< int64_t, int64_t > > LengthsAndDelays
Per-item lengths and delays.
int64_t InferredStartViaDelay
The delay of an inferred start via-in-pad.
Struct to represent one segment where tracks run parallel, including information about absolute and r...
VECTOR2I endA
The ending coordinate of the run on track A.
double endDelayA
Cumulative delay of track A at the start of the parallel run.
double startLenA
Cumulative length of track A at the start of the parallel run.
size_t segB
The index of the parallel segment on track B.
VECTOR2I startA
The starting coordinate of the run on track A.
VECTOR2I startB
The starting coordinate of the run on track B.
double ta0
Normalised values of the start (0) and end (1) coordinates on track A and B These are normalised to t...
double startDelayA
Cumulative delay of track A at the start of the parallel run.
double startLenB
Cumulative length of track B at the start of the parallel run.
double endLenA
Cumulative length of track A at the end of the parallel run.
double endDelayB
Cumulative delay of track A at the start of the parallel run.
double startDelayB
Cumulative delay of track A at the start of the parallel run.
double endLenB
Cumulative length of track B at the end of the parallel run.
size_t segA
The index of the parallel segment on track A.
VECTOR2I endB
The ending coordinate of the run on track B.
Struct to control which optimisations the length calculation code runs on the given path objects.
static VECTOR2I SnapToNearestTrack(const VECTOR2I &aP, BOARD *aBoard, NETINFO_ITEM *aNet, PCB_TRACK **aNearestTrack)
static LINKED_ITEM * PickSegment(ROUTER *aRouter, const VECTOR2I &aWhere, int aLayer, VECTOR2I &aPointOut, const SHAPE_LINE_CHAIN &aBaseline=SHAPE_LINE_CHAIN())
Represents a single line in the tuning profile configuration grid.
wxString result
Test unit parsing edge cases and error handling.
@ BUT_LEFT
Definition tool_event.h:132
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:60
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686