~toykeeper/flashlight-firmware/trunk

« back to all changes in this revision

Viewing changes to ToyKeeper/Ferrero_Rocher/level_calc.py

  • Committer: Selene Scriven
  • Date: 2015-03-17 08:56:50 UTC
  • mto: This revision was merged to the branch mainline in revision 124.
  • Revision ID: ubuntu@toykeeper.net-20150317085650-s89wr9h28n2co7z1
Added TheStar firmwares from _the_

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
2
 
3
 
from __future__ import print_function
4
 
 
5
3
import math
6
4
 
7
 
interactive = False
8
 
# supported shapes: ninth, seventh, fifth, cube, square, log
9
 
#ramp_shape = 'cube'
10
 
 
11
 
max_pwm = 255
12
 
 
13
 
 
14
5
def main(args):
15
6
    """Calculates PWM levels for visually-linear steps.
16
7
    """
17
 
    # Get parameters from the user
18
 
    questions_main = [
19
 
            (str, 'ramp_shape', 'cube', 'Ramp shape? [cube, square, fifth, seventh, ninth, log, N.NN]'),
20
 
            (int, 'num_channels', 1, 'How many power channels?'),
21
 
            (int, 'num_levels', 4, 'How many total levels do you want?'),
22
 
            ]
23
 
    questions_per_channel = [
24
 
            (str, 'type', '7135', 'Type of channel - 7135 or FET:'),
25
 
            (int, 'pwm_min', 6, 'Lowest visible PWM level:'),
26
 
            (float, 'lm_min', 0.25, 'How bright is the lowest level, in lumens?'),
27
 
            #(int, 'pwm_max', max_pwm, 'Highest PWM level:'),
28
 
            (float, 'lm_max', 1000, 'How bright is the highest level, in lumens?'),
29
 
            ]
30
 
 
31
 
    def ask(questions, ans):
32
 
        for typ, name, default, text in questions:
33
 
            value = get_value(text, default, args)
34
 
            if not value:
35
 
                value = default
36
 
            else:
37
 
                value = typ(value)
38
 
            setattr(ans, name, value)
39
 
 
40
 
    answers = Empty()
41
 
    ask(questions_main, answers)
42
 
 
43
 
    global ramp_shape 
44
 
    ramp_shape = answers.ramp_shape
45
 
 
46
 
    channels = []
47
 
    if not args:
48
 
        print('Describe the channels in order of lowest to highest power.')
49
 
    for chan_num in range(answers.num_channels):
50
 
        if not args:
51
 
            print('===== Channel %s =====' % (chan_num+1))
52
 
        chan = Empty()
53
 
        chan.pwm_max = max_pwm
54
 
        ask(questions_per_channel, chan)
55
 
        chan.type = chan.type.upper()
56
 
        if chan.type not in ('7135', 'FET'):
57
 
            raise ValueError('Invalid channel type: %s' % (chan.type,))
58
 
        channels.append(chan)
59
 
 
60
 
    # calculate total output of all previous channels
61
 
    for i, channel in enumerate(channels):
62
 
        channel.prev_lm = 0.0
63
 
        for j in range(i):
64
 
            if channels[j].type == '7135':
65
 
                channel.prev_lm += channels[j].lm_max
66
 
 
67
 
    # figure out the desired PWM values
68
 
    multi_pwm(answers, channels)
69
 
 
70
 
    if interactive: # Wait on exit, in case user invoked us by clicking an icon
71
 
        print('Press Enter to exit:')
72
 
        input_text()
73
 
 
74
 
 
75
 
class Empty:
76
 
    pass
77
 
 
78
 
 
79
 
def multi_pwm(answers, channels):
80
 
    lm_min = channels[0].lm_min
81
 
    # figure out the highest mode
82
 
    lm_max = max([(c.lm_max+c.prev_lm) for c in channels])
83
 
    if channels[-1].type == 'FET':
84
 
        if channels[-1].lm_max > channels[-1].prev_lm:
85
 
            # assume the highest output is with only the FET enabled
86
 
            lm_max = channels[-1].lm_max
87
 
        else:
88
 
            # this would be a stupid driver design
89
 
            raise ValueError("FET channel isn't the most powerful?")
90
 
 
91
 
    visual_min = invpower(lm_min)
92
 
    visual_max = invpower(lm_max)
93
 
    step_size = (visual_max - visual_min) / (answers.num_levels-1)
94
 
 
95
 
    # Determine ideal lumen levels
96
 
    goals = []
97
 
    goal_vis = visual_min
98
 
    for i in range(answers.num_levels):
99
 
        goal_lm = power(goal_vis)
100
 
        goals.append((goal_vis, goal_lm))
101
 
        goal_vis += step_size
102
 
 
103
 
    # Calculate each channel's output for each level
104
 
    for cnum, channel in enumerate(channels):
105
 
        channel.modes = []
106
 
        for i in range(answers.num_levels):
107
 
            goal_vis, goal_lm = goals[i]
108
 
            # This channel already is maxed out
109
 
            if goal_lm >= (channel.lm_max + channel.prev_lm):
110
 
                # This shouldn't happen, the FET is assumed to be the highest channel
111
 
                if channel.type == 'FET':
112
 
                    # this would be a stupid driver design
113
 
                    raise ValueError("FET channel isn't the most powerful?")
114
 
 
115
 
                # Handle FET turbo specially
116
 
                if (i == (answers.num_levels - 1)) \
117
 
                        and (cnum < (len(channels)-1)) \
118
 
                        and (channels[-1].type == 'FET'):
119
 
                    channel.modes.append(0.0)
120
 
                # Normal non-turbo mode or non-FET turbo
