/// <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);
        }