Created
March 23, 2014 18:40
-
-
Save Mon-Ouie/9727561 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Solver | |
BaseCapacitors = [1e-9, 100e-9, 1e-6, 9e-9, 12e-9] | |
Capacitors = BaseCapacitors + | |
BaseCapacitors.product(BaseCapacitors).map { |a, b| a*b/(a+b) } + | |
BaseCapacitors.product(BaseCapacitors).map { |a, b| a+b } | |
Capacitors.uniq! | |
BaseResistors = [100.0, 1e3, 10e3, 100e3, 1e6, | |
2.2e3, | |
3.3e3] | |
Resistors = BaseResistors + | |
BaseResistors.product(BaseResistors).map { |a, b| a*b/(a+b) } + | |
BaseResistors.product(BaseResistors).map { |a, b| a+b } | |
Resistors.uniq! | |
Prefixes = [[1e24, "Y"], | |
[1e21, "Z"], | |
[1e18, "E"], | |
[1e15, "P"], | |
[1e12, "T"], | |
[1e9, "G"], | |
[1e6, "M"], | |
[1e3, "k"], | |
[1e-3, "m"], | |
[1e-6, "μ"], | |
[1e-9, "n"], | |
[1e-12, "p"], | |
[1e-15, "f"], | |
[1e-18, "a"], | |
[1e-21, "z"], | |
[1e-24, "y"]] | |
Value = Struct.new(:value, :unit) do | |
def to_s | |
val, prefix = prefixed_value | |
("%.2f %s%s" % [val, prefix, unit]).strip | |
end | |
def prefixed_value | |
if 1e-2 <= value && value < 1e3 | |
[value, nil] | |
else | |
Prefixes.each do |threshold, c| | |
if value >= threshold | |
return [value / threshold, c] | |
end | |
end | |
[value, nil] | |
end | |
end | |
end | |
EqualityConstraint = Struct.new(:var, :expected, :opts) do | |
def error(solver) | |
(solver.get(var) - expected).abs | |
end | |
def respected?(solver) | |
error(solver) <= if opts[:abs] | |
opts[:abs] | |
elsif opts[:rel] | |
opts[:rel] * expected.abs | |
end | |
end | |
def error_string(solution) | |
actual = solution.variables[var].value | |
unit = solution.variables[var].unit | |
abs = (actual - expected).abs | |
rel = abs/expected | |
"#{Value.new(abs, unit)} (#{"%2d" % (rel*100)}%)" | |
end | |
end | |
Solution = Struct.new(:variables, :constraints) do | |
def get(var) | |
variables[var].value | |
end | |
end | |
def self.solve(&block) | |
components = block.parameters.map { |x| x[1] } | |
solutions = new(components, &block).solve | |
sorted_solutions = solutions.sort_by { |s| | |
s.constraints.map { |c| c.error(s) } | |
} | |
direct, other = sorted_solutions.partition { |s| | |
components.all? { |c| | |
if c.to_s.start_with? "r" | |
BaseResistors.include? s.get(c) | |
elsif c.to_s.start_with? "c" | |
BaseCapacitors.include? s.get(c) | |
end | |
} | |
} | |
TablePrinter.print_solutions direct, other | |
end | |
def initialize(components, &block) | |
@components = components | |
@block = block | |
@solutions = [] | |
@constraints = [] | |
@variables = {} | |
end | |
def attempt(values) | |
values.each do |var, val| | |
@variables[var] = val | |
end | |
instance_exec(*@components.map { |v| get(v) }, &@block) | |
if @constraints.all? { |c| c.respected? self } | |
@solutions << Solution.new(@variables, @constraints) | |
else | |
end | |
clear | |
end | |
def solve | |
solve_with({}, @components) | |
@solutions | |
end | |
def solve_with(values, components) | |
if components.empty? | |
attempt(values) | |
else | |
comp = components[0] | |
comp_name = comp.to_s | |
rest = components[1..-1] | |
candidates, unit = if comp_name.start_with? "r" | |
[Resistors, "Ω"] | |
else | |
[Capacitors, "F"] | |
end | |
candidates.each do |val| | |
values = values.dup | |
values[comp] = Value.new(val, unit) | |
solve_with(values, rest) | |
end | |
end | |
end | |
def clear | |
@variables = {} | |
@constraints = [] | |
end | |
def let(var, val, unit = nil) | |
@variables[var] = Value.new(val, unit) | |
end | |
def get(var) | |
@variables[var].value | |
end | |
def constraint_eq(name, val, opts) | |
@constraints << EqualityConstraint.new(name, val, opts) | |
end | |
def respond_to_missing?(name, include_all) | |
@variables.include? name || super | |
end | |
def method_missing(name, *args, &block) | |
if @variables.include? name | |
get name | |
else | |
super | |
end | |
end | |
end | |
module TablePrinter | |
module_function | |
def column_sizes(header, data) | |
max = data.inject header.map(&:size) do |sizes, row| | |
row.map(&:size).zip(sizes).map(&:max) | |
end | |
max.map { |n| n + 2 } | |
end | |
def format_row(row, sizes, sep, align = :right) | |
sep + row.zip(sizes).map { |t, s| | |
if align == :center | |
t.center(s) | |
else | |
t.rjust(s-1) + " " | |
end | |
}.join(sep) + sep | |
end | |
def print_table(header, direct, other) | |
vert = "│" | |
horiz = "─" | |
top_left = "┌" | |
top_right = "┐" | |
top_inter = "┬" | |
bot_left = "└" | |
bot_right = "┘" | |
bot_inter = "┴" | |
left_inter = "├" | |
right_inter = "┤" | |
cross = "┼" | |
sizes = column_sizes(header, direct + other) | |
top_sep = top_left + sizes.map { |s| horiz * s }.join(top_inter) + top_right | |
bot_sep = bot_left + sizes.map { |s| horiz * s }.join(bot_inter) + bot_right | |
inter_sep = left_inter + sizes.map { |s| horiz * s }.join(cross) + | |
right_inter | |
puts top_sep | |
puts format_row(header, sizes, vert, :center) | |
puts inter_sep | |
unless direct.empty? | |
direct.each do |row| | |
puts format_row(row, sizes, vert) | |
end | |
puts inter_sep unless other.empty? | |
end | |
unless other.empty? | |
other.each do |row| | |
puts format_row(row, sizes, vert) | |
end | |
end | |
puts bot_sep | |
end | |
def print_solutions(direct, other) | |
if direct.empty? && other.empty? | |
puts "No set of components satisfy the constraints!" | |
else | |
first = direct[0] || other[0] | |
header = first.variables.map { |var, val| var.to_s } | |
header += first.constraints.map { |c| "Δ#{c.var}" } | |
direct = direct.map { |s| | |
s.variables.map { |var, val| val.to_s } + s.constraints.map { |c| | |
c.error_string(s) | |
} | |
} | |
other = other.map { |s| | |
s.variables.map { |var, val| val.to_s } + s.constraints.map { |c| | |
c.error_string(s) | |
} | |
} | |
print_table(header, direct, other) | |
end | |
end | |
end | |
module Kernel | |
module_function | |
def solve(&block) | |
Solver.solve(&block) | |
end | |
end | |
solve do |r_a, r_b, c| | |
v_cc = 15 | |
v_drop = 0.7 | |
t_off = 0.7 * r_b * c | |
t_on = Math.log(2)/Math.log((2*v_cc - 3*v_drop)/(v_cc - 3*v_drop)) * r_a * c | |
t = t_on + t_off | |
let :f, 1/t, "Hz" | |
let :duty_cycle, t_on/t | |
constraint_eq :f, 40e3, :abs => 1e3 | |
constraint_eq :duty_cycle, 0.5, :abs => 0.1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment