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