/// <summary> /// Packs arrays of bool-s into an array of bytes. /// Mechatrolink-II packet structure is based on bytes, therefore this is convenient. /// </summary> /// <param name="bits">Bool stand for already decoded bits, not just transitions</param> /// <returns></returns> public static SortedList <int, byte> PackIntoBytes(SortedList <int, bool> bits) { int len = bits.Count; if (len % 8 != 0) { ErrorListener.Add(new ArgumentException(string.Format( "Warning: bit count at {0} is not a multiple 8. It will be truncated.", bits.Keys[0]))); len -= len % 8; } int byteLen = len / 8; SortedList <int, byte> result = new SortedList <int, byte>(byteLen); for (int i = 0; i < byteLen; i++) { byte temp = new byte(); for (int j = 0; j < 8; j++) { temp |= (byte)((bits.Values[i * 8 + j] ? 1 : 0) << j); } result.Add(bits.Keys[i * 8], temp); } return(result); }
/// <summary> /// Decodes Manchester-encoded data (edges). /// </summary> /// <param name="data">Array of edges</param> /// <param name="freq">Communication frequency (equal to speed, 4Mbps = 4MHz for Mechatrolink-I and 10MHz for M-II)</param> /// <param name="error">Allowed tolerance for edge position (fraction of a period, usually 0.25 = 25%)</param> /// <returns>Array of bits</returns> public static SortedList <int, bool> Decode(SortedList <int, bool> data, int freq, double error) { bool req = false; //For interpacket search and then preamble length matching, first look for request preamble (i.e. set req true after interpacket search!) data = new SortedList <int, bool>(data); int p = (int)Math.Round(1E8 / freq); int ph = (int)Math.Round(p * (1 + error)); int pl = (int)Math.Round(p * (1 - error)); SortedList <int, int> pulses = new SortedList <int, int>(data.Count - 1); for (int i = 0; i < data.Count - 1; i++) //Transform edges into pulses { //Skip until first long pulse (interpacket) if (!req && ((data.Keys[i + 1] - data.Keys[i]) < MaxRequestResponseDelay)) { continue; } pulses.Add(data.Keys[i], data.Keys[i + 1] - data.Keys[i]); req = true; } //Look for the preambles: starts with low-to-high, ends with high-to-low (?) SortedList <int, int> preambles = new SortedList <int, int>(pulses.Count / 32); //Assuming the contents are at least as long as the preamble (16*2) int current = 0; //Suitable edges found during preamble search try { int state = 0; for (int i = 0; i < pulses.Count; i++) { switch (state) { case 0: //Look for request preamble if (PreambleHelper(data, pulses, preambles, pl, ph, RequestPreambleLength, ref current, i)) { state++; } break; case 1: //Look for request-response delay if (pulses.Values[i] > MinRequestResponseDelay) { if (pulses.Values[i] < MaxRequestResponseDelay) { state++; } else { state = 0; } } break; case 2: //Look for response preamble if (PreambleHelper(data, pulses, preambles, pl, ph, ResponsePreambleLength, ref current, i)) { state++; } break; case 3: //Look for response-request delay if (pulses.Values[i] > MaxRequestResponseDelay) { state = 0; } break; default: ErrorListener.Add(new Exception("Illegal state value for preamble detection state machine.")); break; } } } catch (ArgumentOutOfRangeException) { ErrorListener.Add(new ArgumentException( "Warning: the bitstream contains an incomplete packet. The latter will be discarded.")); } //DataReporter.ReportProgress("Preambles parsed..."); SortedList <int, bool> bits = new SortedList <int, bool>(data.Capacity); int lastPreamble = preambles.Count - 1; //Btw, int current is now repurposed current = 0; for (int i = 0; i < lastPreamble; i++) { current = PartDecodeManchester(i, current, pl, ph, preambles, data, bits); } //To avoid processing overhead in previous cycle, process the last packet separately if (!IgnoreLastPreamble) { PartDecodeManchester(lastPreamble, current, pl, ph, preambles, data, bits); } bits.TrimExcess(); return(bits); }