KiCad PCB EDA Suite
export_d356.cpp File Reference

Export IPC-D-356 test format. More...

#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <gestfich.h>
#include <kiface_i.h>
#include <wxPcbStruct.h>
#include <trigo.h>
#include <build_version.h>
#include <macros.h>
#include <pcbnew.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_edge_mod.h>
#include <vector>
#include <cctype>

Go to the source code of this file.

Classes

struct  D356_RECORD
 

Functions

static int compute_pad_access_code (BOARD *aPcb, LSET aLayerMask)
 
static int iu_to_d356 (int iu, int clamp)
 
static void build_pad_testpoints (BOARD *aPcb, std::vector< D356_RECORD > &aRecords)
 
static int via_access_code (BOARD *aPcb, int top_layer, int bottom_layer)
 
static void build_via_testpoints (BOARD *aPcb, std::vector< D356_RECORD > &aRecords)
 
static const wxString intern_new_d356_netname (const wxString &aNetname, std::map< wxString, wxString > &aMap, std::set< wxString > &aSet)
 
static void write_D356_records (std::vector< D356_RECORD > &aRecords, FILE *fout)
 

Detailed Description

Export IPC-D-356 test format.

Definition in file export_d356.cpp.

Function Documentation

static void build_pad_testpoints ( BOARD aPcb,
std::vector< D356_RECORD > &  aRecords 
)
static

Definition at line 114 of file export_d356.cpp.

References D356_RECORD::access, B_Mask, compute_pad_access_code(), D356_RECORD::drill, F_Mask, BOARD::GetAuxOrigin(), BOARD_ITEM::GetLayerSet(), D356_RECORD::hole, KiROUND(), BOARD::m_Modules, D356_RECORD::mechanical, D356_RECORD::midpoint, min, D356_RECORD::netname, MODULE::Next(), D_PAD::Next(), PAD_ATTRIB_HOLE_NOT_PLATED, PAD_ATTRIB_SMD, PAD_SHAPE_CIRCLE, D356_RECORD::pin, D356_RECORD::refdes, D356_RECORD::rotation, D356_RECORD::smd, D356_RECORD::soldermask, wxPoint::x, D356_RECORD::x_location, D356_RECORD::x_size, wxPoint::y, D356_RECORD::y_location, and D356_RECORD::y_size.

Referenced by PCB_EDIT_FRAME::GenD356File().

116 {
117  wxPoint origin = aPcb->GetAuxOrigin();
118 
119  for( MODULE *module = aPcb->m_Modules;
120  module; module = module->Next() )
121  {
122  for( D_PAD *pad = module->Pads(); pad; pad = pad->Next() )
123  {
124  D356_RECORD rk;
125  rk.access = compute_pad_access_code( aPcb, pad->GetLayerSet() );
126 
127  // It could be a mask only pad, we only handle pads with copper here
128  if( rk.access != -1 )
129  {
130  rk.netname = pad->GetNetname();
131  rk.refdes = module->GetReference();
132  pad->StringPadName( rk.pin );
133  rk.midpoint = false; // XXX MAYBE need to be computed (how?)
134  const wxSize& drill = pad->GetDrillSize();
135  rk.drill = std::min( drill.x, drill.y );
136  rk.hole = (rk.drill != 0);
137  rk.smd = pad->GetAttribute() == PAD_ATTRIB_SMD;
138  rk.mechanical = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
139  rk.x_location = pad->GetPosition().x - origin.x;
140  rk.y_location = origin.y - pad->GetPosition().y;
141  rk.x_size = pad->GetSize().x;
142 
143  // Rule: round pads have y = 0
144  if( pad->GetShape() == PAD_SHAPE_CIRCLE )
145  rk.y_size = 0;
146  else
147  rk.y_size = pad->GetSize().y;
148 
149  rk.rotation = -KiROUND( pad->GetOrientation() ) / 10;
150  if( rk.rotation < 0 ) rk.rotation += 360;
151 
152  // the value indicates which sides are *not* accessible
153  rk.soldermask = 3;
154  if( pad->GetLayerSet()[F_Mask] )
155  rk.soldermask &= ~1;
156  if( pad->GetLayerSet()[B_Mask] )
157  rk.soldermask &= ~2;
158 
159  aRecords.push_back( rk );
160  }
161  }
162  }
163 }
wxString pin
Definition: export_d356.cpp:57
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
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:63
MODULE * Next() const
Definition: class_module.h:100
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:59
wxString refdes
Definition: export_d356.cpp:56
virtual LSET GetLayerSet() const
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
D_PAD * Next() const
Definition: class_pad.h:106
const wxPoint & GetAuxOrigin() const
Definition: class_board.h:332
wxString netname
Definition: export_d356.cpp:55
DLIST< MODULE > m_Modules
Definition: class_board.h:243
static int compute_pad_access_code(BOARD *aPcb, LSET aLayerMask)
Definition: export_d356.cpp:72
#define min(a, b)
Definition: auxiliary.h:85
static void build_via_testpoints ( BOARD aPcb,
std::vector< D356_RECORD > &  aRecords 
)
static

