5b9554c34c188f22ab332a318d04907619f01a60
[led-pattern-ed] / src / led-pattern-rx44.vala
1 /* This file is part of LED Pattern Editor.
2  *
3  * Copyright (C) 2010 Philipp Zabel
4  *
5  * LED Pattern Editor is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * LED Pattern Editor is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with LED Pattern Editor. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 class LedPatternRX44 : LedPattern {
20         public List<LedCommandRX44> engine_r;
21         public List<LedCommandRX44> engine_g;
22         public List<LedCommandRX44> engine_b;
23
24         public override bool parse (string line) {
25                 string[] key_value = line.split ("=");
26
27                 if (key_value.length != 2) {
28                         print ("pattern line does not contain '=': %s\n", line);
29                         return false;
30                 }
31
32                 string[] p = key_value[1].split (";");
33                 if (p.length != 6) {
34                         print ("pattern does not contain 6 components: %d\n", p.length);
35                         return false;
36                 }
37
38                 if (p[3].length > 16*4 || p[4].length > 16*4 || p[5].length > 16*4) {
39                         print ("pattern too long!\n");
40                         return false;
41                 }
42
43                 name = key_value[0];
44                 priority = p[0].to_int ();
45                 screen_on = p[1].to_int ();
46                 timeout = p[2].to_int ();
47                 engine_r = parse_pattern (p[3]);
48                 engine_g = parse_pattern (p[4]);
49                 engine_b = parse_pattern (p[5]);
50
51                 on_changed ();
52                 return true;
53         }
54
55         private List<LedCommandRX44> parse_pattern (string pattern) {
56                 var list = new List<LedCommandRX44> ();
57                 var length = pattern.length;
58
59                 if (length % 4 != 0)
60                         return list;
61
62                 char *p = ((char*) pattern) + length - 4;
63                 while (p >= (char*) pattern) {
64                         var command = new LedCommandRX44.with_code ((uint16) ((string) p).to_ulong (null, 16));
65                         command.changed.connect (on_changed);
66                         list.prepend (command);
67                         p[0] = '\0';
68                         p -= 4;
69                 }
70
71                 return list;
72         }
73
74         public override string dump () {
75                 return "%s=%d;%d;%d;%s;%s;%s".printf (name, priority, screen_on, timeout,
76                                                       dump_pattern (engine_r), dump_pattern (engine_g), dump_pattern (engine_b));
77         }
78
79         private string dump_pattern (List<LedCommandRX44> list) {
80                 string result = "";
81                 foreach (LedCommandRX44 command in list) {
82                         result += "%04x".printf (command.code);
83                 }
84                 return result;
85         }
86
87         public LedPatternRX44 copy () {
88                 var pattern = new LedPatternRX44 ();
89
90                 pattern.name = name.dup ();
91                 pattern.priority = priority;
92                 pattern.screen_on = screen_on;
93                 pattern.timeout = timeout;
94
95                 pattern.duration = duration;
96
97                 pattern.engine_r = deep_copy (pattern, engine_r);
98                 pattern.engine_g = deep_copy (pattern, engine_g);
99                 pattern.engine_b = deep_copy (pattern, engine_b);
100
101                 return pattern;
102         }
103
104         public List<LedCommandRX44> deep_copy (LedPatternRX44 pattern, List<LedCommandRX44> list) {
105                 var list2 = new List<LedCommandRX44> ();
106
107                 foreach (LedCommandRX44 command in list) {
108                         var command2 = command.copy ();
109                         command2.changed.connect (pattern.on_changed);
110                         list2.append (command2);
111                 }
112
113                 return list2;
114         }
115
116         public void replace_with (LedPatternRX44 pattern) {
117                 name = pattern.name;
118                 priority = pattern.priority;
119                 screen_on = pattern.screen_on;
120                 timeout = pattern.timeout;
121
122                 duration = pattern.duration;
123
124                 engine_r = deep_copy (this, pattern.engine_r);
125                 engine_g = deep_copy (this, pattern.engine_g);
126                 engine_b = deep_copy (this, pattern.engine_b);
127
128                 changed ();
129         }
130
131         public void on_changed () {
132                 bool unresolved = calculate_timing ();
133                 if (unresolved)
134                         unresolved = calculate_timing ();
135                 if (unresolved)
136                         Hildon.Banner.show_information (null, null, "Timing unresolved");
137                 changed ();
138         }
139
140         private bool calculate_timing () {
141                 bool unresolved = false;
142                 // Calculate timing and level info for red engine
143                 double time = 0;
144                 int level = 0;
145                 foreach (LedCommandRX44 command in engine_r) {
146                         command.time = time;
147                         if (command.type == CommandType.SET_PWM) {
148                                 level = command.level;
149                         } else {
150                                 command.level = level;
151                                 level += command.steps;
152                         }
153                         if (command.type == CommandType.TRIGGER &&
154                             (command.code & 0x0180) != 0) {
155                                 command.duration = wait_for_trigger (time, engine_g);
156                                 if (command.duration == 0)
157                                         unresolved = true;
158                                 command.duration += 16 * LedCommandRX44.CYCLE_TIME_MS;
159                         }
160                         time += command.duration;
161                         if (level < 0)
162                                 level = 0;
163                         if (level > 255)
164                                 level = 255;
165                 }
166                 duration = time;
167                 // Calculate timing and level info for green engine
168                 time = 0;
169                 level = 0;
170                 foreach (LedCommandRX44 command in engine_g) {
171                         command.time = time;
172                         if (command.type == CommandType.SET_PWM) {
173                                 level = command.level;
174                         } else {
175                                 command.level = level;
176                                 level += command.steps;
177                         }
178                         if (command.type == CommandType.TRIGGER &&
179                             (command.code & 0x0180) != 0) {
180                                 command.duration = wait_for_trigger (time, engine_r);
181                                 if (command.duration == 0)
182                                         unresolved = true;
183                                 command.duration += 16 * LedCommandRX44.CYCLE_TIME_MS;
184                         }
185                         time += command.duration;
186                         if (level < 0)
187                                 level = 0;
188                         if (level > 255)
189                                 level = 255;
190                 }
191                 if (time > duration)
192                         duration = time;
193                 return unresolved;
194         }
195
196         double wait_for_trigger (double time, List<LedCommandRX44> engine) {
197                 double duration = 0;
198                 bool repeat = false;
199                 foreach (LedCommandRX44 command in engine) {
200                         duration = command.time + command.duration;
201                         if (command.type == CommandType.TRIGGER &&
202                             (command.code & 0x0006) != 0 && command.time > time) {
203                                 return command.time - time;
204                         }
205                         if (command.type == CommandType.GO_TO_START) {
206                                 repeat = true;
207                                 break;
208                         }
209                 }
210                 if (repeat) foreach (LedCommandRX44 command in engine) {
211                         if (command.type == CommandType.TRIGGER &&
212                             (command.code & 0x0006) != 0 && (duration + command.time) > time) {
213                                 return duration + command.time - time;
214                         }
215                 }
216                 return 0;
217         }
218 }
219
220 class LedCommandRX44 : LedCommand {
221         internal const double CYCLE_TIME_MS = 1000.0 / 32768.0;
222
223         public uint16 code;
224
225         public LedCommandRX44 () {
226         }
227
228         public LedCommandRX44.with_code (uint16 _code) {
229                 code = _code;
230                 duration = 16 * CYCLE_TIME_MS;
231                 if ((code & 0x8000) == 0) {
232                         if (code == 0x0000) {
233                                 type = CommandType.GO_TO_START;
234                         } else if ((code & 0x3e00) != 0) {
235                                 type = CommandType.RAMP_WAIT;
236                                 steps = code & 0x7f;
237                                 step_time = code >> 8;
238                                 if ((code & 0x4000) == 0)
239                                         step_time = (code >> 9) * 16 * CYCLE_TIME_MS;
240                                 else {
241                                         step_time = ((code & 0x3e00) >> 9) * 512 * CYCLE_TIME_MS;
242                                 }
243                                 duration = step_time * (steps + 1);
244                                 if ((code & 0x80) != 0)
245                                         steps = -steps;
246                         } else {
247                                 type = CommandType.SET_PWM;
248                                 level = code & 0xff;
249                         }
250                 } else {
251                         if ((code & ~0x1f8f) == 0xa000) {
252                                 type = CommandType.BRANCH;
253                                 // 0x1f80: (loop count - 1) << 7
254                                 // 0x000f: step number
255                         } else if ((code & ~0x1800) == 0xc000) {
256                                 type = CommandType.END;
257                                 // 0x1000: interrupt
258                                 if ((code & 0x0800) != 0) // Reset
259                                         steps = -255;
260                         } else if ((code & ~ 0x13fe) == 0xe000) {
261                                 type = CommandType.TRIGGER;
262                                 // 0x1000: wait ext
263                                 // 0x0380: wait B G R
264                                 // 0x0040: set ext
265                                 // 0x000e: set B G R
266                         }
267                 }
268         }
269
270         public override void set_pwm (int _level) {
271                 code = 0x4000 | _level;
272                 base.set_pwm (_level);
273         }
274
275         public override void ramp_wait (double _step_time, int _steps) requires (_step_time >= (16 * CYCLE_TIME_MS)) {
276                 int step_time;
277                 if (_step_time < 32 * (16 * CYCLE_TIME_MS)) {
278                         step_time = (int) ((_step_time + 0.001) / (16 * CYCLE_TIME_MS));
279                         code = (uint16) step_time << 8;
280                         _step_time = step_time * (16 * CYCLE_TIME_MS);
281                 } else if (_step_time < 32*(512 * CYCLE_TIME_MS)) {
282                         step_time = (int) ((_step_time + 0.001) / (512 * CYCLE_TIME_MS));
283                         code = 0x4000 | (step_time << 8);
284                         _step_time = step_time * (512 * CYCLE_TIME_MS);
285                 } else {
286                         return;
287                 }
288                 if (_steps < 0) {
289                         code |= 0x80 | (-_steps);
290                 } else {
291                         code |= _steps;
292                 }
293                 base.ramp_wait (_step_time, _steps);
294         }
295
296         public override void go_to_start () {
297                 code = 0x0000;
298                 base.go_to_start ();
299         }
300
301         public override void end (bool reset) {
302                 code = 0xc000;
303                 if (reset)
304                         code |= 0x0800;
305                 base.end (reset);
306         }
307
308         public LedCommandRX44 copy () {
309                 var command = new LedCommandRX44 ();
310
311                 command.type = type;
312                 command.time = time;
313                 command.step_time = step_time;
314                 command.duration = duration;
315                 command.level = level;
316                 command.steps = steps;
317
318                 command.code = code;
319
320                 return command;
321         }
322 }