update
This commit is contained in:
+92
-63
@@ -1,4 +1,5 @@
|
||||
#!/bin/python
|
||||
import math
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
@@ -23,40 +24,37 @@ from pycbnew.primitives.gr_elements import (
|
||||
KGrLine,
|
||||
KTable,
|
||||
)
|
||||
from pycbnew.primitives.segment import KSegment
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# Constants / Global Variables set through argparse
|
||||
SCALE = 1.0
|
||||
TEXT_SPACING_MM = 12
|
||||
NO_POWER_NETS = False
|
||||
PCB_FILE = "trainmap.kicad_pcb"
|
||||
# Global Variables set through argparse
|
||||
LOG_LEVEL = logging.INFO
|
||||
NO_POWER_NETS = False
|
||||
NO_TEXT = False
|
||||
NO_TRACES = False
|
||||
SCALE = 1.0
|
||||
PCB_FILE = "trainmap.kicad_pcb"
|
||||
INPUT_XML = ""
|
||||
MC_POS = Point(0, 0)
|
||||
TITLE_FONTFACE = ""
|
||||
FONTFACE = ""
|
||||
MARGIN = {"left": 10, "top": 5}
|
||||
TRACE_WIDTH_MM=0.5
|
||||
CAP_DISTANCE_MM=5
|
||||
TEXT_SPACING_MM=15
|
||||
|
||||
# Fonts
|
||||
LINE_FONT = Font(size=(16, 16))
|
||||
TITLE_FONTFACE = ""
|
||||
FONTFACE = ""
|
||||
LINE_FONT = Font(size=(12, 12))
|
||||
LINE_FONT.face = FONTFACE
|
||||
TITLE_FONT = Font(size=(28, 28))
|
||||
TITLE_FONT.face = TITLE_FONTFACE
|
||||
|
||||
|
||||
# Footprints
|
||||
KFootprint.set_footprint_folders([f"{os.path.dirname(__file__)}/kicad_libs"])
|
||||
FOOTPRINT_C = "Capacitor_SMD:C_0603_1608Metric"
|
||||
FOOTPRINT_LED = "Dialight_587_1032_147F:587"
|
||||
FOOTPRINT_MC = "DFR0654:MODULE_DFR0654"
|
||||
|
||||
# Mappings
|
||||
TEXT_POS_MAPPING = {
|
||||
"above": Vector(0, TEXT_SPACING_MM),
|
||||
"right": Vector(TEXT_SPACING_MM, 0),
|
||||
"below": Vector(0, -TEXT_SPACING_MM),
|
||||
"left": Vector(-TEXT_SPACING_MM, 0),
|
||||
}
|
||||
MC_PADS = {
|
||||
"GND": 30,
|
||||
"GND1": 4,
|
||||
@@ -69,7 +67,6 @@ LED_PADS = {"GND": 0, "VCC": 1, "DI": 2, "DO": 3}
|
||||
NET_GND = KNet(1, "GND")
|
||||
NET_VCC = KNet(2, "5V")
|
||||
|
||||
|
||||
class Map:
|
||||
title: str
|
||||
lines: list
|
||||
@@ -134,17 +131,20 @@ syntax:
|
||||
</line>
|
||||
</map>
|
||||
"""
|
||||
|
||||
|
||||
def parse_map(xml_file):
|
||||
root = ET.parse(xml_file)
|
||||
|
||||
title = root.find("title").text
|
||||
title_node = root.find("title")
|
||||
title=""
|
||||
if title_node != None:
|
||||
title=title_node.text
|
||||
|
||||
map = Map(title)
|
||||
logging.debug(f"map title: {title}")
|
||||
|
||||
width = 0
|
||||
height = 0
|
||||
id=0
|
||||
for line in root.findall("line"):
|
||||
line_name = line.get("name")
|
||||
try:
|
||||
@@ -156,20 +156,18 @@ def parse_map(xml_file):
|
||||
logging.debug(f"Parsing line: {line_parsed}")
|
||||
|
||||
for station in line.findall("station"):
|
||||
id = int(station.get("id"))
|
||||
id=id+1
|
||||
name = station.text
|
||||
x = int(station.get("x"))
|
||||
x = int(float(station.get("x")))
|
||||
# search width
|
||||
if x > width:
|
||||
width = x
|
||||
y = int(station.get("y"))
|
||||
y = int(float(station.get("y")))
|
||||
# search height
|
||||
if y > height:
|
||||
height = y
|
||||
text_pos = station.get("textpos")
|
||||
text_angle = int(station.get("angle"))
|
||||
|
||||
station_parsed = Station(id, name, x, y, text_angle, text_pos)
|
||||
station_parsed = Station(id, name, x, y)
|
||||
logging.debug(station_parsed)
|
||||
|
||||
line_parsed.add(station_parsed)
|
||||
@@ -184,12 +182,13 @@ def parse_map(xml_file):
|
||||
|
||||
return map
|
||||
|
||||
|
||||
"""
|
||||
Creates a capacity between LED's VCC and GND
|
||||
"""
|
||||
def create_capacity(station):
|
||||
cap_pos_index = ([*TEXT_POS_MAPPING].index(station.text_pos) + 2) % len(
|
||||
TEXT_POS_MAPPING
|
||||
)
|
||||
cap_pos = [*TEXT_POS_MAPPING.values()][cap_pos_index]
|
||||
cap_pos_x = CAP_DISTANCE_MM * math.sin(station.angle)
|
||||
cap_pos_y = CAP_DISTANCE_MM * math.cos(station.angle)
|
||||
cap_pos = Vector(cap_pos_x, cap_pos_y)
|
||||
fp = KFootprint(FOOTPRINT_C, at=station.pos + cap_pos, angle=station.angle)
|
||||
|
||||
ref = fp.properties[KFootprintProperty.Type.Reference]
|
||||
@@ -200,21 +199,26 @@ def create_capacity(station):
|
||||
|
||||
return fp
|
||||
|
||||
def calculate_angle(station1, station2):
|
||||
vec = Vector(station2.pos.x, station2.pos.y) - Vector(station1.pos.x, station1.pos.y)
|
||||
angle_rad = vec.self_angle()
|
||||
return math.degrees(angle_rad)
|
||||
|
||||
"""
|
||||
Creates the footprint for a station led
|
||||
adds led, text and C
|
||||
"""
|
||||
|
||||
|
||||
def create_station_footprint(station):
|
||||
fp = KFootprint(FOOTPRINT_LED, at=station.pos) # , angle=station.angle)
|
||||
fp = KFootprint(FOOTPRINT_LED, at=station.pos, angle=station.angle)
|
||||
|
||||
# text
|
||||
ref = fp.properties[KFootprintProperty.Type.Reference]
|
||||
if NO_TEXT:
|
||||
ref.layer = 'User.Comments'
|
||||
|
||||
ref.value = station.name
|
||||
ref.angle = station.angle
|
||||
ref.at = TEXT_POS_MAPPING[station.text_pos]
|
||||
text_pos_x = TEXT_SPACING_MM * math.cos(station.angle)
|
||||
text_pos_y = TEXT_SPACING_MM * math.sin(station.angle)
|
||||
ref.at = Vector(text_pos_x, text_pos_y)
|
||||
|
||||
if not NO_POWER_NETS:
|
||||
fp.pads[LED_PADS["GND"]].net = NET_GND
|
||||
@@ -227,15 +231,27 @@ def create_station_footprint(station):
|
||||
Creates a new net between station 1 out pad and station 2 in pad
|
||||
Connects them on the PCB
|
||||
"""
|
||||
|
||||
|
||||
def connect_stations(board, station1, station2, fp1, fp2):
|
||||
net = KNet(station1.id + 2, f"{station1.id}-{station2.id}")
|
||||
board.add(net)
|
||||
fp1.pads[LED_PADS["DO"]].net = net
|
||||
fp2.pads[LED_PADS["DI"]].net = net
|
||||
|
||||
do = fp1.pads[LED_PADS["DO"]]
|
||||
di = fp2.pads[LED_PADS["DI"]]
|
||||
|
||||
# connect the net
|
||||
do.net = net
|
||||
di.net = net
|
||||
|
||||
if NO_TRACES:
|
||||
return
|
||||
|
||||
# do the trace
|
||||
trace = KSegment(net=net, layer="F.Cu", point1=do.absolute_at, point2=di.absolute_at, width=TRACE_WIDTH_MM)
|
||||
board.add(trace)
|
||||
|
||||
"""
|
||||
Connects the line to the MC
|
||||
"""
|
||||
def connect_line(board, mc, line, station_fp):
|
||||
if connect_line.gpio_ptr > len(MC_PADS["gpios"]):
|
||||
logging.critical(
|
||||
@@ -253,16 +269,12 @@ def connect_line(board, mc, line, station_fp):
|
||||
station_fp.pads[LED_PADS["DI"]].net = net
|
||||
|
||||
connect_line.gpio_ptr += 1
|
||||
|
||||
|
||||
connect_line.gpio_ptr = 0
|
||||
|
||||
|
||||
"""
|
||||
Adds the main microcontroller
|
||||
"""
|
||||
|
||||
|
||||
def add_mc(board):
|
||||
mc = KFootprint(FOOTPRINT_MC, at=MC_POS)
|
||||
|
||||
@@ -278,22 +290,15 @@ def add_mc(board):
|
||||
adds labels for the lines to the next position in the TEXT_POS_MAPPING list
|
||||
from the position of the text label of the station (just a best estimate)
|
||||
"""
|
||||
|
||||
|
||||
def add_line_label(board, line, station):
|
||||
text_pos_x = station.pos.x + TEXT_SPACING_MM * math.sin(-0.25*station.angle)
|
||||
text_pos_y = station.pos.y + TEXT_SPACING_MM * math.cos(-0.25*station.angle)
|
||||
|
||||
# get next position
|
||||
text_pos_index = ([*TEXT_POS_MAPPING].index(station.text_pos) + 1) % len(
|
||||
TEXT_POS_MAPPING
|
||||
)
|
||||
text_pos = [*TEXT_POS_MAPPING.values()][text_pos_index]
|
||||
line_pos = Point(station.pos.x + 2 * text_pos.x,
|
||||
station.pos.y + 2 * text_pos.y)
|
||||
|
||||
text_pos = Vector(text_pos_x, text_pos_y)
|
||||
text = KGrText(
|
||||
layer="F.SilkS", text=f"{line.name}", at=line_pos, angle=station.angle
|
||||
layer="F.SilkS", text=f"{line.name}", at=text_pos
|
||||
)
|
||||
text.font = LINE_FONT
|
||||
#text.font = LINE_FONT
|
||||
|
||||
board.add(text)
|
||||
|
||||
@@ -301,9 +306,10 @@ def add_line_label(board, line, station):
|
||||
"""
|
||||
adds a board title top middle
|
||||
"""
|
||||
|
||||
|
||||
def add_title_label(board, map):
|
||||
if NO_TEXT:
|
||||
return
|
||||
|
||||
logging.debug(f"add map title: {map.title}")
|
||||
text = KGrText(
|
||||
layer="F.SilkS", text=f"{map.title}", at=Point(map.width / 2, MARGIN["top"])
|
||||
@@ -316,8 +322,6 @@ def add_title_label(board, map):
|
||||
"""
|
||||
main pcb generation loop
|
||||
"""
|
||||
|
||||
|
||||
def generate_pcb(map):
|
||||
board = KProject(PCB_FILE)
|
||||
board.add(NET_GND)
|
||||
@@ -330,7 +334,14 @@ def generate_pcb(map):
|
||||
for line in map.lines:
|
||||
prev_station = None
|
||||
prev_led = None
|
||||
if len(line.stations) == 0:
|
||||
continue
|
||||
for station in line.stations:
|
||||
if prev_station is not None:
|
||||
station.angle = calculate_angle(prev_station, station)
|
||||
else:
|
||||
station.angle = calculate_angle(station, line.stations[1])
|
||||
logging.debug(f"angle between stations: {station.angle}")
|
||||
led = create_station_footprint(station)
|
||||
board.add(led)
|
||||
cap = create_capacity(station)
|
||||
@@ -374,7 +385,7 @@ def main():
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
global SCALE, TEXT_SPACING_MM, NO_POWER_NETS, PCB_FILE, LOG_LEVEL, INPUT_XML
|
||||
global SCALE, TEXT_SPACING_MM, NO_TEXT, NO_POWER_NETS, NO_TRACES, PCB_FILE, LOG_LEVEL, INPUT_XML
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="train map pcb generator",
|
||||
@@ -393,10 +404,20 @@ def parse_arguments():
|
||||
default=5,
|
||||
help="spacing of the text from the center point of led (mm)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-text",
|
||||
action="store_true",
|
||||
help="do not produce any text",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-power-nets",
|
||||
action="store_true",
|
||||
help="do not connect (easier visual debugging)",
|
||||
help="do not connect GND and VCC (easier visual debugging)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-traces",
|
||||
action="store_true",
|
||||
help="do not implement traces between leds",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
@@ -418,7 +439,9 @@ def parse_arguments():
|
||||
INPUT_XML = args.input
|
||||
SCALE = args.scale
|
||||
TEXT_SPACING_MM = args.text_space
|
||||
NO_TEXT = args.no_text
|
||||
NO_POWER_NETS = args.no_power_nets
|
||||
NO_TRACES = args.no_traces
|
||||
PCB_FILE = args.output
|
||||
|
||||
if args.verbose > 0:
|
||||
@@ -429,9 +452,15 @@ def parse_arguments():
|
||||
print(f"Warning: Output file '{
|
||||
PCB_FILE}' does not have .kicad_pcb extension.")
|
||||
|
||||
if NO_TEXT:
|
||||
print("Mode: No text")
|
||||
|
||||
if args.no_power_nets:
|
||||
print("Mode: Power nets disabled.")
|
||||
|
||||
if NO_TRACES:
|
||||
print("Mode: No traces")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user