Definition at line 189 of file export_d356.cpp.

References D356_RECORD::access, D356_RECORD::drill, BOARD::GetAuxOrigin(), VIA::GetDrillValue(), NETINFO_ITEM::GetNet(), NETINFO_ITEM::GetNetname(), VIA::GetPosition(), TRACK::GetWidth(), D356_RECORD::hole, VIA::LayerPair(), BOARD::m_Track, D356_RECORD::mechanical, D356_RECORD::midpoint, D356_RECORD::netname, TRACK::Next(), PCB_VIA_T, D356_RECORD::pin, D356_RECORD::refdes, D356_RECORD::rotation, D356_RECORD::smd, D356_RECORD::soldermask, via_access_code(), wxPoint::x, D356_RECORD::x_location, D356_RECORD::x_size, wxPoint::y, D356_RECORD::y_location, and D356_RECORD::y_size.

Referenced by PCB_EDIT_FRAME::GenD356File().

191 {
192  wxPoint origin = aPcb->GetAuxOrigin();
193 
194  // Enumerate all the track segments and keep the vias
195  for( TRACK *track = aPcb->m_Track; track; track = track->Next() )
196  {
197  if( track->Type() == PCB_VIA_T )
198  {
199  VIA *via = (VIA*) track;
200  NETINFO_ITEM *net = track->GetNet();
201 
202  D356_RECORD rk;
203  rk.smd = false;
204  rk.hole = true;
205  if( net )
206  rk.netname = net->GetNetname();
207  else
208  rk.netname = wxEmptyString;
209  rk.refdes = wxT("VIA");
210  rk.pin = wxT("");
211  rk.midpoint = true; // Vias are always midpoints
212  rk.drill = via->GetDrillValue();
213  rk.mechanical = false;
214 
215  PCB_LAYER_ID top_layer, bottom_layer;
216 
217  via->LayerPair( &top_layer, &bottom_layer );
218 
219  rk.access = via_access_code( aPcb, top_layer, bottom_layer );
220  rk.x_location = via->GetPosition().x - origin.x;
221  rk.y_location = origin.y - via->GetPosition().y;
222  rk.x_size = via->GetWidth();
223  rk.y_size = 0; // Round so height = 0
224  rk.rotation = 0;
225  rk.soldermask = 3; // XXX always tented?
226 
227  aRecords.push_back( rk );
228  }
229  }
230 }
wxString pin
Definition: export_d356.cpp:57
PCB_LAYER_ID
A quick note on layer IDs:
wxString refdes
Definition: export_d356.cpp:56
int GetNet() const
Function GetNet.
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
const wxPoint & GetAuxOrigin() const
Definition: class_board.h:332
wxString netname
Definition: export_d356.cpp:55
Class NETINFO_ITEM handles the data for a net.
TRACK * Next() const
Definition: class_track.h:98
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
int GetWidth() const
Definition: class_track.h:115
const wxPoint & GetPosition() const override
Definition: class_track.h:416
static int via_access_code(BOARD *aPcb, int top_layer, int bottom_layer)
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:108
DLIST< TRACK > m_Track
Definition: class_board.h:244
const wxString & GetNetname() const
Function GetNetname.
static int compute_pad_access_code ( BOARD aPcb,
LSET  aLayerMask 
)
static

