/// <summary> /// Given three of the four current channels, calculates the /// missing channel based on the relationship IR = IA + IB + IC. /// </summary> /// <param name="meterInfo">Data context for accessing configuration tables in the database.</param> public DataSeries CalculateMissingCurrentChannel(MeterInfoDataContext meterInfo) { Meter meter; DataSeries missingSeries; // If the data group does not have exactly 3 channels, // then there is no missing channel or there is not // enough data to calculate the missing channel if (DefinedCurrents != 3) { return(null); } // Get the meter associated with the channels in this data group meter = (IA ?? IB).SeriesInfo.Channel.Meter; if (m_iaIndex == -1) { // Calculate IA = IR - IB - IC missingSeries = IR.Add(IB.Negate()).Add(IC.Negate()); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, m_dataGroup, "Current", "AN"); m_iaIndex = m_dataGroup.DataSeries.Count; m_dataGroup.Add(missingSeries); } else if (m_ibIndex == -1) { // Calculate IB = IR - IA - IC missingSeries = IR.Add(IA.Negate()).Add(IC.Negate()); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, m_dataGroup, "Current", "BN"); m_ibIndex = m_dataGroup.DataSeries.Count; m_dataGroup.Add(missingSeries); } else if (m_icIndex == -1) { // Calculate IC = IR - IA - IB missingSeries = IR.Add(IA.Negate()).Add(IB.Negate()); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, m_dataGroup, "Current", "CN"); m_icIndex = m_dataGroup.DataSeries.Count; m_dataGroup.Add(missingSeries); } else { // Calculate IR = IA + IB + IC missingSeries = IA.Add(IB).Add(IC); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, m_dataGroup, "Current", "RES"); m_irIndex = m_dataGroup.DataSeries.Count; m_dataGroup.Add(missingSeries); } return(missingSeries); }
public DataGroup ToSubGroup(int startIndex, int endIndex) { DataGroup subGroup = new DataGroup(); foreach (DataSeries dataSeries in m_dataSeries) { subGroup.Add(dataSeries.ToSubSeries(startIndex, endIndex)); } return(subGroup); }
public DataGroup ToSubGroup(DateTime startTime, DateTime endTime) { DataGroup subGroup = new DataGroup(); foreach (DataSeries dataSeries in m_dataSeries) { subGroup.Add(dataSeries.ToSubSeries(startTime, endTime)); } return(subGroup); }
public static DataGroup Combine(params DataGroup[] dataGroups) { DataGroup combination = new DataGroup(); foreach (DataGroup dataGroup in dataGroups) { foreach (DataSeries dataSeries in dataGroup.DataSeries) { combination.Add(dataSeries); } } return(combination); }
public static CycleDataGroup ToCycleDataGroup(DataSeries dataSeries, double frequency) { DataGroup dataGroup = new DataGroup(); DataSeries rmsSeries = new DataSeries(); DataSeries phaseSeries = new DataSeries(); DataSeries peakSeries = new DataSeries(); DataSeries errorSeries = new DataSeries(); int samplesPerCycle; double[] yValues; double[] tValues; double sum; DateTime cycleTime; SineWave sineFit; if ((object)dataSeries == null) { return(null); } // Set series info to the source series info rmsSeries.SeriesInfo = dataSeries.SeriesInfo; phaseSeries.SeriesInfo = dataSeries.SeriesInfo; peakSeries.SeriesInfo = dataSeries.SeriesInfo; errorSeries.SeriesInfo = dataSeries.SeriesInfo; // Get samples per cycle of the data series based on the given frequency samplesPerCycle = CalculateSamplesPerCycle(dataSeries, frequency); // Initialize arrays of y-values and t-values for calculating cycle data yValues = new double[samplesPerCycle]; tValues = new double[samplesPerCycle]; // Obtain a list of time gaps in the data series List <int> gapIndexes = Enumerable.Range(0, dataSeries.DataPoints.Count - 1) .Where(index => { DataPoint p1 = dataSeries[index]; DataPoint p2 = dataSeries[index + 1]; double cycleDiff = (p2.Time - p1.Time).TotalSeconds * frequency; // Detect gaps larger than a quarter cycle. // Tolerance of 0.000062 calculated // assuming 3.999 samples per cycle return(cycleDiff > 0.250062); }) .ToList(); for (int i = 0; i <= dataSeries.DataPoints.Count - samplesPerCycle; i++) { // If the cycle following i contains a data gap, do not calculate cycle data if (gapIndexes.Any(index => i <= index && (i + samplesPerCycle - 1) > index)) { continue; } // Use the time of the first data point in the cycle as the time of the cycle cycleTime = dataSeries.DataPoints[i].Time; sum = 0.0D; // Copy values from the original data series into the y-value and t-value arrays for (int j = 0; j < samplesPerCycle; j++) { yValues[j] = dataSeries.DataPoints[i + j].Value; tValues[j] = (dataSeries.DataPoints[i + j].Time - cycleTime).TotalSeconds; sum += yValues[j] * yValues[j]; } // Use a curve fitting algorithm to estimate the sine wave over this cycle sineFit = WaveFit.SineFit(yValues, tValues, frequency); // Add data points to each of the cycle data series rmsSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = Math.Sqrt(sum / samplesPerCycle) }); phaseSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = sineFit.Phase }); peakSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = sineFit.Amplitude }); errorSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = tValues .Select(sineFit.CalculateY) .Zip(yValues, (estimate, value) => Math.Abs(estimate - value)) .Sum() }); } // Add a series to the data group for each series of cycle data dataGroup.Add(rmsSeries); dataGroup.Add(phaseSeries); dataGroup.Add(peakSeries); dataGroup.Add(errorSeries); return(new CycleDataGroup(dataGroup)); }
public static CycleDataGroup ToCycleDataGroup(DataSeries dataSeries, double frequency) { DataGroup dataGroup = new DataGroup(); DataSeries rmsSeries = new DataSeries(); DataSeries phaseSeries = new DataSeries(); DataSeries peakSeries = new DataSeries(); DataSeries errorSeries = new DataSeries(); int samplesPerCycle; double[] yValues; double[] tValues; double sum; DateTime cycleTime; SineWave sineFit; if ((object)dataSeries == null) { return(new CycleDataGroup(dataGroup)); } // Set series info to the source series info rmsSeries.SeriesInfo = dataSeries.SeriesInfo; phaseSeries.SeriesInfo = dataSeries.SeriesInfo; peakSeries.SeriesInfo = dataSeries.SeriesInfo; errorSeries.SeriesInfo = dataSeries.SeriesInfo; // Get samples per cycle of the data series based on the given frequency samplesPerCycle = CalculateSamplesPerCycle(dataSeries, frequency); // Initialize arrays of y-values and t-values for calculating cycle data yValues = new double[samplesPerCycle]; tValues = new double[samplesPerCycle]; for (int i = 0; i <= dataSeries.DataPoints.Count - samplesPerCycle; i++) { // Use the time of the first data point in the cycle as the time of the cycle cycleTime = dataSeries.DataPoints[i].Time; sum = 0.0D; // Copy values from the original data series into the y-value and t-value arrays for (int j = 0; j < samplesPerCycle; j++) { yValues[j] = dataSeries.DataPoints[i + j].Value; tValues[j] = (dataSeries.DataPoints[i + j].Time - cycleTime).TotalSeconds; sum += yValues[j] * yValues[j]; } // Use a curve fitting algorithm to estimate the sine wave over this cycle sineFit = WaveFit.SineFit(yValues, tValues, frequency); // Add data points to each of the cycle data series rmsSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = Math.Sqrt(sum / samplesPerCycle) }); phaseSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = sineFit.Phase }); peakSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = sineFit.Amplitude }); errorSeries.DataPoints.Add(new DataPoint() { Time = cycleTime, Value = tValues .Select(sineFit.CalculateY) .Zip(yValues, (estimate, value) => Math.Abs(estimate - value)) .Sum() }); } // Add a series to the data group for each series of cycle data dataGroup.Add(rmsSeries); dataGroup.Add(phaseSeries); dataGroup.Add(peakSeries); dataGroup.Add(errorSeries); return(new CycleDataGroup(dataGroup)); }
// Static Methods public static DataSeries AddMissingCurrentSeries(MeterInfoDataContext meterInfo, Meter meter, DataGroup dataGroup) { DataSeries missingSeries = null; // Get all necessary voltage and current channels in the proper order List <DataSeries> viSeriesList = Enumerable.Range(VAIndex, IRIndex + 1) .GroupJoin(dataGroup.DataSeries.Where(IsInstantaneous), i => i, GetIndex, (i, series) => series.FirstOrDefault()) .ToList(); // Validate that no more than one current channel is missing if (viSeriesList.Count(series => (object)series == null) > 1) { return(null); } // Attempt to fill in missing current channels // based on the relation IR = IA + IB + IC if ((object)viSeriesList[IAIndex] == null) { missingSeries = viSeriesList[IRIndex]; missingSeries = missingSeries.Add(viSeriesList[IBIndex].Negate()); missingSeries = missingSeries.Add(viSeriesList[ICIndex].Negate()); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, dataGroup, IAIndex); missingSeries.SeriesInfo.Channel.Line = viSeriesList[IBIndex].SeriesInfo.Channel.Line; viSeriesList[IAIndex] = missingSeries; } else if ((object)viSeriesList[IBIndex] == null) { missingSeries = viSeriesList[IRIndex]; missingSeries = missingSeries.Add(viSeriesList[IAIndex].Negate()); missingSeries = missingSeries.Add(viSeriesList[ICIndex].Negate()); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, dataGroup, IBIndex); missingSeries.SeriesInfo.Channel.Line = viSeriesList[IAIndex].SeriesInfo.Channel.Line; viSeriesList[IBIndex] = missingSeries; } else if ((object)viSeriesList[ICIndex] == null) { missingSeries = viSeriesList[IRIndex]; missingSeries = missingSeries.Add(viSeriesList[IAIndex].Negate()); missingSeries = missingSeries.Add(viSeriesList[IBIndex].Negate()); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, dataGroup, ICIndex); missingSeries.SeriesInfo.Channel.Line = viSeriesList[IAIndex].SeriesInfo.Channel.Line; viSeriesList[ICIndex] = missingSeries; } else if ((object)viSeriesList[IRIndex] == null) { missingSeries = viSeriesList[IAIndex]; missingSeries = missingSeries.Add(viSeriesList[IBIndex]); missingSeries = missingSeries.Add(viSeriesList[ICIndex]); missingSeries.SeriesInfo = GetSeriesInfo(meterInfo, meter, dataGroup, IRIndex); missingSeries.SeriesInfo.Channel.Line = viSeriesList[IAIndex].SeriesInfo.Channel.Line; viSeriesList[IRIndex] = missingSeries; } if ((object)missingSeries != null) { dataGroup.Add(missingSeries); } return(missingSeries); }