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 );
171 m_type->SetSelection( static_cast<int>( aProfile.m_Type ) );
172 onChangeProfileType( aProfile.m_Type );
173 m_targetImpedance->SetValue( wxString::FromDouble( aProfile.m_TargetImpedance ) );
174 m_enableDelayTuning->SetValue( aProfile.m_EnableTimeDomainTuning );
175 m_modelSolderMask->SetValue( aProfile.m_ModelSolderMask );
176 m_viaPropagationUnits.SetValue( aProfile.m_ViaPropagationDelay );
178
179 double frequency = aProfile.m_Frequency;
180
181 if( frequency >= 1e9 )
182 {
183 frequency /= 1e9;
184 m_frequencyUnits->SetSelection( 3 );
185 }
186 else if( frequency >= 1e6 )
187 {
188 frequency /= 1e6;
189 m_frequencyUnits->SetSelection( 2 );
190 }
191 else if( frequency >= 1e3 )
192 {
193 frequency /= 1e3;
194 m_frequencyUnits->SetSelection( 1 );
195 }
196 else
197 {
198 m_frequencyUnits->SetSelection( 0 );
199 }
200
201 m_frequency->SetValue( wxString::FromDouble( frequency ) );
202
203 for( const auto& entry : aProfile.m_TrackPropagationEntries )
204 {
205 const int row = m_trackPropagationGrid->GetNumberRows();
206 m_trackPropagationGrid->AppendRows();
207
209 board->GetLayerName( entry.GetSignalLayer() ) );
210
211 if( entry.GetTopReferenceLayer() != UNDEFINED_LAYER )
212 {
214 board->GetLayerName( entry.GetTopReferenceLayer() ) );
215 }
216
217 if( entry.GetBottomReferenceLayer() != UNDEFINED_LAYER )
218 {
220 board->GetLayerName( entry.GetBottomReferenceLayer() ) );
221 }
222
223 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_TRACK_WIDTH, entry.GetWidth() );
224 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_TRACK_GAP, entry.GetDiffPairGap() );
225 m_trackPropagationGrid->SetUnitValue( row, TRACK_GRID_DELAY, entry.GetDelay( true ) );
226 }
227
228 for( const auto& entry : aProfile.m_ViaOverrides )
229 {
230 const int row = m_viaOverrides->GetNumberRows();
231 m_viaOverrides->AppendRows();
232
233 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM, board->GetLayerName( entry.m_SignalLayerFrom ) );
234 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO, board->GetLayerName( entry.m_SignalLayerTo ) );
235 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM, board->GetLayerName( entry.m_ViaLayerFrom ) );
236 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO, board->GetLayerName( entry.m_ViaLayerTo ) );
237 m_viaOverrides->SetUnitValue( row, VIA_GRID_DELAY, entry.m_Delay );
238 }
239
241}
242
243
245{
246 TUNING_PROFILE profile;
247 profile.m_ProfileName = m_name->GetValue();
248 profile.m_Type = static_cast<TUNING_PROFILE::PROFILE_TYPE>( m_type->GetSelection() );
249 profile.m_EnableTimeDomainTuning = m_enableDelayTuning->GetValue();
250 profile.m_ModelSolderMask = m_modelSolderMask->GetValue();
251 profile.m_ViaPropagationDelay = m_viaPropagationUnits.GetIntValue();
253
254 double targetImpedance = 0.0;
255
256 if( m_targetImpedance->GetValue().ToDouble( &targetImpedance ) )
257 profile.m_TargetImpedance = targetImpedance;
258 else
259 profile.m_TargetImpedance = 0.0;
260
261 profile.m_Frequency = getFrequency();
262
263 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); row++ )
264 {
266
267 wxString signalLayerName = m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_SIGNAL_LAYER );
268 entry.SetSignalLayer( m_parentPanel->m_layerNamesToIDs[signalLayerName] );
269
270 if( wxString topReferenceLayerName = m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_TOP_REFERENCE );
271 m_parentPanel->m_layerNamesToIDs.contains( topReferenceLayerName ) )
272 {
273 entry.SetTopReferenceLayer( m_parentPanel->m_layerNamesToIDs[topReferenceLayerName] );
274 }
275 else
276 {
278 }
279
280 if( wxString bottomReferenceLayerName =
282 m_parentPanel->m_layerNamesToIDs.contains( bottomReferenceLayerName ) )
283 {
284 entry.SetBottomReferenceLayer( m_parentPanel->m_layerNamesToIDs[bottomReferenceLayerName] );
285 }
286 else
287 {
289 }
290
291 entry.SetWidth( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_TRACK_WIDTH ) );
292 entry.SetDiffPairGap( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_TRACK_GAP ) );
293 entry.SetDelay( m_trackPropagationGrid->GetUnitValue( row, TRACK_GRID_DELAY ) );
295
296 profile.m_TrackPropagationEntries.push_back( entry );
297 profile.m_TrackPropagationEntriesMap[entry.GetSignalLayer()] = entry;
298 }
299
300 for( int row = 0; row < m_viaOverrides->GetNumberRows(); row++ )
301 {
302 const wxString signalLayerFrom = m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM );
303 const wxString signalLayerTo = m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO );
304 const wxString viaLayerFrom = m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_FROM );
305 const wxString viaLayerTo = m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_TO );
306 PCB_LAYER_ID signalLayerIdFrom = m_parentPanel->m_layerNamesToIDs[signalLayerFrom];
307 PCB_LAYER_ID signalLayerIdTo = m_parentPanel->m_layerNamesToIDs[signalLayerTo];
308 PCB_LAYER_ID viaLayerIdFrom = m_parentPanel->m_layerNamesToIDs[viaLayerFrom];
309 PCB_LAYER_ID viaLayerIdTo = m_parentPanel->m_layerNamesToIDs[viaLayerTo];
310
311 // Order layers in stackup order (from F_Cu first)
312 if( IsCopperLayerLowerThan( signalLayerIdFrom, signalLayerIdTo ) )
313 std::swap( signalLayerIdFrom, signalLayerIdTo );
314
315 if( IsCopperLayerLowerThan( viaLayerIdFrom, viaLayerIdTo ) )
316 std::swap( viaLayerIdFrom, viaLayerIdTo );
317
318 const DELAY_PROFILE_VIA_OVERRIDE_ENTRY entry{ signalLayerIdFrom, signalLayerIdTo, viaLayerIdFrom, viaLayerIdTo,
319 m_viaOverrides->GetUnitValue( row, VIA_GRID_DELAY ) };
320 profile.m_ViaOverrides.push_back( entry );
321 }
322
323 return profile;
324}
325
326
328{
329 m_trackPropagationGrid->PopEventHandler( true );
330 m_viaOverrides->PopEventHandler( true );
331}
332
333
335{
336 wxArrayString layerNames, layerNamesWithNone;
337 layerNamesWithNone.push_back( "<None>" );
338 std::ranges::for_each( m_parentPanel->m_layerNames,
339 [&layerNames, &layerNamesWithNone]( const wxString& aLayerName )
340 {
341 layerNames.push_back( aLayerName );
342 layerNamesWithNone.push_back( aLayerName );
343 } );
344
345
346 // Save the current data - track grid
347 std::vector<wxString> currentSignalLayer;
348 std::vector<wxString> currentTopReferenceLayer;
349 std::vector<wxString> currentBottomReferenceLayer;
350
351 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); ++row )
352 {
353 currentSignalLayer.emplace_back( m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_SIGNAL_LAYER ) );
354 currentTopReferenceLayer.emplace_back( m_trackPropagationGrid->GetCellValue( row, TRACK_GRID_TOP_REFERENCE ) );
355 currentBottomReferenceLayer.emplace_back(
357 }
358
359 // Save the current data - via grid
360 std::vector<wxString> currentSignalLayersFrom;
361 std::vector<wxString> currentSignalLayersTo;
362 std::vector<wxString> currentViaLayersFrom;
363 std::vector<wxString> currentViaLayersTo;
364
365 for( int row = 0; row < m_viaOverrides->GetNumberRows(); ++row )
366 {
367 currentSignalLayersFrom.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM ) );
368 currentSignalLayersTo.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO ) );
369 currentViaLayersFrom.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_FROM ) );
370 currentViaLayersTo.emplace_back( m_viaOverrides->GetCellValue( row, VIA_GRID_VIA_LAYER_TO ) );
371 }
372
373 // Reset the via layers lists
374 wxGridCellAttr* attr = new wxGridCellAttr;
375 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
377
378 attr = new wxGridCellAttr;
379 attr->SetEditor( new wxGridCellChoiceEditor( layerNamesWithNone, false ) );
381
382 attr = new wxGridCellAttr;
383 attr->SetEditor( new wxGridCellChoiceEditor( layerNamesWithNone, false ) );
385
386 attr = new wxGridCellAttr;
387 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
388 m_viaOverrides->SetColAttr( VIA_GRID_SIGNAL_LAYER_FROM, attr );
389
390 attr = new wxGridCellAttr;
391 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
392 m_viaOverrides->SetColAttr( VIA_GRID_SIGNAL_LAYER_TO, attr );
393
394 attr = new wxGridCellAttr;
395 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
396 m_viaOverrides->SetColAttr( VIA_GRID_VIA_LAYER_FROM, attr );
397
398 attr = new wxGridCellAttr;
399 attr->SetEditor( new wxGridCellChoiceEditor( layerNames, false ) );
400 m_viaOverrides->SetColAttr( VIA_GRID_VIA_LAYER_TO, attr );
401
402 // Restore the data, changing or resetting layer names if required
403 for( int row = 0; row < m_trackPropagationGrid->GetNumberRows(); ++row )
404 {
405 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentSignalLayer[row] ) )
406 {
407 PCB_LAYER_ID lastSignalId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayer[row]];
408
409 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalId ) )
411 m_parentPanel->m_board->GetLayerName( lastSignalId ) );
412 else
414 m_parentPanel->m_layerNames.front() );
415 }
416 else
417 {
418 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_SIGNAL_LAYER, m_parentPanel->m_layerNames.front() );
419 }
420
421 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentTopReferenceLayer[row] ) )
422 {
423 const PCB_LAYER_ID lastTopReferenceId = m_parentPanel->m_prevLayerNamesToIDs[currentTopReferenceLayer[row]];
424
425 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastTopReferenceId ) )
427 m_parentPanel->m_board->GetLayerName( lastTopReferenceId ) );
428 else
429 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_TOP_REFERENCE, layerNamesWithNone[0] );
430 }
431 else
432 {
433 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_TOP_REFERENCE, layerNamesWithNone[0] );
434 }
435
436 if( m_parentPanel->m_prevLayerNamesToIDs.contains( currentBottomReferenceLayer[row] ) )
437 {
438 const PCB_LAYER_ID lastBottomReferenceId =
439 m_parentPanel->m_prevLayerNamesToIDs[currentBottomReferenceLayer[row]];
440
441 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastBottomReferenceId ) )
443 m_parentPanel->m_board->GetLayerName( lastBottomReferenceId ) );
444 else
445 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, layerNamesWithNone[0] );
446 }
447 else
448 {
449 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, layerNamesWithNone[0] );
450 }
451 }
452
453 for( int row = 0; row < m_viaOverrides->GetNumberRows(); ++row )
454 {
455 const PCB_LAYER_ID lastSignalFromId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayersFrom[row]];
456
457 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalFromId ) )
458 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM,
459 m_parentPanel->m_board->GetLayerName( lastSignalFromId ) );
460 else
461 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_FROM, m_parentPanel->m_layerNames.front() );
462
463 const PCB_LAYER_ID lastSignalToId = m_parentPanel->m_prevLayerNamesToIDs[currentSignalLayersTo[row]];
464
465 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastSignalToId ) )
466 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO,
467 m_parentPanel->m_board->GetLayerName( lastSignalToId ) );
468 else
469 m_viaOverrides->SetCellValue( row, VIA_GRID_SIGNAL_LAYER_TO, m_parentPanel->m_layerNames.back() );
470
471 const PCB_LAYER_ID lastViaFromId = m_parentPanel->m_prevLayerNamesToIDs[currentViaLayersFrom[row]];
472
473 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastViaFromId ) )
474 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM,
475 m_parentPanel->m_board->GetLayerName( lastViaFromId ) );
476 else
477 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_FROM, m_parentPanel->m_layerNames.front() );
478
479 const PCB_LAYER_ID lastViaToId = m_parentPanel->m_prevLayerNamesToIDs[currentViaLayersTo[row]];
480
481 if( m_parentPanel->m_copperLayerIdsToIndex.contains( lastViaToId ) )
482 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO,
483 m_parentPanel->m_board->GetLayerName( lastViaToId ) );
484 else
485 m_viaOverrides->SetCellValue( row, VIA_GRID_VIA_LAYER_TO, m_parentPanel->m_layerNames.back() );
486 }
487}
488
489
491{
492 const int minValueWidth = m_trackPropagationGrid->GetTextExtent( wxT( "000.0000 ps/mm" ) ).x;
493
494 for( int i = 0; i < m_trackPropagationGrid->GetNumberCols(); ++i )
495 {
496 const int titleSize = m_trackPropagationGrid->GetTextExtent( m_trackPropagationGrid->GetColLabelValue( i ) ).x;
497
499 m_trackPropagationGrid->SetColSize( i, titleSize + 30 );
500 else
501 m_trackPropagationGrid->SetColSize( i, std::max( titleSize, minValueWidth ) );
502 }
503
504 for( int i = 0; i < m_viaOverrides->GetNumberCols(); ++i )
505 {
506 const int titleSize = GetTextExtent( m_viaOverrides->GetColLabelValue( i ) ).x;
507 if( i == VIA_GRID_DELAY )
508 m_viaOverrides->SetColSize( i, std::max( titleSize, minValueWidth ) );
509 else
510 m_viaOverrides->SetColSize( i, titleSize + 30 );
511 }
512
513 const int impedanceWidth = m_targetImpedance->GetTextExtent( wxT( "0000.00" ) ).x;
514 m_targetImpedance->SetSize( impedanceWidth, m_targetImpedance->GetSize().GetHeight() );
515
516 Layout();
517}
518
519
521{
522 const wxString newName = event.GetString();
523 m_parentPanel->UpdateProfileName( this, newName );
524}
525
526
534
535
537{
538 m_trackPropagationGrid->CommitPendingChanges();
539 m_viaOverrides->CommitPendingChanges();
540
543 else
545}
546
547
549{
550 const int numRows = m_trackPropagationGrid->GetNumberRows();
551 m_trackPropagationGrid->InsertRows( m_trackPropagationGrid->GetNumberRows() );
552
553 auto setFrontRowLayers = [&]( const int row )
554 {
555 auto nameItr = m_parentPanel->m_layerNames.begin();
556
557 if( nameItr == m_parentPanel->m_layerNames.end() )
558 return;
559
560 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_SIGNAL_LAYER, *nameItr );
561
562 ++nameItr;
563
564 if( nameItr == m_parentPanel->m_layerNames.end() )
565 return;
566
567 m_trackPropagationGrid->SetCellValue( row, TRACK_GRID_BOTTOM_REFERENCE, *nameItr );
568 };
569
570 auto setRowLayers = [&]()
571 {
572 if( numRows == 0 )
573 {
574 setFrontRowLayers( 0 );
575 return;
576 }
577
578 const wxString lastSignalLayerName =
579 m_trackPropagationGrid->GetCellValue( numRows - 1, TRACK_GRID_SIGNAL_LAYER );
580 auto nameItr = std::find( m_parentPanel->m_layerNames.begin(), m_parentPanel->m_layerNames.end(),
581 lastSignalLayerName );
582
583 if( nameItr == m_parentPanel->m_layerNames.end() )
584 return;
585
586 if( nameItr == m_parentPanel->m_layerNames.end() - 1 )
587 {
588 setFrontRowLayers( numRows );
589 return;
590 }
591
592 ++nameItr;
593
594 if( nameItr == m_parentPanel->m_layerNames.end() )
595 return;
596
597 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_SIGNAL_LAYER, *nameItr );
598 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_TOP_REFERENCE, *( nameItr - 1 ) );
599
600 ++nameItr;
601
602 if( nameItr != m_parentPanel->m_layerNames.end() )
603 m_trackPropagationGrid->SetCellValue( numRows, TRACK_GRID_BOTTOM_REFERENCE, *nameItr );
604 };
605
606 setRowLayers();
607
608 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_TRACK_WIDTH, 0 );
609 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_TRACK_GAP, 0 );
610 m_trackPropagationGrid->SetUnitValue( numRows, TRACK_GRID_DELAY, 0 );
612}
613
614
616{
617 wxArrayInt selRows = m_trackPropagationGrid->GetSelectedRows();
618
619 if( selRows.size() == 1 )
620 m_trackPropagationGrid->DeleteRows( selRows[0] );
621}
622
623
625{
626 const int numRows = m_viaOverrides->GetNumberRows();
627 m_viaOverrides->InsertRows( numRows );
628 m_viaOverrides->SetUnitValue( numRows, VIA_GRID_DELAY, 0 );
629 m_viaOverrides->SetCellValue( numRows, VIA_GRID_SIGNAL_LAYER_FROM, m_parentPanel->m_layerNames.front() );
630 m_viaOverrides->SetCellValue( numRows, VIA_GRID_SIGNAL_LAYER_TO, m_parentPanel->m_layerNames.back() );
631 m_viaOverrides->SetCellValue( numRows, VIA_GRID_VIA_LAYER_FROM, m_parentPanel->m_layerNames.front() );
632 m_viaOverrides->SetCellValue( numRows, VIA_GRID_VIA_LAYER_TO, m_parentPanel->m_layerNames.back() );
634}
635
636
638{
639 wxArrayInt selRows = m_viaOverrides->GetSelectedRows();
640
641 if( selRows.size() == 1 )
642 m_viaOverrides->DeleteRows( selRows[0] );
643}
644
645
647{
648 return m_name->GetValue();
649}
650
651
652double PANEL_SETUP_TUNING_PROFILE_INFO::calculateSkinDepth( const double aFreq, const double aMurc,
653 const double aSigma )
654{
655 return 1.0 / sqrt( M_PI * aFreq * aMurc * TRANSLINE_CALCULATIONS::MU0 * aSigma );
656}
657
658
659int PANEL_SETUP_TUNING_PROFILE_INFO::getStackupLayerId( const std::vector<BOARD_STACKUP_ITEM*>& aLayerList,
660 PCB_LAYER_ID aPcbLayerId )
661{
662 bool layerFound = false;
663 int layerStackupId = 0;
664
665 while( layerStackupId < static_cast<int>( aLayerList.size() ) && !layerFound )
666 {
667 if( aLayerList.at( layerStackupId )->GetBrdLayerId() != aPcbLayerId )
668 ++layerStackupId;
669 else
670 layerFound = true;
671 }
672
673 if( !layerFound )
674 return -1;
675
676 return layerStackupId;
677}
678
679
681{
682 const wxString zStr = m_targetImpedance->GetValue();
683
684 double z;
685 if( !zStr.ToDouble( &z ) )
686 z = -1;
687
688 return z;
689}
690
691
693 const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList, const std::vector<int>& dielectricLayerStackupIds,
694 const EDA_IU_SCALE& aIuScale )
695{
696 double totalHeight = 0.0;
697 double e_r = 0.0;
698 double lossTangent = 0.0;
699
700 for( int i : dielectricLayerStackupIds )
701 {
702 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( i );
703
704 for( int subLayerIdx = 0; subLayerIdx < layer->GetSublayersCount(); ++subLayerIdx )
705 {
706 totalHeight += aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
707
708 // Correct for dielectric frequency-dependent model if required
709 double e_r_layer = layer->GetEpsilonR( subLayerIdx );
710 double l_t_layer = layer->GetLossTangent( subLayerIdx );
711 const double spec_freq = layer->GetSpecFreq( subLayerIdx );
712 const double target_freq = getFrequency();
713
715 && std::isfinite( spec_freq ) && spec_freq > 0.0 )
716 {
717 try
718 {
720 ds.Fit( e_r_layer, l_t_layer, spec_freq );
721 e_r_layer = ds.EpsilonRealAt( target_freq );
722 l_t_layer = ds.TanDeltaAt( target_freq );
723 }
724 catch( const std::invalid_argument& )
725 {
726 // Ignore
727 }
728 }
729
730 e_r += e_r_layer * aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
731 lossTangent += l_t_layer * aIuScale.IUTomm( layer->GetThickness( subLayerIdx ) );
732 }
733 }
734
735 // No matching dielectric layers or zero total thickness leaves nothing to average
736 if( totalHeight <= 0.0 )
737 return { 0.0, 0.0, 0.0 };
738
739 e_r = e_r / totalHeight;
740 lossTangent = lossTangent / totalHeight;
741 totalHeight /= 1000.0; // Convert from mm to m
742
743 return { totalHeight, e_r, lossTangent };
744}
745
746
747void PANEL_SETUP_TUNING_PROFILE_INFO::getDielectricLayers( const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList,
748 const int aSignalLayerId, const int aReferenceLayerId,
749 std::vector<int>& aDielectricLayerStackupIds )
750{
751 for( int i = std::min( aSignalLayerId, aReferenceLayerId ) + 1; i < std::max( aSignalLayerId, aReferenceLayerId );
752 ++i )
753 {
754 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( i );
755
756 if( layer->GetType() != BS_ITEM_TYPE_DIELECTRIC )
757 continue;
758
759 if( !layer->HasEpsilonRValue() )
760 continue;
761
762 aDielectricLayerStackupIds.push_back( i );
763 }
764}
765
766
768PANEL_SETUP_TUNING_PROFILE_INFO::getSolderMaskParameters( const std::vector<BOARD_STACKUP_ITEM*>& aStackupLayerList,
769 const EDA_IU_SCALE& aScale, PCB_LAYER_ID aSignalLayerId )
770{
771 PCB_LAYER_ID maskLayerId = UNDEFINED_LAYER;
772
773 if( aSignalLayerId == F_Cu )
774 maskLayerId = F_Mask;
775 else if( aSignalLayerId == B_Cu )
776 maskLayerId = B_Mask;
777 else
778 return { 0.0, 0.0, 0.0 };
779
780 const int layerIdx = getStackupLayerId( aStackupLayerList, maskLayerId );
781
782 if( layerIdx < 0 )
783 return { 0.0, 0.0, 0.0 };
784
785 const BOARD_STACKUP_ITEM* layer = aStackupLayerList.at( layerIdx );
786 const double thickness = aScale.IUTomm( layer->GetThickness() ) / 1000.0;
787
788 return { thickness, layer->GetEpsilonR(), layer->GetLossTangent() };
789}
790
791
793{
794 // Determine if this is a stripline or microstrip geometry
795 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
796
797 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
798 return;
799
800 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs.at( signalLayerName );
801 const TUNING_PROFILE::PROFILE_TYPE profileType = m_type->GetSelection() == 0
804 const bool isMicrostrip = IsFrontLayer( signalLayer ) || IsBackLayer( signalLayer );
805 CalculationType calculationType;
806
807 switch( aCol )
808 {
809 case TRACK_GRID_TRACK_WIDTH: calculationType = CalculationType::WIDTH; break;
810 case TRACK_GRID_TRACK_GAP: calculationType = CalculationType::GAP; break;
811 case TRACK_GRID_DELAY: calculationType = CalculationType::DELAY; break;
812 default: calculationType = CalculationType::WIDTH; break;
813 }
814
815 if( profileType == TUNING_PROFILE::PROFILE_TYPE::DIFFERENTIAL ) // Differential tracks mode
816 {
817 int calculatedWidth = 0;
818 int calculatedGap = 0;
819 int calculatedDelay = 0;
820
822
823 if( isMicrostrip )
824 result = calculateDifferentialMicrostrip( aRow, calculationType );
825 else
826 result = calculateDifferentialStripline( aRow, calculationType );
827
828 if( !result.OK )
829 {
830 DisplayErrorMessage( m_parentPanel->m_dlg, wxString::Format( _( "Error: %s" ), result.ErrorMsg ) );
831 return;
832 }
833
834 calculatedWidth = result.Width;
835 calculatedGap = result.DiffPairGap;
836 calculatedDelay = result.Delay;
837
838 const bool widthOk = calculatedWidth > 0;
839 const bool gapOk = calculatedGap > 0;
840 const bool delayOk = calculatedDelay > 0;
841
842 if( !widthOk )
843 {
844 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track width" ) );
845 return;
846 }
847 else if( !gapOk )
848 {
849 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute differential pair gap" ) );
850 return;
851 }
852 else if( !delayOk )
853 {
854 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track propagation delay" ) );
855 return;
856 }
857
858 if( calculationType == CalculationType::WIDTH )
859 {
860 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH, calculatedWidth );
861 }
862 else if( calculationType == CalculationType::GAP )
863 {
864 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_GAP, calculatedGap );
865 }
866
867 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_DELAY, calculatedDelay );
868 }
869 else // Single track mode
870 {
871 int calculatedWidth = 0;
872 int calculatedDelay = 0;
873
875
876 if( isMicrostrip )
877 result = calculateSingleMicrostrip( aRow, calculationType );
878 else
879 result = calculateSingleStripline( aRow, calculationType );
880
881 if( !result.OK )
882 {
883 DisplayErrorMessage( m_parentPanel->m_dlg, wxString::Format( _( "Error: %s" ), result.ErrorMsg ) );
884 return;
885 }
886
887 calculatedWidth = result.Width;
888 calculatedDelay = result.Delay;
889
890 const bool widthOk = calculatedWidth > 0;
891 const bool delayOk = calculatedDelay > 0;
892
893 if( !widthOk )
894 {
895 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track width" ) );
896 return;
897 }
898 else if( !delayOk )
899 {
900 DisplayErrorMessage( m_parentPanel->m_dlg, _( "Could not compute track propagation delay" ) );
901 return;
902 }
903
904 if( calculationType == CalculationType::WIDTH )
905 {
906 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH, calculatedWidth );
907 }
908
909 m_trackPropagationGrid->SetUnitValue( aRow, TRACK_GRID_DELAY, calculatedDelay );
910 }
911}
912
913
915{
916 if( m_name->GetValue() == wxEmptyString )
917 {
918 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
919
920 const wxString msg = _( "Tuning profile must have a name" );
922 return false;
923 }
924
925 for( size_t i = 0; i < m_parentPanel->m_tuningProfiles->GetPageCount(); ++i )
926 {
927 const auto* otherProfile =
928 static_cast<PANEL_SETUP_TUNING_PROFILE_INFO*>( m_parentPanel->m_tuningProfiles->GetPage( i ) );
929
930 if( otherProfile != this && otherProfile->GetProfileName() == m_name->GetValue() )
931 {
932 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
933
934 const wxString msg = _( "Tuning profile name already in use" );
936 return false;
937 }
938 }
939
940 std::set<wxString> layerNames;
941
942 for( int i = 0; i < m_trackPropagationGrid->GetNumberRows(); ++i )
943 {
944 const wxString& layerName = m_trackPropagationGrid->GetCellValue( i, TRACK_GRID_SIGNAL_LAYER );
945
946 if( layerNames.contains( layerName ) )
947 {
948 m_parentPanel->m_tuningProfiles->SetSelection( aPageIndex );
949
950 const wxString msg = _( "Duplicated signal layer configuration in tuning profile" );
953 return false;
954 }
955
956 layerNames.insert( layerName );
957 }
958
959 return true;
960}
961
962
963/*****************************************************************************************************************
964 * SIMULATION / ANALYSIS PLUMBING
965 ****************************************************************************************************************/
966
970{
971 // Get the signal layer information from the stackup
972 BOARD_STACKUP stackup = m_parentPanel->m_board->GetStackupOrDefault();
973 const std::vector<BOARD_STACKUP_ITEM*>& stackupLayerList = stackup.GetList();
974
975 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
976
977 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
978 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
979
980 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs[signalLayerName];
981
982 // Microstrip can only be on an outer copper layer
983 if( signalLayer != F_Cu && signalLayer != B_Cu )
984 return { {}, CALCULATION_RESULT{ _( "Internal error: Microstrip can only be on an outer copper layer" ) } };
985
986 const int signalLayerStackupId = getStackupLayerId( stackupLayerList, signalLayer );
987
988 if( signalLayerStackupId == -1 )
989 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
990
991 const double signalLayerThickness =
992 aScale.IUTomm( stackupLayerList.at( signalLayerStackupId )->GetThickness() ) / 1000.0;
993
994 if( signalLayerThickness <= 0 )
995 return { {}, CALCULATION_RESULT{ _( "Signal layer thickness must be greater than 0" ) } };
996
997 // Get reference layer
998 wxString referenceLayerName;
999
1000 if( signalLayer == F_Cu )
1001 referenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_BOTTOM_REFERENCE );
1002 else
1003 referenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_TOP_REFERENCE );
1004
1005 if( !m_parentPanel->m_layerNamesToIDs.contains( referenceLayerName ) )
1006 return { {}, CALCULATION_RESULT{ _( "Reference layer not found in stackup" ) } };
1007
1008 const PCB_LAYER_ID referenceLayer = m_parentPanel->m_layerNamesToIDs[referenceLayerName];
1009 const int referenceLayerStackupId = getStackupLayerId( stackupLayerList, referenceLayer );
1010
1011 if( signalLayerStackupId == referenceLayerStackupId )
1012 return { {}, CALCULATION_RESULT{ _( "Reference layer must be different to signal layer" ) } };
1013
1014 // Get the dielectric layers between signal and reference layers
1015 std::vector<int> dielectricLayerStackupIds;
1016 getDielectricLayers( stackupLayerList, signalLayerStackupId, referenceLayerStackupId, dielectricLayerStackupIds );
1017
1018 // Calculate geometric average of the dielectric materials
1019 const DIELECTRIC_INFO dielectricInfo =
1020 calculateAverageDielectricConstants( stackupLayerList, dielectricLayerStackupIds, aScale );
1021
1022 if( dielectricInfo.Height <= 0.0 )
1023 return { {}, CALCULATION_RESULT{ _( "Dielectric height must be greater than 0" ) } };
1024
1025 // Get solder mask parameters
1026 const DIELECTRIC_INFO solderMaskInfo = getSolderMaskParameters( stackupLayerList, aScale, signalLayer );
1027
1028 CALCULATION_BOARD_PARAMETERS boardParameters{ signalLayer,
1029 dielectricInfo.E_r,
1030 dielectricInfo.Height,
1031 0.0,
1032 signalLayerThickness,
1033 dielectricInfo.Loss_Tangent,
1034 solderMaskInfo.E_r,
1035 solderMaskInfo.Height,
1036 solderMaskInfo.Loss_Tangent };
1038 result.OK = true;
1039
1040 return { boardParameters, result };
1041}
1042
1043
1047{
1048 // Get the signal layer information from the stackup
1049 BOARD_STACKUP stackup = m_parentPanel->m_board->GetStackupOrDefault();
1050 const std::vector<BOARD_STACKUP_ITEM*>& stackupLayerList = stackup.GetList();
1051
1052 const wxString signalLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_SIGNAL_LAYER );
1053
1054 if( !m_parentPanel->m_layerNamesToIDs.contains( signalLayerName ) )
1055 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
1056
1057 const PCB_LAYER_ID signalLayer = m_parentPanel->m_layerNamesToIDs[signalLayerName];
1058 const int signalLayerStackupId = getStackupLayerId( stackupLayerList, signalLayer );
1059
1060 if( signalLayerStackupId == -1 )
1061 return { {}, CALCULATION_RESULT{ _( "Signal layer not found in stackup" ) } };
1062
1063 const double signalLayerThickness =
1064 aScale.IUTomm( stackupLayerList.at( signalLayerStackupId )->GetThickness() ) / 1000.0;
1065
1066 if( signalLayerThickness <= 0 )
1067 return { {}, CALCULATION_RESULT{ _( "Signal layer thickness must be greater than 0" ) } };
1068
1069 // Get top reference layer
1070 const wxString topReferenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_TOP_REFERENCE );
1071
1072 if( !m_parentPanel->m_layerNamesToIDs.contains( topReferenceLayerName ) )
1073 return { {}, CALCULATION_RESULT{ _( "Top reference layer not found in stackup" ) } };
1074
1075 const PCB_LAYER_ID topReferenceLayer = m_parentPanel->m_layerNamesToIDs[topReferenceLayerName];
1076 const int topReferenceLayerStackupId = getStackupLayerId( stackupLayerList, topReferenceLayer );
1077
1078 if( !IsCopperLayerLowerThan( signalLayer, topReferenceLayer ) )
1079 return { {}, CALCULATION_RESULT{ _( "Top reference layer must be above signal layer in board stackup" ) } };
1080
1081 // Get bottom reference layer
1082 wxString bottomReferenceLayerName = m_trackPropagationGrid->GetCellValue( aRow, TRACK_GRID_BOTTOM_REFERENCE );
1083
1084 if( !m_parentPanel->m_layerNamesToIDs.contains( bottomReferenceLayerName ) )
1085 return { {}, CALCULATION_RESULT{ _( "Bottom reference layer not found in stackup" ) } };
1086
1087 const PCB_LAYER_ID bottomReferenceLayer = m_parentPanel->m_layerNamesToIDs[bottomReferenceLayerName];
1088 const int bottomReferenceLayerStackupId = getStackupLayerId( stackupLayerList, bottomReferenceLayer );
1089
1090 if( !IsCopperLayerLowerThan( bottomReferenceLayer, signalLayer ) )
1091 return { {}, CALCULATION_RESULT{ _( "Bottom reference layer must be below signal layer in board stackup" ) } };
1092
1093 // Get the dielectric layers between signal and reference layers
1094 std::vector<int> topDielectricLayerStackupIds, bottomDielectricLayerStackupIds;
1095
1096 getDielectricLayers( stackupLayerList, signalLayerStackupId, topReferenceLayerStackupId,
1097 topDielectricLayerStackupIds );
1098 getDielectricLayers( stackupLayerList, signalLayerStackupId, bottomReferenceLayerStackupId,
1099 bottomDielectricLayerStackupIds );
1100
1101 // Calculate geometric average of the dielectric materials
1102 std::vector<int> allDielectricLayerStackupIds( topDielectricLayerStackupIds );
1103 allDielectricLayerStackupIds.insert( allDielectricLayerStackupIds.end(), bottomDielectricLayerStackupIds.begin(),
1104 bottomDielectricLayerStackupIds.end() );
1105
1106 const DIELECTRIC_INFO topDielectricInfo =
1107 calculateAverageDielectricConstants( stackupLayerList, topDielectricLayerStackupIds, aScale );
1108 const DIELECTRIC_INFO bottomDielectricInfo =
1109 calculateAverageDielectricConstants( stackupLayerList, bottomDielectricLayerStackupIds, aScale );
1110 const DIELECTRIC_INFO allDielectricInfo =
1111 calculateAverageDielectricConstants( stackupLayerList, allDielectricLayerStackupIds, aScale );
1112
1113 if( topDielectricInfo.Height <= 0.0 && bottomDielectricInfo.Height <= 0.0 )
1114 return { {}, CALCULATION_RESULT{ _( "Dielectric heights must be greater than 0" ) } };
1115
1116 CALCULATION_BOARD_PARAMETERS boardParameters{
1117 signalLayer, allDielectricInfo.E_r, topDielectricInfo.Height, bottomDielectricInfo.Height,
1118 signalLayerThickness, allDielectricInfo.Loss_Tangent
1119 };
1121 result.OK = true;
1122
1123 return { boardParameters, result };
1124}
1125
1126
1129{
1130 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1131
1132 // Get the target impedance
1133 const double targetZ = getTargetImpedance();
1134
1135 if( targetZ <= 0 )
1136 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1137
1138 // Get board parameters
1139 auto [boardParameters, result] = getMicrostripBoardParameters( aRow, iuScale );
1140
1141 if( !result.OK )
1142 return result;
1143
1144 // Set calculation parameters
1145 if( aCalculationType == CalculationType::WIDTH )
1146 {
1148 }
1149 else if( aCalculationType == CalculationType::DELAY )
1150 {
1151 const int widthInt = m_trackPropagationGrid->GetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1152
1153 const double width = iuScale.IUTomm( widthInt ) / 1000.0;
1155 }
1156
1157 const double frequency = getFrequency();
1158
1159 // Run the synthesis or analysis
1160 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, 1.0 / RHO );
1162 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SKIN_DEPTH, calculateSkinDepth( frequency, 1.0, 1.0 / RHO ) );
1163 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1164 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::H_T, 1e+20 );
1165 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness );
1166 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1167 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0, targetZ );
1168 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::FREQUENCY, frequency );
1170 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1175
1176 // Add solder mask parameters if required
1177 if( m_modelSolderMask->GetValue() )
1178 {
1181 boardParameters.SolderMaskThickness );
1183 boardParameters.SolderMaskDielectricConstant );
1184 m_microstripCalc.SetParameter( TRANSLINE_PARAMETERS::SOLDERMASK_TAND, boardParameters.SolderMaskLossTangent );
1185 }
1186
1187 if( aCalculationType == CalculationType::WIDTH )
1189 else
1190 m_microstripCalc.Analyse();
1191
1192 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1193 [this, aCalculationType]() -> decltype( m_microstripCalc.GetSynthesisResults() )
1194 {
1195 if( aCalculationType == CalculationType::WIDTH )
1196 return m_microstripCalc.GetSynthesisResults();
1197
1198 return m_microstripCalc.GetAnalysisResults();
1199 }();
1200
1202 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1203
1205 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1206
1207 int width = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1208 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1209 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1211
1212 return CALCULATION_RESULT{ width, propDelay };
1213}
1214
1215
1218{
1219 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1220
1221 // Get the target impedance
1222 const double targetZ = getTargetImpedance();
1223
1224 if( targetZ <= 0 )
1225 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1226
1227 // Get board parameters
1228 auto [boardParameters, result] = getStriplineBoardParameters( aRow, iuScale );
1229
1230 if( !result.OK )
1231 return result;
1232
1233 // Set calculation parameters
1234 if( aCalculationType == CalculationType::WIDTH )
1235 {
1237 }
1238 else if( aCalculationType == CalculationType::DELAY )
1239 {
1240 const int widthInt = m_trackPropagationGrid->GetUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1241
1242 const double width = iuScale.IUTomm( widthInt ) / 1000.0;
1244 }
1245
1246 // Run the synthesis
1247 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SKIN_DEPTH, calculateSkinDepth( 1.0, 1.0, 1.0 / RHO ) );
1248 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1249 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1250 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::STRIPLINE_A, boardParameters.TopDielectricLayerThickness );
1251 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness
1252 + boardParameters.SignalLayerThickness
1253 + boardParameters.BottomDielectricLayerThickness );
1254 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0, targetZ );
1257 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1258 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::ANG_L, 1.0 );
1259 m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, 1.0 / RHO );
1261
1262 if( aCalculationType == CalculationType::WIDTH )
1264 else
1265 m_striplineCalc.Analyse();
1266
1267 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1268 [this, aCalculationType]() -> decltype( m_striplineCalc.GetSynthesisResults() )
1269 {
1270 if( aCalculationType == CalculationType::WIDTH )
1271 return m_striplineCalc.GetSynthesisResults();
1272
1273 return m_striplineCalc.GetAnalysisResults();
1274 }();
1275
1277 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1278
1280 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1281
1282 int width = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1283 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1284 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1286
1287 return CALCULATION_RESULT{ width, propDelay };
1288}
1289
1290
1293{
1294 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1295
1296 // Get the target impedance
1297 const double targetZ = getTargetImpedance();
1298
1299 if( targetZ <= 0 )
1300 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1301
1302 // Get board parameters
1303 auto [boardParameters, result] = getMicrostripBoardParameters( aRow, iuScale );
1304
1305 if( !result.OK )
1306 return result;
1307
1308 // Set calculation parameters
1309 double width = 0.0;
1310 double gap = 0.0;
1311
1312 const std::optional<int> widthOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1313 const std::optional<int> gapOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_GAP );
1314
1315 if( aCalculationType == CalculationType::WIDTH )
1316 {
1317 if( !gapOpt || *gapOpt <= 0 )
1318 return CALCULATION_RESULT{ _( "Diff pair gap must be greater than 0 to calculate width" ) };
1319
1320 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1321 }
1322 else if( aCalculationType == CalculationType::GAP )
1323 {
1324 if( !widthOpt || *widthOpt <= 0 )
1325 return CALCULATION_RESULT{ _( "Width must be greater than 0 to calculate diff pair gap" ) };
1326
1327 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1328 }
1329 else if( aCalculationType == CalculationType::DELAY )
1330 {
1331 if( !widthOpt || !gapOpt || *widthOpt <= 0 || *gapOpt <= 0 )
1332 return CALCULATION_RESULT{ _( "Width and diff pair gap must be greater than 0 to calculate delay" ) };
1333
1334 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1335 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1336 }
1337
1338 // Run the synthesis
1339 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_E, targetZ / 2.0 );
1340 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_O, targetZ / 2.0 );
1344 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1346 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::H, boardParameters.TopDielectricLayerThickness );
1347 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1354 m_coupledMicrostripCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, boardParameters.LossTangent );
1356
1357 // Add solder mask parameters if required
1358 if( m_modelSolderMask->GetValue() )
1359 {
1362 boardParameters.SolderMaskThickness );
1364 boardParameters.SolderMaskDielectricConstant );
1366 boardParameters.SolderMaskLossTangent );
1367 }
1368
1369 switch( aCalculationType )
1370 {
1373 case CalculationType::DELAY: m_coupledMicrostripCalc.Analyse(); break;
1374 }
1375
1376 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1377 [this, aCalculationType]() -> decltype( m_microstripCalc.GetSynthesisResults() )
1378 {
1379 if( aCalculationType == CalculationType::WIDTH || aCalculationType == CalculationType::GAP )
1380 return m_coupledMicrostripCalc.GetSynthesisResults();
1381
1382 return m_coupledMicrostripCalc.GetAnalysisResults();
1383 }();
1384
1386 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1387
1388 if( results[TRANSLINE_PARAMETERS::PHYS_S].second != TRANSLINE_STATUS::OK )
1389 return CALCULATION_RESULT{ _( "Diff pair gap calculation failed" ) };
1390
1392 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1393
1394 int calcWidth = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1395 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1396 int calcGap = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1397 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_S].first * 1000.0 ) );
1398 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1400
1401 return CALCULATION_RESULT{ calcWidth, calcGap, propDelay };
1402}
1403
1404
1407{
1408 const EDA_IU_SCALE& iuScale = m_parentPanel->m_unitsProvider->GetIuScale();
1409
1410 // Get the target impedance
1411 const double targetZ = getTargetImpedance();
1412
1413 if( targetZ <= 0 )
1414 return CALCULATION_RESULT{ _( "Target impedance must be greater than 0" ) };
1415
1416 // Get board parameters
1417 auto [boardParameters, result] = getStriplineBoardParameters( aRow, iuScale );
1418
1419 if( !result.OK )
1420 return result;
1421
1422 // Set calculation parameters
1423 double width = 0.0;
1424 double gap = 0.0;
1425
1426 const std::optional<int> widthOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_WIDTH );
1427 const std::optional<int> gapOpt = m_trackPropagationGrid->GetOptionalUnitValue( aRow, TRACK_GRID_TRACK_GAP );
1428
1429 if( aCalculationType == CalculationType::WIDTH )
1430 {
1431 if( !gapOpt || *gapOpt <= 0 )
1432 return CALCULATION_RESULT{ _( "Diff pair gap must be greater than 0 to calculate width" ) };
1433
1434 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1435 }
1436 else if( aCalculationType == CalculationType::GAP )
1437 {
1438 if( !widthOpt || *widthOpt <= 0 )
1439 return CALCULATION_RESULT{ _( "Width must be greater than 0 to calculate diff pair gap" ) };
1440
1441 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1442 }
1443 else if( aCalculationType == CalculationType::DELAY )
1444 {
1445 if( !widthOpt || !gapOpt || *widthOpt <= 0 || *gapOpt <= 0 )
1446 return CALCULATION_RESULT{ _( "Width and diff pair gap must be greater than 0 to calculate delay" ) };
1447
1448 width = iuScale.IUTomm( widthOpt.value() ) / 1000.0;
1449 gap = iuScale.IUTomm( gapOpt.value() ) / 1000.0;
1450 }
1451
1452 // Run the synthesis
1453 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_E, targetZ / 2.0 );
1454 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::Z0_O, targetZ / 2.0 );
1458 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, boardParameters.SignalLayerThickness );
1459
1460 const double totalH = boardParameters.TopDielectricLayerThickness + boardParameters.SignalLayerThickness
1461 + boardParameters.BottomDielectricLayerThickness;
1462
1463 // COUPLED_STRIPLINE::Analyse reads STRIPLINE_A as the strip midplane distance from the bottom
1464 // ground (see isOffsetWithinFiniteThicknessLimits requiring t/2 < a < h - t/2). For a finite-
1465 // thickness signal layer the midplane is Top + T/2, not Top. Using Top as A would feed the
1466 // image-method solver inconsistent virtual plate spacings that do not reduce to the centred
1467 // result even when Top == Bottom.
1468 const double striplineA = boardParameters.TopDielectricLayerThickness
1469 + 0.5 * boardParameters.SignalLayerThickness;
1470
1471 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, totalH );
1472
1473 // Set the offset top dielectric layer thickness if required
1474 if( COUPLED_STRIPLINE::IsCenteredOffset( striplineA, totalH ) )
1475 {
1476 // Keep calculation on the symmetric fast path
1478 }
1479 else
1480 {
1481 // Use the asymmetric calculation path
1483 }
1484
1485 m_coupledStriplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, boardParameters.DielectricConstant );
1492
1493 switch( aCalculationType )
1494 {
1497 case CalculationType::DELAY: m_coupledStriplineCalc.Analyse(); break;
1498 }
1499
1500 std::unordered_map<TRANSLINE_PARAMETERS, std::pair<double, TRANSLINE_STATUS>>& results =
1501 [this, aCalculationType]() -> decltype( m_coupledStriplineCalc.GetSynthesisResults() )
1502 {
1503 if( aCalculationType == CalculationType::WIDTH || aCalculationType == CalculationType::GAP )
1504 return m_coupledStriplineCalc.GetSynthesisResults();
1505
1506 return m_coupledStriplineCalc.GetAnalysisResults();
1507 }();
1508
1510 return CALCULATION_RESULT{ _( "Width calculation failed" ) };
1511
1512 if( results[TRANSLINE_PARAMETERS::PHYS_S].second != TRANSLINE_STATUS::OK )
1513 return CALCULATION_RESULT{ _( "Diff pair gap calculation failed" ) };
1514
1516 return CALCULATION_RESULT{ _( "Delay calculation failed" ) };
1517
1518 int calcWidth = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1519 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_WIDTH].first * 1000.0 ) );
1520 int calcGap = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1521 iuScale, EDA_UNITS::MM, results[TRANSLINE_PARAMETERS::PHYS_S].first * 1000.0 ) );
1522 int propDelay = static_cast<int>( EDA_UNIT_UTILS::UI::FromUserUnit(
1524
1525 return CALCULATION_RESULT{ calcWidth, calcGap, propDelay };
1526}
1527
1528
1530{
1531 double frequency = 0.0;
1532 m_frequency->GetValue().ToDouble( &frequency );
1533
1534 switch( m_frequencyUnits->GetSelection() )
1535 {
1536 case 1: // kHz
1537 frequency *= 1e3;
1538 break;
1539 case 2: // MHz
1540 frequency *= 1e6;
1541 break;
1542 case 3: // GHz
1543 frequency *= 1e9;
1544 break;
1545 default: // Hz
1546 break;
1547 }
1548
1549 return frequency;
1550}
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:410
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:943
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
Add a via override row.
static constexpr double RHO
Electrical resistivity or specific electrical resistance of copper (ohm*meter)
MICROSTRIP m_microstripCalc
Calculator for single microstrip parameters.
COUPLED_STRIPLINE m_coupledStriplineCalc
Calculator for coupled (differential) stripline parameters.
void initPanel()
Initialise all controls on the panel.
void calculateTrackParametersForCell(int aRow, int aCol)
Calculate the required track parameters for the given track parameters grid row and col.
CALCULATION_RESULT calculateSingleStripline(const int aRow, CalculationType aCalculationType)
Calculate the track width or delay for the given propagation grid row.
double getTargetImpedance() const
Get the target impedance for the profile.
CALCULATION_RESULT calculateSingleMicrostrip(const int aRow, CalculationType aCalculationType)
Calculate 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()
Update the displayed layer names in all grids.
static int getStackupLayerId(const std::vector< BOARD_STACKUP_ITEM * > &aLayerList, PCB_LAYER_ID aPcbLayerId)
Get 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)
Calculate 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)
Get the board parameters for microstrip calculations.
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)
Calculate the geometric average of the dielectric material properties.
void LoadProfile(const TUNING_PROFILE &aProfile)
Load the given profile in to the panel.
TUNING_PROFILE GetProfile() const
Save 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)
Get the dielectric information for the solder mask covering a given signallayer.
double getFrequency() const
Get the target frequency in Hz.
PANEL_SETUP_TUNING_PROFILES * m_parentPanel
The parent setup panel.
void OnChangeProfileType(wxCommandEvent &event) override
Change between Single and Differential profiles.
std::pair< CALCULATION_BOARD_PARAMETERS, CALCULATION_RESULT > getStriplineBoardParameters(int aRow, const EDA_IU_SCALE &aScale)
Get the board parameters for stripline calculations.
void getDielectricLayers(const std::vector< BOARD_STACKUP_ITEM * > &aStackupLayerList, int aSignalLayerId, int aReferenceLayerId, std::vector< int > &aDielectricLayerStackupIds)
Get the dielectric layers for dielectrics between the two given copper layer IDs.
void OnRemoveTrackRow(wxCommandEvent &event) override
Remove a row from the track propagation grid.
wxString GetProfileName() const
Get the name of this profile.
COUPLED_MICROSTRIP m_coupledMicrostripCalc
Calculator for coupled (differential) microstrip parameters.
CALCULATION_RESULT calculateDifferentialStripline(int aRow, CalculationType aCalculationType)
Calculate the track width, pair gap, or delay for the given propagation grid row.
void onChangeProfileType(TUNING_PROFILE::PROFILE_TYPE aType) const
Set the panel display for the given tuning type.
wxString m_lastSyncedName
The profile name as of the last rename sync.
void OnRemoveViaOverride(wxCommandEvent &event) override
Remove a via override row.
void OnAddTrackRow(wxCommandEvent &event) override
Add a row to the track propagation grid.
void setColumnWidths()
Set up the widths of all grid columns.
void OnProfileNameChanged(wxCommandEvent &event) override
Update 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:850
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:806
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:829
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:90
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