121
 
                else:
122
 
                    channel.modes.append(channel.pwm_max)
123
 
            # This channel's active ramp-up range
124
 
            #elif goal_lm > (channel.prev_lm + channel.lm_min):
125
 
            elif goal_lm > channel.prev_lm:
126
 
                # assume 7135 channels all add together
127
 
                if channel.type == '7135':
128
 
                    diff = channel.lm_max - channel.lm_min
129
 
                # assume FET channel gets higher output on its own
130
 
                elif channel.type == 'FET':
131
 
                    diff = channel.lm_max - channel.prev_lm - channel.lm_min
132
 
 
133
 
                needed = goal_lm - channel.prev_lm - channel.lm_min
134
 
 
135
 
                ratio = needed / diff * (channel.pwm_max-channel.pwm_min)
136
 
                pwm = max(0, ratio + channel.pwm_min)
137
 
                channel.modes.append(pwm)
138
 
            # This channel isn't active yet, output too low
139
 
            else:
140
 
                channel.modes.append(0)
141
 
 
142
 
    # Show individual levels in detail
143
 
    for i in range(answers.num_levels):
144
 
        goal_vis, goal_lm = goals[i]
145
 
        pwms = []
146
 
        for channel in channels:
147
 
            pwms.append('%.2f/%i' % (channel.modes[i], channel.pwm_max))
148
 
        print('%i: visually %.2f (%.2f lm): %s' % 
149
 
              (i+1, goal_vis, goal_lm, ', '.join(pwms)))
150
 
 
151
 
    # Show values we can paste into source code
152
 
    for cnum, channel in enumerate(channels):
153
 
        print('PWM%s values: %s' % 
154
 
                (cnum+1,
155
 
                 ','.join([str(int(round(i))) for i in channel.modes])))
156
 
 
157
 
    # Show highest level for each channel before next channel starts
158
 
    for cnum, channel in enumerate(channels[:-1]):
159
 
        prev = 0
160
 
        i = 1
161
 
        while (i < answers.num_levels) \
162
 
                and (channel.modes[i] >= channel.modes[i-1]) \
163
 
                and (channels[cnum+1].modes[i] == 0):
164
 
            i += 1
165
 
        print('Ch%i max: %i (%.2f/%s)' % (cnum, i, channel.modes[i-1], max_pwm))
166
 
 
167
 
 
168
 
def get_value(text, default, args):
169
 
    """Get input from the user, or from the command line args."""
170
 
    if args:
171
 
        result = args[0]
172
 
        del args[0]
173
 
    else:
174
 
        global interactive
175
 
        interactive = True
176
 
        print(text + ' (%s) ' % (default), end='')
177
 
        result = input_text()
178
 
    result = result.strip()
179
 
    return result
180
 
 
181
 
 
182
 
shapes = dict(
183
 
        ninth  = (lambda x: x**9,      lambda x: math.pow(x, 1/9.0)),
184
 
        seventh= (lambda x: x**7,      lambda x: math.pow(x, 1/7.0)),
185
 
        fifth  = (lambda x: x**5,      lambda x: math.pow(x, 1/5.0)),
186
 
        cube   = (lambda x: x**3,      lambda x: math.pow(x, 1/3.0)),
187
 
        square = (lambda x: x**2,      lambda x: math.pow(x, 1/2.0)),
188
 
        log    = (lambda x: math.e**x, lambda x: math.log(x, math.e)),
189
 
        # makes no difference; all logs have the same curve
190
 
        #log_2  = (lambda x: 2.0**x,    lambda x: math.log(x, 2.0)),
191
 
        )
192
 
 
193
 
def power(x):
194
 
    try:
195
 
        factor = float(ramp_shape)
196
 
        return math.pow(x, factor)
197
 
    except ValueError:
198
 
        return shapes[ramp_shape][0](x)
199
 
 
200
 
 
201
 
def invpower(x):
202
 
    try:
203
 
        factor = float(ramp_shape)
204
 
        return math.pow(x, 1.0 / factor)
205
 
    except ValueError:
206
 
        return shapes[ramp_shape][1](x)
207
 
 
208
 
 
209
 
def input_text():
210
 
    try:
211
 
        value = raw_input()  # python2
212
 
    except NameError:
213
 
        value = input()  # python3
214
 
    return value
215
 
 
 
8
    # change these values for each device:
 
9
    pwm_min = 0     # lowest visible PWM level, for moon mode
 
10
    lm_min = 10.0   # how bright is moon mode, in lumens?
 
11
    pwm_max = 255   # highest PWM level
 
12
    lm_max = 1300   # how bright is the highest level, in lumens?
 
13
    num_levels = 4  # how many total levels do you want?
 
14
    # The rest should work fine without changes
 
15
    visual_min = math.pow(lm_min, 1.0/3)
 
16
    visual_max = math.pow(lm_max, 1.0/3)
 
17
    step_size = (visual_max - visual_min) / (num_levels-1)
 
18
    modes = []
 
19
    goal = visual_min
 
20
    for i in range(num_levels):
 
21
        pwm_float = (((goal**3) / lm_max) * (256-pwm_min)) + pwm_min - 1
 
22
        pwm = int(round(pwm_float))
 
23
        pwm = max(min(pwm,pwm_max),pwm_min)
 
24
        modes.append(pwm)
 
25
        print '%i: visually %.2f (%.2f lm): %.2f/255' % (i+1, goal, goal ** 3, pwm_float)
 
26
        goal += step_size
 
27
 
 
28
    print ','.join([str(i) for i in modes])
216
29
 
217
30
if __name__ == "__main__":
218
31
    import sys