1518c4b788f40fa4c15bbd61a104d6e637edb369
[led-pattern-ed] / src / led-pattern-rx51.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 LedPatternRX51 : LedPattern {
20         public LedColor color1;
21         public LedColor color2;
22         public List<LedCommandRX51> engine1;
23         public List<LedCommandRX51> engine2;
24
25         public override bool parse (string line) {
26                 string[] key_value = line.split ("=");
27
28                 if (key_value.length != 2) {
29                         print ("pattern line does not contain '=': %s\n", line);
30                         return false;
31                 }
32
33                 string[] p = key_value[1].split (";");
34                 if (p.length != 6) {
35                         print ("pattern does not contain 6 components: %d\n", p.length);
36                         return false;
37                 }
38
39                 if (p[4].length > 16*4 || p[5].length > 16*4) {
40                         print ("pattern too long!\n");
41                         return false;
42                 }
43
44                 name = key_value[0];
45                 priority = p[0].to_int ();
46                 screen_on = p[1].to_int ();
47                 timeout = p[2].to_int ();
48                 parse_led_map (p[3], out color1, out color2);
49                 engine1 = parse_pattern (p[4]);
50                 engine2 = parse_pattern (p[5]);
51
52                 if (engine1.first ().data.code != 0x9d80) {
53                         print ("engine1 pattern doesn't start with refresh mux command\n");
54                 }
55                 if (engine2.first ().data.code != 0x9d80) {
56                         print ("engine2 pattern doesn't start with refresh mux command\n");
57                 }
58
59                 on_changed ();
60                 return true;
61         }
62
63         private void parse_led_map (string led_map, out LedColor color1, out LedColor color2) {
64                 color1 = LedColor.OFF;
65                 color2 = LedColor.OFF;
66                 if ("r" in led_map)
67                         color1 |= LedColor.R;
68                 if ("g" in led_map)
69                         color1 |= LedColor.G;
70                 if ("b" in led_map)
71                         color1 |= LedColor.B;
72                 if ("R" in led_map)
73                         color2 |= LedColor.R;
74                 if ("G" in led_map)
75                         color2 |= LedColor.G;
76                 if ("B" in led_map)
77                         color2 |= LedColor.B;
78         }
79
80         private List<LedCommandRX51> parse_pattern (string pattern) {
81                 var list = new List<LedCommandRX51> ();
82                 var length = pattern.length;
83
84                 if (length % 4 != 0)
85                         return list;
86
87                 char *p = ((char*) pattern) + length - 4;
88                 while (p >= (char*) pattern) {
89                         var command = new LedCommandRX51.with_code ((uint16) ((string) p).to_ulong (null, 16));
90                         command.changed.connect (on_changed);
91                         list.prepend (command);
92                         p[0] = '\0';
93                         p -= 4;
94                 }
95
96                 return list;
97         }
98
99         public override string dump () {
100                 return "%s=%d;%d;%d;%s;%s;%s".printf (name, priority, screen_on, timeout, dump_led_map (),
101                                                       dump_pattern (engine1), dump_pattern (engine2));
102         }
103
104         private string dump_led_map () {
105                 string led_map = "";
106                 if (LedColor.R in color1)
107                         led_map += "r";
108                 if (LedColor.R in color2)
109                         led_map += "R";
110                 if (LedColor.G in color1)
111                         led_map += "g";
112                 if (LedColor.G in color2)
113                         led_map += "G";
114                 if (LedColor.B in color1)
115                         led_map += "b";
116                 if (LedColor.B in color2)
117                         led_map += "B";
118                 return led_map;
119         }
120
121         private string dump_pattern (List<LedCommandRX51> list) {
122                 string result = "";
123                 foreach (LedCommandRX51 command in list) {
124                         result += "%04x".printf (command.code);
125                 }
126                 return result;
127         }
128
129         public LedPatternRX51 copy () {
130                 var pattern = new LedPatternRX51 ();
131
132                 pattern.name = name.dup ();
133                 pattern.priority = priority;
134                 pattern.screen_on = screen_on;
135                 pattern.timeout = timeout;
136
137                 pattern.duration = duration;
138
139                 pattern.color1 = color1;
140                 pattern.color2 = color2;
141                 pattern.engine1 = deep_copy (pattern, engine1);
142                 pattern.engine2 = deep_copy (pattern, engine2);
143
144                 return pattern;
145         }
146
147         public List<LedCommandRX51> deep_copy (LedPatternRX51 pattern, List<LedCommandRX51> list) {
148                 var list2 = new List<LedCommandRX51> ();
149
150                 foreach (LedCommandRX51 command in list) {
151                         var command2 = command.copy ();
152                         command2.changed.connect (pattern.on_changed);
153                         list2.append (command2);
154                 }
155
156                 return list2;
157         }
158
159         public void replace_with (LedPatternRX51 pattern) {
160                 name = pattern.name;
161                 priority = pattern.priority;
162                 screen_on = pattern.screen_on;
163                 timeout = pattern.timeout;
164
165                 duration = pattern.duration;
166
167                 color1 = pattern.color1;
168                 color2 = pattern.color2;
169                 engine1 = deep_copy (this, pattern.engine1);
170                 engine2 = deep_copy (this, pattern.engine2);
171
172                 changed ();
173         }
174
175         public void on_changed () {
176                 bool unresolved = calculate_timing ();
177                 if (unresolved)
178                         unresolved = calculate_timing ();
179                 if (unresolved)
180                         Hildon.Banner.show_information (null, null, "Timing unresolved");
181                 changed ();
182         }
183
184         private bool calculate_timing () {
185                 bool unresolved = false;
186                 // Calculate timing and level info for engine 1
187                 double time = 0;
188                 int level = 0;
189                 foreach (LedCommandRX51 command in engine1) {
190                         command.time = time;
191                         if (command.type == CommandType.SET_PWM) {
192                                 level = command.level;
193                         } else {
194                                 command.level = level;
195                                 level += command.steps;
196                         }
197                         if (command.type == CommandType.TRIGGER &&
198                             (command.code & 0x0180) != 0) {
199                                 command.duration = wait_for_trigger (time, engine2);
200                                 if (command.duration == 0)
201                                         unresolved = true;
202                                 command.duration += 16 * LedCommandRX51.CYCLE_TIME_MS;
203                         }
204                         time += command.duration;
205                         if (level < 0)
206                                 level = 0;
207                         if (level > 255)
208                                 level = 255;
209                 }
210                 duration = time;
211                 // Calculate timing and level info for engine 2
212                 time = 0;
213                 level = 0;
214                 foreach (LedCommandRX51 command in engine2) {
215                         command.time = time;
216                         if (command.type == CommandType.SET_PWM) {
217                                 level = command.level;
218                         } else {
219                                 command.level = level;
220                                 level += command.steps;
221                         }
222                         if (command.type == CommandType.TRIGGER &&
223                             (command.code & 0x0180) != 0) {
224                                 command.duration = wait_for_trigger (time, engine1);
225                                 if (command.duration == 0)
226                                         unresolved = true;
227                                 command.duration += 16 * LedCommandRX51.CYCLE_TIME_MS;
228                         }
229                         time += command.duration;
230                         if (level < 0)
231                                 level = 0;
232                         if (level > 255)
233                                 level = 255;
234                 }
235                 if (time > duration)
236                         duration = time;
237                 return unresolved;
238         }
239
240         double wait_for_trigger (double time, List<LedCommandRX51> engine) {
241                 double duration = 0;
242                 bool repeat = false;
243                 foreach (LedCommandRX51 command in engine) {
244                         duration = command.time + command.duration;
245                         if (command.type == CommandType.TRIGGER &&
246                             (command.code & 0x0006) != 0 && command.time > time) {
247                                 return command.time - time;
248                         }
249                         if (command.type == CommandType.GO_TO_START) {
250                                 repeat = true;
251                                 break;
252                         }
253                 }
254                 if (repeat) foreach (LedCommandRX51 command in engine) {
255                         if (command.type == CommandType.TRIGGER &&
256                             (command.code & 0x0006) != 0 && (duration + command.time) > time) {
257                                 return duration + command.time - time;
258                         }
259                 }
260                 return 0;
261         }
262 }
263
264 class LedCommandRX51 : LedCommand {
265         internal const double CYCLE_TIME_MS = 1000.0 / 32768.0;
266
267         public uint16 code;
268
269         public LedCommandRX51 () {
270         }
271
272         public LedCommandRX51.with_code (uint16 _code) {
273                 code = _code;
274                 duration = 16 * CYCLE_TIME_MS;
275                 if ((code & 0x8000) == 0) {
276                         if (code == 0x0000) {
277                                 type = CommandType.GO_TO_START;
278                         } else if ((code & 0x3e00) != 0) {
279                                 type = CommandType.RAMP_WAIT;
280                                 steps = code & 0xff;
281                                 step_time = code >> 9;
282                                 if ((code & 0x4000) == 0)
283                                         step_time = (code >> 9) * 16 * CYCLE_TIME_MS;
284                                 else {
285                                         step_time = ((code & 0x3e00) >> 9) * 512 * CYCLE_TIME_MS;
286                                 }
287                                 duration = step_time * (steps + 1);
288                                 if ((code & 0x100) != 0)
289                                         steps = -steps;
290                         } else {
291                                 type = CommandType.SET_PWM;
292                                 level = code & 0xff;
293                         }
294                 } else {
295                         if (code == 0x9d80) {
296                                 type = CommandType.RESET_MUX;
297                         } else if ((code & ~0x1f8f) == 0xa000) {
298                                 type = CommandType.BRANCH;
299                                 // 0x1f80: (loop count - 1) << 7
300                                 // 0x000f: step number
301                         } else if ((code & ~0x1800) == 0xc000) {
302                                 type = CommandType.END;
303                                 // 0x1000: interrupt
304                                 if ((code & 0x0800) != 0) // Reset
305                                         steps = -255;
306                         } else if ((code & ~ 0x13fe) == 0xe000) {
307                                 type = CommandType.TRIGGER;
308                                 // 0x1000: wait ext
309                                 // 0x0380: wait B G R
310                                 // 0x0040: set ext
311                                 // 0x000e: set B G R
312                         }
313                 }
314         }
315
316         public override void set_pwm (int _level) {
317                 code = 0x4000 | _level;
318                 base.set_pwm (_level);
319         }
320
321         public override void ramp_wait (double _step_time, int _steps) requires (_step_time >= (16 * CYCLE_TIME_MS)) {
322                 int step_time;
323                 if (_step_time < 32 * (16 * CYCLE_TIME_MS)) {
324                         step_time = (int) ((_step_time + 0.001) / (16 * CYCLE_TIME_MS));
325                         code = (uint16) step_time << 9;
326                         _step_time = step_time * (16 * CYCLE_TIME_MS);
327                 } else if (_step_time < 32*(512 * CYCLE_TIME_MS)) {
328                         step_time = (int) ((_step_time + 0.001) / (512 * CYCLE_TIME_MS));
329                         code = 0x4000 | (step_time << 9);
330                         _step_time = step_time * (512 * CYCLE_TIME_MS);
331                 } else {
332                         return;
333                 }
334                 if (_steps < 0) {
335                         code |= 0x100 | (-_steps);
336                 } else {
337                         code |= _steps;
338                 }
339                 base.ramp_wait (_step_time, _steps);
340         }
341
342         public override void go_to_start () {
343                 code = 0x0000;
344                 base.go_to_start ();
345         }
346
347         public override void end (bool reset) {
348                 code = 0xc000;
349                 if (reset)
350                         code |= 0x0800;
351                 base.end (reset);
352         }
353
354         public LedCommandRX51 copy () {
355                 var command = new LedCommandRX51 ();
356
357                 command.type = type;
358                 command.time = time;
359                 command.step_time = step_time;
360                 command.duration = duration;
361                 command.level = level;
362                 command.steps = steps;
363
364                 command.code = code;
365
366                 return command;
367         }
368 }