KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_setup_tuning_profile_info.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 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24
26
28#include <bitmaps.h>
29#include <confirm.h>
30#include <grid_tricks.h>
32#include <pcb_edit_frame.h>
36#include <widgets/wx_grid.h>
37
39 PANEL_SETUP_TUNING_PROFILES* parentPanel ) :
40 PANEL_SETUP_TUNING_PROFILE_INFO_BASE( aParentWindow, wxID_ANY, wxPoint( -1000, -1000 ) ),
41 m_parentPanel( parentPanel ),
44{
45 Freeze();
46 initPanel();
47 Thaw();
48}
49
50
52{
53 if( EDA_UNIT_UTILS::IsImperialUnit( m_parentPanel->m_unitsProvider->GetUserUnits() ) )
55 else
57
59
60 int x = 0, y = 0;
61 m_name->GetTextExtent( "XXXXXXXXXXXXXXXXXXXXX", &x, &y );
62 m_name->SetMinSize( wxSize( x, -1 ) );
63 m_targetImpedance->GetTextExtent( "XXXXXXXXX", &x, &y );
64 m_targetImpedance->SetMinSize( wxSize( x, -1 ) );
65 GetTextExtent( "GHZ XXXX", &x, &y );
66 m_frequencyUnits->SetMinSize( wxSize( x, -1 ) );
67
68 m_viaPropagationUnits.SetValue( 0 );
69
72
75
76 m_targetImpedance->SetValue( "0" );
77
78 UNITS_PROVIDER* unitsProvider = m_parentPanel->m_unitsProvider.get();
79
80 m_trackPropagationGrid->SetUnitsProvider( unitsProvider );
81 m_viaOverrides->SetUnitsProvider( unitsProvider );
82
83 // Configure the track grid
84 m_trackPropagationGrid->BeginBatch();
85 m_trackPropagationGrid->SetUseNativeColLabels();
86
87 m_trackPropagationGrid->EnsureColLabelsVisible();
89 m_trackPropagationGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
90
91 std::vector<int> trackColIds;
92 m_trackPropagationGrid->SetAutoEvalColUnits( TRACK_GRID_DELAY,
94 trackColIds.push_back( TRACK_GRID_DELAY );
96 unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::DISTANCE ) );
97 trackColIds.push_back( TRACK_GRID_TRACK_WIDTH );
98 m_trackPropagationGrid->SetAutoEvalColUnits( TRACK_GRID_TRACK_GAP,
99 unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::DISTANCE ) );
100 trackColIds.push_back( TRACK_GRID_TRACK_GAP );
101 m_trackPropagationGrid->SetAutoEvalCols( trackColIds );
102
103 // Add the calculation editors
104 wxGridCellAttr* attr = new wxGridCellAttr;
105 attr->SetEditor( new GRID_CELL_RUN_FUNCTION_EDITOR(
106 m_parentPanel->m_dlg,
107 [this]( int row, int col )
108 {
109 calculateTrackParametersForCell( row, col );
110 },
111 false ) );
113
114 attr = new wxGridCellAttr;
115 attr->SetEditor( new GRID_CELL_RUN_FUNCTION_EDITOR(
116 m_parentPanel->m_dlg,
117 [this]( int row, int col )
118 {
119 calculateTrackParametersForCell( row, col );
120 },
121 false ) );
122 m_trackPropagationGrid->SetColAttr( TRACK_GRID_TRACK_GAP, attr );
123
124 attr = new wxGridCellAttr;
125 attr->SetEditor( new GRID_CELL_RUN_FUNCTION_EDITOR(
126 m_parentPanel->m_dlg,
127 [this]( int row, int col )
128 {
129 calculateTrackParametersForCell( row, col );
130 },
131 false ) );
132 m_trackPropagationGrid->SetColAttr( TRACK_GRID_DELAY, attr );
133
134 m_trackPropagationGrid->EndBatch();
135
136 // Configure the via grid
137 m_viaOverrides->BeginBatch();
138 m_viaOverrides->SetUseNativeColLabels();
139
140 m_viaOverrides->EnsureColLabelsVisible();
141 m_viaOverrides->PushEventHandler( new GRID_TRICKS( m_viaOverrides ) );
142 m_viaOverrides->SetSelectionMode( wxGrid::wxGridSelectRows );
143
144 std::vector<int> viaColIds;
145 m_viaOverrides->SetAutoEvalColUnits( VIA_GRID_DELAY, unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::TIME ) );
146 viaColIds.push_back( VIA_GRID_DELAY );
147 m_viaOverrides->SetAutoEvalCols( viaColIds );
148 m_viaOverrides->EndBatch();
149
151
152 // Hide the trace gap as we start in single mode
154
156 Layout();
157}
158
159
161{
162 BOARD* board = m_parentPanel->m_board;
163
164 m_name->SetValue( aProfile.m_ProfileName );
165 m_type->SetSelection( static_cast<int>( aProfile.m_Type ) );
166 onChangeProfileType( aProfile.m_Type );
167 m_targetImpedance->SetValue( wxString::FromDouble( aProfile.m_TargetImpedance ) );
168 m_enableDelayTuning->SetValue( aProfile.m_EnableTimeDomainTuning );
169 m_modelSolderMask->SetValue( aProfile.m_ModelSolderMask );
170 m_viaPropagationUnits.SetValue( aProfile.m_ViaPropagationDelay );
171
172 double frequency = aProfile.m_Frequency;
173
174 if( frequency >= 1e9 )
175 {
176 frequency /= 1e9;
177 m_frequencyUnits->SetSelection( 3 );
178 }
179 else if( frequency >= 1e6 )
180 {
181 frequency /= 1e6;
182 m_frequencyUnits->SetSelection( 2 );
183 }
184 else if( frequency >= 1e3 )
185 {
186 frequency /= 1e3;
187 m_frequencyUnits->SetSelection( 1 );
188 }
189 else
190 {
191 m_frequencyUnits->SetSelection( 0 );
192 }
193
194 m_frequency->SetValue( wxString::FromDouble( frequency ) );
195
196 for( const auto& entry : aProfile.m_TrackPropagationEntries )
197 {
198 const int row = m_trackPropagationGrid->GetNumberRows();
199 m_trackPropagationGrid->AppendRows();
200
202 board->GetLayerName( entry.GetSignalLayer() ) );
203
204 if( entry.GetTopReferenceLayer() != UNDEFINED_LAYER )
205 {
207 board->GetLayerName( entry.GetTopReferenceLayer() ) );
208 }
209
210 if( entry.GetBottomReferenceLayer() != UNDEFINED_LAYER )
211 {
213 board->GetLayerName( entry.GetBottomReferenceLayer() ) );
214 }
215
216 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_TRACK_WIDTH, entry.GetWidth() );
217 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_TRACK_GAP, entry.GetDiffPairGap() );
218 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_DELAY, entry.GetDelay( true ) );
219 }
220
221 for( const auto& entry : aProfile.m_ViaOverrides )
222 {
223 const int row = m_viaOverrides->GetNumberRows();
224 m_viaOverrides->AppendRows();
225
226 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM, board->GetLayerName( entry.m_SignalLayerFrom ) );
227 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO, board->GetLayerName( entry.m_SignalLayerTo ) );
228 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM, board->GetLayerName( entry.m_ViaLayerFrom ) );
229 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO, board->GetLayerName( entry.m_ViaLayerTo ) );
230 m_viaOverrides->SetUnitValue( row, VIA_GRID_DELAY, entry.m_Delay );
231 }
232
234}
235
236
238{
239 TUNING_PROFILE profile;
240 profile.m_ProfileName = m_name->GetValue();
241 profile.m_Type = static_cast<TUNING_PROFILE::PROFILE_TYPE>( m_type->GetSelection() );
242 profile.m_EnableTimeDomainTuning = m_enableDelayTuning->GetValue();
243 profile.m_ModelSolderMask = m_modelSolderMask->GetValue();
244 profile.m_ViaPropagationDelay = m_viaPropagationUnits.GetIntValue();
245
246 double targetImpedance = 0.0;
247
248 if( m_targetImpedance->GetValue().ToDouble( &targetImpedance ) )
249 profile.m_TargetImpedance = targetImpedance;
250 else
251 profile.m_TargetImpedance = 0.0;
252
253 profile.m_Frequency = getFrequency();
254
255 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); row++ )
256 {
258
259 wxString signalLayerName = m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_SIGNAL_LAYER );
260 entry.SetSignalLayer( m_parentPanel->m_layerNamesToIDs[signalLayerName] );
261
262 if( wxString topReferenceLayerName = m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_TOP_REFERENCE );
263 m_parentPanel->m_layerNamesToIDs.contains( topReferenceLayerName ) )
264 {
265 entry.SetTopReferenceLayer( m_parentPanel->m_layerNamesToIDs[topReferenceLayerName] );
266 }
267 else
268 {
270 }
271
272 if( wxString bottomReferenceLayerName =
274 m_parentPanel->m_layerNamesToIDs.contains( bottomReferenceLayerName ) )
275 {
276 entry.SetBottomReferenceLayer( m_parentPanel->m_layerNamesToIDs[bottomReferenceLayerName] );
277 }
278 else
279 {
281 }
282
283 entry.SetWidth( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_TRACK_WIDTH ) );
284 entry.SetDiffPairGap( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_TRACK_GAP ) );
285 entry.SetDelay( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_DELAY ) );
287
288 profile.m_TrackPropagationEntries.push_back( entry );
289 profile.m_TrackPropagationEntriesMap[entry.GetSignalLayer()] = entry;
290 }
291
292 for( int row = 0; row < m_viaOverrides->GetNumberRows(); row++ )
293 {
294 const wxString signalLayerFrom = m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM );
295 const wxString signalLayerTo = m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO );
296 const wxString viaLayerFrom = m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_FROM );
297 const wxString viaLayerTo = m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_TO );
298 PCB_LAYER_ID signalLayerIdFrom = m_parentPanel->m_layerNamesToIDs[signalLayerFrom];
299 PCB_LAYER_ID signalLayerIdTo = m_parentPanel->m_layerNamesToIDs[signalLayerTo];
300 PCB_LAYER_ID viaLayerIdFrom = m_parentPanel->m_layerNamesToIDs[viaLayerFrom];
301 PCB_LAYER_ID viaLayerIdTo = m_parentPanel->m_layerNamesToIDs[viaLayerTo];
302
303 // Order layers in stackup order (from F_Cu first)
304 if( IsCopperLayerLowerThan( signalLayerIdFrom, signalLayerIdTo ) )
305 std::swap( signalLayerIdFrom, signalLayerIdTo );
306
307 if( IsCopperLayerLowerThan( viaLayerIdFrom, viaLayerIdTo ) )
308 std::swap( viaLayerIdFrom, viaLayerIdTo );
309
310 const DELAY_PROFILE_VIA_OVERRIDE_ENTRY entry{ signalLayerIdFrom, signalLayerIdTo, viaLayerIdFrom, viaLayerIdTo,
311 m_viaOverrides->GetUnitValue( row, VIA_GRID_DELAY ) };
312 profile.m_ViaOverrides.push_back( entry );
313 }
314
315 return profile;
316}
317
318
320{
321 m_trackPropagationGrid->PopEventHandler( true );
322 m_viaOverrides->PopEventHandler( true );
323}
324
325
327{
328 wxArrayString layerNames, layerNamesWithNone;
329 layerNamesWithNone.push_back( "<None>" );
330 std::ranges::for_each( m_parentPanel->m_layerNames,
331 [&layerNames, &layerNamesWithNone]( const wxString& aLayerName )
332 {
333 layerNames.push_back( aLayerName );
334 layerNamesWithNone.push_back( aLayerName );
335 } );
336
337
338 // Save the current data - track grid
339 std::vector<wxString> currentSignalLayer;
340 std::vector<wxString> currentTopReferenceLayer;
341 std::vector<wxString> currentBottomReferenceLayer;
342
343 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); ++row )
344 {
345 currentSignalLayer.emplace_back( m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_SIGNAL_LAYER ) );
346 currentTopReferenceLayer.emplace_back( m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_TOP_REFERENCE ) );
347 currentBottomReferenceLayer.emplace_back(
349 }
350
351 // Save the current data - via grid
352 std::vector<wxString> currentSignalLayersFrom;
353 std::vector<wxString> currentSignalLayersTo;
354 std::vector<wxString> currentViaLayersFrom;
355 std::vector<wxString> currentViaLayersTo;
356
357 for( int row = 0; row < m_viaOverrides->GetNumberRows(); ++row )
358 {
359 currentSignalLayersFrom.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM ) );
360 currentSignalLayersTo.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO ) );
361 currentViaLayersFrom.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_FROM ) );
362 currentViaLayersTo.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_TO ) );
363 }
364
365 // Reset the via layers lists
366 wxGridCellAttr* attr = new wxGridCellAttr;
367 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
369
370 attr = new wxGridCellAttr;
371 attr->SetEditor( new wxGridCellChoiceEditor( layerNamesWithNone, false ) );
373
374 attr = new wxGridCellAttr;
375 attr->SetEditor( new wxGridCellChoiceEditor( layerNamesWithNone, false ) );
377
378 attr = new wxGridCellAttr;
379 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
380 m_viaOverrides->SetColAttr( VIA_GRID_SIGNAL_LAYER_FROM, attr );
381
382 attr = new wxGridCellAttr;
383 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
384 m_viaOverrides->SetColAttr( VIA_GRID_SIGNAL_LAYER_TO, attr );
385
386 attr = new wxGridCellAttr;
387 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
388 m_viaOverrides->SetColAttr( VIA_GRID_VIA_LAYER_FROM, attr );
389
390 attr = new wxGridCellAttr;
391 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
392 m_viaOverrides->SetColAttr( VIA_GRID_VIA_LAYER_TO, attr );
393
394 // Restore the data, changing or resetting layer names if required
395 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); ++row )
396 {
397 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentSignalLayer[row] ) )
398 {
399 PCB_LAYER_ID lastSignalId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayer[row]];
400
401 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalId ) )
403 m_parentPanel->m_board->GetLayerName( lastSignalId ) );
404 else
406 m_parentPanel->m_layerNames.front() );
407 }
408 else
409 {
410 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_SIGNAL_LAYER, m_parentPanel->m_layerNames.front() );
411 }
412
413 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentTopReferenceLayer[row] ) )
414 {
415 const PCB_LAYER_ID lastTopReferenceId = m_parentPanel->m_prevLayerNamesToIDs[currentTopReferenceLayer[row]];
416
417 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastTopReferenceId ) )
419 m_parentPanel->m_board->GetLayerName( lastTopReferenceId ) );
420 else
421 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_TOP_REFERENCE, layerNamesWithNone[0] );
422 }
423 else
424 {
425 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_TOP_REFERENCE, layerNamesWithNone[0] );
426 }
427
428 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentBottomReferenceLayer[row] ) )
429 {
430 const PCB_LAYER_ID lastBottomReferenceId =
431 m_parentPanel->m_prevLayerNamesToIDs[currentBottomReferenceLayer[row]];
432
433 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastBottomReferenceId ) )
435 m_parentPanel->m_board->GetLayerName( lastBottomReferenceId ) );
436 else
437 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, layerNamesWithNone[0] );
438 }
439 else
440 {
441 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, layerNamesWithNone[0] );
442 }
443 }
444
445 for( int row = 0; row < m_viaOverrides->GetNumberRows(); ++row )
446 {
447 const PCB_LAYER_ID lastSignalFromId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayersFrom[row]];
448
449 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalFromId ) )
450 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM,
451 m_parentPanel->m_board->GetLayerName( lastSignalFromId ) );
452 else
453 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM, m_parentPanel->m_layerNames.front() );
454
455 const PCB_LAYER_ID lastSignalToId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayersTo[row]];
456
457 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalToId ) )
458 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO,
459 m_parentPanel->m_board->GetLayerName( lastSignalToId ) );
460 else
461 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO, m_parentPanel->m_layerNames.back() );
462
463 const PCB_LAYER_ID lastViaFromId = m_parentPanel->m_prevLayerNamesToIDs[currentViaLayersFrom[row]];
464
465 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastViaFromId ) )
466 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM,
467 m_parentPanel->m_board->GetLayerName( lastViaFromId ) );
468 else
469 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM, m_parentPanel->m_layerNames.front() );
470
471 const PCB_LAYER_ID lastViaToId = m_parentPanel->m_prevLayerNamesToIDs[currentViaLayersTo[row]];
472
473 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastViaToId ) )
474 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO,
475 m_parentPanel->m_board->GetLayerName( lastViaToId ) );
476 else
477 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO, m_parentPanel->m_layerNames.back() );
478 }
479}
480
481
483{
484 const int minValueWidth = m_trackPropagationGrid->GetTextExtent( wxT( "000.0000 ps/mm" ) ).x;
485
486 for( int i = 0; i < m_trackPropagationGrid->GetNumberCols(); ++i )
487 {
488 const int titleSize = m_trackPropagationGrid->GetTextExtent( m_trackPropagationGrid->GetColLabelValue( i ) ).x;
489
491 m_trackPropagationGrid->SetColSize( i, titleSize + 30 );
492 else
493 m_trackPropagationGrid->SetColSize( i, std::max( titleSize, minValueWidth ) );
494 }
495
496 for( int i = 0; i < m_viaOverrides->GetNumberCols(); ++i )
497 {
498 const int titleSize = GetTextExtent( m_viaOverrides->GetColLabelValue( i ) ).x;
499 if( i == VIA_GRID_DELAY )
500 m_viaOverrides->SetColSize( i, std::max( titleSize, minValueWidth ) );
501 else
502 m_viaOverrides->SetColSize( i, titleSize + 30 );
503 }
504
505 const int impedanceWidth = m_targetImpedance->GetTextExtent( wxT( "0000.00" ) ).x;
506 m_targetImpedance->SetSize( impedanceWidth, m_targetImpedance->GetSize().GetHeight() );
507
508 Layout();
509}
510
511
513{
514 const wxString newName = event.GetString();
515 m_parentPanel->UpdateProfileName( this, newName );
516}
517
518
526
527
529{
530 m_trackPropagationGrid->CommitPendingChanges();
531 m_viaOverrides->CommitPendingChanges();
532
535 else
537}
538
539
541{
542 const int numRows = m_trackPropagationGrid->GetNumberRows();
543 m_trackPropagationGrid->InsertRows( m_trackPropagationGrid->GetNumberRows() );
544
545 auto setFrontRowLayers = [&]( const int row )
546 {
547 auto nameItr = m_parentPanel->m_layerNames.begin();
548
549 if( nameItr == m_parentPanel->m_layerNames.end() )
550 return;
551
552 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_SIGNAL_LAYER, *nameItr );
553
554 ++nameItr;
555
556 if( nameItr == m_parentPanel->m_layerNames.end() )
557 return;
558
559 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, *nameItr );
560 };
561
562 auto setRowLayers = [&]()
563 {
564 if( numRows == 0 )
565 {
566 setFrontRowLayers( 0 );
567 return;
568 }
569
570 const wxString lastSignalLayerName =
571 m_trackPropagationGrid->GetCellValue( numRows - 1, TRACK_GRID_SIGNAL_LAYER );
572 auto nameItr = std::find( m_parentPanel->m_layerNames.begin(), m_parentPanel->m_layerNames.end(),
573 lastSignalLayerName );
574
575 if( nameItr == m_parentPanel->m_layerNames.end() )
576 return;
577
578 if( nameItr == m_parentPanel->m_layerNames.end() - 1 )
579 {
580 setFrontRowLayers( numRows );
581 return;
582 }
583
584 ++nameItr;
585
586 if( nameItr == m_parentPanel->m_layerNames.end() )
587 return;
588
589 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_SIGNAL_LAYER, *nameItr );
590 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_TOP_REFERENCE, *( nameItr - 1 ) );
591
592 ++nameItr;
593
594 if( nameItr != m_parentPanel->m_layerNames.end() )
595 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_BOTTOM_REFERENCE, *nameItr );
596 };
597
598 setRowLayers();
599
600 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_TRACK_WIDTH, 0 );
601 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_TRACK_GAP, 0 );
602 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_DELAY, 0 );
604}
605
606
608{
609 wxArrayInt selRows = m_trackPropagationGrid->GetSelectedRows();
610
611 if( selRows.size() == 1 )
612 m_trackPropagationGrid->DeleteRows( selRows[0] );
613}
614
615
617{
618 const int numRows = m_viaOverrides->GetNumberRows();
619 m_viaOverrides->InsertRows( numRows );
620 m_viaOverrides->SetUnitValue( numRows, VIA_GRID_DELAY, 0 );
621 m_viaOverrides->SetCellValue( numRows, VIA_GRID_SIGNAL_LAYER_FROM, m_parentPanel->m_layerNames.front() );
622 m_viaOverrides->SetCellValue( numRows, VIA_GRID_SIGNAL_LAYER_TO, m_parentPanel->m_layerNames.back() );
623 m_viaOverrides->SetCellValue( numRows, VIA_GRID_VIA_LAYER_FROM, m_parentPanel->m_layerNames.front() );
624 m_viaOverrides->SetCellValue( numRows, VIA_GRID_VIA_LAYER_TO, m_parentPanel->m_layerNames.back() );
626}
627
628
630{
631 wxArrayInt selRows = m_viaOverrides->GetSelectedRows();
632
633 if( selRows.size() == 1 )
634 m_viaOverrides->DeleteRows( selRows[0] );
635}
636
637
639{
640 return m_name->GetValue();
641}
642
643
644double PANEL_SETUP_TUNING_PROFILE_INFO::calculateSkinDepth( const double aFreq, const double aMurc,
645 const double aSigma )
646{
647 return 1.0 / sqrt( M_PI * aFreq * aMurc * TRANSLINE_CALCULATIONS::MU0 * aSigma );
648}
649
650
651int PANEL_SETUP_TUNING_PROFILE_INFO::getStackupLayerId( const std::vector<BOARD_STACKUP_ITEM*>& aLayerList,
652 PCB_LAYER_ID aPcbLayerId )
653{
654 bool layerFound = false;
655 int layerStackupId = 0;
656
657 while( layerStackupId < static_cast<int>( aLayerList.size() ) && !layerFound )
658 {
659 if( aLayerList.at( layerStackupId )->GetBrdLayerId() != aPcbLayerId )
660 ++layerStackupId;
661 else
662 layerFound = true;
663 }
664
665 if( !layerFound )
666 return -1;
667
668 return layerStackupId;
669}
670
671
673{
674 const wxString zStr = m_targetImpedance->GetValue();
675
676 double z;
677 if( !zStr.ToDouble( &z ) )
678 z = -1;
679
680 return z;
681}
682
683
685 const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList, const std::vector<int>& dielectricLayerStackupIds,
686 const EDA_IU_SCALE& aIuScale )
687{
688 double totalHeight = 0.0;
689 double e_r = 0.0;
690 double lossTangent = 0.0;
691
692 for( int i : dielectricLayerStackupIds )
693 {
694 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( i );
695
696 for( int subLayerIdx = 0; subLayerIdx < layer->GetSublayersCount(); ++subLayerIdx )
697 {
698 totalHeight += aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
699
700 // Correct for dielectric frequency-dependent model if required
701 double e_r_layer = layer->GetEpsilonR( subLayerIdx );
702 double l_t_layer = layer->GetLossTangent( subLayerIdx );
703 const double spec_freq = layer->GetSpecFreq( subLayerIdx );
704 const double target_freq = getFrequency();
705
707 && std::isfinite( spec_freq ) && spec_freq > 0.0 )
708 {
709 try
710 {
712 ds.Fit( e_r_layer, l_t_layer, spec_freq );
713 e_r_layer = ds.EpsilonRealAt( target_freq );
714 l_t_layer = ds.TanDeltaAt( target_freq );
715 }
716 catch( const std::invalid_argument& )
717 {
718 // Ignore
719 }
720 }
721
722 e_r += e_r_layer * aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
723 lossTangent += l_t_layer * aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
724 }
725 }
726
727 e_r = e_r / totalHeight;
728 lossTangent = lossTangent / totalHeight;
729 totalHeight /= 1000.0; // Convert from mm to m
730
731 return { totalHeight, e_r, lossTangent };
732}
733
734
735void PANEL_SETUP_TUNING_PROFILE_INFO::getDielectricLayers( const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList,
736 const int aSignalLayerId, const int aReferenceLayerId,
737 std::vector<int>& aDielectricLayerStackupIds )
738{
739 for( int i = std::min( aSignalLayerId, aReferenceLayerId ) + 1; i < std::max( aSignalLayerId, aReferenceLayerId );
740 ++i )
741 {
742 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( i );
743
744 if( layer->GetType() != BS_ITEM_TYPE_DIELECTRIC )
745 continue;
746
747 if( !layer->HasEpsilonRValue() )
748 continue;
749
750 aDielectricLayerStackupIds.push_back( i );
751 }
752}
753
754
756PANEL_SETUP_TUNING_PROFILE_INFO::getSolderMaskParameters( const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList,
757 const EDA_IU_SCALE& aScale, PCB_LAYER_ID aSignalLayerId )
758{
759 PCB_LAYER_ID maskLayerId = UNDEFINED_LAYER;
760
761 if( aSignalLayerId == F_Cu )
762 maskLayerId = F_Mask;
763 else if( aSignalLayerId == B_Cu )
764 maskLayerId = B_Mask;
765 else
766 return { 0.0, 0.0, 0.0 };
767
768 const int layerIdx = getStackupLayerId( aStackupLayerList, maskLayerId );
769
770 if( layerIdx < 0 )
771 return { 0.0, 0.0, 0.0 };
772
773 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( layerIdx );
774 const double thickness = aScale.IUTomm( layer->GetThickness() ) / 1000.0;
775
776 return { thickness, layer->GetEpsilonR(), layer->GetLossTangent() };
777}
778
779
781{
782 // Determine if this is a stripline or microstrip geometry
783 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
784
785 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
786 return;
787
788 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs.at( signalLayerName );
789 const TUNING_PROFILE::PROFILE_TYPE profileType = m_type->GetSelection() == 0
792 const bool isMicrostrip = IsFrontLayer( signalLayer ) || IsBackLayer( signalLayer );
793 CalculationType calculationType;
794
795 switch( aCol )
796 {
797 case TRACK_GRID_TRACK_WIDTH: calculationType = CalculationType::WIDTH; break;
798 case TRACK_GRID_TRACK_GAP: calculationType = CalculationType::GAP; break;
799 case TRACK_GRID_DELAY: calculationType = CalculationType::DELAY; break;
800 default: calculationType = CalculationType::WIDTH; break;
801 }
802
803 if( profileType == TUNING_PROFILE::PROFILE_TYPE::DIFFERENTIAL ) // Differential tracks mode
804 {
805 int calculatedWidth = 0;
806 int calculatedGap = 0;
807 int calculatedDelay = 0;
808
810
811 if( isMicrostrip )
812 result = calculateDifferentialMicrostrip( aRow, calculationType );
813 else
814 result = calculateDifferentialStripline( aRow, calculationType );
815
816 if( !result.OK )
817 {
818 DisplayErrorMessage( m_parentPanel->m_dlg, wxString::Format( _( "Error: %s" ), result.ErrorMsg ) );
819 return;
820 }
821
822 calculatedWidth = result.Width;
823 calculatedGap = result.DiffPairGap;
824 calculatedDelay = result.Delay;
825
826 const bool widthOk = calculatedWidth > 0;
827 const bool gapOk = calculatedGap > 0;
828 const bool delayOk = calculatedDelay > 0;
829
830 if( !widthOk )
831 {
832 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track width" ) );
833 return;
834 }
835 else if( !gapOk )
836 {
837 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute differential pair gap" ) );
838 return;
839 }
840 else if( !delayOk )
841 {
842 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track propagation delay" ) );
843 return;
844 }
845
846 if( calculationType == CalculationType::WIDTH )
847 {
848 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH, calculatedWidth );
849 }
850 else if( calculationType == CalculationType::GAP )
851 {
852 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_GAP, calculatedGap );
853 }
854
855 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_DELAY, calculatedDelay );
856 }
857 else // Single track mode
858 {
859 int calculatedWidth = 0;
860 int calculatedDelay = 0;
861
863
864 if( isMicrostrip )
865 result = calculateSingleMicrostrip( aRow, calculationType );
866 else
867 result = calculateSingleStripline( aRow, calculationType );
868
869 if( !result.OK )
870 {
871 DisplayErrorMessage( m_parentPanel->m_dlg, wxString::Format( _( "Error: %s" ), result.ErrorMsg ) );
872 return;
873 }
874
875 calculatedWidth = result.Width;
876 calculatedDelay = result.Delay;
877
878 const bool widthOk = calculatedWidth > 0;
879 const bool delayOk = calculatedDelay > 0;
880
881 if( !widthOk )
882 {
883 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track width" ) );
884 return;
885 }
886 else if( !delayOk )
887 {
888 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track propagation delay" ) );
889 return;
890 }
891
892 if( calculationType == CalculationType::WIDTH )
893 {
894 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH, calculatedWidth );
895 }
896
897 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_DELAY, calculatedDelay );
898 }
899}
900
901
903{
904 if( m_name->GetValue() == wxEmptyString )
905 {
906 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
907
908 const wxString msg = _( "Tuning profile must have a name" );
910 return false;
911 }
912
913 std::set<wxString> layerNames;
914
915 for( int i = 0; i < m_trackPropagationGrid->GetNumberRows(); ++i )
916 {
917 const wxString& layerName = m_trackPropagationGrid->GetCellValue( i, TRACK_GRID_SIGNAL_LAYER );
918
919 if( layerNames.contains( layerName ) )
920 {
921 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
922
923 const wxString msg = _( "Duplicated signal layer configuration in tuning profile" );
926 return false;
927 }
928
929 layerNames.insert( layerName );
930 }
931
932 return true;
933}
934
935
936/*****************************************************************************************************************
937 * SIMULATION / ANALYSIS PLUMBING
938 ****************************************************************************************************************/
939
943{
944 // Get the signal layer information from the stackup
945 BOARD_STACKUP stackup = m_parentPanel->m_board->GetStackupOrDefault();
946 const std::vector<BOARD_STACKUP_ITEM*>& stackupLayerList = stackup.GetList();
947
948 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
949
950 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
951 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
952
953 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs[signalLayerName];
954
955 // Microstrip can only be on an outer copper layer
956 if( signalLayer != F_Cu && signalLayer != B_Cu )
957 return { {}, CALCULATION_RESULT{ _( "Internal error: Microstrip can only be on an outer copper layer" ) } };
958
959 const int signalLayerStackupId = getStackupLayerId( stackupLayerList, signalLayer );
960
961 if( signalLayerStackupId == -1 )
962 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
963
964 const double signalLayerThickness =
965 aScale.IUTomm( stackupLayerList.at( signalLayerStackupId )->GetThickness() ) / 1000.0;
966
967 if( signalLayerThickness <= 0 )
968 return { {}, CALCULATION_RESULT{ _( "Signal layer thickness must be greater than 0" ) } };
969
970 // Get reference layer
971 wxString referenceLayerName;
972
973 if( signalLayer == F_Cu )
974 referenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_BOTTOM_REFERENCE );
975 else
976 referenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_TOP_REFERENCE );
977
978 if( !m_parentPanel->m_layerNamesToIDs.contains( referenceLayerName ) )
979 return { {}, CALCULATION_RESULT{ _( "Reference layer not found in stackup" ) } };
980
981 const PCB_LAYER_ID referenceLayer = m_parentPanel->m_layerNamesToIDs[referenceLayerName];
982 const int referenceLayerStackupId = getStackupLayerId( stackupLayerList, referenceLayer );
983
984 if( signalLayerStackupId == referenceLayerStackupId )
985 return { {}, CALCULATION_RESULT{ _( "Reference layer must be different to signal layer" ) } };
986
987 // Get the dielectric layers between signal and reference layers
988 std::vector<int> dielectricLayerStackupIds;
989 getDielectricLayers( stackupLayerList, signalLayerStackupId, referenceLayerStackupId, dielectricLayerStackupIds );
990
991 // Calculate geometric average of the dielectric materials
992 const DIELECTRIC_INFO dielectricInfo =
993 calculateAverageDielectricConstants( stackupLayerList, dielectricLayerStackupIds, aScale );
994
995 if( dielectricInfo.Height <= 0.0 )
996 return { {}, CALCULATION_RESULT{ _( "Dielectric height must be greater than 0" ) } };
997
998 // Get solder mask parameters
999 const DIELECTRIC_INFO solderMaskInfo = getSolderMaskParameters( stackupLayerList, aScale, signalLayer );
1000
1001 CALCULATION_BOARD_PARAMETERS boardParameters{ signalLayer,
1002 dielectricInfo.E_r,
1003 dielectricInfo.Height,
1004 0.0,
1005 signalLayerThickness,
1006 dielectricInfo.Loss_Tangent,
1007 solderMaskInfo.E_r,
1008 solderMaskInfo.Height,
1009 solderMaskInfo.Loss_Tangent };
1011 result.OK = true;
1012
1013 return { boardParameters, result };
1014}
1015
1016
1020{
1021 // Get the signal layer information from the stackup
1022 BOARD_STACKUP stackup = m_parentPanel->m_board->GetStackupOrDefault();
1023 const std::vector<BOARD_STACKUP_ITEM*>& stackupLayerList = stackup.GetList();
1024
1025 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
1026
1027 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
1028 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
1029
1030 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs[signalLayerName];
1031 const int signalLayerStackupId = getStackupLayerId( stackupLayerList, signalLayer );
1032
1033 if( signalLayerStackupId == -1 )
1034 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
1035
1036 const double signalLayerThickness =
1037 aScale.IUTomm( stackupLayerList.at( signalLayerStackupId )->GetThickness() ) / 1000.0;
1038
1039 if( signalLayerThickness <= 0 )
1040 return { {}, CALCULATION_RESULT{ _( "Signal layer thickness must be greater than 0" ) } };
1041
1042 // Get top reference layer
1043 const wxString topReferenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_TOP_REFERENCE );
1044
1045 if( !m_parentPanel->m_layerNamesToIDs.contains( topReferenceLayerName ) )
1046 return { {}, CALCULATION_RESULT{ _( "Top reference layer not found in stackup" ) } };
1047
1048 const PCB_LAYER_ID topReferenceLayer = m_parentPanel->m_layerNamesToIDs[topReferenceLayerName];
1049 const int topReferenceLayerStackupId = getStackupLayerId( stackupLayerList, topReferenceLayer );
1050
1051 if( !IsCopperLayerLowerThan( signalLayer, topReferenceLayer ) )
1052 return { {}, CALCULATION_RESULT{ _( "Top reference layer must be above signal layer in board stackup" ) } };
1053
1054 // Get bottom reference layer
1055 wxString bottomReferenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_BOTTOM_REFERENCE );
1056
1057 if( !m_parentPanel->m_layerNamesToIDs.contains( bottomReferenceLayerName ) )
1058 return { {}, CALCULATION_RESULT{ _( "Bottom reference layer not found in stackup" ) } };
1059
1060 const PCB_LAYER_ID bottomReferenceLayer = m_parentPanel->m_layerNamesToIDs[bottomReferenceLayerName];
1061 const int bottomReferenceLayerStackupId = getStackupLayerId( stackupLayerList, bottomReferenceLayer );
1062
1063 if( !IsCopperLayerLowerThan( bottomReferenceLayer, signalLayer ) )
1064 return { {}, CALCULATION_RESULT{ _( "Bottom reference layer must be below signal layer in board stackup" ) } };
1065
1066 // Get the dielectric layers between signal and reference layers
1067 std::vector<int> topDielectricLayerStackupIds, bottomDielectricLayerStackupIds;
1068
1069 getDielectricLayers( stackupLayerList, signalLayerStackupId, topReferenceLayerStackupId,
1070 topDielectricLayerStackupIds );
1071 getDielectricLayers( stackupLayerList, signalLayerStackupId, bottomReferenceLayerStackupId,
1072 bottomDielectricLayerStackupIds );
1073
1074 // Calculate geometric average of the dielectric materials
1075 std::vector<int> allDielectricLayerStackupIds( topDielectricLayerStackupIds );
1076 allDielectricLayerStackupIds.insert( allDielectricLayerStackupIds.end(), bottomDielectricLayerStackupIds.begin(),
1077 bottomDielectricLayerStackupIds.end() );
1078
1079 const DIELECTRIC_INFO topDielectricInfo =
1080 calculateAverageDielectricConstants( stackupLayerList, topDielectricLayerStackupIds, aScale );
1081 const DIELECTRIC_INFO bottomDielectricInfo =
1082 calculateAverageDielectricConstants( stackupLayerList, bottomDielectricLayerStackupIds, aScale );
1083 const DIELECTRIC_INFO allDielectricInfo =
1084 calculateAverageDielectricConstants( stackupLayerList, allDielectricLayerStackupIds, aScale );
1085
1086 if( topDielectricInfo.Height <= 0.0 && bottomDielectricInfo.Height <= 0.0 )
1087 return { {}, CALCULATION_RESULT{ _( "Dielectric heights must be greater than 0" ) } };
1088
1089 CALCULATION_BOARD_PARAMETERS boardParameters{
1090 signalLayer, allDielectricInfo.E_r, topDielectricInfo.Height, bottomDielectricInfo.Height,
1091 signalLayerThickness, allDielectricInfo.Loss_Tangent
1092 };
1094 result.OK = true;
1095
1096 return { boardParameters, result };
1097}
1098
1099
1102{
1103 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1104
1105 // Get the target impedance
1106 const double targetZ = getTargetImpedance();
1107
1108 if( targetZ <= 0 )
1109 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1110
1111 // Get board parameters
1112 auto [boardParameters, result] = getMicrostripBoardParameters( aRow, iuScale );
1113
1114 if( !result.OK )
1115 return result;
1116
1117 // Set calculation parameters
1118 if( aCalculationType == CalculationType::WIDTH )
1119 {
1121 }
1122 else if( aCalculationType == CalculationType::DELAY )
1123 {
1124 const int widthInt = m_trackPropagationGrid->GetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1125
1126 const double width = iuScale.IUTomm( widthInt ) / 1000.0;
1128 }
1129
1130 const double frequency = getFrequency();
1131
1132 // Run the synthesis or analysis
1133 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, 1.0 / RHO );
1135 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SKIN_DEPTH, calculateSkinDepth( frequency, 1.0, 1.0 / RHO ) );
1136 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1137 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::H_T, 1e+20 );
1138 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness );
1139 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1140 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0, targetZ );
1141 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::FREQUENCY, frequency );
1143 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1148
1149 // Add solder mask parameters if required
1150 if( m_modelSolderMask->GetValue() )
1151 {
1154 boardParameters.SolderMaskThickness );
1156 boardParameters.SolderMaskDielectricConstant );
1157 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SOLDERMASK_TAND, boardParameters.SolderMaskLossTangent );
1158 }
1159
1160 if( aCalculationType == CalculationType::WIDTH )
1162 else
1163 m_microstripCalc.Analyse();
1164
1165 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1166 [this, aCalculationType]() -> decltype( m_microstripCalc.GetSynthesisResults() )
1167 {
1168 if( aCalculationType == CalculationType::WIDTH )
1169 return m_microstripCalc.GetSynthesisResults();
1170
1171 return m_microstripCalc.GetAnalysisResults();
1172 }();
1173
1175 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1176
1178 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1179
1180 int width = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1181 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1182 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1184
1185 return CALCULATION_RESULT{ width, propDelay };
1186}
1187
1188
1191{
1192 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1193
1194 // Get the target impedance
1195 const double targetZ = getTargetImpedance();
1196
1197 if( targetZ <= 0 )
1198 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1199
1200 // Get board parameters
1201 auto [boardParameters, result] = getStriplineBoardParameters( aRow, iuScale );
1202
1203 if( !result.OK )
1204 return result;
1205
1206 // Set calculation parameters
1207 if( aCalculationType == CalculationType::WIDTH )
1208 {
1210 }
1211 else if( aCalculationType == CalculationType::DELAY )
1212 {
1213 const int widthInt = m_trackPropagationGrid->GetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1214
1215 const double width = iuScale.IUTomm( widthInt ) / 1000.0;
1217 }
1218
1219 // Run the synthesis
1220 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SKIN_DEPTH, calculateSkinDepth( 1.0, 1.0, 1.0 / RHO ) );
1221 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1222 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1223 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::STRIPLINE_A, boardParameters.TopDielectricLayerThickness );
1224 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness
1225 + boardParameters.SignalLayerThickness
1226 + boardParameters.BottomDielectricLayerThickness );
1227 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0, targetZ );
1230 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1231 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::ANG_L, 1.0 );
1232 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, 1.0 / RHO );
1234
1235 if( aCalculationType == CalculationType::WIDTH )
1237 else
1238 m_striplineCalc.Analyse();
1239
1240 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1241 [this, aCalculationType]() -> decltype( m_striplineCalc.GetSynthesisResults() )
1242 {
1243 if( aCalculationType == CalculationType::WIDTH )
1244 return m_striplineCalc.GetSynthesisResults();
1245
1246 return m_striplineCalc.GetAnalysisResults();
1247 }();
1248
1250 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1251
1253 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1254
1255 int width = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1256 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1257 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1259
1260 return CALCULATION_RESULT{ width, propDelay };
1261}
1262
1263
1266{
1267 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1268
1269 // Get the target impedance
1270 const double targetZ = getTargetImpedance();
1271
1272 if( targetZ <= 0 )
1273 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1274
1275 // Get board parameters
1276 auto [boardParameters, result] = getMicrostripBoardParameters( aRow, iuScale );
1277
1278 if( !result.OK )
1279 return result;
1280
1281 // Set calculation parameters
1282 double width = 0.0;
1283 double gap = 0.0;
1284
1285 const std::optional<int> widthOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1286 const std::optional<int> gapOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_GAP );
1287
1288 if( aCalculationType == CalculationType::WIDTH )
1289 {
1290 if( !gapOpt || *gapOpt <= 0 )
1291 return CALCULATION_RESULT{ _( "Diff pair gap must be greater than 0 to calculate width" ) };
1292
1293 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1294 }
1295 else if( aCalculationType == CalculationType::GAP )
1296 {
1297 if( !widthOpt || *widthOpt <= 0 )
1298 return CALCULATION_RESULT{ _( "Width must be greater than 0 to calculate diff pair gap" ) };
1299
1300 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1301 }
1302 else if( aCalculationType == CalculationType::DELAY )
1303 {
1304 if( !widthOpt || !gapOpt || *widthOpt <= 0 || *gapOpt <= 0 )
1305 return CALCULATION_RESULT{ _( "Width and diff pair gap must be greater than 0 to calculate delay" ) };
1306
1307 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1308 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1309 }
1310
1311 // Run the synthesis
1312 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_E, targetZ / 2.0 );
1313 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_O, targetZ / 2.0 );
1317 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1319 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness );
1320 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1327 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1329
1330 // Add solder mask parameters if required
1331 if( m_modelSolderMask->GetValue() )
1332 {
1335 boardParameters.SolderMaskThickness );
1337 boardParameters.SolderMaskDielectricConstant );
1339 boardParameters.SolderMaskLossTangent );
1340 }
1341
1342 switch( aCalculationType )
1343 {
1346 case CalculationType::DELAY: m_coupledMicrostripCalc.Analyse(); break;
1347 }
1348
1349 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1350 [this, aCalculationType]() -> decltype( m_microstripCalc.GetSynthesisResults() )
1351 {
1352 if( aCalculationType == CalculationType::WIDTH || aCalculationType == CalculationType::GAP )
1353 return m_coupledMicrostripCalc.GetSynthesisResults();
1354
1355 return m_coupledMicrostripCalc.GetAnalysisResults();
1356 }();
1357
1359 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1360
1361 if( results[TRANSLINE_PARAMETERS::PHYS_S].second != TRANSLINE_STATUS::OK )
1362 return CALCULATION_RESULT{ _( "Diff pair gap calculation failed" ) };
1363
1365 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1366
1367 int calcWidth = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1368 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1369 int calcGap = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1370 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_S].first * 1000.0 ) );
1371 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1373
1374 return CALCULATION_RESULT{ calcWidth, calcGap, propDelay };
1375}
1376
1377
1380{
1381 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1382
1383 // Get the target impedance
1384 const double targetZ = getTargetImpedance();
1385
1386 if( targetZ <= 0 )
1387 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1388
1389 // Get board parameters
1390 auto [boardParameters, result] = getStriplineBoardParameters( aRow, iuScale );
1391
1392 if( !result.OK )
1393 return result;
1394
1395 // Set calculation parameters
1396 double width = 0.0;
1397 double gap = 0.0;
1398
1399 const std::optional<int> widthOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1400 const std::optional<int> gapOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_GAP );
1401
1402 if( aCalculationType == CalculationType::WIDTH )
1403 {
1404 if( !gapOpt || *gapOpt <= 0 )
1405 return CALCULATION_RESULT{ _( "Diff pair gap must be greater than 0 to calculate width" ) };
1406
1407 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1408 }
1409 else if( aCalculationType == CalculationType::GAP )
1410 {
1411 if( !widthOpt || *widthOpt <= 0 )
1412 return CALCULATION_RESULT{ _( "Width must be greater than 0 to calculate diff pair gap" ) };
1413
1414 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1415 }
1416 else if( aCalculationType == CalculationType::DELAY )
1417 {
1418 if( !widthOpt || !gapOpt || *widthOpt <= 0 || *gapOpt <= 0 )
1419 return CALCULATION_RESULT{ _( "Width and diff pair gap must be greater than 0 to calculate delay" ) };
1420
1421 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1422 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1423 }
1424
1425 // Run the synthesis
1426 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_E, targetZ / 2.0 );
1427 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_O, targetZ / 2.0 );
1431 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1432
1433 const double totalH = boardParameters.TopDielectricLayerThickness + boardParameters.SignalLayerThickness
1434 + boardParameters.BottomDielectricLayerThickness;
1435
1436 // COUPLED_STRIPLINE::Analyse reads STRIPLINE_A as the strip midplane distance from the bottom
1437 // ground (see isOffsetWithinFiniteThicknessLimits requiring t/2 < a < h - t/2). For a finite-
1438 // thickness signal layer the midplane is Top + T/2, not Top. Using Top as A would feed the
1439 // image-method solver inconsistent virtual plate spacings that do not reduce to the centred
1440 // result even when Top == Bottom.
1441 const double striplineA = boardParameters.TopDielectricLayerThickness
1442 + 0.5 * boardParameters.SignalLayerThickness;
1443
1444 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, totalH );
1445
1446 // Set the offset top dielectric layer thickness if required
1447 if( COUPLED_STRIPLINE::IsCenteredOffset( striplineA, totalH ) )
1448 {
1449 // Keep calculation on the symmetric fast path
1451 }
1452 else
1453 {
1454 // Use the asymmetric calculation path
1456 }
1457
1458 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1465
1466 switch( aCalculationType )
1467 {
1470 case CalculationType::DELAY: m_coupledStriplineCalc.Analyse(); break;
1471 }
1472
1473 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1474 [this, aCalculationType]() -> decltype( m_coupledStriplineCalc.GetSynthesisResults() )
1475 {
1476 if( aCalculationType == CalculationType::WIDTH || aCalculationType == CalculationType::GAP )
1477 return m_coupledStriplineCalc.GetSynthesisResults();
1478
1479 return m_coupledStriplineCalc.GetAnalysisResults();
1480 }();
1481
1483 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1484
1485 if( results[TRANSLINE_PARAMETERS::PHYS_S].second != TRANSLINE_STATUS::OK )
1486 return CALCULATION_RESULT{ _( "Diff pair gap calculation failed" ) };
1487
1489 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1490
1491 int calcWidth = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1492 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1493 int calcGap = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1494 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_S].first * 1000.0 ) );
1495 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1497
1498 return CALCULATION_RESULT{ calcWidth, calcGap, propDelay };
1499}
1500
1501
1503{
1504 double frequency = 0.0;
1505 m_frequency->GetValue().ToDouble( &frequency );
1506
1507 switch( m_frequencyUnits->GetSelection() )
1508 {
1509 case 1: // kHz
1510 frequency *= 1e3;
1511 break;
1512 case 2: // MHz
1513 frequency *= 1e6;
1514 break;
1515 case 3: // GHz
1516 frequency *= 1e9;
1517 break;
1518 default: // Hz
1519 break;
1520 }
1521
1522 return frequency;
1523}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
@ BS_ITEM_TYPE_DIELECTRIC
Manage one layer needed to make a physical board.
DIELECTRIC_MODEL GetDielectricModel(int aDielectricSubLayer=0) const
int GetSublayersCount() const
double GetEpsilonR(int aDielectricSubLayer=0) const
bool HasEpsilonRValue() const
int GetThickness(int aDielectricSubLayer=0) const
BOARD_STACKUP_ITEM_TYPE GetType() const
double GetSpecFreq(int aDielectricSubLayer=0) const
double GetLossTangent(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:745
static bool IsCenteredOffset(double a, double h)
Returns true when the strip plane offset a is effectively at the centre (a = h/2 within numerical tol...
Represents a single line in a time domain profile track propagation setup.
void SetWidth(const int aWidth)
void SetEnableTimeDomainTuning(bool aEnable)
void SetDiffPairGap(const int aDiffPairGap)
void SetTopReferenceLayer(const PCB_LAYER_ID aLayer)
void SetSignalLayer(const PCB_LAYER_ID aLayer)
void SetDelay(const int aDelay)
void SetBottomReferenceLayer(const PCB_LAYER_ID aLayer)
PCB_LAYER_ID GetSignalLayer() const
Kramers-Kronig-consistent wideband dielectric model after Djordjevic et al.
double TanDeltaAt(double aF) const
Loss tangent tan delta = -Im(eps) / Re(eps) at aF.
double EpsilonRealAt(double aF) const
Real part of relative permittivity at aF.
void Fit(double aEpsRSpec, double aTanDSpec, double aFSpec, double aF1=1.0e3, double aF2=1.0e12)
Fit the model from a single (epsR, tan delta) datapoint at f_spec.
A cell editor which runs a provided function when the grid cell button is clicked.
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
static PAGED_DIALOG * GetDialog(wxWindow *aWindow)
void SetError(const wxString &aMessage, const wxString &aPageName, int aCtrlId, int aRow=-1, int aCol=-1)
PANEL_SETUP_TUNING_PROFILE_INFO_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(719, 506), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
void OnAddViaOverride(wxCommandEvent &event) override
Adds a via override row.
MICROSTRIP m_microstripCalc
Calculator for single microstrip parameters.
COUPLED_STRIPLINE m_coupledStriplineCalc
Calculator for coupled (differential) stripline parameters.
void initPanel()
Initialises all controls on the panel.
void calculateTrackParametersForCell(int aRow, int aCol)
Calculates the required track parameters for the given track parameters grid row and col.
CALCULATION_RESULT calculateSingleStripline(const int aRow, CalculationType aCalculationType)
Calculates the track width or delay for the given propagation grid row.
double getTargetImpedance() const
Gets the target impedance for the profile.
CALCULATION_RESULT calculateSingleMicrostrip(const int aRow, CalculationType aCalculationType)
Calculates the track width or delay for the given propagation grid row.
STRIPLINE m_striplineCalc
Calculator for single stripline parameters.
static double calculateSkinDepth(double aFreq, double aMurc, double aSigma)
Calculate the effective skin depth for the given parameters.
bool ValidateProfile(size_t aPageIndex)
Validate this panel's data.
void UpdateLayerNames()
Updates the displayed layer names in all grids.
static int getStackupLayerId(const std::vector< BOARD_STACKUP_ITEM * > &aLayerList, PCB_LAYER_ID aPcbLayerId)
Gets the index in to the layer list for the given layer.
UNIT_BINDER m_viaPropagationUnits
Units for global via propagation unit delay.
CALCULATION_RESULT calculateDifferentialMicrostrip(int aRow, CalculationType aCalculationType)
Calculates the track width, pair gap, or delay for the given propagation grid row.
std::pair< CALCULATION_BOARD_PARAMETERS, CALCULATION_RESULT > getMicrostripBoardParameters(int aRow, const EDA_IU_SCALE &aScale)
Gets the board parameters for microstrip calculations @parameter aRow The grid row to calculate board...
PANEL_SETUP_TUNING_PROFILE_INFO(wxWindow *aParentWindow, PANEL_SETUP_TUNING_PROFILES *parentPanel)
DIELECTRIC_INFO calculateAverageDielectricConstants(const std::vector< BOARD_STACKUP_ITEM * > &aStackupLayerList, const std::vector< int > &dielectricLayerStackupIds, const EDA_IU_SCALE &aIuScale)
Calculates the geometric average of the dielectric material properties.
void LoadProfile(const TUNING_PROFILE &aProfile)
Loads the given profile in to the panel.
TUNING_PROFILE GetProfile() const
Saves the panel to the given profile.
static DIELECTRIC_INFO getSolderMaskParameters(const std::vector< BOARD_STACKUP_ITEM * > &aStackupLayerList, const EDA_IU_SCALE &aScale, PCB_LAYER_ID aSignalLayerId)
Gets the dielectric information for the solder mask covering a given signallayer.
double getFrequency() const
Gets the target frequency in Hz.
PANEL_SETUP_TUNING_PROFILES * m_parentPanel
The parent setup panel.
void OnChangeProfileType(wxCommandEvent &event) override
Changes between Single and Differential profiles.
std::pair< CALCULATION_BOARD_PARAMETERS, CALCULATION_RESULT > getStriplineBoardParameters(int aRow, const EDA_IU_SCALE &aScale)
Gets the board parameters for stripline calculations @parameter aRow The grid row to calculate board ...
void getDielectricLayers(const std::vector< BOARD_STACKUP_ITEM * > &aStackupLayerList, int aSignalLayerId, int aReferenceLayerId, std::vector< int > &aDielectricLayerStackupIds)
Gets the dielectric layers for dielectrics between the two given copper layer IDs.
void OnRemoveTrackRow(wxCommandEvent &event) override
Removes a row from the track propagation grid.
wxString GetProfileName() const
Gets the name of this profile.
COUPLED_MICROSTRIP m_coupledMicrostripCalc
Calculator for coupled (differential) microstrip parameters.
CALCULATION_RESULT calculateDifferentialStripline(int aRow, CalculationType aCalculationType)
Calculates the track width, pair gap, or delay for the given propagation grid row.
void onChangeProfileType(TUNING_PROFILE::PROFILE_TYPE aType) const
Sets the panel display for the given tuning type.
void OnRemoveViaOverride(wxCommandEvent &event) override
Removes a via override row.
void OnAddTrackRow(wxCommandEvent &event) override
Adds a row to the track propagation grid.
void setColumnWidths()
Set up the widths of all grid columns.
void OnProfileNameChanged(wxCommandEvent &event) override
Updates the parent notebook control.
EDA_UNITS GetUnitsFromType(EDA_DATA_TYPE aType) const
Gets the units to use in the conversion based on the underlying user units.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:221
This file is part of the common library.
#define _(s)
@ PS_PER_INCH
Definition eda_units.h:59
bool IsCopperLayerLowerThan(PCB_LAYER_ID aLayerA, PCB_LAYER_ID aLayerB)
Return true if copper aLayerA is placed lower than aLayerB, false otherwise.
Definition layer_ids.h:826
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:782
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:805
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
KICOMMON_API double FromUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Return in internal units the value aValue given in a real unit such as "in", "mm",...
KICOMMON_API bool IsImperialUnit(EDA_UNITS aUnit)
Definition eda_units.cpp:47
Represents a single line in the time domain configuration via overrides configuration grid.
constexpr double IUTomm(int iu) const
Definition base_units.h:92
Represents a single line in the tuning profile configuration grid.
std::map< PCB_LAYER_ID, DELAY_PROFILE_TRACK_PROPAGATION_ENTRY > m_TrackPropagationEntriesMap
std::vector< DELAY_PROFILE_VIA_OVERRIDE_ENTRY > m_ViaOverrides
PROFILE_TYPE m_Type
std::vector< DELAY_PROFILE_TRACK_PROPAGATION_ENTRY > m_TrackPropagationEntries
wxString result
Test unit parsing edge cases and error handling.
#define M_PI