Пример #1
0
        public ProcessingReturnValues FitAndPlotFastFlyby(
            Dictionary <int, SingleMultiFrameMeasurement> measurements,
            FlybyMeasurementContext meaContext,
            FittingContext fittingContext,
            FittingValue fittingValue,
            GetFrameStateDataCallback getFrameStateDataCallback,
            Graphics g, FlybyPlottingContext plottingContext, float xScale, float yScale, int imageWidth, int imageHeight,
            out double motionRate)
        {
            // Do linear regression, use residual based exclusion rules
            // Two possible modes: (A) non trailed and (B) trailed images
            // A) Non Trailed Images
            // Report interpolated times for video normal position [How to use the MPC page for this?]
            // Don't forget to add the video normal position flag in the OBS file
            // Expect elongated images and apply instrumental delay corrections (in both integrated and non integrated modes)
            // B) Trailed Images
            // TODO: R&D Required

            if (fittingContext.ObjectExposureQuality == ObjectExposureQuality.GoodSignal)
            {
                return(FitAndPlotSlowFlyby(
                           measurements, meaContext, fittingContext, fittingValue, getFrameStateDataCallback,
                           g, plottingContext, xScale, yScale, imageWidth, imageHeight, out motionRate));
            }
            else
            {
                MessageBox.Show("This operation is currently not suppored.");
                motionRate = double.NaN;
                return(new ProcessingReturnValues());
            }
        }
