KiCad PCB EDA Suite
panel_track_width.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 (C) 2011 jean-pierre.charras
5  * Copyright (C) 1992-2015 Kicad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /* see
22  * http://www.desmith.net/NMdS/Electronics/TraceWidth.html
23  * http://www.ultracad.com/articles/pcbtemp.pdf
24  * for more info
25  */
26 
27 #include <cassert>
28 #include <cmath>
29 #include <kiface_base.h>
30 
33 #include <string_utils.h>
34 #include <units_scales.h>
35 
36 #include <widgets/unit_selector.h>
37 
38 #include <i18n_utility.h> // For _HKI definition
41 
42 extern double DoubleFromString( const wxString& TextValue );
43 
44 // The IPC2221 formula used to calculate track width is valid only for copper material
45 const double copper_resistivity = 1.72e-8;
46 
47 
48 PANEL_TRACK_WIDTH::PANEL_TRACK_WIDTH( wxWindow* parent, wxWindowID id,
49  const wxPoint& pos, const wxSize& size,
50  long style, const wxString& name ) :
51  PANEL_TRACK_WIDTH_BASE( parent, id, pos, size, style, name ),
52  m_TWMode( TW_MASTER_CURRENT ),
53  m_TWNested( false )
54 {
55  m_trackTempUnits->SetLabel( wxT( "°C" ) );
56  m_resistivityUnits->SetLabel( wxT( "Ω⋅m" ) );
57 
58  m_extTrackResUnits->SetLabel( wxT( "Ω" ) );
59  m_intTrackResUnits->SetLabel( wxT( "Ω" ) );
60 
61  m_staticText63->SetLabel( _( "Temperature rise" ) + wxT( " (ΔT):" ) );
62 
63  // Needed on wxWidgets 3.0 to ensure sizers are correctly set
64  GetSizer()->SetSizeHints( this );
65 }
66 
67 
69 {
70 }
71 
72 
74 {
76 }
77 
78 
80 {
81  aCfg->m_TrackWidth.current = m_TrackCurrentValue->GetValue();
82  aCfg->m_TrackWidth.delta_tc = m_TrackDeltaTValue->GetValue();
83  aCfg->m_TrackWidth.track_len = m_TrackLengthValue->GetValue();
85  aCfg->m_TrackWidth.resistivity = m_TWResistivity->GetValue();
94 }
95 
96 
97 void PANEL_TRACK_WIDTH::OnTWParametersChanged( wxCommandEvent& event )
98 {
99  switch(m_TWMode)
100  {
101  case TW_MASTER_CURRENT: OnTWCalculateFromCurrent( event ); break;
102  case TW_MASTER_EXT_WIDTH: OnTWCalculateFromExtWidth( event ); break;
103  case TW_MASTER_INT_WIDTH: OnTWCalculateFromIntWidth( event ); break;
104  }
105 }
106 
107 
108 void PANEL_TRACK_WIDTH::OnTWCalculateFromCurrent( wxCommandEvent& event )
109 {
110  // Setting the calculated values generates further events. Stop them.
111  if( m_TWNested )
112  {
113  event.StopPropagation();
114  return;
115  }
116 
117  m_TWNested = true;
118 
119  // Update state.
120  if( m_TWMode != TW_MASTER_CURRENT )
121  {
124  }
125 
126  // Prepare parameters:
127  double current = std::abs( DoubleFromString( m_TrackCurrentValue->GetValue() ) );
128  double extThickness = std::abs( DoubleFromString( m_ExtTrackThicknessValue->GetValue() ) );
129  double intThickness = std::abs( DoubleFromString( m_IntTrackThicknessValue->GetValue() ) );
130  double deltaT_C = std::abs( DoubleFromString( m_TrackDeltaTValue->GetValue() ) );
131 
132  // Normalize by units.
133  extThickness *= m_ExtTrackThicknessUnit->GetUnitScale();
134  intThickness *= m_IntTrackThicknessUnit->GetUnitScale();
135 
136  // Calculate the widths.
137  double extTrackWidth = TWCalculateWidth( current, extThickness, deltaT_C, false );
138  double intTrackWidth = TWCalculateWidth( current, intThickness, deltaT_C, true );
139 
140  // Update the display.
141  TWDisplayValues( current, extTrackWidth, intTrackWidth, extThickness, intThickness );
142 
143  // Re-enable the events.
144  m_TWNested = false;
145 }
146 
147 
148 void PANEL_TRACK_WIDTH::OnTWCalculateFromExtWidth( wxCommandEvent& event )
149 {
150  // Setting the calculated values generates further events. Stop them.
151  if( m_TWNested )
152  {
153  event.StopPropagation();
154  return;
155  }
156  m_TWNested = true;
157 
158  // Update state.
160  {
163  }
164 
165  // Load parameters.
166  double current;
167  double extThickness = std::abs( DoubleFromString( m_ExtTrackThicknessValue->GetValue() ) );
168  double intThickness = std::abs( DoubleFromString( m_IntTrackThicknessValue->GetValue() ) );
169  double deltaT_C = std::abs( DoubleFromString( m_TrackDeltaTValue->GetValue() ) );
170  double extTrackWidth = std::abs( DoubleFromString( m_ExtTrackWidthValue->GetValue() ) );
171  double intTrackWidth;
172 
173  // Normalize units.
174  extThickness *= m_ExtTrackThicknessUnit->GetUnitScale();
175  intThickness *= m_IntTrackThicknessUnit->GetUnitScale();
176  extTrackWidth *= m_TW_ExtTrackWidth_choiceUnit->GetUnitScale();
177 
178  // Calculate the maximum current.
179  current = TWCalculateCurrent( extTrackWidth, extThickness, deltaT_C, false );
180 
181  // And now calculate the corresponding internal width.
182  intTrackWidth = TWCalculateWidth( current, intThickness, deltaT_C, true );
183 
184  // Update the display.
185  TWDisplayValues( current, extTrackWidth, intTrackWidth, extThickness, intThickness );
186 
187  // Re-enable the events.
188  m_TWNested = false;
189 }
190 
191 
192 void PANEL_TRACK_WIDTH::OnTWCalculateFromIntWidth( wxCommandEvent& event )
193 {
194  // Setting the calculated values generates further events. Stop them.
195  if( m_TWNested )
196  {
197  event.StopPropagation();
198  return;
199  }
200 
201  m_TWNested = true;
202 
203  // Update state.
205  {
208  }
209 
210  // Load parameters.
211  double current;
212  double extThickness = std::abs( DoubleFromString( m_ExtTrackThicknessValue->GetValue() ) );
213  double intThickness = std::abs( DoubleFromString( m_IntTrackThicknessValue->GetValue() ) );
214  double deltaT_C = std::abs( DoubleFromString( m_TrackDeltaTValue->GetValue() ) );
215  double extTrackWidth;
216  double intTrackWidth = std::abs( DoubleFromString( m_IntTrackWidthValue->GetValue() ) );
217 
218  // Normalize units.
219  extThickness *= m_ExtTrackThicknessUnit->GetUnitScale();
220  intThickness *= m_IntTrackThicknessUnit->GetUnitScale();
221  intTrackWidth *= m_TW_IntTrackWidth_choiceUnit->GetUnitScale();
222 
223  // Calculate the maximum current.
224  current = TWCalculateCurrent( intTrackWidth, intThickness, deltaT_C, true );
225 
226  // And now calculate the corresponding external width.
227  extTrackWidth = TWCalculateWidth( current, extThickness, deltaT_C, false );
228 
229  // Update the display.
230  TWDisplayValues( current, extTrackWidth, intTrackWidth, extThickness, intThickness );
231 
232  // Re-enable the events.
233  m_TWNested = false;
234 }
235 
236 
237 void PANEL_TRACK_WIDTH::OnTWResetButtonClick( wxCommandEvent& event )
238 {
239  // Note: a wxString:Format( "%g", xx) is used to use local separator in floats
240 
241  // Init main parameters:
242  m_TrackCurrentValue->SetValue( wxString::Format( "%g", 1.0 ) );
243  m_TrackDeltaTValue->SetValue( wxString::Format( "%g", 10.0 ) );
244  m_TrackLengthValue->SetValue( wxString::Format( "%g", 20.0 ) );
245  m_TW_CuLength_choiceUnit->SetSelection( 0 );
247 
248  // m_ExtTrackWidthValue is not reinitialized: it will be calculated from previous parameters
249  m_TW_ExtTrackWidth_choiceUnit->SetSelection( 0 );
250  m_ExtTrackThicknessValue->SetValue( wxString::Format( "%g", 0.035 ) );
251  m_ExtTrackThicknessUnit->SetSelection( 0 );
252 
253  // m_IntTrackWidthValue is not reinitialized: it will be calculated from previous parameters
254  m_TW_IntTrackWidth_choiceUnit->SetSelection( 0 );
255  m_IntTrackThicknessValue->SetValue( wxString::Format( "%g", 0.035 ) );
256  m_IntTrackThicknessUnit->SetSelection( 0 );
257 }
258 
259 
260 void PANEL_TRACK_WIDTH::TWDisplayValues( double aCurrent, double aExtWidth, double aIntWidth,
261  double aExtThickness, double aIntThickness )
262 {
263  wxString msg;
264 
265  // Show the current.
266  if( m_TWMode != TW_MASTER_CURRENT )
267  {
268  msg.Printf( wxT( "%g" ), aCurrent );
269  m_TrackCurrentValue->SetValue( msg );
270  }
271 
272  // Load scale factors to convert into output units.
273  double extScale = m_TW_ExtTrackWidth_choiceUnit->GetUnitScale();
274  double intScale = m_TW_IntTrackWidth_choiceUnit->GetUnitScale();
275 
276  // Display the widths.
278  {
279  msg.Printf( wxT( "%g" ), aExtWidth / extScale );
280  m_ExtTrackWidthValue->SetValue( msg );
281  }
282 
284  {
285  msg.Printf( wxT( "%g" ), aIntWidth / intScale );
286  m_IntTrackWidthValue->SetValue( msg );
287  }
288 
289  // Display cross-sectional areas.
290  msg.Printf( wxT( "%g" ), (aExtWidth * aExtThickness) / (extScale * extScale) );
291  m_ExtTrackAreaValue->SetLabel( msg );
292  msg.Printf( wxT( "%g" ), (aIntWidth * aIntThickness) / (intScale * intScale) );
293  m_IntTrackAreaValue->SetLabel( msg );
294 
295  // Show area units.
296  wxString strunit = m_TW_ExtTrackWidth_choiceUnit->GetUnitName();
297  msg = strunit + wxT( "²" );
298  m_extTrackAreaUnitLabel->SetLabel( msg );
300  msg = strunit + wxT( "²" );
301  m_intTrackAreaUnitLabel->SetLabel( msg );
302 
303  // Load resistivity and length of traces.
304  double rho = std::abs( DoubleFromString( m_TWResistivity->GetValue() ) );
305  double trackLen = std::abs( DoubleFromString( m_TrackLengthValue->GetValue() ) );
306  trackLen *= m_TW_CuLength_choiceUnit->GetUnitScale();
307 
308  // Calculate resistance.
309  double extResistance = ( rho * trackLen ) / ( aExtWidth * aExtThickness );
310  double intResistance = ( rho * trackLen ) / ( aIntWidth * aIntThickness );
311 
312  // Display resistance.
313  msg.Printf( wxT( "%g" ), extResistance );
314  m_ExtTrackResistValue->SetLabel( msg );
315  msg.Printf( wxT( "%g" ), intResistance );
316  m_IntTrackResistValue->SetLabel( msg );
317 
318  // Display voltage drop along trace.
319  double extV = extResistance * aCurrent;
320  msg.Printf( wxT( "%g" ), extV );
321  m_ExtTrackVDropValue->SetLabel( msg );
322  double intV = intResistance * aCurrent;
323  msg.Printf( wxT( "%g" ), intV );
324  m_IntTrackVDropValue->SetLabel( msg );
325 
326  // And power loss.
327  msg.Printf( wxT( "%g" ), extV * aCurrent );
328  m_ExtTrackLossValue->SetLabel( msg );
329  msg.Printf( wxT( "%g" ), intV * aCurrent );
330  m_IntTrackLossValue->SetLabel( msg );
331 }
332 
333 
335 {
336  wxFont labelfont;
337  wxFont controlfont;
338 
339  // Set the font weight of the current.
340  labelfont = m_staticTextCurrent->GetFont();
341  controlfont = m_TrackCurrentValue->GetFont();
342 
343  if( m_TWMode == TW_MASTER_CURRENT )
344  {
345  labelfont.SetWeight( wxFONTWEIGHT_BOLD );
346  controlfont.SetWeight( wxFONTWEIGHT_BOLD );
347  }
348  else
349  {
350  labelfont.SetWeight( wxFONTWEIGHT_NORMAL );
351  controlfont.SetWeight( wxFONTWEIGHT_NORMAL );
352  }
353 
354  m_staticTextCurrent->SetFont( labelfont );
355  m_TrackCurrentValue->SetFont( controlfont );
356 
357  // Set the font weight of the external track width.
358  labelfont = m_staticTextExtWidth->GetFont();
359  controlfont = m_ExtTrackWidthValue->GetFont();
360 
362  {
363  labelfont.SetWeight( wxFONTWEIGHT_BOLD );
364  controlfont.SetWeight( wxFONTWEIGHT_BOLD );
365  }
366  else
367  {
368  labelfont.SetWeight( wxFONTWEIGHT_NORMAL );
369  controlfont.SetWeight( wxFONTWEIGHT_NORMAL );
370  }
371 
372  m_staticTextExtWidth->SetFont( labelfont );
373  m_ExtTrackWidthValue->SetFont( controlfont );
374 
375  // Set the font weight of the internal track width.
376  labelfont = m_staticTextIntWidth->GetFont();
377  controlfont = m_IntTrackWidthValue->GetFont();
378 
380  {
381  labelfont.SetWeight( wxFONTWEIGHT_BOLD );
382  controlfont.SetWeight( wxFONTWEIGHT_BOLD );
383  }
384  else
385  {
386  labelfont.SetWeight( wxFONTWEIGHT_NORMAL );
387  controlfont.SetWeight( wxFONTWEIGHT_NORMAL );
388  }
389 
390  m_staticTextIntWidth->SetFont( labelfont );
391  m_IntTrackWidthValue->SetFont( controlfont );
392 
393  // Text sizes have changed when the font weight was changes
394  // So, run the page layout to reflect the changes
395  GetSizer()->Layout();
396 }
397 
398 /* calculate track width for external or internal layers
399  *
400  * Imax = 0.048 * dT^0.44 * A^0.725 for external layer
401  * Imax = 0.024 * dT^0.44 * A^0.725 for internal layer
402  * with A = area = aThickness * trackWidth ( in mils )
403  * and dT = temperature rise in degree C
404  * Of course we want to know trackWidth
405  */
406 double PANEL_TRACK_WIDTH::TWCalculateWidth( double aCurrent, double aThickness, double aDeltaT_C,
407  bool aUseInternalLayer )
408 {
409  // Appropriate scale for requested layer.
410  double scale = aUseInternalLayer ? 0.024 : 0.048;
411 
412  // aThickness is given in normalize units (in meters) and we need mil
413  aThickness /= UNIT_MIL;
414 
415  /* formula is Imax = scale * dT^0.44 * A^0.725
416  * or
417  * log(Imax) = log(scale) + 0.44*log(dT) +(0.725*(log(aThickness) + log(trackWidth))
418  * log(trackWidth) * 0.725 = log(Imax) - log(scale) - 0.44*log(dT) - 0.725*log(aThickness)
419  */
420  double dtmp = log( aCurrent ) - log( scale ) - 0.44 * log( aDeltaT_C ) - 0.725 * log( aThickness );
421  dtmp /= 0.725;
422  double trackWidth = exp( dtmp );
423 
424  trackWidth *= UNIT_MIL; // We are using normalize units (sizes in meters) and we have mil
425  return trackWidth; // in meters
426 }
427 
428 
429 double PANEL_TRACK_WIDTH::TWCalculateCurrent( double aWidth, double aThickness, double aDeltaT_C,
430  bool aUseInternalLayer )
431 {
432  // Appropriate scale for requested layer.
433  double scale = aUseInternalLayer ? 0.024 : 0.048;
434 
435  // Convert thickness and width to mils.
436  aThickness /= UNIT_MIL;
437  aWidth /= UNIT_MIL;
438 
439  double area = aThickness * aWidth;
440  double current = scale * pow( aDeltaT_C, 0.44 ) * pow( area, 0.725 );
441 
442  return current;
443 }
444 
445 
447 {
448  wxString msg;
449 
450  // Disable calculations while we initialise.
451  m_TWNested = true;
452 
453  // Read parameter values.
454  m_TrackCurrentValue->SetValue( aCfg->m_TrackWidth.current );
455  m_TrackDeltaTValue->SetValue( aCfg->m_TrackWidth.delta_tc );
456  m_TrackLengthValue->SetValue( aCfg->m_TrackWidth.track_len );
458 #if 0 // the IPC formula is valid for copper traces, so we do not currently adjust the resistivity
459  m_TWResistivity->SetValue( aCfg->m_TrackWidth.resistivity );
460 #else
462 #endif
471 
472  if( tracks_width_versus_current_formula.StartsWith( "<!" ) )
474  else
475  {
476  wxString html_txt;
477  ConvertMarkdown2Html( wxGetTranslation( tracks_width_versus_current_formula ), html_txt );
478  m_htmlWinFormulas->SetPage( html_txt );
479  }
480 
481  // Make sure the correct master mode is displayed.
483 
484  // Enable calculations and perform the initial one.
485  m_TWNested = false;
486 
487  wxCommandEvent dummy;
489 }
void OnTWCalculateFromIntWidth(wxCommandEvent &event) override
Update the calculations when the user changes the desired internal trace width.
void TWDisplayValues(double aCurrent, double aExtWidth, double aIntWidth, double aExtThickness, double aIntThickness)
Display the results of a calculation (including resulting values such as the resistance and power los...
void OnTWCalculateFromExtWidth(wxCommandEvent &event) override
Update the calculations when the user changes the desired external trace width.
Class PANEL_TRACK_WIDTH_BASE.
UNIT_SELECTOR_LEN * m_TW_IntTrackWidth_choiceUnit
double GetUnitScale() override
Function GetUnitScale.
wxStaticText * m_extTrackAreaUnitLabel
wxStaticText * m_IntTrackResistValue
double TWCalculateCurrent(double aWidth, double aThickness, double aDeltaT_C, bool aUseInternalLayer)
Calculate maximum current based on given width and temperature rise.
void OnTWParametersChanged(wxCommandEvent &event) override
Update the calculations the user changes the general parameters.
UNIT_SELECTOR_THICKNESS * m_IntTrackThicknessUnit
double TWCalculateWidth(double aCurrent, double aThickness, double aDeltaT_C, bool aUseInternalLayer)
Calculate track width required based on given current and temperature rise.
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
void ConvertMarkdown2Html(const wxString &aMarkdownInput, wxString &aHtmlOutput)
wxStaticText * m_intTrackAreaUnitLabel
UNIT_SELECTOR_LEN * m_TW_ExtTrackWidth_choiceUnit
bool SetPage(const wxString &aSource) override
Definition: html_window.cpp:38
#define _(s)
double DoubleFromString(const wxString &TextValue)
wxString GetUnitName()
Definition: unit_selector.h:51
UNIT_SELECTOR_THICKNESS * m_ExtTrackThicknessUnit
Some functions to handle hotkeys in KiCad.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
const double copper_resistivity
enum PANEL_TRACK_WIDTH::@37 m_TWMode
const int scale
void ThemeChanged()
Definition: html_window.cpp:63
void ThemeChanged() override
Update UI elements of the panel when the theme changes to ensure the images and fonts/colors are appr...
const char * name
Definition: DXF_plotter.cpp:56
double GetUnitScale() override
Function GetUnitScale.
UNIT_SELECTOR_LEN * m_TW_CuLength_choiceUnit
void TWUpdateModeDisplay()
Update the fields to show whether the maximum current, external trace width, or internal trace width ...
wxStaticText * m_ExtTrackResistValue
PANEL_TRACK_WIDTH(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
void OnTWCalculateFromCurrent(wxCommandEvent &event) override
Update the calculations when the user changes the desired maximum current.
#define UNIT_MIL
Definition: units_scales.h:35
void SaveSettings(PCB_CALCULATOR_SETTINGS *aCfg) override
Save the settings from the panel.
wxString tracks_width_versus_current_formula
void OnTWResetButtonClick(wxCommandEvent &event) override
Update the calculations when the user clicks the reset button.
void LoadSettings(PCB_CALCULATOR_SETTINGS *aCfg) override
Load the settings into the panel.