/* This file is part of LED Pattern Editor. * * Copyright (C) 2010 Philipp Zabel * * LED Pattern Editor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * LED Pattern Editor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LED Pattern Editor. If not, see . */ class LedPatternRX51 : LedPattern { public LedColor color1; public LedColor color2; public List engine1; public List engine2; public override void parse (string line) throws LedPatternError { string[] key_value = line.split ("="); if (key_value.length != 2) throw new LedPatternError.INVALID_PATTERN ("pattern line does not contain '=': " + line); name = key_value[0]; string[] p = key_value[1].split (";"); if (p.length != 6) throw new LedPatternError.INVALID_PATTERN ("%s does not contain 6 components: %d".printf (name, p.length)); if (p[4].length > 16*4 || p[5].length > 16*4) throw new LedPatternError.INVALID_PATTERN ("%s engine pattern too long!".printf (name)); if (p[4].length % 4 != 0 || p[5].length % 4 != 0) throw new LedPatternError.INVALID_PATTERN ("%s engine pattern not an even number of bytes!".printf (name)); priority = p[0].to_int (); screen_on = p[1].to_int (); timeout = p[2].to_int (); parse_led_map (p[3], out color1, out color2); engine1 = parse_pattern (p[4]); engine2 = parse_pattern (p[5]); if (engine1.first ().data.code != 0x9d80) { print ("engine1 pattern doesn't start with refresh mux command\n"); } if (engine2.first ().data.code != 0x9d80) { print ("engine2 pattern doesn't start with refresh mux command\n"); } on_changed (); } private void parse_led_map (string led_map, out LedColor color1, out LedColor color2) { color1 = LedColor.OFF; color2 = LedColor.OFF; if ("r" in led_map) color1 |= LedColor.R; if ("g" in led_map) color1 |= LedColor.G; if ("b" in led_map) color1 |= LedColor.B; if ("R" in led_map) color2 |= LedColor.R; if ("G" in led_map) color2 |= LedColor.G; if ("B" in led_map) color2 |= LedColor.B; } private List parse_pattern (string pattern) { var list = new List (); var length = pattern.length; if (length % 4 != 0) return list; char *p = ((char*) pattern) + length - 4; while (p >= (char*) pattern) { var command = new LedCommandRX51.with_code ((uint16) ((string) p).to_ulong (null, 16)); command.changed.connect (on_changed); list.prepend (command); p[0] = '\0'; p -= 4; } return list; } public override string dump () { return "%s=%d;%d;%d;%s;%s;%s".printf (name, priority, screen_on, timeout, dump_led_map (), dump_pattern (engine1), dump_pattern (engine2)); } private string dump_led_map () { string led_map = ""; if (LedColor.R in color1) led_map += "r"; if (LedColor.R in color2) led_map += "R"; if (LedColor.G in color1) led_map += "g"; if (LedColor.G in color2) led_map += "G"; if (LedColor.B in color1) led_map += "b"; if (LedColor.B in color2) led_map += "B"; return led_map; } private string dump_pattern (List list) { string result = ""; foreach (LedCommandRX51 command in list) { result += "%04x".printf (command.code); } return result; } public LedPatternRX51 copy () { var pattern = new LedPatternRX51 (); pattern.name = name.dup (); pattern.priority = priority; pattern.screen_on = screen_on; pattern.timeout = timeout; pattern.duration = duration; pattern.color1 = color1; pattern.color2 = color2; pattern.engine1 = deep_copy (pattern, engine1); pattern.engine2 = deep_copy (pattern, engine2); return pattern; } public List deep_copy (LedPatternRX51 pattern, List list) { var list2 = new List (); foreach (LedCommandRX51 command in list) { var command2 = command.copy (); command2.changed.connect (pattern.on_changed); list2.append (command2); } return list2; } public void replace_with (LedPatternRX51 pattern) { name = pattern.name; priority = pattern.priority; screen_on = pattern.screen_on; timeout = pattern.timeout; duration = pattern.duration; color1 = pattern.color1; color2 = pattern.color2; engine1 = deep_copy (this, pattern.engine1); engine2 = deep_copy (this, pattern.engine2); changed (); } public void on_changed () { bool unresolved = calculate_timing (); if (unresolved) { unresolved = calculate_timing (); } if (unresolved) { Hildon.Banner.show_information (null, null, "Timing unresolved"); } changed (); } private bool calculate_timing () { bool unresolved = false; // Calculate timing and level info for engine 1 double time = 0; int level = 0; foreach (LedCommandRX51 command in engine1) { command.time = time; if (command.type == CommandType.SET_PWM) { level = command.level; } else { command.level = level; level += command.steps; } if (command.type == CommandType.TRIGGER && (command.code & 0x0180) != 0) { command.duration = wait_for_trigger (time, engine2); if (command.duration == 0) unresolved = true; command.duration += 16 * LedCommandRX51.CYCLE_TIME_MS; } time += command.duration; if (level < 0) level = 0; if (level > 255) level = 255; } duration = time; // Calculate timing and level info for engine 2 time = 0; level = 0; foreach (LedCommandRX51 command in engine2) { command.time = time; if (command.type == CommandType.SET_PWM) { level = command.level; } else { command.level = level; level += command.steps; } if (command.type == CommandType.TRIGGER && (command.code & 0x0180) != 0) { command.duration = wait_for_trigger (time, engine1); if (command.duration == 0) unresolved = true; command.duration += 16 * LedCommandRX51.CYCLE_TIME_MS; } time += command.duration; if (level < 0) level = 0; if (level > 255) level = 255; } if (time > duration) duration = time; return unresolved; } double wait_for_trigger (double time, List engine) { double duration = 0; bool repeat = false; foreach (LedCommandRX51 command in engine) { duration = command.time + command.duration; if (command.type == CommandType.TRIGGER && (command.code & 0x0006) != 0 && command.time > time) { return command.time - time; } if (command.type == CommandType.GO_TO_START) { repeat = true; break; } } if (repeat) foreach (LedCommandRX51 command in engine) { if (command.type == CommandType.TRIGGER && (command.code & 0x0006) != 0 && (duration + command.time) > time) { return duration + command.time - time; } } return 0; } } class LedCommandRX51 : LedCommand { internal const double CYCLE_TIME_MS = 1000.0 / 32768.0; public uint16 code; public LedCommandRX51 () { } public LedCommandRX51.with_code (uint16 _code) { code = _code; duration = 16 * CYCLE_TIME_MS; if ((code & 0x8000) == 0) { if (code == 0x0000) { type = CommandType.GO_TO_START; } else if ((code & 0x3e00) != 0) { type = CommandType.RAMP_WAIT; steps = code & 0xff; step_time = code >> 9; if ((code & 0x4000) == 0) step_time = (code >> 9) * 16 * CYCLE_TIME_MS; else { step_time = ((code & 0x3e00) >> 9) * 512 * CYCLE_TIME_MS; } duration = step_time * (steps + 1); if ((code & 0x100) != 0) steps = -steps; } else { type = CommandType.SET_PWM; level = code & 0xff; } } else { if (code == 0x9d80) { type = CommandType.RESET_MUX; } else if ((code & ~0x1f8f) == 0xa000) { type = CommandType.BRANCH; // 0x1f80: (loop count - 1) << 7 // 0x000f: step number } else if ((code & ~0x1800) == 0xc000) { type = CommandType.END; // 0x1000: interrupt if ((code & 0x0800) != 0) // Reset steps = -255; } else if ((code & ~ 0x13fe) == 0xe000) { type = CommandType.TRIGGER; // 0x1000: wait ext // 0x0380: wait B G R // 0x0040: set ext // 0x000e: set B G R } } } public override void set_pwm (int _level) { code = 0x4000 | _level; base.set_pwm (_level); } public override void ramp_wait (double _step_time, int _steps) requires (_step_time >= (16 * CYCLE_TIME_MS)) { int step_time; if (_step_time < 32 * (16 * CYCLE_TIME_MS)) { step_time = (int) ((_step_time + 0.001) / (16 * CYCLE_TIME_MS)); code = (uint16) step_time << 9; _step_time = step_time * (16 * CYCLE_TIME_MS); } else if (_step_time < 32*(512 * CYCLE_TIME_MS)) { step_time = (int) ((_step_time + 0.001) / (512 * CYCLE_TIME_MS)); code = 0x4000 | (step_time << 9); _step_time = step_time * (512 * CYCLE_TIME_MS); } else { return; } if (_steps < 0) { code |= 0x100 | (-_steps); } else { code |= _steps; } base.ramp_wait (_step_time, _steps); } public override void go_to_start () { code = 0x0000; base.go_to_start (); } public override void end (bool reset) { code = 0xc000; if (reset) code |= 0x0800; base.end (reset); } public LedCommandRX51 copy () { var command = new LedCommandRX51 (); command.type = type; command.time = time; command.step_time = step_time; command.duration = duration; command.level = level; command.steps = steps; command.code = code; return command; } }