Пример #2
0
        public ProcessingReturnValues FitAndPlotSlowFlyby(
            Dictionary<int, SingleMultiFrameMeasurement> measurements,
            FlybyMeasurementContext meaContext,
            FittingContext fittingContext,
            FittingValue fittingValue,
            GetFrameStateDataCallback getFrameStateDataCallback,
            Graphics g, FlybyPlottingContext plottingContext, float xScale, float yScale, int imageWidth, int imageHight,
            out double motionRate)
        {
            try
            {
                #region Building Test Cases
                if (m_DumpTestCaseData)
                {
                    var mvSer = new XmlSerializer(typeof(FlybyMeasurementContext));
                    var sb = new StringBuilder();
                    using (var wrt = new StringWriter(sb))
                    {
                        mvSer.Serialize(wrt, meaContext);
                    }
                    Trace.WriteLine(sb.ToString());
                    var fcSer = new XmlSerializer(typeof(FittingContext));
                    sb.Clear();
                    using (var wrt = new StringWriter(sb))
                    {
                        fcSer.Serialize(wrt, fittingContext);
                    }
                    Trace.WriteLine(sb.ToString());

                    var smfmSer = new XmlSerializer(typeof(SingleMultiFrameMeasurement));
                    foreach (int key in measurements.Keys)
                    {
                        sb.Clear();
                        using (var wrt2 = new StringWriter(sb))
                        {
                            smfmSer.Serialize(wrt2, measurements[key]);
                            if (measurements[key].FrameNo != key)
                                throw new InvalidOperationException();
                            Trace.WriteLine(sb.ToString());
                        }
                    }
                }
                #endregion

                // Do linear regression, use residual based exclusion rules
                // Report the interpolated position at the middle of the measured interva
                // Don't forget to add the video normal position flag in the OBS file
                // Expect elongated images and apply instrumental delay corrections

                motionRate = double.NaN;

                var rv = new ProcessingReturnValues();

                int numFramesUser = 0;

                rv.EarliestFrame = int.MaxValue;
                rv.LatestFrame = int.MinValue;

                var intervalValues = new Dictionary<int, Tuple<List<double>, List<double>>>();
                var intervalMedians = new Dictionary<double, double>();
                var intervalWeights = new Dictionary<double, double>();

                LinearRegression regression = null;
                if (measurements.Values.Count > 1)
                {
                    rv.EarliestFrame = measurements.Values.Select(m => m.FrameNo).Min();
                    rv.LatestFrame = measurements.Values.Select(m => m.FrameNo).Max();

                    var minUncertainty = meaContext.MinPositionUncertaintyPixels * meaContext.ArsSecsInPixel;

                    foreach (SingleMultiFrameMeasurement measurement in measurements.Values)
                    {
                        int integrationInterval = (measurement.FrameNo - fittingContext.FirstFrameIdInIntegrationPeroid) / fittingContext.IntegratedFramesCount;

                        Tuple<List<double>,List<double>> intPoints;
                        if (!intervalValues.TryGetValue(integrationInterval, out intPoints))
                        {
                            intPoints = Tuple.Create(new List<double>(), new List<double>());
                            intervalValues.Add(integrationInterval, intPoints);
                        }

                        if (fittingValue == FittingValue.RA)
                        {
                            intPoints.Item1.Add(measurement.RADeg);
                            intPoints.Item2.Add(ComputePositionWeight(measurement.SolutionUncertaintyRACosDEArcSec, measurement, minUncertainty, fittingContext.Weighting));
                        }
                        else
                        {
                            intPoints.Item1.Add(measurement.DEDeg);
                            intPoints.Item2.Add(ComputePositionWeight(measurement.SolutionUncertaintyDEArcSec, measurement, minUncertainty, fittingContext.Weighting));
                        }
                    }

                    if (intervalValues.Count > 2)
                    {
                        regression = new LinearRegression();

                        foreach (int integratedFrameNo in intervalValues.Keys)
                        {
                            Tuple<List<double>, List<double>> data = intervalValues[integratedFrameNo];

                            double median;
                            double medianWeight;

                            WeightedMedian(data, out median, out medianWeight);

                            // Assign the data point to the middle of the integration interval (using frame numbers)
                            //
                            // |--|--|--|--|--|--|--|--|
                            // |           |           |
                            //
                            // Because the time associated with the first frame is the middle of the frame, but the
                            // time associated with the middle of the interval is the end of the field then the correction
                            // is (N / 2) - 0.5 frames

                            double dataPointFrameNo =
                                rv.EarliestFrame +
                                fittingContext.IntegratedFramesCount * integratedFrameNo
                                + (fittingContext.IntegratedFramesCount / 2)
                                - 0.5;

                            intervalMedians.Add(dataPointFrameNo, median);
                            intervalWeights.Add(dataPointFrameNo, medianWeight);
                            if (fittingContext.Weighting != WeightingMode.None)
                                regression.AddDataPoint(dataPointFrameNo, median, medianWeight);
                            else
                                regression.AddDataPoint(dataPointFrameNo, median);
                        }

                        regression.Solve();

                        var firstPos = measurements[rv.EarliestFrame];
                        var lastPos = measurements[rv.LatestFrame];
                        double distanceArcSec = AngleUtility.Elongation(firstPos.RADeg, firstPos.DEDeg, lastPos.RADeg, lastPos.DEDeg) * 3600;
                        var firstTime = GetTimeForFrame(fittingContext, rv.EarliestFrame, meaContext.FirstVideoFrame, getFrameStateDataCallback, firstPos.OCRedTimeStamp);
                        var lastTime = GetTimeForFrame(fittingContext, rv.LatestFrame, meaContext.FirstVideoFrame, getFrameStateDataCallback, lastPos.OCRedTimeStamp);
                        double elapsedSec = new TimeSpan(lastTime.UT.Ticks - firstTime.UT.Ticks).TotalSeconds;
                        motionRate = distanceArcSec / elapsedSec;
                    }
                }

                FrameTime resolvedTime = null;
                if (int.MinValue != meaContext.UserMidFrame)
                {
                    // Find the closest video 'normal' MPC time and compute the frame number for it
                    // Now compute the RA/DE for the computed 'normal' frame
                    resolvedTime = GetTimeForFrame(fittingContext, meaContext.UserMidFrame, meaContext.FirstVideoFrame, getFrameStateDataCallback, measurements[meaContext.UserMidFrame].OCRedTimeStamp);

                    #region Plotting Code
                    if (g != null)
                    {
                        float xPosBeg = (float)(resolvedTime.ClosestNormalIntervalFirstFrameNo - rv.EarliestFrame) * xScale + 5;
                        float xPosEnd = (float)(resolvedTime.ClosestNormalIntervalLastFrameNo - rv.EarliestFrame) * xScale + 5;

                        g.FillRectangle(s_NormalTimeIntervalHighlightBrush, xPosBeg, 1, (xPosEnd - xPosBeg), imageHight - 2);
                    }
                    #endregion
                }

                Dictionary<double, double> secondPassData = new Dictionary<double, double>();

                int minFrameId = measurements.Keys.Min();

                #region Plotting Code
                if (g != null)
                {
                    foreach (SingleMultiFrameMeasurement measurement in measurements.Values)
                    {
                        float x = (measurement.FrameNo - minFrameId) * xScale + 5;

                        ProcessingValues val = new ProcessingValues()
                        {
                            Value = fittingValue == FittingValue.RA ? measurement.RADeg : measurement.DEDeg,
                            StdDev = fittingValue == FittingValue.RA ? measurement.StdDevRAArcSec / 3600.0 : measurement.StdDevDEArcSec / 3600.0
                        };

                        double valueFrom = val.Value - val.StdDev;
                        double valueTo = val.Value + val.StdDev;

                        float yFrom = (float)(valueFrom - plottingContext.MinValue) * yScale + 5;
                        float yTo = (float)(valueTo - plottingContext.MinValue) * yScale + 5;

                        g.DrawLine(plottingContext.IncludedPen, x, yFrom, x, yTo);
                        g.DrawLine(plottingContext.IncludedPen, x - 1, yFrom, x + 1, yFrom);
                        g.DrawLine(plottingContext.IncludedPen, x - 1, yTo, x + 1, yTo);
                    }
                }
                #endregion

                foreach (double integrFrameNo in intervalMedians.Keys)
                {
                    double val = intervalMedians[integrFrameNo];

                    double fittedValAtFrame = regression != null
                        ? regression.ComputeY(integrFrameNo)
                        : double.NaN;

                    bool included = Math.Abs(fittedValAtFrame - val) < 3 * regression.StdDev;

                    #region Plotting Code
                    if (g != null)
                    {
                        if (fittingContext.IntegratedFramesCount > 1)
                        {
                            Pen mPen = included ? plottingContext.IncludedPen : plottingContext.ExcludedPen;

                            float x = (float)(integrFrameNo - minFrameId) * xScale + 5;
                            float y = (float)(val - plottingContext.MinValue) * yScale + 5;

                            g.DrawEllipse(mPen, x - 3, y - 3, 6, 6);
                            g.DrawLine(mPen, x - 5, y - 5, x + 5, y + 5);
                            g.DrawLine(mPen, x + 5, y - 5, x - 5, y + 5);
                        }
                    }
                    #endregion

                    if (included) secondPassData.Add(integrFrameNo, val);
                }

                #region Second Pass
                regression = null;
                if (secondPassData.Count > 2)
                {
                    regression = new LinearRegression();
                    foreach (double frameNo in secondPassData.Keys)
                    {
                        if (fittingContext.Weighting != WeightingMode.None)
                            regression.AddDataPoint(frameNo, secondPassData[frameNo], intervalWeights[frameNo]);
                        else
                            regression.AddDataPoint(frameNo, secondPassData[frameNo]);
                    }
                    regression.Solve();
                }
                #endregion

                if (regression != null)
                {
                    #region Plotting Code
                    if (g != null)
                    {
                        double leftFittedVal = regression.ComputeY(rv.EarliestFrame);
                        double rightFittedVal = regression.ComputeY(rv.LatestFrame);

                        double err = 3 * regression.StdDev;

                        float leftAve = (float)(leftFittedVal - plottingContext.MinValue) * yScale + 5;
                        float rightAve = (float)(rightFittedVal - plottingContext.MinValue) * yScale + 5;
                        float leftX = 5 + (float)(rv.EarliestFrame - rv.EarliestFrame) * xScale;
                        float rightX = 5 + (float)(rv.LatestFrame - rv.EarliestFrame) * xScale;

                        g.DrawLine(plottingContext.AveragePen, leftX, leftAve - 1, rightX, rightAve - 1);
                        g.DrawLine(plottingContext.AveragePen, leftX, leftAve, rightX, rightAve);
                        g.DrawLine(plottingContext.AveragePen, leftX, leftAve + 1, rightX, rightAve + 1);

                        float leftMin = (float)(leftFittedVal - err - plottingContext.MinValue) * yScale + 5;
                        float leftMax = (float)(leftFittedVal + err - plottingContext.MinValue) * yScale + 5;
                        float rightMin = (float)(rightFittedVal - err - plottingContext.MinValue) * yScale + 5;
                        float rightMax = (float)(rightFittedVal + err - plottingContext.MinValue) * yScale + 5;

                        g.DrawLine(plottingContext.AveragePen, leftX, leftMin, rightX, rightMin);
                        g.DrawLine(plottingContext.AveragePen, leftX, leftMax, rightX, rightMax);
                    }
                    #endregion

                    if (int.MinValue != meaContext.UserMidFrame &&
                        resolvedTime != null)
                    {
                        // Find the closest video 'normal' MPC time and compute the frame number for it
                        // Now compute the RA/DE for the computed 'normal' frame

                        double fittedValueUncertainty;
                        double fittedValueAtMiddleFrame = regression.ComputeYWithError(resolvedTime.ClosestNormalFrameNo, out fittedValueUncertainty);

                        Trace.WriteLine(string.Format("{0}; Included: {1}; Normal Frame No: {2}; Fitted Val: {3} +/- {4:0.00}",
                            meaContext.UserMidValue.ToString("0.00000"),
                            numFramesUser, resolvedTime.ClosestNormalFrameNo,
                            AstroConvert.ToStringValue(fittedValueAtMiddleFrame, "+HH MM SS.T"),
                            regression.StdDev * 60 * 60));

                        // Report the interpolated position at the middle of the measured interval
                        // Don't forget to add the video normal position flag in the OBS file
                        // Expect elongated images and apply instrumental delay corrections

                        rv.FittedValue = fittedValueAtMiddleFrame;
                        rv.FittedValueTime = resolvedTime.ClosestNormalFrameTime;
                        rv.IsVideoNormalPosition = true;
                        rv.FittedNormalFrame = resolvedTime.ClosestNormalFrameNo;
                        rv.FittedValueUncertaintyArcSec = fittedValueUncertainty * 60 * 60;

                        #region Plotting Code
                        if (g != null)
                        {
                            // Plot the frame
                            float xPos = (float)(resolvedTime.ClosestNormalFrameNo - rv.EarliestFrame) * xScale + 5;
                            float yPos = (float)(rv.FittedValue - plottingContext.MinValue) * yScale + 5;
                            g.DrawLine(Pens.Yellow, xPos, 1, xPos, imageHight - 2);
                            g.FillEllipse(Brushes.Yellow, xPos - 3, yPos - 3, 6, 6);
                        }
                        #endregion
                    }
                    else
                        rv.FittedValue = double.NaN;
                }
                else
                    rv.FittedValue = double.NaN;

                return rv;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.GetFullStackTrace());
                motionRate = 0;
                return null;
            }
        }
