Example #1
0
        private void ReadDigitalValues(byte[] buffer, int index)
        {
            int valueIndex = m_schema.AnalogChannels.Length;

            // Parse all digital record values
            for (int i = 0; i < m_schema.DigitalWords; i++)
            {
                // Read next digital word
                ushort digitalWord = LittleEndian.ToUInt16(buffer, index);
                index += 2;

                // Distribute each bit of digital word through next 16 digital values
                for (int j = 0; j < 16 && valueIndex < Values.Length; j++, valueIndex++)
                {
                    Values[valueIndex] = digitalWord.CheckBits(BitExtensions.BitVal(j)) ? 1.0D : 0.0D;
                }
            }
        }
Example #2
0
        /// <summary>
        /// Writes next COMTRADE record in ASCII format.
        /// </summary>
        /// <param name="output">Destination stream.</param>
        /// <param name="schema">Source schema.</param>
        /// <param name="timestamp">Record timestamp (implicitly castable as <see cref="DateTime"/>).</param>
        /// <param name="values">Values to write - 16-bit digitals should exist as a word in an individual double value, method will write out bits.</param>
        /// <param name="sample">User incremented sample index.</param>
        /// <param name="injectFracSecValue">Determines if FRACSEC value should be automatically injected into stream as first digital - defaults to <c>true</c>.</param>
        /// <param name="fracSecValue">FRACSEC value to inject into output stream - defaults to 0x0000.</param>
        /// <remarks>
        /// This function is primarily intended to write COMTRADE ASCII data records based on synchrophasor data
        /// (see Annex H: Schema for Phasor Data 2150 Using the COMTRADE File Standard in IEEE C37.111-2010),
        /// it may be necessary to manually write records for other COMTRADE needs (e.g., non 16-bit digitals).
        /// </remarks>
        public static void WriteNextRecordAscii(StreamWriter output, Schema schema, Ticks timestamp, double[] values, uint sample, bool injectFracSecValue = true, ushort fracSecValue = 0x0000)
        {
            // Make timestamp relative to beginning of file
            timestamp -= schema.StartTime.Value;

            uint          microseconds   = (uint)(timestamp.ToMicroseconds() / schema.TimeFactor);
            StringBuilder line           = new StringBuilder();
            bool          isFirstDigital = true;

            line.Append(sample);
            line.Append(',');
            line.Append(microseconds);

            for (int i = 0; i < values.Length; i++)
            {
                double value = values[i];

                if (i < schema.AnalogChannels.Length)
                {
                    value -= schema.AnalogChannels[i].Adder;
                    value /= schema.AnalogChannels[i].Multiplier;

                    line.Append(',');
                    line.Append(value.ToString(CultureInfo.InvariantCulture));
                }
                else
                {
                    if (isFirstDigital)
                    {
                        // Handle automatic injection of IEEE C37.118 FRACSEC digital value if requested
                        isFirstDigital = false;

                        if (injectFracSecValue)
                        {
                            for (int j = 0; j < 16; j++)
                            {
                                line.Append(',');
                                line.Append(fracSecValue.CheckBits(BitExtensions.BitVal(j)) ? 1 : 0);
                            }
                        }
                    }

                    ushort digitalWord = (ushort)value;

                    for (int j = 0; j < 16; j++)
                    {
                        line.Append(',');
                        line.Append(digitalWord.CheckBits(BitExtensions.BitVal(j)) ? 1 : 0);
                    }
                }
            }

            // Make sure FRACSEC values are injected
            if (isFirstDigital && injectFracSecValue)
            {
                for (int j = 0; j < 16; j++)
                {
                    line.Append(',');
                    line.Append(fracSecValue.CheckBits(BitExtensions.BitVal(j)) ? 1 : 0);
                }
            }

            output.WriteLine(line.ToString());
        }
Example #3
0
        // Handle binary file read
        private bool ReadNextBinary()
        {
            FileStream currentFile  = m_fileStreams[m_streamIndex];
            int        recordLength = m_schema.BinaryRecordLength;

            byte[] buffer = new byte[recordLength];

            // Read next record from file
            int bytesRead = currentFile.Read(buffer, 0, recordLength);

            // See if we have reached the end of this file
            if (bytesRead == 0)
            {
                m_streamIndex++;

                // There is more to read if there is another file
                return(m_streamIndex < m_fileStreams.Length && ReadNext());
            }

            if (bytesRead == recordLength)
            {
                int index = 0;

                // Read sample index
                uint sample = LittleEndian.ToUInt32(buffer, index);
                index += 4;

                // Get timestamp of this record
                m_timestamp = DateTime.MinValue;

                // If sample rates are defined, this is the preferred method for timestamp resolution
                if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0)
                {
                    // Find rate for given sample
                    SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample);

                    if (sampleRate.Rate > 0.0D)
                    {
                        m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value);
                    }
                }

                // Read microsecond timestamp
                uint microseconds = LittleEndian.ToUInt32(buffer, index);
                index += 4;

                // Fall back on specified microsecond time
                if (m_timestamp == DateTime.MinValue)
                {
                    m_timestamp = new DateTime(Ticks.FromMicroseconds(microseconds * m_schema.TimeFactor) + m_schema.StartTime.Value);
                }

                // Parse all analog record values
                for (int i = 0; i < m_schema.AnalogChannels.Length; i++)
                {
                    // Read next value
                    m_values[i] = LittleEndian.ToInt16(buffer, index) * m_schema.AnalogChannels[i].Multiplier + m_schema.AnalogChannels[i].Adder;
                    index      += 2;
                }

                int    valueIndex   = m_schema.AnalogChannels.Length;
                int    digitalWords = m_schema.DigitalWords;
                ushort digitalWord;

                for (int i = 0; i < digitalWords; i++)
                {
                    // Read next digital word
                    digitalWord = LittleEndian.ToUInt16(buffer, index);
                    index      += 2;

                    // Distribute each bit of digital word through next 16 digital values
                    for (int j = 0; j < 16 && valueIndex < m_values.Length; j++, valueIndex++)
                    {
                        m_values[valueIndex] = digitalWord.CheckBits(BitExtensions.BitVal(j)) ? 1.0D : 0.0D;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Failed to read enough bytes from COMTRADE file for a record as defined by schema - possible schema/data file mismatch or file corruption.");
            }

            return(true);
        }