public static string ToString(DegreeMinutesSeconds dms, AngleFormat format, int precision) { switch (format) { // todo here do we need to combine min and sec ??? or we need diffrent format option case AngleFormat.Degrees: return(string.Format("{0:D}°", dms.Degrees)); case AngleFormat.DegreesMinutes: return(string.Format("{0:D}°{1:D}'", dms.Degrees, dms.Minutes)); case AngleFormat.DegreesMinutesSeconds: var secondsPrecisionFormat = "F" + Math.Abs(precision).ToString("D"); //string d = dms.Degrees.ToString(""); //CultureInfo currentCulture = CultureInfo.CurrentCulture; var stringFormat = "{0:F0}° {1:F0}' {2:" + secondsPrecisionFormat + "}\""; return(string.Format(stringFormat, dms.Degrees, dms.Minutes, dms.Seconds)); case AngleFormat.Radians: // todo convert to radians ?? break; } throw new ArgumentOutOfRangeException("format"); }
public void SetAngle(double input, AngleFormat format = AngleFormat.Unit) { bool inverted = input < 0; double absolute = (inverted) ? -input : input; switch (format) { case AngleFormat.Unit: angle = (uint)((absolute % 1) * uint.MaxValue); break; case AngleFormat.Radians: angle = (uint)(((absolute / (Math.PI * 2)) % 1) * uint.MaxValue); break; case AngleFormat.Degrees: angle = (uint)(((absolute / 360) % 1) * uint.MaxValue); break; default: throw new NotImplementedException(format + " format is not supportd by this method.(unreachable code)"); } if (inverted) { angle = unchecked (uint.MaxValue - angle + 1); } }
/// <summary> /// Creates a new <see cref="ConnectionParameters"/>. /// </summary> public ConnectionParameters() { m_useETRConfiguration = DefaultUseETRConfiguration; m_guessConfiguration = DefaultGuessConfiguration; m_parseRedundantASDUs = DefaultParseRedundantASDUs; m_ignoreSignatureValidationFailures = DefaultIgnoreSignatureValidationFailures; m_ignoreSampleSizeValidationFailures = DefaultIgnoreSampleSizeValidationFailures; m_phasorAngleFormat = (AngleFormat)Enum.Parse(typeof(AngleFormat), DefaultPhasorAngleFormat, true); }
/// <summary> /// Creates a new <see cref="ConnectionParameters"/> from serialization parameters. /// </summary> /// <param name="info">The <see cref="SerializationInfo"/> with populated with data.</param> /// <param name="context">The source <see cref="StreamingContext"/> for this deserialization.</param> protected ConnectionParameters(SerializationInfo info, StreamingContext context) { // Deserialize connection parameters m_useETRConfiguration = info.GetOrDefault("useETRConfiguration", DefaultUseETRConfiguration); m_guessConfiguration = info.GetOrDefault("guessConfiguration", DefaultGuessConfiguration); m_parseRedundantASDUs = info.GetOrDefault("parseRedundantASDUs", DefaultParseRedundantASDUs); m_ignoreSignatureValidationFailures = info.GetOrDefault("ignoreSignatureValidationFailures", DefaultIgnoreSignatureValidationFailures); m_ignoreSampleSizeValidationFailures = info.GetOrDefault("ignoreSampleSizeValidationFailures", DefaultIgnoreSampleSizeValidationFailures); m_phasorAngleFormat = info.GetOrDefault("phasorAngleFormat", (AngleFormat)Enum.Parse(typeof(AngleFormat), DefaultPhasorAngleFormat, true)); }
/// <summary> /// Gives a text representation of the instance. /// </summary> /// <param name="angleFormat">A <see cref="AngleFormat" /> member.</param> /// <param name="decimalPlaces">The number of decimal places (after decimal point) for the last unit.</param> /// <param name="separator">The text between latitude and longitude.</param> /// <param name="forceAsciiCharacters">If true: Use 7 Bit ASCII characters only instead of special characters.</param> /// <returns>The text representation.</returns> public string ToString( AngleFormat angleFormat, int decimalPlaces, string separator, bool forceAsciiCharacters ) { string result = GetFormattedText(Latitude, angleFormat, decimalPlaces, forceAsciiCharacters) + " N" + separator + GetFormattedText(Longitude, angleFormat, decimalPlaces, forceAsciiCharacters) + " E" ; return(result); }
public void AngleFormatTest() { AngleFormat af = new AngleFormat(AngleFormat.PatternLatitudeDDMMSS); String s1 = String.Format(af, "{0}", Math.PI); s1 = String.Format(af, "{0:DdMmSs}", Math.PI); Double v1 = af.Parse(s1); Assert.AreEqual(Math.PI, v1); s1 = String.Format(af, "{0:D°M'S\"N}", -0.9 * Math.PI); v1 = af.Parse(s1); Assert.AreEqual(-0.9 * Math.PI, v1); }
public void SetAngle(uint input, AngleFormat format = AngleFormat.Unit) { switch (format) { case AngleFormat.Unit: angle = input; break; case AngleFormat.Radians: case AngleFormat.Degrees: SetAngle((double)input, format); break; default: throw new NotImplementedException(format + " format is not supportd by this method.(unreachable code)"); } }
public static string ToString(double Angle, AngleFormat Format, string DegreeFormat) { double absA = Math.Abs(Angle); int deg = (int)Math.Floor(absA); double rem = 60.0 * (absA - deg); int min = (int)Math.Floor(rem); rem -= min; int hhss; string s = (Angle < 0.0) ? "-" : ""; switch (Format) { case AngleFormat.DDMMmm: hhss = (int)Math.Round(100.0 * rem); if (hhss >= 100) { hhss -= 100; min++; } break; case AngleFormat.DDMMSS: default: hhss = (int)Math.Round(60.0 * rem); if (hhss >= 60) { hhss -= 60; min++; } break; } if (min >= 60) { min -= 60; deg++; } s += deg.ToString(DegreeFormat, CultureInfo.CurrentCulture) + min.ToString("00", CultureInfo.CurrentCulture); if (Format == AngleFormat.DDMMmm) { s += "."; } s += hhss.ToString("00", CultureInfo.CurrentCulture); return(s); }
public static string ToString(Degree degree, AngleFormat format, int precision) { // DegreeMinutesSeconds dms = ToDDegreeMinutesSeconds(degree); switch (format) { // todo use precision case AngleFormat.Degrees: case AngleFormat.DegreesMinutes: case AngleFormat.DegreesMinutesSeconds: return(ToString(ToDDegreeMinutesSeconds(degree), format, precision)); case AngleFormat.Radians: return(ToString((Radian)degree, format, precision)); default: throw new ArgumentOutOfRangeException(nameof(format), format, null); } }
public double GetAngle(AngleFormat format) { double temp = angle / (((double)uint.MaxValue) + 1); switch (format) { case AngleFormat.Unit: return(temp); case AngleFormat.Radians: return(temp * (Math.PI * 2)); case AngleFormat.Degrees: return(temp * 360); default: throw new NotImplementedException(format + " format is not supportd by this method.(unreachable code)"); } }
public static string ToString(Radian radian, AngleFormat format, int precision) { switch (format) { case AngleFormat.Radians: { var formStr = "{0:F" + precision + "}"; return(string.Format(formStr, radian)); } case AngleFormat.Degrees: case AngleFormat.DegreesMinutes: case AngleFormat.DegreesMinutesSeconds: return(ToString((Degree)radian, format, precision)); default: throw new NotImplementedException(); } }
public static string ToString(double Angle, AngleKind Kind, AngleFormat Format) { string degFmt; char sign; switch (Kind) { case AngleKind.Latitude: degFmt = "00"; sign = (Angle < 0.0) ? 'S' : 'N'; break; case AngleKind.Longitude: default: degFmt = "000"; sign = (Angle < 0.0) ? 'W' : 'E'; break; } string s = ToString(Math.Abs(Angle), Format, degFmt) + sign; return(s); }
/// <summary> /// Retrieves a formatted text for a degree value /// according to a given angle format. /// </summary> /// <param name="decDegrees">The degree value (with decimal places).</param> /// <param name="angleFormat">A <see cref="AngleFormat" /> member.</param> /// <param name="decimalPlaces">The number of decimal places (after decimal point) for the last unit.</param> /// <param name="forceAsciiCharacters">If true: Use 7 Bit ASCII characters only instead of special characters.</param> /// <returns>The formatted text.</returns> public static string GetFormattedText(double decDegrees, AngleFormat angleFormat, int decimalPlaces, bool forceAsciiCharacters) { string degreeSymbol, minuteSymbol, secondsSymbol; if (forceAsciiCharacters) { degreeSymbol = "d"; minuteSymbol = "'"; secondsSymbol = "\""; } else { degreeSymbol = "\x00B0".ToString(); //DEGREE SIGN minuteSymbol = "\x2032".ToString(); //PRIME secondsSymbol = "\x2033".ToString(); //DOUBLE PRIME } string separator = " "; string result; switch (angleFormat) { case AngleFormat.sD: { bool isNegative; double degrees; Utilities.DegreesToD(decDegrees, out isNegative, out degrees); result = GetSignSymbol(isNegative) + string.Format(Utilities.GetFormatString(1, decimalPlaces), degrees) + degreeSymbol ; } break; case AngleFormat.sDD: { bool isNegative; double degrees; Utilities.DegreesToD(decDegrees, out isNegative, out degrees); result = GetSignSymbol(isNegative) + string.Format(Utilities.GetFormatString(2, decimalPlaces), degrees) + degreeSymbol ; } break; case AngleFormat.sDM: { bool isNegative; int degrees; double minutes; Utilities.DegreesToDM(decDegrees, out isNegative, out degrees, out minutes); Utilities.Round(decimalPlaces, ref degrees, ref minutes); result = GetSignSymbol(isNegative) + string.Format(Utilities.GetFormatString(1, 0), degrees) + degreeSymbol + separator + string.Format(Utilities.GetFormatString(1, decimalPlaces), minutes) + minuteSymbol ; } break; case AngleFormat.sDDMM: { bool isNegative; int degrees; double minutes; Utilities.DegreesToDM(decDegrees, out isNegative, out degrees, out minutes); Utilities.Round(decimalPlaces, ref degrees, ref minutes); result = GetSignSymbol(isNegative) + string.Format(Utilities.GetFormatString(2, 0), degrees) + degreeSymbol + separator + string.Format(Utilities.GetFormatString(2, decimalPlaces), minutes) + minuteSymbol ; } break; case AngleFormat.sDMS: { bool isNegative; int degrees; int minutes; double seconds; Utilities.DegreesToDMS(decDegrees, out isNegative, out degrees, out minutes, out seconds); Utilities.Round(decimalPlaces, ref degrees, ref minutes, ref seconds); result = GetSignSymbol(isNegative) + string.Format(Utilities.GetFormatString(1, 0), degrees) + degreeSymbol + separator + string.Format(Utilities.GetFormatString(1, 0), minutes) + minuteSymbol + separator + string.Format(Utilities.GetFormatString(1, decimalPlaces), seconds) + secondsSymbol ; } break; case AngleFormat.sDDMMSS: { bool isNegative; int degrees; int minutes; double seconds; Utilities.DegreesToDMS(decDegrees, out isNegative, out degrees, out minutes, out seconds); Utilities.Round(decimalPlaces, ref degrees, ref minutes, ref seconds); result = GetSignSymbol(isNegative) + string.Format(Utilities.GetFormatString(2, 0), degrees) + degreeSymbol + separator + string.Format(Utilities.GetFormatString(2, 0), minutes) + minuteSymbol + separator + string.Format(Utilities.GetFormatString(2, decimalPlaces), seconds) + secondsSymbol ; } break; default: throw new NotSupportedException(angleFormat.ToString()); } return(result); }
/// <summary> /// Creates a new <see cref="ConnectionParameters"/>. /// </summary> public ConnectionParameters() { m_useETRConfiguration = DefaultUseETRConfiguration; m_guessConfiguration = DefaultGuessConfiguration; m_parseRedundantASDUs = DefaultParseRedundantASDUs; m_ignoreSignatureValidationFailures = DefaultIgnoreSignatureValidationFailures; m_ignoreSampleSizeValidationFailures = DefaultIgnoreSampleSizeValidationFailures; m_phasorAngleFormat = (AngleFormat)Enum.Parse(typeof(AngleFormat), DefaultPhasorAngleFormat, true); }
public Angle(uint input, AngleFormat format = AngleFormat.Unit) : this() { SetAngle(input, format); }
/// <summary> /// Creates a new <see cref="CommonFrameHeader"/> from given <paramref name="buffer"/>. /// </summary> /// <param name="configurationFrame">IEC 61850-90-5 <see cref="ConfigurationFrame"/> if already parsed.</param> /// <param name="useETRConfiguration">Determines if system should find associated ETR file using MSVID with same name for configuration.</param> /// <param name="guessConfiguration">Determines if system should try to guess at a possible configuration given payload size.</param> /// <param name="parseRedundantASDUs">Determines if system should expose redundantly parsed ASDUs.</param> /// <param name="ignoreSignatureValidationFailures">Determines if system should ignore checksum signature validation errors.</param> /// <param name="ignoreSampleSizeValidationFailures">Determines if system should ignore sample size validation errors.</param> /// <param name="angleFormat">Allows customization of the angle parsing format.</param> /// <param name="buffer">Buffer that contains data to parse.</param> /// <param name="startIndex">Start index into buffer where valid data begins.</param> /// <param name="length">Maximum length of valid data from offset.</param> // ReSharper disable once UnusedParameter.Local public CommonFrameHeader(ConfigurationFrame configurationFrame, bool useETRConfiguration, bool guessConfiguration, bool parseRedundantASDUs, bool ignoreSignatureValidationFailures, bool ignoreSampleSizeValidationFailures, AngleFormat angleFormat, byte[] buffer, int startIndex, int length) { const byte VersionNumberMask = (byte)IEC61850_90_5.FrameType.VersionNumberMask; // Cache behavioral connection parameters m_useETRConfiguration = useETRConfiguration; m_guessConfiguration = guessConfiguration; m_parseRedundantASDUs = parseRedundantASDUs; m_ignoreSignatureValidationFailures = ignoreSignatureValidationFailures; m_ignoreSampleSizeValidationFailures = ignoreSampleSizeValidationFailures; m_angleFormat = angleFormat; // Ignore the time base from configuration frame if available. The timebase is not adjustable for 61850. m_timebase = Common.Timebase; // See if frame is for a common IEEE C37.118 frame (e.g., for configuration or command) if (buffer[startIndex] == PhasorProtocols.Common.SyncByte) { // Strip out frame type and version information... m_frameType = (FrameType)(buffer[startIndex + 1] & ~VersionNumberMask); m_version = (byte)(buffer[startIndex + 1] & VersionNumberMask); m_frameLength = BigEndian.ToUInt16(buffer, startIndex + 2); m_idCode = BigEndian.ToUInt16(buffer, startIndex + 4); uint secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 6); uint fractionOfSecond = BigEndian.ToUInt32(buffer, startIndex + 10); long ticksBeyondSecond; // Without timebase, the best timestamp you can get is down to the whole second m_timestamp = (new UnixTimeTag((double)secondOfCentury)).ToDateTime().Ticks; // "Actual fractional seconds" are obtained by taking fractionOfSecond and dividing by timebase. // Since we are converting to ticks, we need to multiply by Ticks.PerSecond. // We do the multiplication first so that the whole operation can be done using integer arithmetic. // m_timebase / 2L is added before dividing by timebase in order to round the result. ticksBeyondSecond = (fractionOfSecond & ~Common.TimeQualityFlagsMask) * Ticks.PerSecond; m_timestamp += (ticksBeyondSecond + m_timebase / 2L) / m_timebase; if ((object)configurationFrame != null) { // Hang on to configured frame rate and ticks per frame m_framesPerSecond = configurationFrame.FrameRate; m_ticksPerFrame = Ticks.PerSecond / (double)m_framesPerSecond; } m_timeQualityFlags = fractionOfSecond & Common.TimeQualityFlagsMask; } else if (buffer[startIndex + 1] == Common.CltpTag) { // Make sure there is enough data to parse session header from frame if (length > Common.SessionHeaderSize) { // Manually assign frame type - this is an IEC 61850-90-5 data frame m_frameType = IEC61850_90_5.FrameType.DataFrame; // Calculate CLTP tag length int cltpTagLength = buffer[startIndex] + 1; // Initialize buffer parsing index starting past connectionless transport protocol header int index = startIndex + cltpTagLength; // Start calculating total frame length int frameLength = cltpTagLength; // Get session type (Goose, sampled values, etc.) SessionType sessionType = (SessionType)buffer[index++]; // Make sure session type is sampled values if (sessionType == SessionType.SampledValues) { byte headerSize = buffer[index]; // Make sure header size is standard if (headerSize == Common.SessionHeaderSize) { // Skip common header tag index += 3; // Get SPDU length m_spduLength = BigEndian.ToUInt32(buffer, index); index += 4; // Add SPDU length to total frame length (updated as of 10/3/2012 to accommodate extra 6 bytes) frameLength += (int)m_spduLength + 8; // Make sure full frame of data is available - cannot calculate full frame length needed for check sum // without the entire frame since signature algorithm calculation length varies by type and size if (length > m_spduLength + 13) { // Get SPDU packet number m_packetNumber = BigEndian.ToUInt32(buffer, index); // Get security algorithm type m_securityAlgorithm = (SecurityAlgorithm)buffer[index + 12]; // Get signature algorithm type m_signatureAlgorithm = (SignatureAlgorithm)buffer[index + 13]; // Get current key ID m_keyID = BigEndian.ToUInt32(buffer, index + 14); // Add signature calculation result length to total frame length switch (m_signatureAlgorithm) { case SignatureAlgorithm.None: break; case SignatureAlgorithm.Sha80: frameLength += 11; break; case SignatureAlgorithm.Sha128: case SignatureAlgorithm.Aes128: frameLength += 17; break; case SignatureAlgorithm.Sha256: frameLength += 33; break; case SignatureAlgorithm.Aes64: frameLength += 9; break; default: throw new InvalidOperationException("Invalid IEC 61850-90-5 signature algorithm detected: 0x" + buffer[index].ToString("X").PadLeft(2, '0')); } // Check signature algorithm packet checksum here, this step is skipped in data frame parsing due to non-standard location... if (m_signatureAlgorithm != SignatureAlgorithm.None) { int packetIndex = startIndex + cltpTagLength; int hmacIndex = (int)(packetIndex + m_spduLength + 2); // Check for signature tag if (buffer[hmacIndex++] == 0x85) { // KeyID is technically a lookup into derived rotating keys, but all these are using dummy key for now HMAC hmac = m_signatureAlgorithm <= SignatureAlgorithm.Sha256 ? (HMAC)(new ShaHmac(Common.DummyKey)) : (HMAC)(new AesHmac(Common.DummyKey)); int result = 0; switch (m_signatureAlgorithm) { case SignatureAlgorithm.None: break; case SignatureAlgorithm.Aes64: m_sourceHash = buffer.BlockCopy(hmacIndex, 8); m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 8); result = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 8); break; case SignatureAlgorithm.Sha80: m_sourceHash = buffer.BlockCopy(hmacIndex, 10); m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 10); result = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 10); break; case SignatureAlgorithm.Sha128: case SignatureAlgorithm.Aes128: m_sourceHash = buffer.BlockCopy(hmacIndex, 16); m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 16); result = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 16); break; case SignatureAlgorithm.Sha256: m_sourceHash = buffer.BlockCopy(hmacIndex, 32); m_calculatedHash = hmac.ComputeHash(buffer, packetIndex, (int)m_spduLength).BlockCopy(0, 32); result = m_sourceHash.CompareTo(0, m_calculatedHash, 0, 32); break; default: throw new NotSupportedException(string.Format("IEC 61850-90-5 signature algorithm \"{0}\" is not currently supported: ", m_signatureAlgorithm)); } if (result != 0 && !m_ignoreSignatureValidationFailures) { throw new CrcException("Invalid binary image detected - IEC 61850-90-5 check sum does not match."); } } else { throw new CrcException("Invalid binary image detected - expected IEC 61850-90-5 check sum does not exist."); } } // Get payload length index += 18; m_dataLength = (ushort)BigEndian.ToUInt32(buffer, index); index += 4; // Confirm payload type tag is sampled values if (buffer[index] != 0x82) { throw new InvalidOperationException("Encountered a payload that is not tagged 0x82 for sampled values: 0x" + buffer[index].ToString("X").PadLeft(2, '0')); } index++; // Get simulated bit value m_simulatedData = buffer[index++] != 0; // Get application ID m_applicationID = BigEndian.ToUInt16(buffer, index); index += 2; // Get ASDU payload size m_payloadSize = BigEndian.ToUInt16(buffer, index); index += 2; // Validate sampled value PDU tag exists and skip past it buffer.ValidateTag(SampledValueTag.SvPdu, ref index); // Parse number of ASDUs tag m_asduCount = buffer.ParseByteTag(SampledValueTag.AsduCount, ref index); if (m_asduCount == 0) { throw new InvalidOperationException("Total number of ADSUs must be greater than zero."); } // Validate sequence of ASDU tag exists and skip past it buffer.ValidateTag(SampledValueTag.SequenceOfAsdu, ref index); // Set header length m_headerLength = (ushort)(index - startIndex); // Set calculated frame length m_frameLength = (ushort)frameLength; } } else { throw new InvalidOperationException("Bad data stream, encountered an invalid session header size: " + headerSize); } } else { throw new InvalidOperationException(string.Format("This library can only parse IEC 61850-90-5 sampled value sessions, type \"{0}\" is not supported.", sessionType)); } } } else { throw new InvalidOperationException("Bad data stream, expected sync byte 0xAA or 0x01 as first byte in IEC 61850-90-5 frame, got 0x" + buffer[startIndex].ToString("X").PadLeft(2, '0')); } }
/// <summary> /// Creates a new <see cref="ConnectionParameters"/> from serialization parameters. /// </summary> /// <param name="info">The <see cref="SerializationInfo"/> with populated with data.</param> /// <param name="context">The source <see cref="StreamingContext"/> for this deserialization.</param> protected ConnectionParameters(SerializationInfo info, StreamingContext context) { // Deserialize connection parameters m_useETRConfiguration = info.GetOrDefault("useETRConfiguration", DefaultUseETRConfiguration); m_guessConfiguration = info.GetOrDefault("guessConfiguration", DefaultGuessConfiguration); m_parseRedundantASDUs = info.GetOrDefault("parseRedundantASDUs", DefaultParseRedundantASDUs); m_ignoreSignatureValidationFailures = info.GetOrDefault("ignoreSignatureValidationFailures", DefaultIgnoreSignatureValidationFailures); m_ignoreSampleSizeValidationFailures = info.GetOrDefault("ignoreSampleSizeValidationFailures", DefaultIgnoreSampleSizeValidationFailures); m_phasorAngleFormat = info.GetOrDefault("phasorAngleFormat", (AngleFormat)Enum.Parse(typeof(AngleFormat), DefaultPhasorAngleFormat, true)); }