Definition at line 72 of file export_d356.cpp.

References LSET::AllCuMask(), B_Cu, F_Cu, BOARD::GetCopperLayerCount(), and In1_Cu.

Referenced by build_pad_testpoints().

73 {
74  // Non-copper is not interesting here
75  aLayerMask &= LSET::AllCuMask();
76  if( !aLayerMask.any() )
77  return -1;
78 
79  // Traditional TH pad
80  if( aLayerMask[F_Cu] && aLayerMask[B_Cu] )
81  return 0;
82 
83  // Front SMD pad
84  if( aLayerMask[F_Cu] )
85  return 1;
86 
87  // Back SMD pad
88  if( aLayerMask[B_Cu] )
89  return aPcb->GetCopperLayerCount();
90 
91  // OK, we have an inner-layer only pad (and I have no idea about
92  // what could be used for); anyway, find the first copper layer
93  // it's on
94  for( LAYER_NUM layer = In1_Cu; layer < B_Cu; ++layer )
95  {
96  if( aLayerMask[layer] )
97  return layer + 1;
98  }
99 
100  // This shouldn't happen
101  return -1;
102 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:639
int GetCopperLayerCount() const
Function GetCopperLayerCount.
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
static const wxString intern_new_d356_netname ( const wxString &  aNetname,
std::map< wxString, wxString > &  aMap,
std::set< wxString > &  aSet 
)
static

Definition at line 233 of file export_d356.cpp.

Referenced by write_D356_records().

235 {
236  wxString canon;
237  for (wxString::const_iterator i = aNetname.begin();
238  i != aNetname.end(); ++i)
239  {
240  // Rule: we can only use the standard ASCII, control excluded
241  char ch = *i;
242  if( ch > 126 || !std::isgraph( ch ) )
243  ch = '?';
244  canon += ch;
245  }
246 
247  // Rule: only uppercase (unofficial, but known to give problems
248  // otherwise)
249  canon.MakeUpper();
250 
251  // Rule: maximum length is 14 characters, otherwise we keep the tail
252  if( canon.size() > 14 )
253  {
254  canon = canon.Right( 14 );
255  }
256 
257  // Check if it's still unique
258  if( aSet.count( canon ) )
259  {
260  // Nope, need to uniquify it, trim it more and add a number
261  wxString base( canon );
262  if( base.size() > 10 )
263  {
264  base = base.Right( 10 );
265  }
266 
267  int ctr = 0;
268  do
269  {
270  ++ctr;
271  canon = base;
272  canon << '#' << ctr;
273  } while ( aSet.count( canon ) );
274  }
275 
276  // Register it
277  aMap[aNetname] = canon;
278  aSet.insert( canon );
279  return canon;
280 }
static int iu_to_d356 ( int  iu,
int  clamp 
)
static

Definition at line 105 of file export_d356.cpp.

References KiROUND().

Referenced by write_D356_records().

106 {
107  int val = KiROUND( iu / ( IU_PER_MILS / 10 ) );
108  if( val > clamp ) return clamp;
109  if( val < -clamp ) return -clamp;
110  return val;
111 }
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
static int via_access_code ( BOARD aPcb,
int  top_layer,
int  bottom_layer 
)
static

Definition at line 168 of file export_d356.cpp.

References B_Cu, F_Cu, and BOARD::GetCopperLayerCount().

Referenced by build_via_testpoints().

169 {
170  // Easy case for through vias: top_layer is component, bottom_layer is
171  // solder, access code is 0
172  if( (top_layer == F_Cu) && (bottom_layer == B_Cu) )
173  return 0;
174 
175  // Blind via, reachable from front
176  if( top_layer == F_Cu )
177  return 1;
178 
179  // Blind via, reachable from bottom
180  if( bottom_layer == B_Cu )
181  return aPcb->GetCopperLayerCount();
182 
183  // It's a buried via, accessible from some inner layer
184  // (maybe could be used for testing before laminating? no idea)
185  return bottom_layer + 1; // XXX is this correct?
186 }
int GetCopperLayerCount() const
Function GetCopperLayerCount.
static void write_D356_records ( std::vector< D356_RECORD > &  aRecords,
FILE *  fout 
)
static

Definition at line 283 of file export_d356.cpp.

References D356_RECORD::access, D356_RECORD::drill, D356_RECORD::hole, intern_new_d356_netname(), iu_to_d356(), D356_RECORD::mechanical, D356_RECORD::midpoint, D356_RECORD::netname, D356_RECORD::pin, D356_RECORD::refdes, D356_RECORD::rotation, D356_RECORD::smd, D356_RECORD::soldermask, TO_UTF8, D356_RECORD::x_location, D356_RECORD::x_size, D356_RECORD::y_location, and D356_RECORD::y_size.

Referenced by PCB_EDIT_FRAME::GenD356File().

285 {
286  // Sanified and shorted network names and set of short names
287  std::map<wxString, wxString> d356_net_map;
288  std::set<wxString> d356_net_set;
289 
290  for (unsigned i = 0; i < aRecords.size(); i++)
291  {
292  D356_RECORD &rk = aRecords[i];
293 
294  // Try to sanify the network name (there are limits on this), if
295  // not already done. Also 'empty' net are marked as N/C, as
296  // specified.
297  wxString d356_net( wxT("N/C") );
298  if( !rk.netname.empty() )
299  {
300  d356_net = d356_net_map[rk.netname];
301 
302  if( d356_net.empty() )
303  d356_net = intern_new_d356_netname( rk.netname, d356_net_map,
304  d356_net_set );
305  }
306 
307  // Choose the best record type
308  int rktype;
309  if( rk.smd )
310  rktype = 327;
311  else
312  {
313  if( rk.mechanical )
314  rktype = 367;
315  else
316  rktype = 317;
317  }
318 
319  // Operation code, signal and component
320  fprintf( fout, "%03d%-14.14s %-6.6s%c%-4.4s%c",
321  rktype, TO_UTF8(d356_net),
322  TO_UTF8(rk.refdes),
323  rk.pin.empty()?' ':'-',
324  TO_UTF8(rk.pin),
325  rk.midpoint?'M':' ' );
326 
327  // Hole definition
328  if( rk.hole )
329  {
330  fprintf( fout, "D%04d%c",
331  iu_to_d356( rk.drill, 9999 ),
332  rk.mechanical ? 'U':'P' );
333  }
334  else
335  fprintf( fout, " " );
336 
337  // Test point access
338  fprintf( fout, "A%02dX%+07dY%+07dX%04dY%04dR%03d",
339  rk.access,
340  iu_to_d356( rk.x_location, 999999 ),
341  iu_to_d356( rk.y_location, 999999 ),
342  iu_to_d356( rk.x_size, 9999 ),
343  iu_to_d356( rk.y_size, 9999 ),
344  rk.rotation );
345 
346  // Soldermask
347  fprintf( fout, "S%d\n", rk.soldermask );
348  }
349 }
wxString pin
Definition: export_d356.cpp:57
static int iu_to_d356(int iu, int clamp)
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
wxString refdes
Definition: export_d356.cpp:56
wxString netname
Definition: export_d356.cpp:55
static const wxString intern_new_d356_netname(const wxString &aNetname, std::map< wxString, wxString > &aMap, std::set< wxString > &aSet)