aboutsummaryrefslogtreecommitdiff
path: root/bin/level_calc.py
diff options
context:
space:
mode:
authorSelene ToyKeeper2016-05-22 18:42:01 -0600
committerSelene ToyKeeper2016-05-22 18:42:01 -0600
commit4b794fa1566ed670e185f4045472ad75406c2a9a (patch)
tree0200a9c7b868f7f47d01bd51a8a07a7525685c5d /bin/level_calc.py
parentDifferent tiny25 fuses: No brownout detection, and slower startup. (diff)
downloadanduril-4b794fa1566ed670e185f4045472ad75406c2a9a.tar.gz
anduril-4b794fa1566ed670e185f4045472ad75406c2a9a.tar.bz2
anduril-4b794fa1566ed670e185f4045472ad75406c2a9a.zip
Made level_calc.py work with any number of power channels.
Results are slightly different than before, and I'm not sure if I broke something or fixed something. Hard to tell. Regardless, it seems to work fine.
Diffstat (limited to 'bin/level_calc.py')
-rwxr-xr-xbin/level_calc.py192
1 files changed, 96 insertions, 96 deletions
diff --git a/bin/level_calc.py b/bin/level_calc.py
index 63926ba..6a82563 100755
--- a/bin/level_calc.py
+++ b/bin/level_calc.py
@@ -4,115 +4,112 @@ import math
interactive = False
+
def main(args):
"""Calculates PWM levels for visually-linear steps.
"""
# Get parameters from the user
- v = dict(pwm_max=255, pwm2_max=255)
- questions = [
+ questions_main = [
+ (int, 'num_channels', 1, 'How many power channels?'),
(int, 'num_levels', 4, 'How many total levels do you want?'),
- (int, 'pwm_min', 6, 'Lowest visible PWM level, for moon mode:'),
- (float, 'lm_min', 0.25, 'How bright is moon mode, in lumens?'),
+ ]
+ questions_per_channel = [
+ (int, 'pwm_min', 6, 'Lowest visible PWM level:'),
+ (float, 'lm_min', 0.25, 'How bright is the lowest level, in lumens?'),
#(int, 'pwm_max', 255, 'Highest PWM level:'),
(float, 'lm_max', 1000, 'How bright is the highest level, in lumens?'),
- (str, 'dual_pwm', 'n', 'Use dual PWM? [y/n]'),
- (float, 'pwm2_min', 6, 'Second channel, lowest visible PWM level:'),
- (float, 'lm2_min', 0.25, 'Second channel, how bright is the lowest mode, in lumens?'),
- #(float, 'pwm2_max', 255, 'Second channel, highest PWM level:'),
- (float, 'lm2_max', 140, 'Second channel, how bright is maximum, in lumens?'),
]
- for typ, name, default, text in questions:
- value = get_value(text, default, args)
- if not value:
- value = default
- else:
- value = typ(value)
- v[name] = value
- if (name == 'dual_pwm' and value == 'n'):
- # skip remaining questions if not using dual PWM
- break
-
- if v['dual_pwm'] == 'y':
- dual_pwm(v)
- else:
- single_pwm(v)
+
+ def ask(questions, ans):
+ for typ, name, default, text in questions:
+ value = get_value(text, default, args)
+ if not value:
+ value = default
+ else:
+ value = typ(value)
+ setattr(ans, name, value)
+
+ answers = Empty()
+ ask(questions_main, answers)
+ channels = []
+ if not args:
+ print('Describe the channels in order of lowest to highest power.')
+ for chan_num in range(answers.num_channels):
+ if not args:
+ print('===== Channel %s =====' % (chan_num+1))
+ chan = Empty()
+ chan.pwm_max = 255
+ ask(questions_per_channel, chan)
+ channels.append(chan)
+
+ multi_pwm(answers, channels)
if interactive: # Wait on exit, in case user invoked us by clicking an icon
print 'Press Enter to exit:'
raw_input()
-def single_pwm(v):
- """Estimate the PWM levels for a one-channel driver."""
- visual_min = invpower(v['lm_min'])
- visual_max = invpower(v['lm_max'])
- step_size = (visual_max - visual_min) / (v['num_levels']-1)
- modes = []
- goal = visual_min
- for i in range(v['num_levels']):
- goal_lm = power(goal)
- #pwm_float = ((goal_lm / v['lm_max']) * (256-v['pwm_min'])) + v['pwm_min'] - 1
- pwm_float = (((goal_lm-v['lm_min']) / (v['lm_max']-v['lm_min'])) \
- * (255-v['pwm_min'])) \
- + v['pwm_min']
- pwm = int(round(pwm_float))
- pwm = max(min(pwm,v['pwm_max']),v['pwm_min'])
- modes.append(pwm)
- print '%i: visually %.2f (%.2f lm): %.2f/255' % (i+1, goal, goal_lm, pwm_float)
- goal += step_size
-
- print 'PWM values:', ','.join([str(i) for i in modes])
-
-def dual_pwm(v):
- """Estimate the PWM levels for a two-channel driver.
- Assume the first channel is the brighter one, and second will be used for moon/low modes.
- """
- #visual_min = math.pow(v['lm2_min'], 1.0/power)
- #visual_max = math.pow(v['lm_max'], 1.0/power)
- visual_min = invpower(v['lm2_min'])
- visual_max = invpower(v['lm_max'])
- step_size = (visual_max - visual_min) / (v['num_levels']-1)
- modes = []
- goal = visual_min
- for i in range(v['num_levels']):
- goal_lm = power(goal)
- # Up to the second channel's limit, calculate things just like a
- # single-channel driver (first channel will be zero)
- if goal_lm <= v['lm2_max']:
- pwm1_float = 0.0
- #pwm2_float = ((goal_lm / v['lm2_max']) * (256-v['pwm2_min'])) + v['pwm2_min'] - 1
- pwm2_float = (((goal_lm-v['lm2_min']) / (v['lm2_max']-v['lm2_min'])) \
- * (255-v['pwm2_min'])) \
- + v['pwm2_min']
- pwm1 = int(round(pwm1_float))
- pwm2 = int(round(pwm2_float))
- pwm2 = max(min(pwm2,v['pwm2_max']),v['pwm2_min'])
- modes.append((int(pwm1),int(pwm2)))
- # Above the second channel's limit, things get a little more
- # complicated (second channel will be 255, first channel will be
- # adjusted down by the max output of the second channel)
- else:
- if len(modes) == v['num_levels'] -1: # turbo is special
- #pwm1_float = ((goal_lm / v['lm_max']) * (256-v['pwm_min'])) + v['pwm_min'] - 1
- pwm1_float = float(v['pwm_max'])
- # on a FET+7135 driver, turbo works better without the 7135
- # (we're assuming FET+7135 here)
- pwm2_float = 0.0
- else: # not the highest mode yet
- #pwm1_float = (((goal_lm-v['lm2_max']) / v['lm_max']) * (256-v['pwm_min'])) + v['pwm_min'] - 1
- pwm1_float = (((goal_lm-v['lm_min']-v['lm2_max']) / (v['lm_max']-v['lm_min'])) \
- * (255-v['pwm_min'])) \
- + v['pwm_min']
- pwm2_float = 255.0
- pwm1 = int(round(pwm1_float))
- pwm2 = int(round(pwm2_float))
- pwm1 = max(min(pwm1,v['pwm_max']),v['pwm_min'])
- modes.append((int(pwm1),int(pwm2)))
- print '%i: visually %.2f (%.2f lm): %.2f/255, %.2f/255' % (i+1, goal, goal_lm, pwm1_float, pwm2_float)
- goal += step_size
-
- print 'PWM1/FET values:', ','.join([str(i[0]) for i in modes])
- print 'PWM2/7135 values:', ','.join([str(i[1]) for i in modes])
- print 'On a non-FET driver, the last mode should be 255 on both channels.'
+
+class Empty:
+ pass
+
+
+def multi_pwm(answers, channels):
+ lm_min = channels[0].lm_min
+ lm_max = channels[-1].lm_max
+ visual_min = invpower(lm_min)
+ visual_max = invpower(lm_max)
+ step_size = (visual_max - visual_min) / (answers.num_levels-1)
+
+ # Determine ideal lumen levels
+ goals = []
+ goal_vis = visual_min
+ for i in range(answers.num_levels):
+ goal_lm = power(goal_vis)
+ goals.append((goal_vis, goal_lm))
+ goal_vis += step_size
+
+ # Calculate each channel's output for each level
+ for cnum, channel in enumerate(channels):
+ prev_channel = Empty() ; prev_channel.lm_max = 0.0
+ if cnum > 0:
+ prev_channel = channels[cnum-1]
+ channel.modes = []
+ for i in range(answers.num_levels):
+ goal_vis, goal_lm = goals[i]
+ # This channel already is maxed out
+ if goal_lm >= channel.lm_max:
+ # Handle turbo specially, enable only biggest channel
+ if (i == (answers.num_levels - 1)) and (cnum < (len(channels)-1)):
+ channel.modes.append(0.0)
+ else:
+ channel.modes.append(channel.pwm_max)
+ # This channel's active ramp-up range
+ elif goal_lm > prev_channel.lm_max:
+ # FIXME: This produces somewhat different values than the
+ # dual_pwm() algorithm, and I'm not sure which one is "right"
+ diff = channel.lm_max - prev_channel.lm_max
+ needed = goal_lm - prev_channel.lm_max
+ ratio = needed / diff * (channel.pwm_max-channel.pwm_min)
+ channel.modes.append(ratio + channel.pwm_min)
+ # This channel isn't active yet, output too low
+ else:
+ channel.modes.append(0)
+
+ # Show individual levels in detail
+ for i in range(answers.num_levels):
+ goal_vis, goal_lm = goals[i]
+ pwms = []
+ for channel in channels:
+ pwms.append('%.2f/%i' % (channel.modes[i], channel.pwm_max))
+ print('%i: visually %.2f (%.2f lm): %s' %
+ (i+1, goal_vis, goal_lm, ', '.join(pwms)))
+
+ # Show values we can paste into source code
+ for cnum, channel in enumerate(channels):
+ print('PWM%s values: %s' %
+ (cnum+1,
+ ','.join([str(int(round(i))) for i in channel.modes])))
+
def get_value(text, default, args):
"""Get input from the user, or from the command line args."""
@@ -127,6 +124,7 @@ def get_value(text, default, args):
result = result.strip()
return result
+
def power(x):
#return x**5
return x**3
@@ -134,6 +132,7 @@ def power(x):
#return math.e**x
#return 2.0**x
+
def invpower(x):
#return math.pow(x, 1/5.0)
return math.pow(x, 1/3.0)
@@ -141,6 +140,7 @@ def invpower(x):
#return math.log(x, math.e)
#return math.log(x, 2.0)
+
if __name__ == "__main__":
import sys
main(sys.argv[1:])