Пример #3
0
        public ProcessingReturnValues FitAndPlotFastFlyby(
            Dictionary<int, SingleMultiFrameMeasurement> measurements,
            FlybyMeasurementContext meaContext,
            FittingContext fittingContext,
            FittingValue fittingValue,
            GetFrameStateDataCallback getFrameStateDataCallback,
            Graphics g, FlybyPlottingContext plottingContext, float xScale, float yScale, int imageWidth, int imageHeight,
            out double motionRate)
        {
            // Do linear regression, use residual based exclusion rules
            // Two possible modes: (A) non trailed and (B) trailed images
            // A) Non Trailed Images
            // Report interpolated times for video normal position [How to use the MPC page for this?]
            // Don't forget to add the video normal position flag in the OBS file
            // Expect elongated images and apply instrumental delay corrections (in both integrated and non integrated modes)
            // B) Trailed Images
            // TODO: R&D Required

            if (fittingContext.ObjectExposureQuality == ObjectExposureQuality.GoodSignal)
            {
                return FitAndPlotSlowFlyby(
                    measurements, meaContext, fittingContext, fittingValue, getFrameStateDataCallback,
                    g, plottingContext, xScale, yScale, imageWidth, imageHeight, out motionRate);
            }
            else
            {
                MessageBox.Show("This operation is currently not suppored.");
                motionRate = double.NaN;
                return new ProcessingReturnValues();
            }
        }
