/// <summary> /// Remove zero bits stuffed in for transparency of the protocol /// </summary> /// <param name="data">Bool stands for already decoded bits, not just transitions</param> /// <returns>Array of bits</returns> public static SortedList <int, bool> DestuffZeroes(SortedList <int, bool> data) { int ones = 0; data = new SortedList <int, bool>(data); for (int i = 0; i < data.Count; i++) { if (data.Values[i]) { ones++; } else { ones = 0; } try { if ((ones == 5) && !data.Values[i + 1]) { data.RemoveAt(i + 1); ones = 0; } } catch (ArgumentOutOfRangeException) { ErrorListener.Add(new ArgumentException("Warning: incomplete bit sequence detected!")); break; } } return(data); }
public virtual string GetReport(params byte[] bytes) { //Get only the bytes that belong to the field bytes = bytes.Where((byte i, int j) => { return(Bytes.Contains(j + 2)); }).ToArray(); if (LittleEndian) { bytes = bytes.Reverse().ToArray(); } StringBuilder res = new StringBuilder(string.Format(@"----------------- Field: {0} ---------------- ", Name)); res.Append(string.Join(" ", bytes.Select(x => x.ToString("X2")))); res.Append(" = "); //Add binary representation res.AppendLine(string.Join(" ", bytes.Select(x => Convert.ToString(x, 2).PadLeft(8, '0')))); if (CustomParser != null) { try { res.AppendLine(CustomParser(bytes)); } catch (Exception e) { ErrorListener.Add(new Exception( string.Format("Error in a custom parser for field {0}", Name), e)); } } //res.AppendLine("------------------"); return(res.ToString()); }
public static LogicAnalyzerData CreateFromKingst(string filePath, int columnIndex, int limit = int.MaxValue) { if (limit < int.MaxValue) { limit++; //Take header into account } string[] fileContents; SortedList <int, bool> result; int lineLimit = LoadDataHelper(filePath, limit, out fileContents, out result); for (int i = 1; i < lineLimit; i++) { string[] split = fileContents[i].Split(','); split[0] = split[0].Replace(".", ""); //Switch to fixed-point arithmetic split[1] = split[columnIndex].TrimStart(); try { int temp = int.Parse(split[0]); if (UseTimeLimit) { if (temp > limit) { break; } } result.Add(int.Parse(split[0]), int.Parse(split[1]) > 0); } catch (FormatException e) { ErrorListener.Add(new Exception("Can't parse Kingst data line: " + fileContents[i], e)); } } return(new LogicAnalyzerData(result)); }
public static LogicAnalyzerData CreateFromLogicSnifferSTM(string filePath, int limit = int.MaxValue) { string[] fileContents; SortedList <int, bool> result; int lineLimit = LoadDataHelper(filePath, limit, out fileContents, out result); for (int i = fileContents[0].Contains(':') ? 0 : 1; i < lineLimit; i++) { string[] split = fileContents[i].Split(':'); try { int temp = int.Parse(split[0]); if (UseTimeLimit) { if (temp > limit) { break; } } result.Add(int.Parse(split[0]), int.Parse(split[1]) > 0); } catch (FormatException e) { ErrorListener.Add(new Exception("Can't parse LogicSnifferSTM data line: " + fileContents[i], e)); } } return(new LogicAnalyzerData(result)); }
/// <summary> /// Sorts packet's bytes into easily accessible structures. Manipulates endianess. /// </summary> /// <param name="data">Expects bytes, composed of fully decoded bits (Decode, then SeparatePackets, then DestuffZeros, then PackIntoBytes).</param> /// <param name="time">x10nS</param> /// <param name="littleEndian">See Communications.Parse</param> /// <returns></returns> public static EncoderPacket Parse(byte[] data, int time, bool littleEndian = false) { //bool containsSub = data.Length > OrdinaryPacketLength; byte[][] result = new byte[FieldLength.Count][]; int dataLen = data.Length - FieldLength[Fields.FCS]; List <byte> fcs = new List <byte>(dataLen > -1 ? dataLen : 0); int current = 0; try { for (int i = 0; i < FieldLength.Count; i++) { if (OrdinaryPacketFieldsToExclude.Any(x => x == (Fields)i)) { continue; } int l = FieldLength[(Fields)i]; if (l == -1) { l = data.Length - FieldLength.Sum(x => x.Value) - 1; //-1 stands for "variable length" } result[i] = new byte[l]; //Multibyte fields may be little-endian at physical layer (in fact they should be, but it turns out they're not...) //All in all, we'd better implement a switch for (int j = 0; j < l; j++) { result[i][j] = data[current + (littleEndian ? (l - j - 1) : (j))]; if ((Fields)i != Fields.FCS) { fcs.Add(result[i][j]); } } current += l; } } catch (OverflowException) { ErrorListener.Add(new Exception(string.Format("Packet at {0} is incomplete!", time))); } var toParse = result[(int)Fields.Unknown]; //if (containsSub) toParse = toParse.Concat(result[(int)Fields.SubcommandData]).ToArray(); var packet = new EncoderPacket(result, EncoderCommand.Parse(toParse), time); packet.ComputedFCS = HDLCManchesterDecoder.ComputeFCS(fcs.ToArray()); try { packet.FCSError = !packet.ComputedFCS.SequenceEqual(packet.ParsedData[(int)Fields.FCS]); } catch (ArgumentNullException) { packet.FCSError = true; } packet.DatabaseReport = EncoderCommandDatabase.GetReport(packet); return(packet); }
public static string GetReport(EncoderPacket packet) { try { /*if (packet.ParsedData[(int)MechatrolinkPacket.Fields.Address][0] == SyncFrameAddress) return SyncFrameReport; * var info = Database[packet.Command.ParsedFields[(int)MechatrolinkCommand.Fields.Code][0]]; * return info.GetReport(packet.Command.ParsedFields[(int)MechatrolinkCommand.Fields.Data], * packet.ParsedData[(int)MechatrolinkPacket.Fields.Control][0] == ResponseControlCode);*/ } catch (KeyNotFoundException) { } catch (Exception e) { ErrorListener.Add(new Exception("Error during database search. Operation aborted.", e)); } return(""); }
/// <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); }