def pcbfunc(Filename = None): if Filename: my_board = pcbnew.LoadBoard (Filename) else: my_board = pcbnew.GetBoard() filename = change_extension (my_board.GetFileName(), ".pos") f = open (filename, "w") f.write ("### Module positions - created on %s ###" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) f.write ("### Printed by get_pos v1") f.write ("## Unit = mm, Angle = deg.") f.write ("## Side : All") f.write ("# Ref Val Package PosX PosY Rot Side Type") origin = my_board.GetAuxOrigin() # for v5 use GetLibItemName() instead of GetFootprintName() for module in my_board.GetModules(): f.write ("%s \"%s\" %s %1.3f %1.3f %1.3f %s %s" % ( module.GetReference(), module.GetValue(), module.GetFPID().GetFootprintName(), pcbnew.ToMM(module.GetPosition().x - origin.x), pcbnew.ToMM(-module.GetPosition().y + origin.y), module.GetOrientation() / 10.0, "top" if module.GetLayer() == 0 else "bottom", get_class ( module.GetAttributes() ) )) f.write ("## End") f.close () print ("Written to %s" % filename)
def BuildThisFootprint(self): steps = self.pads["steps"] bands = self.pads["bands"] touch_width = self.pads["width"] touch_length = self.pads["length"] touch_clearance = self.pads["clearance"] step_length = float(touch_length) / float(steps) t_size = self.GetTextSize() w_text = self.draw.GetLineThickness() ypos = touch_width / 2 + t_size / 2 + w_text self.draw.Value(0, -ypos, t_size) ypos += t_size + w_text * 2 self.draw.Reference(0, -ypos, t_size) # set SMD attribute self.module.SetAttributes(MOD_CMS) # starting pad band_width = touch_width / bands xpos = -0.5 * (steps - 1) * step_length ypos = -0.5 * (bands - 1) * band_width pos = wxPointMM(pcbnew.ToMM(xpos), pcbnew.ToMM(ypos)) for b in range(bands): self.AddStrip(pos, steps, band_width, step_length, touch_clearance) pos += wxPoint(0, band_width)
def GetValue(self): return "QFP-{nx:g}x{ny:g}_{x:g}x{y:g}_Pitch{p:g}mm".format( nx=self.pads['nx'], ny=self.pads['ny'], x=pcbnew.ToMM(self.package['width']), y=pcbnew.ToMM(self.package['height']), p=pcbnew.ToMM(self.pads['pitch']))
def selfToMainDialog(self): self.mainDlg.lstLayer.SetItems(list(self.layerMap.values())) #maui self.mainDlg.lstLayer.SetSelection(self.layerId) self.mainDlg.txtNetFilter.SetItems(self.netFilterList) self.mainDlg.txtNetFilter.SetSelection( self.netFilterList.index(self.netFilter)) self.mainDlg.txtViaOffset.SetValue(str(pcbnew.ToMM(self.viaOffset))) self.mainDlg.txtViaPitch.SetValue(str(pcbnew.ToMM(self.viaPitch))) self.mainDlg.txtViaDrill.SetValue(str(pcbnew.ToMM(self.viaDrill))) self.mainDlg.txtViaSize.SetValue(str(pcbnew.ToMM(self.viaSize))) self.mainDlg.lstViaNet.SetItems( [item.GetNetname() for item in self.netMap.values()]) for i, item in enumerate(self.netMap.values()): if self.mainDlg.lstViaNet.GetString(i) in ["GND", "/GND"]: self.mainDlg.lstViaNet.SetSelection(i) break self.mainDlg.chkNetFilter.SetValue(self.isNetFilterChecked) self.mainDlg.txtNetFilter.Enable(self.isNetFilterChecked) self.mainDlg.chkLayer.SetValue(self.isLayerChecked) self.mainDlg.lstLayer.Enable(self.isLayerChecked) self.mainDlg.chkIncludeDrawing.SetValue(self.isIncludeDrawingChecked) self.mainDlg.chkIncludeSelection.SetValue( self.isIncludeSelectionChecked) self.mainDlg.chkDebugDump.SetValue(self.isDebugDumpChecked) self.mainDlg.chkRemoveViasWithClearanceViolation.SetValue( self.isRemoveViasWithClearanceViolationChecked) self.mainDlg.chkSameNetZoneViasOnly.SetValue( self.isSameNetZoneViasOnlyChecked) self.mainDlg.m_buttonDelete.Bind(wx.EVT_BUTTON, self.onDeleteClick)
def AddYElectrodes(self, touch_length, touch_width, y_width, electrodeName): yElectrodeSpacing = 4 yElectrodeCount = int( ((pcbnew.ToMM(touch_width) - 4) / yElectrodeSpacing)) + 1 yElectrodeThickness = y_width currentLayer = pcbnew.F_Cu yPosition = (pcbnew.ToMM(touch_width) / 2) - ( (pcbnew.ToMM(touch_width) - ((yElectrodeCount - 1) * yElectrodeSpacing)) / 2.0) for i in range(0, yElectrodeCount): pad = self.smdRectPad( self.module, wxSize(touch_length, yElectrodeThickness), wxPoint(0, pcbnew.FromMM(yPosition)), str(electrodeName), ) self.module.Add(pad) yPosition -= yElectrodeSpacing # Leftmost line that connects all electrodes pad = self.smdRectPad( self.module, wxSize( yElectrodeThickness, (yElectrodeCount - 1) * pcbnew.FromMM(yElectrodeSpacing) + yElectrodeThickness, ), wxPoint(-touch_length / 2, 0), str(electrodeName), ) self.module.Add(pad)
def CheckParameters(self): # check that the package is large enough width = pcbnew.ToMM(self.parameters['Pads']['pitchX'] * self.parameters['Pads']['columns']) length = pcbnew.ToMM(self.parameters['Pads']['pitchY'] * self.parameters['Pads']['rows'])
def drill(self): if self.type in ('standard', 'hole_not_plated'): if self._pad.GetDrillShape() == pcbnew.PAD_DRILL_OBLONG: return pcbnew.ToMM(self._pad.GetDrillSize()) else: return pcbnew.ToMM(self._pad.GetDrillSize())[0] else: return None
def GetValue(self): return "QFN-{n}_{ep}{x:g}x{y:g}_Pitch{p:g}mm".format( n=self.pads['n'], ep="EP_" if self.epad['epad'] else '', x=pcbnew.ToMM(self.package['width']), y=pcbnew.ToMM(self.package['height']), p=pcbnew.ToMM(self.pads['pitch']))
def GetValue(self): return "QFN-{n}_{ep}{x:g}x{y:g}mm_Pitch{p:g}mm".format( n=2 * (self.pads["pins in X direction"] + self.pads["pins in Y direction"]), ep="1EP_" if self.epad['epad'] else '', x=pcbnew.ToMM(self.package['width']), y=pcbnew.ToMM(self.package['height']), p=pcbnew.ToMM(self.pads['pitch']))
def get_placement_info(self): self.placement_info_top = [] self.placement_info_bottom = [] self.numALL = 0 self.numSMT = 0 #components = self.get_components() origin = self.board.GetAuxOrigin() for module in self.board.GetModules(): reference = module.GetReference() #comp = self.get_component_by_ref(components, reference) #if comp: # excluded = (self.get_user_field(comp, u'Исключён из ПЭ') != None) #else: # excluded = True #if excluded: # continue if self.is_non_annotated_ref(reference): continue value = module.GetValue() excluded = False for ig in IGINOR: if value == ig: excluded = True if excluded: continue self.numALL += 1 package = str(module.GetFPID().GetLibItemName()) pos = module.GetPosition() - origin pos_x = pcbnew.ToMM(pos.x) if module.IsFlipped(): pos_x = -pos_x pos_y = -pcbnew.ToMM(pos.y) rotation = module.GetOrientationDegrees() if module.IsFlipped(): placement_info = self.placement_info_bottom else: placement_info = self.placement_info_top is_smd = self.is_smd_module(module) if is_smd: self.numSMT += 1 placement_info.append( [reference, value, package, pos_x, pos_y, rotation, is_smd]) self.sort_placement_info_by_ref()
def CheckParameters(self): # check that the package is large enough width = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['columns']) length = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['rows']) self.CheckParam('Package','width',min_value=width,info="Package width is too small (< {w}mm)".format(w=width)) self.CheckParam('Package','length',min_value=length,info="Package length is too small (< {l}mm".format(l=length))
def GetValue(self): pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"]) return "BGA-{n}_{a}x{b}_{x}x{y}mm".format( n = pins, a = self.parameters['Pads']['columns'], b = self.parameters['Pads']['rows'], x = pcbnew.ToMM(self.parameters['Package']['width']), y = pcbnew.ToMM(self.parameters['Package']['length']) )
def generate_position_csv(name, output_dir): board = pcbnew.LoadBoard(name) board_name, ext = os.path.splitext(name) print('Position file') with open(os.path.join(output_dir, board_name + '-top-pos.csv'), 'w', newline='') as out_top,\ open(os.path.join(output_dir, board_name + '-bottom-pos.csv'), 'w', newline='') as out_bot: fieldnames = ['Ref', 'Val', 'Package', 'PosX', 'PosY', 'Rot', 'Side'] csv_top = csv.DictWriter(out_top, fieldnames=fieldnames, quoting=csv.QUOTE_NONNUMERIC) csv_bot = csv.DictWriter(out_bot, fieldnames=fieldnames, quoting=csv.QUOTE_NONNUMERIC) csv_top.writeheader() csv_bot.writeheader() def sorted_nicely(l): convert = lambda text: int(text) if text.isdigit() else text alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key.GetReference()) ] return sorted(l, key=alphanum_key) sorted_modules = sorted_nicely(board.GetModules()) aux_origin = board.GetAuxOrigin() for m in sorted_modules: is_cms = (m.GetAttributes() == pcbnew.MOD_CMS) is_top = not m.IsFlipped() is_bot = not is_top if not is_cms: continue side = 'top' if is_top \ else 'bottom' if is_bot \ else 'none' values = { 'Ref': m.GetReference(), 'Val': m.GetValue(), 'Package': m.GetFPID().GetLibItemName(), 'PosX': pcbnew.ToMM(m.GetPosition().x - aux_origin.x), 'PosY': pcbnew.ToMM(aux_origin.y - m.GetPosition().y), 'Rot': m.GetOrientationDegrees(), 'Side': side, } if side == 'top': csv_top.writerow(values) if side == 'bottom': csv_bot.writerow(values)
def get_placement_info(self): self.info = [] self.numALL = 0 self.numSMT = 0 origin = self.board.GetAuxOrigin() for module in self.board.GetFootprints(): reference = module.GetReference() if self.is_non_annotated_ref(reference): continue value = module.GetValue() excluded = False ''' for ig in IGINOR: if value == ig: excluded = True if excluded: continue ''' self.numALL += 1 package = str(module.GetFPID().GetLibItemName()) pos = module.GetPosition() - origin pos_x = pcbnew.ToMM(pos.x) if module.IsFlipped(): pos_x = -pos_x pos_y = -pcbnew.ToMM(pos.y) rotation = module.GetOrientationDegrees() if module.IsFlipped(): side = u'bottom' else: side = u'top' is_smd = self.is_smd_module(module) smdor = u'HID' if is_smd: self.numSMT +=1 smdor = u'SMD' self.info.append([reference, value, package, pos_x, pos_y, rotation, side, smdor]) self.info.sort(key=self.get_ref_num) self.info.sort(key=self.get_ref_group) self.info.sort(key=self.get_side_group, reverse=True)
def GetValue(self): name = "{0:.2f}_{1:0.2f}_{2:.0f}".format( pcbnew.ToMM(self.parameters["Corner"]["width"]), pcbnew.ToMM(self.parameters["Corner"]["radius"]), (self.parameters["Corner"]["angle"])) if not self.parameters["Corner"]["line"]: pref = "uwArc" else: pref = "uwLine" if self.parameters["Corner"]["rectangle"]: pref += "R" return pref + "%s" % name
def GetValue(self): pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"]) if self.parameters["Pads"]["rowoffsetx"] != 0: pins = pins // 2 pins -= len(self.parameters["Pads"]["missing pads"].split(',')) return "CSP-{n}_{x}x{y}mm_Layout{cols}x{rows}_P{px}x{py}-missing[{missing}]".format( n=pins, x=pcbnew.ToMM(self.parameters['Package']['width']), y=pcbnew.ToMM(self.parameters['Package']['length']), cols=self.parameters['Pads']['columns'], rows=self.parameters['Pads']['rows'], px=pcbnew.ToMM(self.parameters['Pads']['pitchX']), py=pcbnew.ToMM(self.parameters['Pads']['pitchY']), missing=self.parameters['Pads']['missing pads'])
def get_length(self): # current point and layer lenght = self.get_new_endpoints(self.start_point, self.start_layer, 0, self.tracks_on_net, 0, ["pad1"]) length_alt = [] resistance = [] # caluclate again size = len(self.track_list) for i in range(size): length_alt.append(0) resistance.append(0) for track in self.track_list[i][1:-1]: length_alt[i] = length_alt[i] + pcbnew.ToMM(track.GetLength()) track_res = track.GetLength() / SCALE * ( 0.0000000168 * 1000) / (0.035 * track.GetWidth() / SCALE) resistance[i] = resistance[i] + track_res # if connection vas not found, raise an exception if not length_alt: raise LookupError( "Did not find a connection between pads\nThe connection might be partial or through the zone." ) # find minimum and get only that track list min_length = min(length_alt) min_res = min(resistance) index = length_alt.index(min_length) # go through the list and find minimum min_length = min(lenght) return min_length, min_res
def set_hole_pos_by_sw(pcb, sw_name): sw = pcb.FindModuleByReference(sw_name) sw_x, sw_y = pcbnew.ToMM(sw.GetPosition()) hole_name = "HOLE" + str(sw_name.replace("SW", "")) hole_obj = pcb.FindModuleByReference(hole_name) hole_obj.SetPosition(pcbnew.wxPointMM(sw_x + HOLE_PITCH, sw_y + HOLE_PITCH))
def set_diode_position_by_sw(pcb, sw_name): sw = pcb.FindModuleByReference(sw_name) sw_x, sw_y = pcbnew.ToMM(sw.GetPosition()) d_name = "D" + str(sw_name.replace("SW", "")) d_obj = pcb.FindModuleByReference(d_name) d_obj.SetOrientation(ROT_DIODE * 10) d_obj.SetPosition(pcbnew.wxPointMM(sw_x + X_OFS, sw_y + Y_OFS))
def CheckParameters(self): pads = self.parameters['Pads'] numbering = self.parameters['Numbering'] outline = self.parameters['Outline'] padRotation = self.parameters['Pad rotation'] # Check that pads do not overlap pad_dia = pcbnew.ToMM(pads['diameter']) centres = pcbnew.ToMM(pads['center diameter']) n_pads = pads['count'] self.CheckParam('Pads','diameter',max_value=centres*math.pi/n_pads,info="Pads overlap") # Check that the pads fit inside the outline d_min = pad_dia + centres self.CheckParam("Outline","diameter",min_value=d_min, info="Outline diameter is too small")
def avoid_mounting_holes(pcb): """Just a workaround to avoid mounting holes for some diodes.""" conflicted_diodes = [2, 3, 8, 9, 14, 15] X_OFS_4_HOLE = -float(2.5) for c_d in conflicted_diodes: d_name = "D{}".format(c_d) d = pcb.FindModuleByReference(d_name) x, y = pcbnew.ToMM(d.GetPosition()) d.SetPosition(pcbnew.wxPointMM(x + X_OFS_4_HOLE, y))
def get_board_properties(filename, chip_ref): """ Returns a dict from netclass name -> (tolerance, nets) Where tolerance is maximum difference in matched track lengths. And nets is a list of (netname, length) for each net in the netclass. Only netclasses with length matching tolerances are returned """ pcb = pcbnew.LoadBoard(filename) pcb.BuildListOfNets() # required so 'pcb' contains valid netclass data tolerances = {} nets = {} tracks = pcb.GetTracks() # tuples of (netname, classname) netclasses = list(set(t.GetNet().GetClassName() for t in tracks)) # unique netclass names result = {} die_lengths = {} pads = pcb.FindModuleByReference(chip_ref).Pads() for pad in pads: name = pad.GetNetname() length = pcbnew.ToMM(pad.GetPadToDieLength()) die_lengths[name] = length for netclass in netclasses: tolerance = get_tolerance(netclass) if tolerance is None: continue tracks_netclass = [ t for t in tracks if t.GetNet().GetClassName() == netclass ] # tracks in this netclass netnames = list(set([t.GetNet().GetNetname() for t in tracks_netclass ])) # unique netnames in this netclass netnames.sort() nets = [(n, sum( pcbnew.ToMM(t.GetLength()) for t in tracks_netclass if t.GetNet().GetNetname() == n)) for n in netnames] result[netclass] = (tolerance, nets) return (result, die_lengths)
def set_trace_widths(board, target_widths): board.BuildListOfNets() # required so 'board' contains valid netclass data # build list with copper layer names copper_layer_count = board.GetCopperLayerCount() layer_names = [ board.GetLayerName(layer_id) for layer_id in range(board.GetCopperLayerCount() - 1) ] + [board.GetLayerName(31)] # check the target widths structure for nc, width_map in target_widths.items(): # TODO check for valid net class (API only available in kicad 5) # nc = board.GetAllNetClasses() for layer_name in width_map.keys(): if layer_name != 'Default' and layer_name not in layer_names: raise Exception('Invalid layer name: %s' % layer_name) # initialize counters for changes count = collections.OrderedDict() for layer_name in layer_names: count.setdefault(layer_name, 0) for track in board.GetTracks(): for nc, width_map in target_widths.items(): default_width = width_map['Default'] if type(track) == pcbnew.TRACK and track.GetNet().GetClassName( ) == nc: layer_name = track.GetLayerName() if layer_name in width_map: track.SetWidth(pcbnew.FromMils(width_map[layer_name])) else: if default_width <= 0: pos = track.GetPosition() x = pcbnew.ToMM(pos.x) y = pcbnew.ToMM(pos.y) raise Exception( 'Found track on net %s on unexpected layer: %s at position %.2fx%.2f mm' % (track.GetNetname(), layer_name, x, y)) else: track.SetWidth(pcbnew.FromMils(default_width)) count[layer_name] += 1 return count
def move_right_modules_left_mirror(x_center, module_corresponts): for rl, rr in module_corresponts.items(): ml = FindModuleByReference(rl) mr = FindModuleByReference(rr) l_pos = pcbnew.ToMM(ml.GetPosition()) l_ori = ml.GetOrientationDegrees() r_pos = pcbnew.wxPointMM(x_center * 2 - l_pos[0], l_pos[1]) r_ori = 360 - l_ori mr.SetPosition(r_pos) mr.SetOrientationDegrees(r_ori)
def get_centroid_data(parts): spec_dict = collections.OrderedDict([ # db_column header transform func ('reference', ('RefDes', lambda x: '"%s"'%x)), ('side', ('Layer', lambda x: '"%s"'%x)), ('pos_x', ('LocationX', lambda x: '"%.4f"'%pcbnew.ToMM(x))), ('pos_y', ('LocationY', lambda x: '"%.4f"'%pcbnew.ToMM(x))), ('rotation_deg', ('Rotation', lambda x: '"%.4f"'%x)), ]) return get_data_str( parts, spec_dict, separator = ',', populated_only = True, smt_only = True, file_doc = 'Units used = mm / deg', header_on = True )
def WriteToFootprint(name: str, path: str, polygons, createNew: bool = True): header = \ "(module {0} (layer F.Cu,) (tedit 5F08A9C4)\n".format(name) + \ " (fp_text reference REF** (at -0.03 -2.63) (layer F.SilkS)\n" + \ " (effects (font (size 1 1) (thickness 0.15)))\n" + \ " )\n" + \ " (fp_text value {} (at 0.08 -4.13) (layer F.Fab) hide\n".format(name) + \ " (effects (font (size 1 1) (thickness 0.15)))\n" + \ " )\n" path = "/home/dom/kicad/FP/test.pretty/" dir = QDir(path) filePath = dir.absoluteFilePath(name + ".kicad_mod") if createNew: # delete it since we are creating it new if (QFile(filePath).exists()): LogInfo("Deleting existing footprint at: {}".format(filePath)) os.remove(filePath) fileFP = open(filePath,'w') fileFP.write(header) # no iterate through the polygon and write # pcbnew.DRAWSEGMENT.GetPolyShape(). # pcbnew.DRAWSEGMENT.BuildPolyPointsList() # (fp_poly (pts (xy 18.79 0.499) (xy 17.74 0.499) (xy 17.74 -0.501) (xy 18.79 -0.501)) (layer F.Mask) (width 0)) for polygon in polygons: points = polygon.BuildPolyPointsList() pcbnew.DRAWSEGMENT points_str = "" for point in points: points_str += " (xy {:.3f} {:.3f})".format(pcbnew.ToMM(point.x),pcbnew.ToMM(point.y)) fileFP.write("(fp_poly (pts {}) (layer {}) (width {}))".format(points_str, pcbnew.BOARD_GetStandardLayerName(polygon.GetLayer()), polygon.GetWidth())) # write the final closing ')' fileFP.write("\n)") fileFP.close()
def extractRings(geometryList): """ Walks a list of DRAWSEGMENT entities and produces a lists of continuous rings returned as list of list of indices from the geometryList. """ coincidencePoints = {} for i, geom in enumerate(geometryList): start = toTuple(getStartPoint(geom)) coincidencePoints.setdefault(start, CoincidenceList()).append(i) end = toTuple(getEndPoint(geom)) coincidencePoints.setdefault(end, CoincidenceList()).append(i) for point, items in coincidencePoints.items(): if len(items) != 2: raise RuntimeError("Wrong number of entities ({}) at [{}, {}]".format( len(items), pcbnew.ToMM(point[0]), pcbnew.ToMM(point[1]) )) rings = [] unused = [True] * len(geometryList) while any(unused): start = getUnused(unused) rings.append(findRing(start, geometryList, coincidencePoints, unused)) return rings
def get_pos_props(self, m): ''' gather all module props. in the same format as found in the .pos file m: pcbnew.MODULE instance returns: a dict like ... { 'description': u'Capacitor,non-Polarized, Chip;1.65mm L X 0.85mm W X 1.00mm H, IPC Medium Density', 'package': u'CAPC1709X100N', 'position_mm': (-248.4374, -144.8816), 'orientation_deg': 0.0, 'reference': u'C1', 'side': 'bottom', 'value': u'CC0603_22UF_6.3V_20%_X5R' } ''' layer = m.GetLayer() if layer not in (pcbnew.F_Cu, pcbnew.B_Cu): raise RuntimeError('on illegal layer: ' + m.GetReference()) pos = m.GetPosition() pos -= self.board.GetAuxOrigin() # subtract user place offset if layer == pcbnew.B_Cu: # match pcbnew behaviour pos.x = -pos.x return { 'description': m.GetDescription(), 'value': m.GetValue(), 'reference': m.GetReference(), 'side': 'top' if layer == pcbnew.F_Cu else 'bottom', 'package': m.GetFPID().GetLibItemName().wx_str(), 'orientation_deg': m.GetOrientation() / 10.0, # pcbnew has the minus on posy as well 'position_mm': (pcbnew.ToMM(pos[0]), -pcbnew.ToMM(pos[1])) }
def print_parameter_table(self): for name, section in self.parameters.iteritems(): print " %s:" % name for key, value in section.iteritems(): unit = "" if (type(value) is int or type(value) is float) and not "*" in key: unit = "mm" if "*" in key: key = key[1:] else: value = pcbnew.ToMM(value) print " %s: %s%s" % (key, value, unit)
def arrange_diodes(): for i in range(1, 46 + 1): r = "SW" + str(i) sw = FindModuleByReference("SW" + str(i)) d = FindModuleByReference("D" + str(i)) angle = sw.GetOrientationDegrees() tmp_pos = pcbnew.ToMM(sw.GetPosition()) sw_pos = MyPosition(tmp_pos[0], tmp_pos[1]) d_pos = sw_pos + SwitchPosition(0, 0, -angle).up(-0.3).to_mm() d.SetPosition(pcbnew.wxPointMM(d_pos.x, d_pos.y)) d.SetOrientationDegrees(angle) # ref position (relative from switch position) ref = d.Reference() ref.SetTextAngle(0) ref_pos_mm = (4.1, 0) if len(str(i)) == 1 else (4.6, 0) ref_pos = pcbnew.wxPoint(pcbnew.FromMM(ref_pos_mm[0]), pcbnew.FromMM(ref_pos_mm[1])) ref.SetPos0(ref_pos)