summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Blake Kongslie2022-05-11 19:35:03 -0700
committerJulian Blake Kongslie2022-05-11 19:35:03 -0700
commit0e8096b0663b793a30973d5904e1f02f083436a2 (patch)
treeba360687ba559ddfad809933b029416755efe813
downloadlace-0e8096b0663b793a30973d5904e1f02f083436a2.tar.xz
Initial commit.
-rw-r--r--lace.rb290
-rwxr-xr-xtest.rb33
2 files changed, 323 insertions, 0 deletions
diff --git a/lace.rb b/lace.rb
new file mode 100644
index 0000000..f6f871d
--- /dev/null
+++ b/lace.rb
@@ -0,0 +1,290 @@
1require "bigdecimal"
2require "set"
3
4module Lace
5
6 extend self
7
8 def en(series, sigfigs, n)
9 return BigDecimal((10.0**Math.log10(n.to_f**series.to_f).round.to_f)**(1.0/series.to_f), sigfigs).round
10 end
11
12 HISTORIC_E24_FIX_TABLE = {
13 Math.log10(2.6) => Math.log10(2.7),
14 Math.log10(2.9) => Math.log10(3.0),
15 Math.log10(3.2) => Math.log10(3.3),
16 Math.log10(3.5) => Math.log10(3.6),
17 Math.log10(3.8) => Math.log10(3.9),
18 Math.log10(4.2) => Math.log10(4.3),
19 Math.log10(4.6) => Math.log10(4.7),
20 }
21 def historic_e24_fix(n)
22 exp, rem = Math.log10(n).divmod(1)
23 HISTORIC_E24_FIX_TABLE.each do | accurate, historic |
24 rem = historic if (rem - accurate).abs <= 0.0001
25 end
26 return (10 ** (exp + rem)).round
27 end
28
29 def e3(n)
30 return historic_e24_fix(en(3, 2, n))
31 end
32
33 def e6(n)
34 return historic_e24_fix(en(6, 2, n))
35 end
36
37 def e12(n)
38 return historic_e24_fix(en(12, 2, n))
39 end
40
41 def e24(n)
42 return historic_e24_fix(en(24, 2, n))
43 end
44
45 def e48(n)
46 return en(48, 3, n)
47 end
48
49 def e96(n)
50 return en(96, 3, n)
51 end
52
53 def e192(n)
54 return en(192, 3, n)
55 end
56
57 class Netlist
58
59 attr_reader :components
60 attr_reader :nets
61
62 def initialize(&p)
63 @components = {}
64 @nets = {}
65
66 edit(&p) if p
67 end
68
69 def merge_nets(a, b)
70 into = @nets[[a.object_id, b.object_id].min]
71 if a.name
72 raise "Nets #{into.name} and #{a.name} merged!" if into.name and into.name != a.name
73 into.name = a.name
74 end
75 if b.name
76 raise "Nets #{into.name} and #{b.name} merged!" if into.name and into.name != b.name
77 into.name = b.name
78 end
79 @nets[a.object_id] = into
80 @nets[b.object_id] = into
81 return into
82 end
83
84 def edit(&p)
85 instance_eval &p
86 end
87
88 def comp(*args)
89 c = Component.new(self, *args)
90 @components[c.object_id] = c
91 end
92
93 def net(*args)
94 n = Net.new(self, *args)
95 @nets[n.object_id] = n
96 end
97
98 def real_refs
99 refs = {}
100 counters = {}
101 seen = Set.new
102 @components.each do | id, comp |
103 next if seen.member?(comp.object_id)
104 seen.add(comp.object_id)
105 counter = counters[comp.refbase] || 1
106 counters[comp.refbase] = counter + 1
107 ref = "#{comp.refbase}#{counter}"
108 refs[ref] = comp
109 end
110 return refs
111 end
112
113 def real_nets(refs)
114 n = 1
115 nets = {}
116 seen = Set.new
117 @nets.each do | id, net |
118 next if seen.member?(net.object_id)
119 seen.add(net.object_id)
120 name = net.name
121 unless name
122 name = "$unnamed#{n}"
123 n += 1
124 end
125 raise "Multiple disconnected nets named #{name}" if nets.member?(name)
126 nodes = []
127 refs.each do | ref, comp |
128 comp.pins.each do | pin, pinnet |
129 nodes << {:ref => ref, :pin => pin} if @nets[pinnet].object_id == net.object_id
130 end
131 end
132 nets[name] = nodes if nodes.size > 1
133 end
134 return nets
135 end
136
137 def real
138 refs = real_refs
139 nets = real_nets(refs)
140 return [refs, nets]
141 end
142
143 def kicad(stream=$stdout)
144 refs, nets = real
145 stream.write(<<END)
146(export
147 (version D)
148 (components
149END
150 refs.each do | ref, comp |
151 stream.write(<<END)
152 (comp
153 (ref #{ref.inspect})
154 (footprint #{comp.footprint.inspect})
155 (value #{comp.value.inspect})
156 (libsource (lib NO_LIB) (part #{comp.name.inspect}))
157 )
158END
159 end
160 stream.write(<<END)
161 )
162 (nets
163END
164 n = 1
165 nets.each do | name, nodes |
166 stream.write(<<END)
167 (net
168 (code #{n})
169 (name #{name.inspect})
170END
171 nodes.each do | node |
172 stream.write(<<END)
173 (node
174 (ref #{node[:ref].inspect})
175 (pin #{node[:pin]})
176 )
177END
178 end
179 stream.write(<<END)
180 )
181END
182 n += 1
183 end
184 stream.write(<<END)
185 )
186)
187END
188 end
189
190 def summary(stream=$stdout)
191 refs, nets = real
192 refs.each do | ref, comp |
193 stream.write("#{ref} is a #{comp.footprint} #{comp.desc}\n")
194 end
195 nets.each do | name, nodes |
196 stream.write("Net #{name}:\n")
197 nodes.each do | node |
198 stream.write(" #{node[:ref]}[#{node[:pin]}]\n")
199 end
200 end
201 end
202
203 def bom
204 bom = {}
205 real_refs.each do | ref, comp |
206 desc = "#{comp.footprint} #{comp.desc}"
207 bom[desc] = 0 unless bom.member?(desc)
208 bom[desc] += 1
209 end
210 return bom
211 end
212 end
213
214 class Wireable
215
216 attr_reader :netlist
217
218 attr_reader :left
219 attr_reader :right
220
221 def initialize(netlist, left=nil, right=nil)
222 @netlist = netlist
223
224 @left = left
225 @right = right || left
226 end
227
228 def -(that)
229 @netlist.merge_nets(@right, that.left)
230 return Wireable.new(@netlist, @left, that.right)
231 end
232
233 end
234
235 class Component < Wireable
236
237 attr_reader :refbase
238 attr_reader :footprint
239 attr_reader :name
240 attr_reader :value
241
242 attr_reader :pins
243
244 def initialize(netlist, refbase, footprint, name, value=nil)
245 super(netlist)
246
247 @refbase = refbase
248 @footprint = footprint
249 @name = name
250 @value = value || name
251
252 @pins = {}
253 end
254
255 def left
256 return self[1]
257 end
258
259 def right
260 return self[2]
261 end
262
263 def [](pin)
264 unless pins.member?(pin)
265 n = @netlist.net
266 pins[pin] = n.object_id
267 end
268 return @netlist.nets[pins[pin]]
269 end
270
271 def desc
272 return "#{@value} #{@name}" if @name != @value
273 return @name
274 end
275
276 end
277
278 class Net < Wireable
279
280 attr_accessor :name
281
282 def initialize(netlist, name=nil)
283 super(netlist, self)
284
285 @name = name
286 end
287
288 end
289
290end
diff --git a/test.rb b/test.rb
new file mode 100755
index 0000000..86d1415
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,33 @@
1#!/usr/bin/ruby
2
3require "./lace"
4
5def r(size, suffix="ohm")
6 comp("R", "0805", "resistor", "#{Lace::e12(size)}#{suffix}")
7end
8
9def c(size, suffix="nF")
10 comp("C", "0805", "capacitor", "#{Lace::e12(size)}#{suffix}")
11end
12
13def led(color="red")
14 comp("D", "0805", "LED", "1.5V 10mA #{color}")
15end
16
17nl = Lace::Netlist.new do
18 vcc = net("vcc")
19 gnd = net("gnd")
20
21 vcc - r((5 - 1.5)/0.010) - led - gnd
22 vcc - c(100) - gnd
23end
24
25nl.kicad
26$stdout.write("\n")
27nl.summary
28
29$stdout.write("\nBOM:\n")
30bom = nl.bom
31bom.keys.sort.each do | item |
32 $stdout.write(" #{bom[item]}\t#{item}\n")
33end