KiCad PCB EDA Suite
microwave_inductor.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) 2017 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, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "microwave_inductor.h"
25 
26 #include <wx/wx.h>
27 
28 #include <base_units.h>
29 #include <validators.h>
30 #include <wxPcbStruct.h>
31 
32 #include <class_pad.h>
33 #include <class_edge_mod.h>
34 #include <class_module.h>
35 
36 
37 using namespace MWAVE;
38 
49 static void gen_arc( std::vector <wxPoint>& aBuffer,
50  const wxPoint& aStartPoint,
51  const wxPoint& aCenter,
52  int a_ArcAngle )
53 {
54  const int SEGM_COUNT_PER_360DEG = 16;
55  auto first_point = aStartPoint - aCenter;
56  int seg_count = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
57 
58  if( seg_count == 0 )
59  seg_count = 1;
60 
61  double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
62 
63  // Creates nb_seg point to approximate arc by segments:
64  for( int ii = 1; ii <= seg_count; ii++ )
65  {
66  double rot_angle = increment_angle * ii;
67  double fcos = cos( rot_angle );
68  double fsin = sin( rot_angle );
69  wxPoint currpt;
70 
71  // Rotate current point:
72  currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
73  currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
74 
75  auto corner = aCenter + currpt;
76  aBuffer.push_back( corner );
77  }
78 }
79 
80 
90 static int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
91  const wxPoint& aStartPoint,
92  const wxPoint& aEndPoint,
93  int aLength, int aWidth )
94 {
95 /* We must determine:
96  * segm_count = number of segments perpendicular to the direction
97  * segm_len = length of a strand
98  * radius = radius of rounded parts of the coil
99  * stubs_len = length of the 2 stubs( segments parallel to the direction)
100  * connecting the start point to the start point of the S shape
101  * and the ending point to the end point of the S shape
102  * The equations are (assuming the area size of the entire shape is Size:
103  * Size.x = 2 * radius + segm_len
104  * Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
105  * inductorPattern.m_length = 2 * delta // connections to the coil
106  * + (segm_count-2) * segm_len // length of the strands except 1st and last
107  * + (segm_count) * (PI * radius) // length of rounded
108  * segm_len + / 2 - radius * 2) // length of 1st and last bit
109  *
110  * The constraints are:
111  * segm_count >= 2
112  * radius < m_Size.x
113  * Size.y = (radius * 4) + (2 * stubs_len)
114  * segm_len > radius * 2
115  *
116  * The calculation is conducted in the following way:
117  * first:
118  * segm_count = 2
119  * radius = 4 * Size.x (arbitrarily fixed value)
120  * Then:
121  * Increasing the number of segments to the desired length
122  * (radius decreases if necessary)
123  */
124  wxPoint size;
125 
126  // This scale factor adjusts the arc length to handle
127  // the arc to segment approximation.
128  // because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
129  // the trace len must be corrected when calculated using arcs
130  // this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
131  // because trace using segment is shorter the corresponding arc
132  // ADJUST_SIZE is the ratio between tline len and the arc len for an arc
133  // of 360/ADJUST_SIZE angle
134  #define ADJUST_SIZE 0.988
135 
136  auto pt = aEndPoint - aStartPoint;
137  double angle = -ArcTangente( pt.y, pt.x );
138  int min_len = KiROUND( EuclideanNorm( pt ) );
139  int segm_len = 0; // length of segments
140  int full_len; // full len of shape (sum of length of all segments + arcs)
141 
142 
143  /* Note: calculations are made for a vertical coil (more easy calculations)
144  * and after points are rotated to their actual position
145  * So the main direction is the Y axis.
146  * the 2 stubs are on the Y axis
147  * the others segments are parallel to the X axis.
148  */
149 
150  // Calculate the size of area (for a vertical shape)
151  size.x = min_len / 2;
152  size.y = min_len;
153 
154  // Choose a reasonable starting value for the radius of the arcs.
155  int radius = std::min( aWidth * 5, size.x / 4 );
156 
157  int segm_count; // number of full len segments
158  // the half size segments (first and last segment) are not counted here
159  int stubs_len = 0; // length of first or last segment (half size of others segments)
160 
161  for( segm_count = 0; ; segm_count++ )
162  {
163  stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
164 
165  if( stubs_len < size.y / 10 ) // Reduce radius.
166  {
167  stubs_len = size.y / 10;
168  radius = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
169 
170  if( radius < aWidth ) // Radius too small.
171  {
172  // Unable to create line: Requested length value is too large for room
173  return 0;
174  }
175  }
176 
177  segm_len = size.x - ( radius * 2 );
178  full_len = 2 * stubs_len; // Length of coil connections.
179  full_len += segm_len * segm_count; // Length of full length segments.
180  full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius ); // Ard arcs len
181  full_len += segm_len - (2 * radius); // Length of first and last segments
182  // (half size segments len = segm_len/2 - radius).
183 
184  if( full_len >= aLength )
185  break;
186  }
187 
188  // Adjust len by adjusting segm_len:
189  int delta_size = full_len - aLength;
190 
191  // reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
192  segm_len -= delta_size / (segm_count + 1);
193 
194  // Generate first line (the first stub) and first arc (90 deg arc)
195  pt = aStartPoint;
196  aBuffer.push_back( pt );
197  pt.y += stubs_len;
198  aBuffer.push_back( pt );
199 
200  auto centre = pt;
201  centre.x -= radius;
202  gen_arc( aBuffer, pt, centre, -900 );
203  pt = aBuffer.back();
204 
205  int half_size_seg_len = segm_len / 2 - radius;
206 
207  if( half_size_seg_len )
208  {
209  pt.x -= half_size_seg_len;
210  aBuffer.push_back( pt );
211  }
212 
213  // Create shape.
214  int ii;
215  int sign = 1;
216  segm_count += 1; // increase segm_count to create the last half_size segment
217 
218  for( ii = 0; ii < segm_count; ii++ )
219  {
220  int arc_angle;
221 
222  if( ii & 1 ) // odd order arcs are greater than 0
223  sign = -1;
224  else
225  sign = 1;
226 
227  arc_angle = 1800 * sign;
228  centre = pt;
229  centre.y += radius;
230  gen_arc( aBuffer, pt, centre, arc_angle );
231  pt = aBuffer.back();
232  pt.x += segm_len * sign;
233  aBuffer.push_back( pt );
234  }
235 
236  // The last point is false:
237  // it is the end of a full size segment, but must be
238  // the end of the second half_size segment. Change it.
239  sign *= -1;
240  aBuffer.back().x = aStartPoint.x + radius * sign;
241 
242  // create last arc
243  pt = aBuffer.back();
244  centre = pt;
245  centre.y += radius;
246  gen_arc( aBuffer, pt, centre, 900 * sign );
247  aBuffer.back();
248 
249  // Rotate point
250  angle += 900;
251 
252  for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
253  {
254  RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, angle );
255  }
256 
257  // push last point (end point)
258  aBuffer.push_back( aEndPoint );
259 
260  return 1;
261 }
262 
263 
265  PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
266 {
267  /* Build a microwave inductor footprint.
268  * - Length Mself.lng
269  * - Extremities Mself.m_Start and Mself.m_End
270  * We must determine:
271  * Mself.nbrin = number of segments perpendicular to the direction
272  * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
273  * Mself.lbrin = length of a strand
274  * Mself.radius = radius of rounded parts of the coil
275  * Mself.delta = segments extremities connection between him and the coil even
276  *
277  * The equations are
278  * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
279  * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
280  * Mself.lng = 2 * Mself.delta / / connections to the coil
281  + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
282  + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
283  * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
284  *
285  * The constraints are:
286  * Nbrin >= 2
287  * Mself.radius < Mself.m_Size.x
288  * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
289  * Mself.lbrin> Mself.radius * 2
290  *
291  * The calculation is conducted in the following way:
292  * Initially:
293  * Nbrin = 2
294  * Radius = 4 * m_Size.x (arbitrarily fixed value)
295  * Then:
296  * Increasing the number of segments to the desired length
297  * (Radius decreases if necessary)
298  */
299 
300  D_PAD* pad;
301  int ll;
302  wxString msg;
303 
304  auto pt = inductorPattern.m_End - inductorPattern.m_Start;
305  int min_len = KiROUND( EuclideanNorm( pt ) );
306  inductorPattern.m_length = min_len;
307 
308  // Enter the desired length.
309  msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
310  wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), msg );
311 
312  if( dlg.ShowModal() != wxID_OK )
313  return nullptr; // canceled by user
314 
315  msg = dlg.GetValue();
316  inductorPattern.m_length = ValueFromString( g_UserUnit, msg );
317 
318  // Control values (ii = minimum length)
319  if( inductorPattern.m_length < min_len )
320  {
321  aErrorMessage = _( "Requested length < minimum length" );
322  return nullptr;
323  }
324 
325  // Calculate the elements.
326  std::vector <wxPoint> buffer;
327  ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start,
328  inductorPattern.m_End, inductorPattern.m_length,
329  inductorPattern.m_Width );
330 
331  if( !ll )
332  {
333  aErrorMessage = _( "Requested length too large" );
334  return nullptr;
335  }
336 
337  // Generate footprint. the value is also used as footprint name.
338  msg = "L";
339  wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), msg );
340  cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
341 
342  if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
343  return nullptr; // Aborted by user
344 
345  MODULE* module = aPcbFrame->CreateNewModule( msg );
346 
347  // here the module is already in the BOARD, CreateNewModule() does that.
348  module->SetFPID( LIB_ID( std::string( "mw_inductor" ) ) );
349  module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
350  module->ClearFlags();
351  module->SetPosition( inductorPattern.m_End );
352 
353  // Generate segments
354  for( unsigned jj = 1; jj < buffer.size(); jj++ )
355  {
356  EDGE_MODULE* PtSegm;
357  PtSegm = new EDGE_MODULE( module );
358  PtSegm->SetStart( buffer[jj - 1] );
359  PtSegm->SetEnd( buffer[jj] );
360  PtSegm->SetWidth( inductorPattern.m_Width );
361  PtSegm->SetLayer( module->GetLayer() );
362  PtSegm->SetShape( S_SEGMENT );
363  PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
364  PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
365  module->GraphicalItems().PushBack( PtSegm );
366  }
367 
368  // Place a pad on each end of coil.
369  pad = new D_PAD( module );
370 
371  module->Pads().PushFront( pad );
372 
373  pad->SetPadName( "1" );
374  pad->SetPosition( inductorPattern.m_End );
375  pad->SetPos0( pad->GetPosition() - module->GetPosition() );
376 
377  pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) );
378 
379  pad->SetLayerSet( LSET( module->GetLayer() ) );
381  pad->SetShape( PAD_SHAPE_CIRCLE );
382 
383  D_PAD* newpad = new D_PAD( *pad );
384 
385  module->Pads().Insert( newpad, pad->Next() );
386 
387  pad = newpad;
388  pad->SetPadName( "2" );
389  pad->SetPosition( inductorPattern.m_Start );
390  pad->SetPos0( pad->GetPosition() - module->GetPosition() );
391 
392  // Modify text positions.
393  wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2,
394  ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 );
395 
396  wxPoint valPos = refPos;
397 
398  refPos.y -= module->Reference().GetTextSize().y;
399  module->Reference().SetPosition( refPos );
400  valPos.y += module->Value().GetTextSize().y;
401  module->Value().SetPosition( valPos );
402 
403  module->CalculateBoundingBox();
404  return module;
405 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:104
void SetEnd0(const wxPoint &aPoint)
TEXTE_MODULE & Reference()
Definition: class_module.h:455
void SetShape(STROKE_T aShape)
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
void PushFront(T *aNewElement)
Function PushFront puts aNewElement at front of list sequence.
Definition: dlist.h:240
Implementation of conversion functions that require both schematic and board internal units...
void CalculateBoundingBox()
Function CalculateBoundingBox calculates the bounding box in board coordinates.
const wxPoint & GetPosition() const override
Definition: class_module.h:143
wxString StringFromValue(EDA_UNITS_T aUnit, int aValue, bool aAddUnitSymbol)
Function StringFromValue returns the string from aValue according to units (inch, mm ...
Definition: base_units.cpp:203
Parameters for construction of a microwave inductor.
virtual void SetLayer(LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:59
Set for modules listed in the automatic insertion list (usually SMD footprints)
Definition: class_module.h:76
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:169
usual segment : line with rounded ends
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
Class LIB_ID.
Definition: lib_id.h:56
MODULE * CreateNewModule(const wxString &aModuleName)
Function CreateNewModule Creates a new module or footprint, at position 0,0 The new module contains o...
Definition: librairi.cpp:743
LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
#define abs(a)
Definition: auxiliary.h:84
DLIST< BOARD_ITEM > & GraphicalItems()
Definition: class_module.h:136
Class FILE_NAME_CHAR_VALIDATOR.
Definition: validators.h:43
void PushBack(T *aNewElement)
Function PushBack puts aNewElement at the end of the list sequence.
Definition: dlist.h:250
Class LSET is a set of LAYER_IDs.
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:271
TEXTE_MODULE & Value()
read/write accessors:
Definition: class_module.h:454
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:175
const wxPoint & GetPosition() const override
Definition: class_pad.h:170
static void gen_arc(std::vector< wxPoint > &aBuffer, const wxPoint &aStartPoint, const wxPoint &aCenter, int a_ArcAngle)
Function gen_arc generates an arc using arc approximation by lines: Center aCenter Angle "angle" (in ...
int ValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application...
Definition: base_units.cpp:368
D_PAD * Next() const
Definition: class_pad.h:106
void SetSize(const wxSize &aSize)
Definition: class_pad.h:181
static int BuildCornersList_S_Shape(std::vector< wxPoint > &aBuffer, const wxPoint &aStartPoint, const wxPoint &aEndPoint, int aLength, int aWidth)
Function BuildCornersList_S_Shape Create a path like a S-shaped coil.
virtual void SetPosition(const wxPoint &aPos) override
void SetPosition(const wxPoint &aPos) override
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:297
Pad object description.
void SetStart(const wxPoint &aStart)
EDA_UNITS_T g_UserUnit
Global variables definitions.
Definition: common.cpp:56
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:234
MODULE * CreateMicrowaveInductor(INDUCTOR_PATTERN &aPattern, PCB_EDIT_FRAME *aPcbFrame, wxString &aErrorMessage)
Creates an S-shaped coil footprint for microwave applications.
#define ADJUST_SIZE
Virtual component: when created by copper shapes on board (Like edge card connectors, mounting hole...)
Definition: class_module.h:78
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetStart0(const wxPoint &aPoint)
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:167
void SetPadName(const wxString &name)
Set the pad name (sometimes called pad number, although it can be an array ref like AA12 the pad name...
Definition: class_pad.cpp:404
void SetEnd(const wxPoint &aEnd)
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:254
DLIST< D_PAD > & Pads()
Definition: class_module.h:133
Module description (excepted pads)
const wxSize & GetTextSize() const
Definition: eda_text.h:215
EDGE_MODULE class definition.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
void SetFPID(const LIB_ID &aFPID)
Definition: class_module.h:152
Custom text control validator definitions.
void Insert(T *aNewElement, T *aElementAfterMe)
Function Insert puts aNewElement just in front of aElementAfterMe in the list sequence.
Definition: dlist.h:200
void SetAttributes(int aAttributes)
Definition: class_module.h:185
#define min(a, b)
Definition: auxiliary.h:85
int sign(T val)
Definition: math_util.h:44
void SetWidth(int aWidth)