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, see <https://www.gnu.org/licenses/>.
18 */
19
20
22
24#include <bitmaps.h>
25#include <confirm.h>
26#include <grid_tricks.h>
28#include <pcb_edit_frame.h>
32#include <widgets/wx_grid.h>
33
47
48
50{
51 if( EDA_UNIT_UTILS::IsImperialUnit( m_parentPanel->m_unitsProvider->GetUserUnits() ) )
52 {
55 }
56 else
57 {
60 }
61
63
64 int x = 0, y = 0;
65 m_name->GetTextExtent( "XXXXXXXXXXXXXXXXXXXXX", &x, &y );
66 m_name->SetMinSize( wxSize( x, -1 ) );
67 m_targetImpedance->GetTextExtent( "XXXXXXXXX", &x, &y );
68 m_targetImpedance->SetMinSize( wxSize( x, -1 ) );
69 GetTextExtent( "GHZ XXXX", &x, &y );
70 m_frequencyUnits->SetMinSize( wxSize( x, -1 ) );
71
72 m_viaPropagationUnits.SetValue( 0 );
74
77
80
81 m_targetImpedance->SetValue( "0" );
82
83 UNITS_PROVIDER* unitsProvider = m_parentPanel->m_unitsProvider.get();
84
85 m_trackPropagationGrid->SetUnitsProvider( unitsProvider );
86 m_viaOverrides->SetUnitsProvider( unitsProvider );
87
88 // Configure the track grid
89 m_trackPropagationGrid->BeginBatch();
90 m_trackPropagationGrid->SetUseNativeColLabels();
91
92 m_trackPropagationGrid->EnsureColLabelsVisible();
94 m_trackPropagationGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
95
96 std::vector<int> trackColIds;
97 m_trackPropagationGrid->SetAutoEvalColUnits( TRACK_GRID_DELAY,
99 trackColIds.push_back( TRACK_GRID_DELAY );
101 unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::DISTANCE ) );
102 trackColIds.push_back( TRACK_GRID_TRACK_WIDTH );
103 m_trackPropagationGrid->SetAutoEvalColUnits( TRACK_GRID_TRACK_GAP,
104 unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::DISTANCE ) );
105 trackColIds.push_back( TRACK_GRID_TRACK_GAP );
106 m_trackPropagationGrid->SetAutoEvalCols( trackColIds );
107
108 // Add the calculation editors
109 wxGridCellAttr* attr = new wxGridCellAttr;
110 attr->SetEditor( new GRID_CELL_RUN_FUNCTION_EDITOR(
111 m_parentPanel->m_dlg,
112 [this]( int row, int col )
113 {
114 calculateTrackParametersForCell( row, col );
115 },
116 false ) );
118
119 attr = new wxGridCellAttr;
120 attr->SetEditor( new GRID_CELL_RUN_FUNCTION_EDITOR(
121 m_parentPanel->m_dlg,
122 [this]( int row, int col )
123 {
124 calculateTrackParametersForCell( row, col );
125 },
126 false ) );
127 m_trackPropagationGrid->SetColAttr( TRACK_GRID_TRACK_GAP, attr );
128
129 attr = new wxGridCellAttr;
130 attr->SetEditor( new GRID_CELL_RUN_FUNCTION_EDITOR(
131 m_parentPanel->m_dlg,
132 [this]( int row, int col )
133 {
134 calculateTrackParametersForCell( row, col );
135 },
136 false ) );
137 m_trackPropagationGrid->SetColAttr( TRACK_GRID_DELAY, attr );
138
139 m_trackPropagationGrid->EndBatch();
140
141 // Configure the via grid
142 m_viaOverrides->BeginBatch();
143 m_viaOverrides->SetUseNativeColLabels();
144
145 m_viaOverrides->EnsureColLabelsVisible();
146 m_viaOverrides->PushEventHandler( new GRID_TRICKS( m_viaOverrides ) );
147 m_viaOverrides->SetSelectionMode( wxGrid::wxGridSelectRows );
148
149 std::vector<int> viaColIds;
150 m_viaOverrides->SetAutoEvalColUnits( VIA_GRID_DELAY, unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::TIME ) );
151 viaColIds.push_back( VIA_GRID_DELAY );
152 m_viaOverrides->SetAutoEvalCols( viaColIds );
153 m_viaOverrides->EndBatch();
154
156
157 // Hide the trace gap as we start in single mode
159
161 Layout();
162}
163
164
166{
167 BOARD* board = m_parentPanel->m_board;
168
169 m_name->SetValue( aProfile.m_ProfileName );
170 m_type->SetSelection( static_cast<int>( aProfile.m_Type ) );
171 onChangeProfileType( aProfile.m_Type );
172 m_targetImpedance->SetValue( wxString::FromDouble( aProfile.m_TargetImpedance ) );
173 m_enableDelayTuning->SetValue( aProfile.m_EnableTimeDomainTuning );
174 m_modelSolderMask->SetValue( aProfile.m_ModelSolderMask );
175 m_viaPropagationUnits.SetValue( aProfile.m_ViaPropagationDelay );
177
178 double frequency = aProfile.m_Frequency;
179
180 if( frequency >= 1e9 )
181 {
182 frequency /= 1e9;
183 m_frequencyUnits->SetSelection( 3 );
184 }
185 else if( frequency >= 1e6 )
186 {
187 frequency /= 1e6;
188 m_frequencyUnits->SetSelection( 2 );
189 }
190 else if( frequency >= 1e3 )
191 {
192 frequency /= 1e3;
193 m_frequencyUnits->SetSelection( 1 );
194 }
195 else
196 {
197 m_frequencyUnits->SetSelection( 0 );
198 }
199
200 m_frequency->SetValue( wxString::FromDouble( frequency ) );
201
202 for( const auto& entry : aProfile.m_TrackPropagationEntries )
203 {
204 const int row = m_trackPropagationGrid->GetNumberRows();
205 m_trackPropagationGrid->AppendRows();
206
208 board->GetLayerName( entry.GetSignalLayer() ) );
209
210 if( entry.GetTopReferenceLayer() != UNDEFINED_LAYER )
211 {
213 board->GetLayerName( entry.GetTopReferenceLayer() ) );
214 }
215
216 if( entry.GetBottomReferenceLayer() != UNDEFINED_LAYER )
217 {
219 board->GetLayerName( entry.GetBottomReferenceLayer() ) );
220 }
221
222 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_TRACK_WIDTH, entry.GetWidth() );
223 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_TRACK_GAP, entry.GetDiffPairGap() );
224 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_DELAY, entry.GetDelay( true ) );
225 }
226
227 for( const auto& entry : aProfile.m_ViaOverrides )
228 {
229 const int row = m_viaOverrides->GetNumberRows();
230 m_viaOverrides->AppendRows();
231
232 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM, board->GetLayerName( entry.m_SignalLayerFrom ) );
233 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO, board->GetLayerName( entry.m_SignalLayerTo ) );
234 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM, board->GetLayerName( entry.m_ViaLayerFrom ) );
235 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO, board->GetLayerName( entry.m_ViaLayerTo ) );
236 m_viaOverrides->SetUnitValue( row, VIA_GRID_DELAY, entry.m_Delay );
237 }
238
240}
241
242
244{
245 TUNING_PROFILE profile;
246 profile.m_ProfileName = m_name->GetValue();
247 profile.m_Type = static_cast<TUNING_PROFILE::PROFILE_TYPE>( m_type->GetSelection() );
248 profile.m_EnableTimeDomainTuning = m_enableDelayTuning->GetValue();
249 profile.m_ModelSolderMask = m_modelSolderMask->GetValue();
250 profile.m_ViaPropagationDelay = m_viaPropagationUnits.GetIntValue();
252
253 double targetImpedance = 0.0;
254
255 if( m_targetImpedance->GetValue().ToDouble( &targetImpedance ) )
256 profile.m_TargetImpedance = targetImpedance;
257 else
258 profile.m_TargetImpedance = 0.0;
259
260 profile.m_Frequency = getFrequency();
261
262 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); row++ )
263 {
265
266 wxString signalLayerName = m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_SIGNAL_LAYER );
267 entry.SetSignalLayer( m_parentPanel->m_layerNamesToIDs[signalLayerName] );
268
269 if( wxString topReferenceLayerName = m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_TOP_REFERENCE );
270 m_parentPanel->m_layerNamesToIDs.contains( topReferenceLayerName ) )
271 {
272 entry.SetTopReferenceLayer( m_parentPanel->m_layerNamesToIDs[topReferenceLayerName] );
273 }
274 else
275 {
277 }
278
279 if( wxString bottomReferenceLayerName =
281 m_parentPanel->m_layerNamesToIDs.contains( bottomReferenceLayerName ) )
282 {
283 entry.SetBottomReferenceLayer( m_parentPanel->m_layerNamesToIDs[bottomReferenceLayerName] );
284 }
285 else
286 {
288 }
289
290 entry.SetWidth( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_TRACK_WIDTH ) );
291 entry.SetDiffPairGap( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_TRACK_GAP ) );
292 entry.SetDelay( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_DELAY ) );
294
295 profile.m_TrackPropagationEntries.push_back( entry );
296 profile.m_TrackPropagationEntriesMap[entry.GetSignalLayer()] = entry;
297 }
298
299 for( int row = 0; row < m_viaOverrides->GetNumberRows(); row++ )
300 {
301 const wxString signalLayerFrom = m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM );
302 const wxString signalLayerTo = m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO );
303 const wxString viaLayerFrom = m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_FROM );
304 const wxString viaLayerTo = m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_TO );
305 PCB_LAYER_ID signalLayerIdFrom = m_parentPanel->m_layerNamesToIDs[signalLayerFrom];
306 PCB_LAYER_ID signalLayerIdTo = m_parentPanel->m_layerNamesToIDs[signalLayerTo];
307 PCB_LAYER_ID viaLayerIdFrom = m_parentPanel->m_layerNamesToIDs[viaLayerFrom];
308 PCB_LAYER_ID viaLayerIdTo = m_parentPanel->m_layerNamesToIDs[viaLayerTo];
309
310 // Order layers in stackup order (from F_Cu first)
311 if( IsCopperLayerLowerThan( signalLayerIdFrom, signalLayerIdTo ) )
312 std::swap( signalLayerIdFrom, signalLayerIdTo );
313
314 if( IsCopperLayerLowerThan( viaLayerIdFrom, viaLayerIdTo ) )
315 std::swap( viaLayerIdFrom, viaLayerIdTo );
316
317 const DELAY_PROFILE_VIA_OVERRIDE_ENTRY entry{ signalLayerIdFrom, signalLayerIdTo, viaLayerIdFrom, viaLayerIdTo,
318 m_viaOverrides->GetUnitValue( row, VIA_GRID_DELAY ) };
319 profile.m_ViaOverrides.push_back( entry );
320 }
321
322 return profile;
323}
324
325
327{
328 m_trackPropagationGrid->PopEventHandler( true );
329 m_viaOverrides->PopEventHandler( true );
330}
331
332
334{
335 wxArrayString layerNames, layerNamesWithNone;
336 layerNamesWithNone.push_back( "<None>" );
337 std::ranges::for_each( m_parentPanel->m_layerNames,
338 [&layerNames, &layerNamesWithNone]( const wxString& aLayerName )
339 {
340 layerNames.push_back( aLayerName );
341 layerNamesWithNone.push_back( aLayerName );
342 } );
343
344
345 // Save the current data - track grid
346 std::vector<wxString> currentSignalLayer;
347 std::vector<wxString> currentTopReferenceLayer;
348 std::vector<wxString> currentBottomReferenceLayer;
349
350 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); ++row )
351 {
352 currentSignalLayer.emplace_back( m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_SIGNAL_LAYER ) );
353 currentTopReferenceLayer.emplace_back( m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_TOP_REFERENCE ) );
354 currentBottomReferenceLayer.emplace_back(
356 }
357
358 // Save the current data - via grid
359 std::vector<wxString> currentSignalLayersFrom;
360 std::vector<wxString> currentSignalLayersTo;
361 std::vector<wxString> currentViaLayersFrom;
362 std::vector<wxString> currentViaLayersTo;
363
364 for( int row = 0; row < m_viaOverrides->GetNumberRows(); ++row )
365 {
366 currentSignalLayersFrom.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM ) );
367 currentSignalLayersTo.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO ) );
368 currentViaLayersFrom.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_FROM ) );
369 currentViaLayersTo.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_TO ) );
370 }
371
372 // Reset the via layers lists
373 wxGridCellAttr* attr = new wxGridCellAttr;
374 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
376
377 attr = new wxGridCellAttr;
378 attr->SetEditor( new wxGridCellChoiceEditor( layerNamesWithNone, false ) );
380
381 attr = new wxGridCellAttr;
382 attr->SetEditor( new wxGridCellChoiceEditor( layerNamesWithNone, false ) );
384
385 attr = new wxGridCellAttr;
386 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
387 m_viaOverrides->SetColAttr( VIA_GRID_SIGNAL_LAYER_FROM, attr );
388
389 attr = new wxGridCellAttr;
390 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
391 m_viaOverrides->SetColAttr( VIA_GRID_SIGNAL_LAYER_TO, attr );
392
393 attr = new wxGridCellAttr;
394 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
395 m_viaOverrides->SetColAttr( VIA_GRID_VIA_LAYER_FROM, attr );
396
397 attr = new wxGridCellAttr;
398 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
399 m_viaOverrides->SetColAttr( VIA_GRID_VIA_LAYER_TO, attr );
400
401 // Restore the data, changing or resetting layer names if required
402 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); ++row )
403 {
404 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentSignalLayer[row] ) )
405 {
406 PCB_LAYER_ID lastSignalId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayer[row]];
407
408 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalId ) )
410 m_parentPanel->m_board->GetLayerName( lastSignalId ) );
411 else
413 m_parentPanel->m_layerNames.front() );
414 }
415 else
416 {
417 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_SIGNAL_LAYER, m_parentPanel->m_layerNames.front() );
418 }
419
420 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentTopReferenceLayer[row] ) )
421 {
422 const PCB_LAYER_ID lastTopReferenceId = m_parentPanel->m_prevLayerNamesToIDs[currentTopReferenceLayer[row]];
423
424 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastTopReferenceId ) )
426 m_parentPanel->m_board->GetLayerName( lastTopReferenceId ) );
427 else
428 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_TOP_REFERENCE, layerNamesWithNone[0] );
429 }
430 else
431 {
432 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_TOP_REFERENCE, layerNamesWithNone[0] );
433 }
434
435 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentBottomReferenceLayer[row] ) )
436 {
437 const PCB_LAYER_ID lastBottomReferenceId =
438 m_parentPanel->m_prevLayerNamesToIDs[currentBottomReferenceLayer[row]];
439
440 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastBottomReferenceId ) )
442 m_parentPanel->m_board->GetLayerName( lastBottomReferenceId ) );
443 else
444 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, layerNamesWithNone[0] );
445 }
446 else
447 {
448 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, layerNamesWithNone[0] );
449 }
450 }
451
452 for( int row = 0; row < m_viaOverrides->GetNumberRows(); ++row )
453 {
454 const PCB_LAYER_ID lastSignalFromId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayersFrom[row]];
455
456 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalFromId ) )
457 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM,
458 m_parentPanel->m_board->GetLayerName( lastSignalFromId ) );
459 else
460 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM, m_parentPanel->m_layerNames.front() );
461
462 const PCB_LAYER_ID lastSignalToId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayersTo[row]];
463
464 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalToId ) )
465 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO,
466 m_parentPanel->m_board->GetLayerName( lastSignalToId ) );
467 else
468 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO, m_parentPanel->m_layerNames.back() );
469
470 const PCB_LAYER_ID lastViaFromId = m_parentPanel->m_prevLayerNamesToIDs[currentViaLayersFrom[row]];
471
472 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastViaFromId ) )
473 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM,
474 m_parentPanel->m_board->GetLayerName( lastViaFromId ) );
475 else
476 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM, m_parentPanel->m_layerNames.front() );
477
478 const PCB_LAYER_ID lastViaToId = m_parentPanel->m_prevLayerNamesToIDs[currentViaLayersTo[row]];
479
480 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastViaToId ) )
481 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO,
482 m_parentPanel->m_board->GetLayerName( lastViaToId ) );
483 else
484 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO, m_parentPanel->m_layerNames.back() );
485 }
486}
487
488
490{
491 const int minValueWidth = m_trackPropagationGrid->GetTextExtent( wxT( "000.0000 ps/mm" ) ).x;
492
493 for( int i = 0; i < m_trackPropagationGrid->GetNumberCols(); ++i )
494 {
495 const int titleSize = m_trackPropagationGrid->GetTextExtent( m_trackPropagationGrid->GetColLabelValue( i ) ).x;
496
498 m_trackPropagationGrid->SetColSize( i, titleSize + 30 );
499 else
500 m_trackPropagationGrid->SetColSize( i, std::max( titleSize, minValueWidth ) );
501 }
502
503 for( int i = 0; i < m_viaOverrides->GetNumberCols(); ++i )
504 {
505 const int titleSize = GetTextExtent( m_viaOverrides->GetColLabelValue( i ) ).x;
506 if( i == VIA_GRID_DELAY )
507 m_viaOverrides->SetColSize( i, std::max( titleSize, minValueWidth ) );
508 else
509 m_viaOverrides->SetColSize( i, titleSize + 30 );
510 }
511
512 const int impedanceWidth = m_targetImpedance->GetTextExtent( wxT( "0000.00" ) ).x;
513 m_targetImpedance->SetSize( impedanceWidth, m_targetImpedance->GetSize().GetHeight() );
514
515 Layout();
516}
517
518
520{
521 const wxString newName = event.GetString();
522 m_parentPanel->UpdateProfileName( this, newName );
523}
524
525
533
534
536{
537 m_trackPropagationGrid->CommitPendingChanges();
538 m_viaOverrides->CommitPendingChanges();
539
542 else
544}
545
546
548{
549 const int numRows = m_trackPropagationGrid->GetNumberRows();
550 m_trackPropagationGrid->InsertRows( m_trackPropagationGrid->GetNumberRows() );
551
552 auto setFrontRowLayers = [&]( const int row )
553 {
554 auto nameItr = m_parentPanel->m_layerNames.begin();
555
556 if( nameItr == m_parentPanel->m_layerNames.end() )
557 return;
558
559 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_SIGNAL_LAYER, *nameItr );
560
561 ++nameItr;
562
563 if( nameItr == m_parentPanel->m_layerNames.end() )
564 return;
565
566 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, *nameItr );
567 };
568
569 auto setRowLayers = [&]()
570 {
571 if( numRows == 0 )
572 {
573 setFrontRowLayers( 0 );
574 return;
575 }
576
577 const wxString lastSignalLayerName =
578 m_trackPropagationGrid->GetCellValue( numRows - 1, TRACK_GRID_SIGNAL_LAYER );
579 auto nameItr = std::find( m_parentPanel->m_layerNames.begin(), m_parentPanel->m_layerNames.end(),
580 lastSignalLayerName );
581
582 if( nameItr == m_parentPanel->m_layerNames.end() )
583 return;
584
585 if( nameItr == m_parentPanel->m_layerNames.end() - 1 )
586 {
587 setFrontRowLayers( numRows );
588 return;
589 }
590
591 ++nameItr;
592
593 if( nameItr == m_parentPanel->m_layerNames.end() )
594 return;
595
596 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_SIGNAL_LAYER, *nameItr );
597 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_TOP_REFERENCE, *( nameItr - 1 ) );
598
599 ++nameItr;
600
601 if( nameItr != m_parentPanel->m_layerNames.end() )
602 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_BOTTOM_REFERENCE, *nameItr );
603 };
604
605 setRowLayers();
606
607 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_TRACK_WIDTH, 0 );
608 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_TRACK_GAP, 0 );
609 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_DELAY, 0 );
611}
612
613
615{
616 wxArrayInt selRows = m_trackPropagationGrid->GetSelectedRows();
617
618 if( selRows.size() == 1 )
619 m_trackPropagationGrid->DeleteRows( selRows[0] );
620}
621
622
624{
625 const int numRows = m_viaOverrides->GetNumberRows();
626 m_viaOverrides->InsertRows( numRows );
627 m_viaOverrides->SetUnitValue( numRows, VIA_GRID_DELAY, 0 );
628 m_viaOverrides->SetCellValue( numRows, VIA_GRID_SIGNAL_LAYER_FROM, m_parentPanel->m_layerNames.front() );
629 m_viaOverrides->SetCellValue( numRows, VIA_GRID_SIGNAL_LAYER_TO, m_parentPanel->m_layerNames.back() );
630 m_viaOverrides->SetCellValue( numRows, VIA_GRID_VIA_LAYER_FROM, m_parentPanel->m_layerNames.front() );
631 m_viaOverrides->SetCellValue( numRows, VIA_GRID_VIA_LAYER_TO, m_parentPanel->m_layerNames.back() );
633}
634
635
637{
638 wxArrayInt selRows = m_viaOverrides->GetSelectedRows();
639
640 if( selRows.size() == 1 )
641 m_viaOverrides->DeleteRows( selRows[0] );
642}
643
644
646{
647 return m_name->GetValue();
648}
649
650
651double PANEL_SETUP_TUNING_PROFILE_INFO::calculateSkinDepth( const double aFreq, const double aMurc,
652 const double aSigma )
653{
654 return 1.0 / sqrt( M_PI * aFreq * aMurc * TRANSLINE_CALCULATIONS::MU0 * aSigma );
655}
656
657
658int PANEL_SETUP_TUNING_PROFILE_INFO::getStackupLayerId( const std::vector<BOARD_STACKUP_ITEM*>& aLayerList,
659 PCB_LAYER_ID aPcbLayerId )
660{
661 bool layerFound = false;
662 int layerStackupId = 0;
663
664 while( layerStackupId < static_cast<int>( aLayerList.size() ) && !layerFound )
665 {
666 if( aLayerList.at( layerStackupId )->GetBrdLayerId() != aPcbLayerId )
667 ++layerStackupId;
668 else
669 layerFound = true;
670 }
671
672 if( !layerFound )
673 return -1;
674
675 return layerStackupId;
676}
677
678
680{
681 const wxString zStr = m_targetImpedance->GetValue();
682
683 double z;
684 if( !zStr.ToDouble( &z ) )
685 z = -1;
686
687 return z;
688}
689
690
692 const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList, const std::vector<int>& dielectricLayerStackupIds,
693 const EDA_IU_SCALE& aIuScale )
694{
695 double totalHeight = 0.0;
696 double e_r = 0.0;
697 double lossTangent = 0.0;
698
699 for( int i : dielectricLayerStackupIds )
700 {
701 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( i );
702
703 for( int subLayerIdx = 0; subLayerIdx < layer->GetSublayersCount(); ++subLayerIdx )
704 {
705 totalHeight += aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
706
707 // Correct for dielectric frequency-dependent model if required
708 double e_r_layer = layer->GetEpsilonR( subLayerIdx );
709 double l_t_layer = layer->GetLossTangent( subLayerIdx );
710 const double spec_freq = layer->GetSpecFreq( subLayerIdx );
711 const double target_freq = getFrequency();
712
714 && std::isfinite( spec_freq ) && spec_freq > 0.0 )
715 {
716 try
717 {
719 ds.Fit( e_r_layer, l_t_layer, spec_freq );
720 e_r_layer = ds.EpsilonRealAt( target_freq );
721 l_t_layer = ds.TanDeltaAt( target_freq );
722 }
723 catch( const std::invalid_argument& )
724 {
725 // Ignore
726 }
727 }
728
729 e_r += e_r_layer * aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
730 lossTangent += l_t_layer * aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
731 }
732 }
733
734 // No matching dielectric layers or zero total thickness leaves nothing to average
735 if( totalHeight <= 0.0 )
736 return { 0.0, 0.0, 0.0 };
737
738 e_r = e_r / totalHeight;
739 lossTangent = lossTangent / totalHeight;
740 totalHeight /= 1000.0; // Convert from mm to m
741
742 return { totalHeight, e_r, lossTangent };
743}
744
745
746void PANEL_SETUP_TUNING_PROFILE_INFO::getDielectricLayers( const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList,
747 const int aSignalLayerId, const int aReferenceLayerId,
748 std::vector<int>& aDielectricLayerStackupIds )
749{
750 for( int i = std::min( aSignalLayerId, aReferenceLayerId ) + 1; i < std::max( aSignalLayerId, aReferenceLayerId );
751 ++i )
752 {
753 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( i );
754
755 if( layer->GetType() != BS_ITEM_TYPE_DIELECTRIC )
756 continue;
757
758 if( !layer->HasEpsilonRValue() )
759 continue;
760
761 aDielectricLayerStackupIds.push_back( i );
762 }
763}
764
765
767PANEL_SETUP_TUNING_PROFILE_INFO::getSolderMaskParameters( const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList,
768 const EDA_IU_SCALE& aScale, PCB_LAYER_ID aSignalLayerId )
769{
770 PCB_LAYER_ID maskLayerId = UNDEFINED_LAYER;
771
772 if( aSignalLayerId == F_Cu )
773 maskLayerId = F_Mask;
774 else if( aSignalLayerId == B_Cu )
775 maskLayerId = B_Mask;
776 else
777 return { 0.0, 0.0, 0.0 };
778
779 const int layerIdx = getStackupLayerId( aStackupLayerList, maskLayerId );
780
781 if( layerIdx < 0 )
782 return { 0.0, 0.0, 0.0 };
783
784 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( layerIdx );
785 const double thickness = aScale.IUTomm( layer->GetThickness() ) / 1000.0;
786
787 return { thickness, layer->GetEpsilonR(), layer->GetLossTangent() };
788}
789
790
792{
793 // Determine if this is a stripline or microstrip geometry
794 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
795
796 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
797 return;
798
799 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs.at( signalLayerName );
800 const TUNING_PROFILE::PROFILE_TYPE profileType = m_type->GetSelection() == 0
803 const bool isMicrostrip = IsFrontLayer( signalLayer ) || IsBackLayer( signalLayer );
804 CalculationType calculationType;
805
806 switch( aCol )
807 {
808 case TRACK_GRID_TRACK_WIDTH: calculationType = CalculationType::WIDTH; break;
809 case TRACK_GRID_TRACK_GAP: calculationType = CalculationType::GAP; break;
810 case TRACK_GRID_DELAY: calculationType = CalculationType::DELAY; break;
811 default: calculationType = CalculationType::WIDTH; break;
812 }
813
814 if( profileType == TUNING_PROFILE::PROFILE_TYPE::DIFFERENTIAL ) // Differential tracks mode
815 {
816 int calculatedWidth = 0;
817 int calculatedGap = 0;
818 int calculatedDelay = 0;
819
821
822 if( isMicrostrip )
823 result = calculateDifferentialMicrostrip( aRow, calculationType );
824 else
825 result = calculateDifferentialStripline( aRow, calculationType );
826
827 if( !result.OK )
828 {
829 DisplayErrorMessage( m_parentPanel->m_dlg, wxString::Format( _( "Error: %s" ), result.ErrorMsg ) );
830 return;
831 }
832
833 calculatedWidth = result.Width;
834 calculatedGap = result.DiffPairGap;
835 calculatedDelay = result.Delay;
836
837 const bool widthOk = calculatedWidth > 0;
838 const bool gapOk = calculatedGap > 0;
839 const bool delayOk = calculatedDelay > 0;
840
841 if( !widthOk )
842 {
843 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track width" ) );
844 return;
845 }
846 else if( !gapOk )
847 {
848 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute differential pair gap" ) );
849 return;
850 }
851 else if( !delayOk )
852 {
853 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track propagation delay" ) );
854 return;
855 }
856
857 if( calculationType == CalculationType::WIDTH )
858 {
859 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH, calculatedWidth );
860 }
861 else if( calculationType == CalculationType::GAP )
862 {
863 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_GAP, calculatedGap );
864 }
865
866 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_DELAY, calculatedDelay );
867 }
868 else // Single track mode
869 {
870 int calculatedWidth = 0;
871 int calculatedDelay = 0;
872
874
875 if( isMicrostrip )
876 result = calculateSingleMicrostrip( aRow, calculationType );
877 else
878 result = calculateSingleStripline( aRow, calculationType );
879
880 if( !result.OK )
881 {
882 DisplayErrorMessage( m_parentPanel->m_dlg, wxString::Format( _( "Error: %s" ), result.ErrorMsg ) );
883 return;
884 }
885
886 calculatedWidth = result.Width;
887 calculatedDelay = result.Delay;
888
889 const bool widthOk = calculatedWidth > 0;
890 const bool delayOk = calculatedDelay > 0;
891
892 if( !widthOk )
893 {
894 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track width" ) );
895 return;
896 }
897 else if( !delayOk )
898 {
899 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track propagation delay" ) );
900 return;
901 }
902
903 if( calculationType == CalculationType::WIDTH )
904 {
905 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH, calculatedWidth );
906 }
907
908 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_DELAY, calculatedDelay );
909 }
910}
911
912
914{
915 if( m_name->GetValue() == wxEmptyString )
916 {
917 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
918
919 const wxString msg = _( "Tuning profile must have a name" );
921 return false;
922 }
923
924 std::set<wxString> layerNames;
925
926 for( int i = 0; i < m_trackPropagationGrid->GetNumberRows(); ++i )
927 {
928 const wxString& layerName = m_trackPropagationGrid->GetCellValue( i, TRACK_GRID_SIGNAL_LAYER );
929
930 if( layerNames.contains( layerName ) )
931 {
932 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
933
934 const wxString msg = _( "Duplicated signal layer configuration in tuning profile" );
937 return false;
938 }
939
940 layerNames.insert( layerName );
941 }
942
943 return true;
944}
945
946
947/*****************************************************************************************************************
948 * SIMULATION / ANALYSIS PLUMBING
949 ****************************************************************************************************************/
950
954{
955 // Get the signal layer information from the stackup
956 BOARD_STACKUP stackup = m_parentPanel->m_board->GetStackupOrDefault();
957 const std::vector<BOARD_STACKUP_ITEM*>& stackupLayerList = stackup.GetList();
958
959 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
960
961 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
962 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
963
964 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs[signalLayerName];
965
966 // Microstrip can only be on an outer copper layer
967 if( signalLayer != F_Cu && signalLayer != B_Cu )
968 return { {}, CALCULATION_RESULT{ _( "Internal error: Microstrip can only be on an outer copper layer" ) } };
969
970 const int signalLayerStackupId = getStackupLayerId( stackupLayerList, signalLayer );
971
972 if( signalLayerStackupId == -1 )
973 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
974
975 const double signalLayerThickness =
976 aScale.IUTomm( stackupLayerList.at( signalLayerStackupId )->GetThickness() ) / 1000.0;
977
978 if( signalLayerThickness <= 0 )
979 return { {}, CALCULATION_RESULT{ _( "Signal layer thickness must be greater than 0" ) } };
980
981 // Get reference layer
982 wxString referenceLayerName;
983
984 if( signalLayer == F_Cu )
985 referenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_BOTTOM_REFERENCE );
986 else
987 referenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_TOP_REFERENCE );
988
989 if( !m_parentPanel->m_layerNamesToIDs.contains( referenceLayerName ) )
990 return { {}, CALCULATION_RESULT{ _( "Reference layer not found in stackup" ) } };
991
992 const PCB_LAYER_ID referenceLayer = m_parentPanel->m_layerNamesToIDs[referenceLayerName];
993 const int referenceLayerStackupId = getStackupLayerId( stackupLayerList, referenceLayer );
994
995 if( signalLayerStackupId == referenceLayerStackupId )
996 return { {}, CALCULATION_RESULT{ _( "Reference layer must be different to signal layer" ) } };
997
998 // Get the dielectric layers between signal and reference layers
999 std::vector<int> dielectricLayerStackupIds;
1000 getDielectricLayers( stackupLayerList, signalLayerStackupId, referenceLayerStackupId, dielectricLayerStackupIds );
1001
1002 // Calculate geometric average of the dielectric materials
1003 const DIELECTRIC_INFO dielectricInfo =
1004 calculateAverageDielectricConstants( stackupLayerList, dielectricLayerStackupIds, aScale );
1005
1006 if( dielectricInfo.Height <= 0.0 )
1007 return { {}, CALCULATION_RESULT{ _( "Dielectric height must be greater than 0" ) } };
1008
1009 // Get solder mask parameters
1010 const DIELECTRIC_INFO solderMaskInfo = getSolderMaskParameters( stackupLayerList, aScale, signalLayer );
1011
1012 CALCULATION_BOARD_PARAMETERS boardParameters{ signalLayer,
1013 dielectricInfo.E_r,
1014 dielectricInfo.Height,
1015 0.0,
1016 signalLayerThickness,
1017 dielectricInfo.Loss_Tangent,
1018 solderMaskInfo.E_r,
1019 solderMaskInfo.Height,
1020 solderMaskInfo.Loss_Tangent };
1022 result.OK = true;
1023
1024 return { boardParameters, result };
1025}
1026
1027
1031{
1032 // Get the signal layer information from the stackup
1033 BOARD_STACKUP stackup = m_parentPanel->m_board->GetStackupOrDefault();
1034 const std::vector<BOARD_STACKUP_ITEM*>& stackupLayerList = stackup.GetList();
1035
1036 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
1037
1038 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
1039 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
1040
1041 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs[signalLayerName];
1042 const int signalLayerStackupId = getStackupLayerId( stackupLayerList, signalLayer );
1043
1044 if( signalLayerStackupId == -1 )
1045 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
1046
1047 const double signalLayerThickness =
1048 aScale.IUTomm( stackupLayerList.at( signalLayerStackupId )->GetThickness() ) / 1000.0;
1049
1050 if( signalLayerThickness <= 0 )
1051 return { {}, CALCULATION_RESULT{ _( "Signal layer thickness must be greater than 0" ) } };
1052
1053 // Get top reference layer
1054 const wxString topReferenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_TOP_REFERENCE );
1055
1056 if( !m_parentPanel->m_layerNamesToIDs.contains( topReferenceLayerName ) )
1057 return { {}, CALCULATION_RESULT{ _( "Top reference layer not found in stackup" ) } };
1058
1059 const PCB_LAYER_ID topReferenceLayer = m_parentPanel->m_layerNamesToIDs[topReferenceLayerName];
1060 const int topReferenceLayerStackupId = getStackupLayerId( stackupLayerList, topReferenceLayer );
1061
1062 if( !IsCopperLayerLowerThan( signalLayer, topReferenceLayer ) )
1063 return { {}, CALCULATION_RESULT{ _( "Top reference layer must be above signal layer in board stackup" ) } };
1064
1065 // Get bottom reference layer
1066 wxString bottomReferenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_BOTTOM_REFERENCE );
1067
1068 if( !m_parentPanel->m_layerNamesToIDs.contains( bottomReferenceLayerName ) )
1069 return { {}, CALCULATION_RESULT{ _( "Bottom reference layer not found in stackup" ) } };
1070
1071 const PCB_LAYER_ID bottomReferenceLayer = m_parentPanel->m_layerNamesToIDs[bottomReferenceLayerName];
1072 const int bottomReferenceLayerStackupId = getStackupLayerId( stackupLayerList, bottomReferenceLayer );
1073
1074 if( !IsCopperLayerLowerThan( bottomReferenceLayer, signalLayer ) )
1075 return { {}, CALCULATION_RESULT{ _( "Bottom reference layer must be below signal layer in board stackup" ) } };
1076
1077 // Get the dielectric layers between signal and reference layers
1078 std::vector<int> topDielectricLayerStackupIds, bottomDielectricLayerStackupIds;
1079
1080 getDielectricLayers( stackupLayerList, signalLayerStackupId, topReferenceLayerStackupId,
1081 topDielectricLayerStackupIds );
1082 getDielectricLayers( stackupLayerList, signalLayerStackupId, bottomReferenceLayerStackupId,
1083 bottomDielectricLayerStackupIds );
1084
1085 // Calculate geometric average of the dielectric materials
1086 std::vector<int> allDielectricLayerStackupIds( topDielectricLayerStackupIds );
1087 allDielectricLayerStackupIds.insert( allDielectricLayerStackupIds.end(), bottomDielectricLayerStackupIds.begin(),
1088 bottomDielectricLayerStackupIds.end() );
1089
1090 const DIELECTRIC_INFO topDielectricInfo =
1091 calculateAverageDielectricConstants( stackupLayerList, topDielectricLayerStackupIds, aScale );
1092 const DIELECTRIC_INFO bottomDielectricInfo =
1093 calculateAverageDielectricConstants( stackupLayerList, bottomDielectricLayerStackupIds, aScale );
1094 const DIELECTRIC_INFO allDielectricInfo =
1095 calculateAverageDielectricConstants( stackupLayerList, allDielectricLayerStackupIds, aScale );
1096
1097 if( topDielectricInfo.Height <= 0.0 && bottomDielectricInfo.Height <= 0.0 )
1098 return { {}, CALCULATION_RESULT{ _( "Dielectric heights must be greater than 0" ) } };
1099
1100 CALCULATION_BOARD_PARAMETERS boardParameters{
1101 signalLayer, allDielectricInfo.E_r, topDielectricInfo.Height, bottomDielectricInfo.Height,
1102 signalLayerThickness, allDielectricInfo.Loss_Tangent
1103 };
1105 result.OK = true;
1106
1107 return { boardParameters, result };
1108}
1109
1110
1113{
1114 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1115
1116 // Get the target impedance
1117 const double targetZ = getTargetImpedance();
1118
1119 if( targetZ <= 0 )
1120 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1121
1122 // Get board parameters
1123 auto [boardParameters, result] = getMicrostripBoardParameters( aRow, iuScale );
1124
1125 if( !result.OK )
1126 return result;
1127
1128 // Set calculation parameters
1129 if( aCalculationType == CalculationType::WIDTH )
1130 {
1132 }
1133 else if( aCalculationType == CalculationType::DELAY )
1134 {
1135 const int widthInt = m_trackPropagationGrid->GetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1136
1137 const double width = iuScale.IUTomm( widthInt ) / 1000.0;
1139 }
1140
1141 const double frequency = getFrequency();
1142
1143 // Run the synthesis or analysis
1144 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, 1.0 / RHO );
1146 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SKIN_DEPTH, calculateSkinDepth( frequency, 1.0, 1.0 / RHO ) );
1147 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1148 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::H_T, 1e+20 );
1149 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness );
1150 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1151 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0, targetZ );
1152 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::FREQUENCY, frequency );
1154 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1159
1160 // Add solder mask parameters if required
1161 if( m_modelSolderMask->GetValue() )
1162 {
1165 boardParameters.SolderMaskThickness );
1167 boardParameters.SolderMaskDielectricConstant );
1168 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SOLDERMASK_TAND, boardParameters.SolderMaskLossTangent );
1169 }
1170
1171 if( aCalculationType == CalculationType::WIDTH )
1173 else
1174 m_microstripCalc.Analyse();
1175
1176 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1177 [this, aCalculationType]() -> decltype( m_microstripCalc.GetSynthesisResults() )
1178 {
1179 if( aCalculationType == CalculationType::WIDTH )
1180 return m_microstripCalc.GetSynthesisResults();
1181
1182 return m_microstripCalc.GetAnalysisResults();
1183 }();
1184
1186 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1187
1189 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1190
1191 int width = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1192 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1193 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1195
1196 return CALCULATION_RESULT{ width, propDelay };
1197}
1198
1199
1202{
1203 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1204
1205 // Get the target impedance
1206 const double targetZ = getTargetImpedance();
1207
1208 if( targetZ <= 0 )
1209 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1210
1211 // Get board parameters
1212 auto [boardParameters, result] = getStriplineBoardParameters( aRow, iuScale );
1213
1214 if( !result.OK )
1215 return result;
1216
1217 // Set calculation parameters
1218 if( aCalculationType == CalculationType::WIDTH )
1219 {
1221 }
1222 else if( aCalculationType == CalculationType::DELAY )
1223 {
1224 const int widthInt = m_trackPropagationGrid->GetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1225
1226 const double width = iuScale.IUTomm( widthInt ) / 1000.0;
1228 }
1229
1230 // Run the synthesis
1231 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SKIN_DEPTH, calculateSkinDepth( 1.0, 1.0, 1.0 / RHO ) );
1232 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1233 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1234 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::STRIPLINE_A, boardParameters.TopDielectricLayerThickness );
1235 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness
1236 + boardParameters.SignalLayerThickness
1237 + boardParameters.BottomDielectricLayerThickness );
1238 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0, targetZ );
1241 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1242 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::ANG_L, 1.0 );
1243 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, 1.0 / RHO );
1245
1246 if( aCalculationType == CalculationType::WIDTH )
1248 else
1249 m_striplineCalc.Analyse();
1250
1251 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1252 [this, aCalculationType]() -> decltype( m_striplineCalc.GetSynthesisResults() )
1253 {
1254 if( aCalculationType == CalculationType::WIDTH )
1255 return m_striplineCalc.GetSynthesisResults();
1256
1257 return m_striplineCalc.GetAnalysisResults();
1258 }();
1259
1261 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1262
1264 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1265
1266 int width = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1267 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1268 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1270
1271 return CALCULATION_RESULT{ width, propDelay };
1272}
1273
1274
1277{
1278 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1279
1280 // Get the target impedance
1281 const double targetZ = getTargetImpedance();
1282
1283 if( targetZ <= 0 )
1284 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1285
1286 // Get board parameters
1287 auto [boardParameters, result] = getMicrostripBoardParameters( aRow, iuScale );
1288
1289 if( !result.OK )
1290 return result;
1291
1292 // Set calculation parameters
1293 double width = 0.0;
1294 double gap = 0.0;
1295
1296 const std::optional<int> widthOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1297 const std::optional<int> gapOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_GAP );
1298
1299 if( aCalculationType == CalculationType::WIDTH )
1300 {
1301 if( !gapOpt || *gapOpt <= 0 )
1302 return CALCULATION_RESULT{ _( "Diff pair gap must be greater than 0 to calculate width" ) };
1303
1304 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1305 }
1306 else if( aCalculationType == CalculationType::GAP )
1307 {
1308 if( !widthOpt || *widthOpt <= 0 )
1309 return CALCULATION_RESULT{ _( "Width must be greater than 0 to calculate diff pair gap" ) };
1310
1311 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1312 }
1313 else if( aCalculationType == CalculationType::DELAY )
1314 {
1315 if( !widthOpt || !gapOpt || *widthOpt <= 0 || *gapOpt <= 0 )
1316 return CALCULATION_RESULT{ _( "Width and diff pair gap must be greater than 0 to calculate delay" ) };
1317
1318 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1319 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1320 }
1321
1322 // Run the synthesis
1323 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_E, targetZ / 2.0 );
1324 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_O, targetZ / 2.0 );
1328 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1330 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness );
1331 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1338 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1340
1341 // Add solder mask parameters if required
1342 if( m_modelSolderMask->GetValue() )
1343 {
1346 boardParameters.SolderMaskThickness );
1348 boardParameters.SolderMaskDielectricConstant );
1350 boardParameters.SolderMaskLossTangent );
1351 }
1352
1353 switch( aCalculationType )
1354 {
1357 case CalculationType::DELAY: m_coupledMicrostripCalc.Analyse(); break;
1358 }
1359
1360 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1361 [this, aCalculationType]() -> decltype( m_microstripCalc.GetSynthesisResults() )
1362 {
1363 if( aCalculationType == CalculationType::WIDTH || aCalculationType == CalculationType::GAP )
1364 return m_coupledMicrostripCalc.GetSynthesisResults();
1365
1366 return m_coupledMicrostripCalc.GetAnalysisResults();
1367 }();
1368
1370 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1371
1372 if( results[TRANSLINE_PARAMETERS::PHYS_S].second != TRANSLINE_STATUS::OK )
1373 return CALCULATION_RESULT{ _( "Diff pair gap calculation failed" ) };
1374
1376 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1377
1378 int calcWidth = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1379 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1380 int calcGap = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1381 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_S].first * 1000.0 ) );
1382 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1384
1385 return CALCULATION_RESULT{ calcWidth, calcGap, propDelay };
1386}
1387
1388
1391{
1392 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1393
1394 // Get the target impedance
1395 const double targetZ = getTargetImpedance();
1396
1397 if( targetZ <= 0 )
1398 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1399
1400 // Get board parameters
1401 auto [boardParameters, result] = getStriplineBoardParameters( aRow, iuScale );
1402
1403 if( !result.OK )
1404 return result;
1405
1406 // Set calculation parameters
1407 double width = 0.0;
1408 double gap = 0.0;
1409
1410 const std::optional<int> widthOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1411 const std::optional<int> gapOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_GAP );
1412
1413 if( aCalculationType == CalculationType::WIDTH )
1414 {
1415 if( !gapOpt || *gapOpt <= 0 )
1416 return CALCULATION_RESULT{ _( "Diff pair gap must be greater than 0 to calculate width" ) };
1417
1418 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1419 }
1420 else if( aCalculationType == CalculationType::GAP )
1421 {
1422 if( !widthOpt || *widthOpt <= 0 )
1423 return CALCULATION_RESULT{ _( "Width must be greater than 0 to calculate diff pair gap" ) };
1424
1425 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1426 }
1427 else if( aCalculationType == CalculationType::DELAY )
1428 {
1429 if( !widthOpt || !gapOpt || *widthOpt <= 0 || *gapOpt <= 0 )
1430 return CALCULATION_RESULT{ _( "Width and diff pair gap must be greater than 0 to calculate delay" ) };
1431
1432 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1433 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1434 }
1435
1436 // Run the synthesis
1437 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_E, targetZ / 2.0 );
1438 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_O, targetZ / 2.0 );
1442 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1443
1444 const double totalH = boardParameters.TopDielectricLayerThickness + boardParameters.SignalLayerThickness
1445 + boardParameters.BottomDielectricLayerThickness;
1446
1447 // COUPLED_STRIPLINE::Analyse reads STRIPLINE_A as the strip midplane distance from the bottom
1448 // ground (see isOffsetWithinFiniteThicknessLimits requiring t/2 < a < h - t/2). For a finite-
1449 // thickness signal layer the midplane is Top + T/2, not Top. Using Top as A would feed the
1450 // image-method solver inconsistent virtual plate spacings that do not reduce to the centred
1451 // result even when Top == Bottom.
1452 const double striplineA = boardParameters.TopDielectricLayerThickness
1453 + 0.5 * boardParameters.SignalLayerThickness;
1454
1455 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, totalH );
1456
1457 // Set the offset top dielectric layer thickness if required
1458 if( COUPLED_STRIPLINE::IsCenteredOffset( striplineA, totalH ) )
1459 {
1460 // Keep calculation on the symmetric fast path
1462 }
1463 else
1464 {
1465 // Use the asymmetric calculation path
1467 }
1468
1469 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1476
1477 switch( aCalculationType )
1478 {
1481 case CalculationType::DELAY: m_coupledStriplineCalc.Analyse(); break;
1482 }
1483
1484 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1485 [this, aCalculationType]() -> decltype( m_coupledStriplineCalc.GetSynthesisResults() )
1486 {
1487 if( aCalculationType == CalculationType::WIDTH || aCalculationType == CalculationType::GAP )
1488 return m_coupledStriplineCalc.GetSynthesisResults();
1489
1490 return m_coupledStriplineCalc.GetAnalysisResults();
1491 }();
1492
1494 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1495
1496 if( results[TRANSLINE_PARAMETERS::PHYS_S].second != TRANSLINE_STATUS::OK )
1497 return CALCULATION_RESULT{ _( "Diff pair gap calculation failed" ) };
1498
1500 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1501
1502 int calcWidth = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1503 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1504 int calcGap = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1505 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_S].first * 1000.0 ) );
1506 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1508
1509 return CALCULATION_RESULT{ calcWidth, calcGap, propDelay };
1510}
1511
1512
1514{
1515 double frequency = 0.0;
1516 m_frequency->GetValue().ToDouble( &frequency );
1517
1518 switch( m_frequencyUnits->GetSelection() )
1519 {
1520 case 1: // kHz
1521 frequency *= 1e3;
1522 break;
1523 case 2: // MHz
1524 frequency *= 1e6;
1525 break;
1526 case 3: // GHz
1527 frequency *= 1e9;
1528 break;
1529 default: // Hz
1530 break;
1531 }
1532
1533 return frequency;
1534}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ 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:373
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:802
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:57
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.
UNIT_BINDER m_netchainBridgePropagationUnits
Units for global net chain bridge propagation unit delay.
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:217
This file is part of the common library.
#define _(s)
@ PS_PER_INCH
Definition eda_units.h:55
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:830
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:786
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:809
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
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:43
Represents a single line in the time domain configuration via overrides configuration grid.
constexpr double IUTomm(int iu) const
Definition base_units.h:88
Represents a single line in the tuning profile configuration grid.
int m_NetChainBridgePropagationDelay
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