aboutsummaryrefslogtreecommitdiff
path: root/bin/level_calc.py
blob: 63926baa172befbaf6b047c85deedef2ae133a50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env python

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 = [
            (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?'),
            #(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)

    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.'

def get_value(text, default, args):
    """Get input from the user, or from the command line args."""
    if args:
        result = args[0]
        del args[0]
    else:
        global interactive
        interactive = True
        print text, '(%s)' % (default),
        result = raw_input()
    result = result.strip()
    return result

def power(x):
    #return x**5
    return x**3
    #return x**2
    #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)
    #return math.pow(x, 1/2.0)
    #return math.log(x, math.e)
    #return math.log(x, 2.0)

if __name__ == "__main__":
    import sys
    main(sys.argv[1:])