KiCad PCB EDA Suite
tracks_width_versus_current.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 <wx/wx.h>
30 #include <kiface_i.h>
31 #include <dialog_helpers.h>
32 
34 
35 #include <pcb_calculator.h>
37 #include <UnitSelector.h>
38 #include <units_scales.h>
39 
42 
43 extern double DoubleFromString( const wxString& TextValue );
44 
45 
47 {
48  // Save current parameters values in config.
49  auto cfg = static_cast<PCB_CALCULATOR_SETTINGS*>( Kiface().KifaceSettings() );
50 
51  cfg->m_TrackWidth.current = m_TrackCurrentValue->GetValue();
52  cfg->m_TrackWidth.delta_tc = m_TrackDeltaTValue->GetValue();
53  cfg->m_TrackWidth.track_len = m_TrackLengthValue->GetValue();
54  cfg->m_TrackWidth.track_len_units = m_TW_CuLength_choiceUnit->GetSelection();
55  cfg->m_TrackWidth.resistivity = m_TWResistivity->GetValue();
56  cfg->m_TrackWidth.ext_track_width = m_ExtTrackWidthValue->GetValue();
57  cfg->m_TrackWidth.ext_track_width_units = m_TW_ExtTrackWidth_choiceUnit->GetSelection();
58  cfg->m_TrackWidth.ext_track_thickness = m_ExtTrackThicknessValue->GetValue();
59  cfg->m_TrackWidth.ext_track_thickness_units = m_ExtTrackThicknessUnit->GetSelection();
60  cfg->m_TrackWidth.int_track_width = m_IntTrackWidthValue->GetValue();
61  cfg->m_TrackWidth.int_track_width_units = m_TW_IntTrackWidth_choiceUnit->GetSelection();
62  cfg->m_TrackWidth.int_track_thickness = m_IntTrackThicknessValue->GetValue();
63  cfg->m_TrackWidth.int_track_thickness_units = m_IntTrackThicknessUnit->GetSelection();
64 }
65 
66 
67 void PCB_CALCULATOR_FRAME::OnTWParametersChanged( wxCommandEvent& event )
68 {
69  switch(m_TWMode)
70  {
71  case TW_MASTER_CURRENT:
72  OnTWCalculateFromCurrent( event );
73  break;
76  break;
79  break;
80  }
81 }
82 
83 
85 {
86  // Setting the calculated values generates further events. Stop them.
87  if( m_TWNested )
88  {
89  event.StopPropagation();
90  return;
91  }
92 
93  m_TWNested = true;
94 
95  // Update state.
97  {
100  }
101 
102  // Prepare parameters:
103  double current = std::abs( DoubleFromString( m_TrackCurrentValue->GetValue() ) );
104  double extThickness = std::abs( DoubleFromString( m_ExtTrackThicknessValue->GetValue() ) );
105  double intThickness = std::abs( DoubleFromString( m_IntTrackThicknessValue->GetValue() ) );
106  double deltaT_C = std::abs( DoubleFromString( m_TrackDeltaTValue->GetValue() ) );
107 
108  // Normalize by units.
109  extThickness *= m_ExtTrackThicknessUnit->GetUnitScale();
110  intThickness *= m_IntTrackThicknessUnit->GetUnitScale();
111 
112  // Calculate the widths.
113  double extTrackWidth = TWCalculateWidth( current, extThickness, deltaT_C, false );
114  double intTrackWidth = TWCalculateWidth( current, intThickness, deltaT_C, true );
115 
116  // Update the display.
117  TWDisplayValues( current, extTrackWidth, intTrackWidth, extThickness, intThickness );
118 
119  // Re-enable the events.
120  m_TWNested = false;
121 }
122 
123 
125 {
126  // Setting the calculated values generates further events. Stop them.
127  if( m_TWNested )
128  {
129  event.StopPropagation();
130  return;
131  }
132  m_TWNested = true;
133 
134  // Update state.
136  {
139  }
140 
141  // Load parameters.
142  double current;
143  double extThickness = std::abs( DoubleFromString( m_ExtTrackThicknessValue->GetValue() ) );
144  double intThickness = std::abs( DoubleFromString( m_IntTrackThicknessValue->GetValue() ) );
145  double deltaT_C = std::abs( DoubleFromString( m_TrackDeltaTValue->GetValue() ) );
146  double extTrackWidth = std::abs( DoubleFromString( m_ExtTrackWidthValue->GetValue() ) );
147  double intTrackWidth;
148 
149  // Normalize units.
150  extThickness *= m_ExtTrackThicknessUnit->GetUnitScale();
151  intThickness *= m_IntTrackThicknessUnit->GetUnitScale();
152  extTrackWidth *= m_TW_ExtTrackWidth_choiceUnit->GetUnitScale();
153 
154  // Calculate the maximum current.
155  current = TWCalculateCurrent( extTrackWidth, extThickness, deltaT_C, false );
156 
157  // And now calculate the corresponding internal width.
158  intTrackWidth = TWCalculateWidth( current, intThickness, deltaT_C, true );
159 
160  // Update the display.
161  TWDisplayValues( current, extTrackWidth, intTrackWidth, extThickness, intThickness );
162 
163  // Re-enable the events.
164  m_TWNested = false;
165 }
166 
167 
169 {
170  // Setting the calculated values generates further events. Stop them.
171  if( m_TWNested )
172  {
173  event.StopPropagation();
174  return;
175  }
176 
177  m_TWNested = true;
178 
179  // Update state.
181  {
184  }
185 
186  // Load parameters.
187  double current;
188  double extThickness = std::abs( DoubleFromString( m_ExtTrackThicknessValue->GetValue() ) );
189  double intThickness = std::abs( DoubleFromString( m_IntTrackThicknessValue->GetValue() ) );
190  double deltaT_C = std::abs( DoubleFromString( m_TrackDeltaTValue->GetValue() ) );
191  double extTrackWidth;
192  double intTrackWidth = std::abs( DoubleFromString( m_IntTrackWidthValue->GetValue() ) );
193 
194  // Normalize units.
195  extThickness *= m_ExtTrackThicknessUnit->GetUnitScale();
196  intThickness *= m_IntTrackThicknessUnit->GetUnitScale();
197  intTrackWidth *= m_TW_IntTrackWidth_choiceUnit->GetUnitScale();
198 
199  // Calculate the maximum current.
200  current = TWCalculateCurrent( intTrackWidth, intThickness, deltaT_C, true );
201 
202  // And now calculate the corresponding external width.
203  extTrackWidth = TWCalculateWidth( current, extThickness, deltaT_C, false );
204 
205  // Update the display.
206  TWDisplayValues( current, extTrackWidth, intTrackWidth, extThickness, intThickness );
207 
208  // Re-enable the events.
209  m_TWNested = false;
210 }
211 
212 
213 void PCB_CALCULATOR_FRAME::OnTWResetButtonClick( wxCommandEvent& event )
214 {
215  m_TrackCurrentValue->SetValue( wxT( "1.0" ) );
216  m_TrackDeltaTValue->SetValue( wxT( "10.0" ) );
217  m_TrackLengthValue->SetValue( wxT( "20" ) );
218  m_TW_CuLength_choiceUnit->SetSelection( 0 );
219  m_TWResistivity->SetValue( wxT( "1.72e-8" ) );
220  m_ExtTrackWidthValue->SetValue( wxT( "0.2" ) );
221  m_TW_ExtTrackWidth_choiceUnit->SetSelection( 0 );
222  m_ExtTrackThicknessValue->SetValue( wxT( "0.035" ) );
223  m_ExtTrackThicknessUnit->SetSelection( 0 );
224  m_IntTrackWidthValue->SetValue( wxT( "0.2" ) );
225  m_TW_IntTrackWidth_choiceUnit->SetSelection( 0 );
226  m_IntTrackThicknessValue->SetValue( wxT( "0.035" ) );
227  m_IntTrackThicknessUnit->SetSelection( 0 );
228 }
229 
230 
231 void PCB_CALCULATOR_FRAME::TWDisplayValues( double aCurrent, double aExtWidth,
232  double aIntWidth, double aExtThickness, double aIntThickness )
233 {
234  wxString msg;
235 
236  // Show the current.
237  if( m_TWMode != TW_MASTER_CURRENT )
238  {
239  msg.Printf( wxT( "%g" ), aCurrent );
240  m_TrackCurrentValue->SetValue( msg );
241  }
242 
243  // Load scale factors to convert into output units.
244  double extScale = m_TW_ExtTrackWidth_choiceUnit->GetUnitScale();
245  double intScale = m_TW_IntTrackWidth_choiceUnit->GetUnitScale();
246 
247  // Display the widths.
249  {
250  msg.Printf( wxT( "%g" ), aExtWidth / extScale );
251  m_ExtTrackWidthValue->SetValue( msg );
252  }
253 
255  {
256  msg.Printf( wxT( "%g" ), aIntWidth / intScale );
257  m_IntTrackWidthValue->SetValue( msg );
258  }
259 
260  // Display cross-sectional areas.
261  msg.Printf( wxT( "%g" ), (aExtWidth * aExtThickness) / (extScale * extScale) );
262  m_ExtTrackAreaValue->SetLabel( msg );
263  msg.Printf( wxT( "%g" ), (aIntWidth * aIntThickness) / (intScale * intScale) );
264  m_IntTrackAreaValue->SetLabel( msg );
265 
266  // Show area units.
267  wxString strunit = m_TW_ExtTrackWidth_choiceUnit->GetUnitName();
268  msg = strunit + wxT( " x " ) + strunit;
269  m_ExtTrackAreaUnitLabel->SetLabel( msg );
271  msg = strunit + wxT( " x " ) + strunit;
272  m_IntTrackAreaUnitLabel->SetLabel( msg );
273 
274  // Load resistivity and length of traces.
275  double rho = std::abs( DoubleFromString( m_TWResistivity->GetValue() ) );
276  double trackLen = std::abs( DoubleFromString( m_TrackLengthValue->GetValue() ) );
277  trackLen *= m_TW_CuLength_choiceUnit->GetUnitScale();
278 
279  // Calculate resistance.
280  double extResistance = ( rho * trackLen ) / ( aExtWidth * aExtThickness );
281  double intResistance = ( rho * trackLen ) / ( aIntWidth * aIntThickness );
282 
283  // Display resistance.
284  msg.Printf( wxT( "%g" ), extResistance );
285  m_ExtTrackResistValue->SetLabel( msg );
286  msg.Printf( wxT( "%g" ), intResistance );
287  m_IntTrackResistValue->SetLabel( msg );
288 
289  // Display voltage drop along trace.
290  double extV = extResistance * aCurrent;
291  msg.Printf( wxT( "%g" ), extV );
292  m_ExtTrackVDropValue->SetLabel( msg );
293  double intV = intResistance * aCurrent;
294  msg.Printf( wxT( "%g" ), intV );
295  m_IntTrackVDropValue->SetLabel( msg );
296 
297  // And power loss.
298  msg.Printf( wxT( "%g" ), extV * aCurrent );
299  m_ExtTrackLossValue->SetLabel( msg );
300  msg.Printf( wxT( "%g" ), intV * aCurrent );
301  m_IntTrackLossValue->SetLabel( msg );
302 }
303 
304 
306 {
307  wxFont labelfont;
308  wxFont controlfont;
309 
310  // Set the font weight of the current.
311  labelfont = m_staticTextCurrent->GetFont();
312  controlfont = m_TrackCurrentValue->GetFont();
313 
314  if( m_TWMode == TW_MASTER_CURRENT )
315  {
316  labelfont.SetWeight( wxFONTWEIGHT_BOLD );
317  controlfont.SetWeight( wxFONTWEIGHT_BOLD );
318  }
319  else
320  {
321  labelfont.SetWeight( wxFONTWEIGHT_NORMAL );
322  controlfont.SetWeight( wxFONTWEIGHT_NORMAL );
323  }
324 
325  m_staticTextCurrent->SetFont( labelfont );
326  m_TrackCurrentValue->SetFont( controlfont );
327 
328  // Set the font weight of the external track width.
329  labelfont = m_staticTextExtWidth->GetFont();
330  controlfont = m_ExtTrackWidthValue->GetFont();
331 
333  {
334  labelfont.SetWeight( wxFONTWEIGHT_BOLD );
335  controlfont.SetWeight( wxFONTWEIGHT_BOLD );
336  }
337  else
338  {
339  labelfont.SetWeight( wxFONTWEIGHT_NORMAL );
340  controlfont.SetWeight( wxFONTWEIGHT_NORMAL );
341  }
342 
343  m_staticTextExtWidth->SetFont( labelfont );
344  m_ExtTrackWidthValue->SetFont( controlfont );
345 
346  // Set the font weight of the internal track width.
347  labelfont = m_staticTextIntWidth->GetFont();
348  controlfont = m_IntTrackWidthValue->GetFont();
349 
351  {
352  labelfont.SetWeight( wxFONTWEIGHT_BOLD );
353  controlfont.SetWeight( wxFONTWEIGHT_BOLD );
354  }
355  else
356  {
357  labelfont.SetWeight( wxFONTWEIGHT_NORMAL );
358  controlfont.SetWeight( wxFONTWEIGHT_NORMAL );
359  }
360 
361  m_staticTextIntWidth->SetFont( labelfont );
362  m_IntTrackWidthValue->SetFont( controlfont );
363 
364  // Text sizes have changed when the font weight was changes
365  // So, run the page layout to reflect the changes
366  wxWindow* page = m_Notebook->GetPage ( 1 );
367  page->GetSizer()->Layout();
368 }
369 
370 /* calculate track width for external or internal layers
371  *
372  * Imax = 0.048 * dT^0.44 * A^0.725 for external layer
373  * Imax = 0.024 * dT^0.44 * A^0.725 for internal layer
374  * with A = area = aThickness * trackWidth ( in mils )
375  * and dT = temperature rise in degree C
376  * Of course we want to know trackWidth
377  */
378 double PCB_CALCULATOR_FRAME::TWCalculateWidth( double aCurrent, double aThickness, double aDeltaT_C,
379  bool aUseInternalLayer )
380 {
381  // Appropriate scale for requested layer.
382  double scale = aUseInternalLayer ? 0.024 : 0.048;
383 
384  // aThickness is given in normalize units (in meters) and we need mil
385  aThickness /= UNIT_MIL;
386 
387  /* formula is Imax = scale * dT^0.44 * A^0.725
388  * or
389  * log(Imax) = log(scale) + 0.44*log(dT)
390  * +(0.725*(log(aThickness) + log(trackWidth))
391  * log(trackWidth) * 0.725 = log(Imax) - log(scale) - 0.44*log(dT) - 0.725*log(aThickness)
392  */
393  double dtmp = log( aCurrent ) - log( scale ) - 0.44 * log( aDeltaT_C ) - 0.725 * log( aThickness );
394  dtmp /= 0.725;
395  double trackWidth = exp( dtmp );
396 
397  trackWidth *= UNIT_MIL; // We are using normalize units (sizes in meters) and we have mil
398  return trackWidth; // in meters
399 }
400 
401 
402 double PCB_CALCULATOR_FRAME::TWCalculateCurrent( double aWidth, double aThickness, double aDeltaT_C,
403  bool aUseInternalLayer )
404 {
405  // Appropriate scale for requested layer.
406  double scale = aUseInternalLayer ? 0.024 : 0.048;
407 
408  // Convert thickness and width to mils.
409  aThickness /= UNIT_MIL;
410  aWidth /= UNIT_MIL;
411 
412  double area = aThickness * aWidth;
413  double current = scale * pow( aDeltaT_C, 0.44 ) * pow( area, 0.725 );
414  return current;
415 }
416 
417 
419 {
420  wxString msg;
421 
422  // Disable calculations while we initialise.
423  m_TWNested = true;
424 
425  // Read parameter values.
426  auto cfg = static_cast<PCB_CALCULATOR_SETTINGS*>( Kiface().KifaceSettings() );
427 
428  m_TrackCurrentValue->SetValue( cfg->m_TrackWidth.current );
429  m_TrackDeltaTValue->SetValue( cfg->m_TrackWidth.delta_tc );
430  m_TrackLengthValue->SetValue( cfg->m_TrackWidth.track_len );
431  m_TW_CuLength_choiceUnit->SetSelection( cfg->m_TrackWidth.track_len_units );
432  m_TWResistivity->SetValue( cfg->m_TrackWidth.resistivity );
433  m_ExtTrackWidthValue->SetValue( cfg->m_TrackWidth.ext_track_width );
434  m_TW_ExtTrackWidth_choiceUnit->SetSelection( cfg->m_TrackWidth.ext_track_width_units );
435  m_ExtTrackThicknessValue->SetValue( cfg->m_TrackWidth.ext_track_thickness );
436  m_ExtTrackThicknessUnit->SetSelection( cfg->m_TrackWidth.ext_track_thickness_units );
437  m_IntTrackWidthValue->SetValue( cfg->m_TrackWidth.int_track_width );
438  m_TW_IntTrackWidth_choiceUnit->SetSelection( cfg->m_TrackWidth.int_track_width_units );
439  m_IntTrackThicknessValue->SetValue( cfg->m_TrackWidth.int_track_thickness );
440  m_IntTrackThicknessUnit->SetSelection( cfg->m_TrackWidth.int_track_thickness_units );
441 
442  if( tracks_width_versus_current_formula.StartsWith( "<!" ) )
444  else
445  {
446  wxString html_txt;
447  ConvertMarkdown2Html( wxGetTranslation( tracks_width_versus_current_formula ), html_txt );
448  m_htmlWinFormulas->SetPage( html_txt );
449  }
450 
451  // Make sure the correct master mode is displayed.
453 
454  // Enable calculations and perform the initial one.
455  m_TWNested = false;
456  wxCommandEvent dummy;
458 }
void OnTWParametersChanged(wxCommandEvent &event) override
Function OnTWParametersChanged Called when the user changes the general parameters (i....
enum PCB_CALCULATOR_FRAME::@42 m_TWMode
UNIT_SELECTOR_LEN * m_TW_ExtTrackWidth_choiceUnit
UNIT_SELECTOR_LEN * m_TW_IntTrackWidth_choiceUnit
void ConvertMarkdown2Html(const wxString &aMarkdownInput, wxString &aHtmlOutput)
virtual double GetUnitScale() override
Function GetUnitScale.
double TWCalculateCurrent(double aWidth, double aThickness, double aDeltaT_C, bool aUseInternalLayer)
Function TWCalculateCurrent Calculate maximum current based on given width and temperature rise.
void OnTWResetButtonClick(wxCommandEvent &event) override
Function OnTWResetButtonClick Called when the user clicks the reset button.
double TWCalculateWidth(double aCurrent, double aThickness, double aDeltaT_C, bool aUseInternalLayer)
Function TWCalculateWidth Calculate track width required based on given current and temperature rise.
void TW_Init()
Function TW_Init Read config and init dialog widgets values.
void OnTWCalculateFromCurrent(wxCommandEvent &event) override
Function OnTWCalculateFromCurrent Called when the user changes the desired maximum current.
void TWUpdateModeDisplay()
Function TWUpdateModeDisplay Updates the fields to show whether the maximum current,...
void OnTWCalculateFromIntWidth(wxCommandEvent &event) override
Function OnTWCalculateFromIntWidth Called when the user changes the desired internal trace width.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
Helper dialog and control classes.
wxString tracks_width_versus_current_formula
wxString GetUnitName()
Definition: UnitSelector.h:51
double DoubleFromString(const wxString &TextValue)
void TW_WriteConfig()
Function TW_WriteConfig Write Track width prameters in config.
UNIT_SELECTOR_THICKNESS * m_IntTrackThicknessUnit
const int scale
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
virtual double GetUnitScale() override
Function GetUnitScale.
void OnTWCalculateFromExtWidth(wxCommandEvent &event) override
Function OnTWCalculateFromExtWidth Called when the user changes the desired external trace width.
#define UNIT_MIL
Definition: units_scales.h:35
void TWDisplayValues(double aCurrent, double aExtWidth, double aIntWidth, double aExtThickness, double aIntThickness)
Function TWDisplayValues Displays the results of a calculation (including resulting values such as th...
UNIT_SELECTOR_LEN * m_TW_CuLength_choiceUnit
a wxChoiceBox to select units in Pcb_Calculator
UNIT_SELECTOR_THICKNESS * m_ExtTrackThicknessUnit