Пример #4
0
        public ProcessingReturnValues FitAndPlotSlowFlyby(
            Dictionary <int, SingleMultiFrameMeasurement> measurements,
            FlybyMeasurementContext meaContext,
            FittingContext fittingContext,
            FittingValue fittingValue,
            GetFrameStateDataCallback getFrameStateDataCallback,
            Graphics g, FlybyPlottingContext plottingContext, float xScale, float yScale, int imageWidth, int imageHight,
            out double motionRate)
        {
            try
            {
                #region Building Test Cases
                if (m_DumpTestCaseData)
                {
                    var mvSer = new XmlSerializer(typeof(FlybyMeasurementContext));
                    var sb    = new StringBuilder();
                    using (var wrt = new StringWriter(sb))
                    {
                        mvSer.Serialize(wrt, meaContext);
                    }
                    Trace.WriteLine(sb.ToString());
                    var fcSer = new XmlSerializer(typeof(FittingContext));
                    sb.Clear();
                    using (var wrt = new StringWriter(sb))
                    {
                        fcSer.Serialize(wrt, fittingContext);
                    }
                    Trace.WriteLine(sb.ToString());

                    var smfmSer = new XmlSerializer(typeof(SingleMultiFrameMeasurement));
                    foreach (int key in measurements.Keys)
                    {
                        sb.Clear();
                        using (var wrt2 = new StringWriter(sb))
                        {
                            smfmSer.Serialize(wrt2, measurements[key]);
                            if (measurements[key].FrameNo != key)
                            {
                                throw new InvalidOperationException();
                            }
                            Trace.WriteLine(sb.ToString());
                        }
                    }
                }
                #endregion

                // Do linear regression, use residual based exclusion rules
                // Report the interpolated position at the middle of the measured interva
                // Don't forget to add the video normal position flag in the OBS file
                // Expect elongated images and apply instrumental delay corrections

                motionRate = double.NaN;

                var rv = new ProcessingReturnValues();

                int numFramesUser = 0;

                rv.EarliestFrame = int.MaxValue;
                rv.LatestFrame   = int.MinValue;

                var intervalValues  = new Dictionary <int, Tuple <List <double>, List <double> > >();
                var intervalMedians = new Dictionary <double, double>();
                var intervalWeights = new Dictionary <double, double>();

                LinearRegression regression = null;
                if (measurements.Values.Count > 1)
                {
                    rv.EarliestFrame = measurements.Values.Select(m => m.FrameNo).Min();
                    rv.LatestFrame   = measurements.Values.Select(m => m.FrameNo).Max();

                    var minUncertainty = meaContext.MinPositionUncertaintyPixels * meaContext.ArsSecsInPixel;

                    foreach (SingleMultiFrameMeasurement measurement in measurements.Values)
                    {
                        int integrationInterval = (measurement.FrameNo - fittingContext.FirstFrameIdInIntegrationPeroid) / fittingContext.IntegratedFramesCount;

                        Tuple <List <double>, List <double> > intPoints;
                        if (!intervalValues.TryGetValue(integrationInterval, out intPoints))
                        {
                            intPoints = Tuple.Create(new List <double>(), new List <double>());
                            intervalValues.Add(integrationInterval, intPoints);
                        }

                        if (fittingValue == FittingValue.RA)
                        {
                            intPoints.Item1.Add(measurement.RADeg);
                            intPoints.Item2.Add(ComputePositionWeight(measurement.SolutionUncertaintyRACosDEArcSec, measurement, minUncertainty, fittingContext.Weighting));
                        }
                        else
                        {
                            intPoints.Item1.Add(measurement.DEDeg);
                            intPoints.Item2.Add(ComputePositionWeight(measurement.SolutionUncertaintyDEArcSec, measurement, minUncertainty, fittingContext.Weighting));
                        }
                    }

                    if (intervalValues.Count > 2)
                    {
                        regression = new LinearRegression();

                        foreach (int integratedFrameNo in intervalValues.Keys)
                        {
                            Tuple <List <double>, List <double> > data = intervalValues[integratedFrameNo];

                            double median;
                            double medianWeight;

                            WeightedMedian(data, out median, out medianWeight);

                            // Assign the data point to the middle of the integration interval (using frame numbers)
                            //
                            // |--|--|--|--|--|--|--|--|
                            // |           |           |
                            //
                            // Because the time associated with the first frame is the middle of the frame, but the
                            // time associated with the middle of the interval is the end of the field then the correction
                            // is (N / 2) - 0.5 frames when integration is used or no correction when integration of x1 is used.

                            double dataPointFrameNo =
                                rv.EarliestFrame +
                                fittingContext.IntegratedFramesCount * integratedFrameNo
                                + (fittingContext.IntegratedFramesCount / 2)
                                - (fittingContext.IntegratedFramesCount > 1 ? 0.5 : 0);

                            intervalMedians.Add(dataPointFrameNo, median);
                            intervalWeights.Add(dataPointFrameNo, medianWeight);
                            if (fittingContext.Weighting != WeightingMode.None)
                            {
                                regression.AddDataPoint(dataPointFrameNo, median, medianWeight);
                            }
                            else
                            {
                                regression.AddDataPoint(dataPointFrameNo, median);
                            }
                        }

                        regression.Solve();

                        var    firstPos       = measurements[rv.EarliestFrame];
                        var    lastPos        = measurements[rv.LatestFrame];
                        double distanceArcSec = AngleUtility.Elongation(firstPos.RADeg, firstPos.DEDeg, lastPos.RADeg, lastPos.DEDeg) * 3600;
                        var    firstTime      = GetTimeForFrame(fittingContext, rv.EarliestFrame, meaContext.FirstVideoFrame, getFrameStateDataCallback, firstPos.FrameTimeStamp);
                        var    lastTime       = GetTimeForFrame(fittingContext, rv.LatestFrame, meaContext.FirstVideoFrame, getFrameStateDataCallback, lastPos.FrameTimeStamp);
                        double elapsedSec     = new TimeSpan(lastTime.UT.Ticks - firstTime.UT.Ticks).TotalSeconds;
                        motionRate = distanceArcSec / elapsedSec;
                    }
                }

                FrameTime resolvedTime = null;
                if (int.MinValue != meaContext.UserMidFrame)
                {
                    // Find the closest video 'normal' MPC time and compute the frame number for it
                    // Now compute the RA/DE for the computed 'normal' frame
                    resolvedTime = GetTimeForFrame(fittingContext, meaContext.UserMidFrame, meaContext.FirstVideoFrame, getFrameStateDataCallback, measurements[meaContext.UserMidFrame].FrameTimeStamp);

                    #region Plotting Code
                    if (g != null)
                    {
                        float xPosBeg = (float)(resolvedTime.ClosestNormalIntervalFirstFrameNo - rv.EarliestFrame) * xScale + 5;
                        float xPosEnd = (float)(resolvedTime.ClosestNormalIntervalLastFrameNo - rv.EarliestFrame) * xScale + 5;

                        g.FillRectangle(s_NormalTimeIntervalHighlightBrush, xPosBeg, 1, (xPosEnd - xPosBeg), imageHight - 2);
                    }
                    #endregion
                }

                Dictionary <double, double> secondPassData = new Dictionary <double, double>();

                int minFrameId = measurements.Keys.Min();

                #region Plotting Code
                if (g != null)
                {
                    foreach (SingleMultiFrameMeasurement measurement in measurements.Values)
                    {
                        float x = (measurement.FrameNo - minFrameId) * xScale + 5;

                        ProcessingValues val = new ProcessingValues()
                        {
                            Value  = fittingValue == FittingValue.RA ? measurement.RADeg : measurement.DEDeg,
                            StdDev = fittingValue == FittingValue.RA ? measurement.StdDevRAArcSec / 3600.0 : measurement.StdDevDEArcSec / 3600.0
                        };

                        double valueFrom = val.Value - val.StdDev;
                        double valueTo   = val.Value + val.StdDev;

                        float yFrom = (float)(valueFrom - plottingContext.MinValue) * yScale + 5;
                        float yTo   = (float)(valueTo - plottingContext.MinValue) * yScale + 5;

                        g.DrawLine(plottingContext.IncludedPen, x, yFrom, x, yTo);
                        g.DrawLine(plottingContext.IncludedPen, x - 1, yFrom, x + 1, yFrom);
                        g.DrawLine(plottingContext.IncludedPen, x - 1, yTo, x + 1, yTo);
                    }
                }
                #endregion

                foreach (double integrFrameNo in intervalMedians.Keys)
                {
                    double val = intervalMedians[integrFrameNo];

                    double fittedValAtFrame = regression != null
                        ? regression.ComputeY(integrFrameNo)
                        : double.NaN;

                    bool included = Math.Abs(fittedValAtFrame - val) < 3 * regression.StdDev;

                    #region Plotting Code
                    if (g != null)
                    {
                        if (fittingContext.IntegratedFramesCount > 1)
                        {
                            Pen mPen = included ? plottingContext.IncludedPen : plottingContext.ExcludedPen;

                            float x = (float)(integrFrameNo - minFrameId) * xScale + 5;
                            float y = (float)(val - plottingContext.MinValue) * yScale + 5;

                            g.DrawEllipse(mPen, x - 3, y - 3, 6, 6);
                            g.DrawLine(mPen, x - 5, y - 5, x + 5, y + 5);
                            g.DrawLine(mPen, x + 5, y - 5, x - 5, y + 5);
                        }
                    }
                    #endregion

                    if (included)
                    {
                        secondPassData.Add(integrFrameNo, val);
                    }
                }

                #region Second Pass
                regression = null;
                if (secondPassData.Count > 2)
                {
                    regression = new LinearRegression();
                    foreach (double frameNo in secondPassData.Keys)
                    {
                        if (fittingContext.Weighting != WeightingMode.None)
                        {
                            regression.AddDataPoint(frameNo, secondPassData[frameNo], intervalWeights[frameNo]);
                        }
                        else
                        {
                            regression.AddDataPoint(frameNo, secondPassData[frameNo]);
                        }
                    }
                    regression.Solve();
                }
                #endregion

                if (regression != null)
                {
                    #region Plotting Code
                    if (g != null)
                    {
                        double leftFittedVal  = regression.ComputeY(rv.EarliestFrame);
                        double rightFittedVal = regression.ComputeY(rv.LatestFrame);

                        double err = 3 * regression.StdDev;

                        float leftAve  = (float)(leftFittedVal - plottingContext.MinValue) * yScale + 5;
                        float rightAve = (float)(rightFittedVal - plottingContext.MinValue) * yScale + 5;
                        float leftX    = 5 + (float)(rv.EarliestFrame - rv.EarliestFrame) * xScale;
                        float rightX   = 5 + (float)(rv.LatestFrame - rv.EarliestFrame) * xScale;

                        g.DrawLine(plottingContext.AveragePen, leftX, leftAve - 1, rightX, rightAve - 1);
                        g.DrawLine(plottingContext.AveragePen, leftX, leftAve, rightX, rightAve);
                        g.DrawLine(plottingContext.AveragePen, leftX, leftAve + 1, rightX, rightAve + 1);

                        float leftMin  = (float)(leftFittedVal - err - plottingContext.MinValue) * yScale + 5;
                        float leftMax  = (float)(leftFittedVal + err - plottingContext.MinValue) * yScale + 5;
                        float rightMin = (float)(rightFittedVal - err - plottingContext.MinValue) * yScale + 5;
                        float rightMax = (float)(rightFittedVal + err - plottingContext.MinValue) * yScale + 5;

                        g.DrawLine(plottingContext.AveragePen, leftX, leftMin, rightX, rightMin);
                        g.DrawLine(plottingContext.AveragePen, leftX, leftMax, rightX, rightMax);
                    }
                    #endregion

                    if (int.MinValue != meaContext.UserMidFrame &&
                        resolvedTime != null)
                    {
                        // Find the closest video 'normal' MPC time and compute the frame number for it
                        // Now compute the RA/DE for the computed 'normal' frame

                        double fittedValueUncertainty;
                        double fittedValueAtMiddleFrame = regression.ComputeYWithError(resolvedTime.ClosestNormalFrameNo, out fittedValueUncertainty);

                        Trace.WriteLine(string.Format("{0}; Included: {1}; Normal Frame No: {2}; Fitted Val: {3} +/- {4:0.00}",
                                                      meaContext.UserMidValue.ToString("0.00000"),
                                                      numFramesUser, resolvedTime.ClosestNormalFrameNo,
                                                      AstroConvert.ToStringValue(fittedValueAtMiddleFrame, "+HH MM SS.T"),
                                                      regression.StdDev * 60 * 60));

                        // Report the interpolated position at the middle of the measured interval
                        // Don't forget to add the video normal position flag in the OBS file
                        // Expect elongated images and apply instrumental delay corrections

                        rv.FittedValue                  = fittedValueAtMiddleFrame;
                        rv.FittedValueTime              = resolvedTime.ClosestNormalFrameTime;
                        rv.IsVideoNormalPosition        = true;
                        rv.FittedNormalFrame            = resolvedTime.ClosestNormalFrameNo;
                        rv.FittedValueUncertaintyArcSec = fittedValueUncertainty * 60 * 60;

                        #region Plotting Code
                        if (g != null)
                        {
                            // Plot the frame
                            float xPos = (float)(resolvedTime.ClosestNormalFrameNo - rv.EarliestFrame) * xScale + 5;
                            float yPos = (float)(rv.FittedValue - plottingContext.MinValue) * yScale + 5;
                            g.DrawLine(Pens.Yellow, xPos, 1, xPos, imageHight - 2);
                            g.FillEllipse(Brushes.Yellow, xPos - 3, yPos - 3, 6, 6);
                        }
                        #endregion
                    }
                    else
                    {
                        rv.FittedValue = double.NaN;
                    }
                }
                else
                {
                    rv.FittedValue = double.NaN;
                }

                return(rv);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.GetFullStackTrace());
                motionRate = 0;
                return(null);
            }
        }