require "bigdecimal" require "set" module Lace extend self def en(series, sigfigs, n) return BigDecimal((10.0**Math.log10(n.to_f**series.to_f).round.to_f)**(1.0/series.to_f), sigfigs).round end HISTORIC_E24_FIX_TABLE = { Math.log10(2.6) => Math.log10(2.7), Math.log10(2.9) => Math.log10(3.0), Math.log10(3.2) => Math.log10(3.3), Math.log10(3.5) => Math.log10(3.6), Math.log10(3.8) => Math.log10(3.9), Math.log10(4.2) => Math.log10(4.3), Math.log10(4.6) => Math.log10(4.7), } def historic_e24_fix(n) exp, rem = Math.log10(n).divmod(1) HISTORIC_E24_FIX_TABLE.each do | accurate, historic | rem = historic if (rem - accurate).abs <= 0.0001 end return (10 ** (exp + rem)).round end def e3(n) return historic_e24_fix(en(3, 2, n)) end def e6(n) return historic_e24_fix(en(6, 2, n)) end def e12(n) return historic_e24_fix(en(12, 2, n)) end def e24(n) return historic_e24_fix(en(24, 2, n)) end def e48(n) return en(48, 3, n) end def e96(n) return en(96, 3, n) end def e192(n) return en(192, 3, n) end class Netlist attr_reader :components attr_reader :nets def initialize(&p) @components = {} @nets = {} edit(&p) if p end def merge_nets(a, b) into = @nets[[a.object_id, b.object_id].min] if a.name raise "Nets #{into.name} and #{a.name} merged!" if into.name and into.name != a.name into.name = a.name end if b.name raise "Nets #{into.name} and #{b.name} merged!" if into.name and into.name != b.name into.name = b.name end @nets[a.object_id] = into @nets[b.object_id] = into return into end def edit(&p) instance_eval &p end def comp(*args) c = Component.new(self, *args) @components[c.object_id] = c end def net(*args) n = Net.new(self, *args) @nets[n.object_id] = n end def real_refs refs = {} counters = {} seen = Set.new @components.each do | id, comp | next if seen.member?(comp.object_id) seen.add(comp.object_id) counter = counters[comp.refbase] || 1 counters[comp.refbase] = counter + 1 ref = "#{comp.refbase}#{counter}" refs[ref] = comp end return refs end def real_nets(refs) n = 1 nets = {} seen = Set.new @nets.each do | id, net | next if seen.member?(net.object_id) seen.add(net.object_id) name = net.name unless name name = "$unnamed#{n}" n += 1 end raise "Multiple disconnected nets named #{name}" if nets.member?(name) nodes = [] refs.each do | ref, comp | comp.pins.each do | pin, pinnet | nodes << {:ref => ref, :pin => pin} if @nets[pinnet].object_id == net.object_id end end nets[name] = nodes if nodes.size > 1 end return nets end def real refs = real_refs nets = real_nets(refs) return [refs, nets] end def kicad(stream=$stdout) refs, nets = real stream.write(<