Exemple #1
0
        public override void MouseClick(ObjectClickEventArgs e)
        {
            if (m_State == FittingsState.Configuring)
            {
                m_OSDExcluderTool.MouseClick(e);
            }
            if (m_State == FittingsState.Solved)
            {
                IStarMap starMap = AstrometryContext.Current.StarMap;
                if (starMap != null)
                {
                    int            x, y;
                    StarMapFeature feature = starMap.GetFeatureInRadius(e.Pixel.X, e.Pixel.Y, 5);
                    if (feature != null)
                    {
                        x = feature.GetCenter().X;
                        y = feature.GetCenter().Y;
                    }
                    else
                    {
                        x = e.Pixel.X;
                        y = e.Pixel.Y;
                    }

                    int searchArea = Control.ModifierKeys == Keys.Shift ? 5 : 10;

                    PSFFit     psfFit;
                    ImagePixel pixelCent = starMap.GetPSFFit(x, y, searchArea, out psfFit);
                    if (pixelCent != null && pixelCent != ImagePixel.Unspecified)
                    {
                        PlateConstStarPair          selectedPair = null;
                        LeastSquareFittedAstrometry astrometry   = FittedAstrometryFromUserSelectedFitGrade();
                        if (astrometry != null)
                        {
                            foreach (PlateConstStarPair pair in astrometry.FitInfo.AllStarPairs)
                            {
                                if (Math.Abs(pair.x - pixelCent.X) < 2 &&
                                    Math.Abs(pair.y - pixelCent.Y) < 2)
                                {
                                    selectedPair = pair;
                                    break;
                                }
                            }
                        }

                        DrawHighResFeature(pixelCent, selectedPair, astrometry);
                    }
                    else
                    {
                        ClearZoomImage();
                    }
                }
            }
        }
Exemple #2
0
        public void NextFrame(int frameNo, IAstroImage astroImage, IStarMap starMap, LeastSquareFittedAstrometry astrometricFit)
        {
            IsTrackedSuccessfully = false;

            ImagePixel centroid = AstrometryContext.Current.StarMap.GetCentroid(
                (int)TrackedObject.LastKnownX,
                (int)TrackedObject.LastKnownY,
                CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius);

            if (centroid != null)
            {
                PSFFit psfFit;
                AstrometryContext.Current.StarMap.GetPSFFit(
                    centroid.X, centroid.Y, PSFFittingMethod.NonLinearFit, out psfFit);

                if (psfFit != null)
                {
                    double ra, de;
                    astrometricFit.GetRADEFromImageCoords(psfFit.XCenter, psfFit.YCenter, out ra, out de);

                    double maxPosDiffArcSec =
                            astrometricFit.GetDistanceInArcSec(astrometricFit.Image.CenterXImage, astrometricFit.Image.CenterYImage,
                            astrometricFit.Image.CenterXImage + CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius, astrometricFit.Image.CenterYImage);

                    if (!double.IsNaN(TrackedObject.RAHours))
                    {
                        double posDif = 3600 * AngleUtility.Elongation(15 * TrackedObject.RAHours, TrackedObject.DEDeg, ra, de);
                        if (posDif > maxPosDiffArcSec)
                        {
                            // NOTE: Not a valid measurement
                            Trace.WriteLine(string.Format("The target position is too far from the last measured position", posDif));
                            return;
                        }
                    }

                    TrackedObject.RAHours = ra / 15.0;
                    TrackedObject.DEDeg = de;
                    TrackedObject.LastKnownX = psfFit.XCenter;
                    TrackedObject.LastKnownY = psfFit.YCenter;
                    TrackedObject.PSFFit = psfFit;

                    IsTrackedSuccessfully = true;
                }
            }
        }
Exemple #3
0
        public void NextFrame(int frameNo, IAstroImage astroImage, IStarMap starMap, LeastSquareFittedAstrometry astrometricFit)
        {
            IsTrackedSuccessfully = false;

            ImagePixel centroid = AstrometryContext.Current.StarMap.GetCentroid(
                (int)TrackedObject.LastKnownX,
                (int)TrackedObject.LastKnownY,
                CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius);

            if (centroid != null)
            {
                PSFFit psfFit;
                AstrometryContext.Current.StarMap.GetPSFFit(
                    centroid.X, centroid.Y, PSFFittingMethod.NonLinearFit, out psfFit);

                if (psfFit != null)
                {
                    double ra, de;
                    astrometricFit.GetRADEFromImageCoords(psfFit.XCenter, psfFit.YCenter, out ra, out de);

                    double maxPosDiffArcSec =
                        astrometricFit.GetDistanceInArcSec(astrometricFit.Image.CenterXImage, astrometricFit.Image.CenterYImage,
                                                           astrometricFit.Image.CenterXImage + CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius, astrometricFit.Image.CenterYImage);

                    if (!double.IsNaN(TrackedObject.RAHours))
                    {
                        double posDif = 3600 * AngleUtility.Elongation(15 * TrackedObject.RAHours, TrackedObject.DEDeg, ra, de);
                        if (posDif > maxPosDiffArcSec)
                        {
                            // NOTE: Not a valid measurement
                            Trace.WriteLine(string.Format("The target position is too far from the last measured position", posDif));
                            return;
                        }
                    }

                    TrackedObject.RAHours    = ra / 15.0;
                    TrackedObject.DEDeg      = de;
                    TrackedObject.LastKnownX = psfFit.XCenter;
                    TrackedObject.LastKnownY = psfFit.YCenter;
                    TrackedObject.PSFFit     = psfFit;

                    IsTrackedSuccessfully = true;
                }
            }
        }
Exemple #4
0
        public void InitNewMatch(IStarMap imageFeatures, PyramidMatchType matchType, Dictionary <PSFFit, IStar> manualStars)
        {
            m_StarMap = imageFeatures;

            if (manualStars != null)
            {
                SetManuallyIdentifiedHints(manualStars);
            }

            matchType = PyramidMatchType.PlateSolve;

            m_IsCalibration = matchType == PyramidMatchType.ConfigCalibration;

            Context.Initialize(m_RA0Deg, m_DE0Deg, m_CelestialStars, m_PyramidMinMag, m_PyramidMaxMag, m_DetermineAutoLimitMagnitude, m_ManualStarMatch);

#if ASTROMETRY_DEBUG
            AstrometricFitDebugger.Init(m_FitSettings, m_PyramidMinMag, m_PyramidMaxMag, m_AstrometryMinMag, m_AstrometryMaxMag);
#endif
        }
		internal FieldAlignmentResult DoFieldAlignment(IStarMap starMap)
		{
			return DoFieldAlignment(starMap, (FitInfo)null, false);
		}
		private bool CheckTriangleWithRatios(IStarMap starMap, CheckTriangleWithRatiosCallback callback, int i, int j, int k, double toleranceInArcSec)
		{
			m_Feature_i = starMap.Features[i - 1];
			m_Feature_j = starMap.Features[j - 1];
			m_Feature_k = starMap.Features[k - 1];

			m_FeatureId_i = m_Feature_i.FeatureId;
			m_FeatureId_j = m_Feature_j.FeatureId;
			m_FeatureId_k = m_Feature_k.FeatureId;

			long idx_ij = (m_FeatureId_i << 32) + m_FeatureId_j;
			long idx_ik = (m_FeatureId_i << 32) + m_FeatureId_k;
			long idx_jk = (m_FeatureId_j << 32) + m_FeatureId_k;

			ImagePixel feature_i_Center = null;
			ImagePixel feature_j_Center = null;
			ImagePixel feature_k_Center = null;

			double dist_ij;
			if (!m_FeaturesDistanceCache.TryGetValue(idx_ij, out dist_ij))
			{
				if (feature_i_Center == null) feature_i_Center = GetCenterOfFeature(starMap.GetFeatureById((int)m_FeatureId_i), starMap);
				if (feature_j_Center == null) feature_j_Center = GetCenterOfFeature(starMap.GetFeatureById((int)m_FeatureId_j), starMap);
			}

			double dist_ik;
			if (!m_FeaturesDistanceCache.TryGetValue(idx_ik, out dist_ik))
			{
				if (feature_i_Center == null) feature_i_Center = GetCenterOfFeature(starMap.GetFeatureById((int)m_FeatureId_i), starMap);
				if (feature_k_Center == null) feature_k_Center = GetCenterOfFeature(starMap.GetFeatureById((int)m_FeatureId_k), starMap);
			}

			double dist_jk;
			if (!m_FeaturesDistanceCache.TryGetValue(idx_jk, out dist_jk))
			{
				if (feature_j_Center == null) feature_j_Center = GetCenterOfFeature(starMap.GetFeatureById((int)m_FeatureId_j), starMap);
				if (feature_k_Center == null) feature_k_Center = GetCenterOfFeature(starMap.GetFeatureById((int)m_FeatureId_k), starMap);
			}

			if (callback(i, j, k, feature_i_Center, feature_j_Center, feature_k_Center, toleranceInArcSec))
				// solution found
				return true;

			return false;			
		}
Exemple #7
0
        public void NextFrame(int frameNo, IAstroImage astroImage, IStarMap starMap, LeastSquareFittedAstrometry astrometricFit)
        {
            IsTrackedSuccessfully = false;
            int searchRadius = (int)Math.Ceiling(Math.Max(m_LastMovementPixels, CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius));

            PSFFit psfFit = null;

            int startingX = (int)TrackedObject.LastKnownX;
            int startingY = (int)TrackedObject.LastKnownY;

            if (m_RepeatedIntergationPositions * 4 < m_PastFrameNos.Count)
            {
                var expectedPos = GetExpectedPosition(frameNo);
                if (expectedPos != null)
                {
                    startingX = expectedPos.X;
                    startingY = expectedPos.Y;
                }
            }

            var nearbyFeatures = starMap.GetFeaturesInRadius(startingX, startingY, searchRadius).ToArray();

            var nonStarNearbyFeature = new List <StarMapFeature>();

            foreach (var feature in nearbyFeatures)
            {
                var center = feature.GetCenter();
                var referenceStarFeatures = astrometricFit.FitInfo.AllStarPairs.Where(x => x.FitInfo.UsedInSolution).ToList();
                var refStar = referenceStarFeatures.FirstOrDefault(s => Math.Sqrt((s.x - center.X) * (s.x - center.X) + (s.y - center.Y) * (s.y - center.Y)) < 2);

                double raf, def;
                astrometricFit.GetRADEFromImageCoords(center.XDouble, center.YDouble, out raf, out def);
                var pastKnownStar = m_LastFrameStars.FirstOrDefault(s => AngleUtility.Elongation(s.RADeg, s.DEDeg, raf, def) * 3600.0 < 2);

                if (refStar == null && pastKnownStar == null)
                {
                    nonStarNearbyFeature.Add(feature);
                }
            }

            if (nonStarNearbyFeature.Count > 0)
            {
                StarMapFeature closestFeature   = nonStarNearbyFeature[0];
                var            lastKnownCenter  = new ImagePixel(TrackedObject.LastKnownX, TrackedObject.LastKnownY);
                var            smallestDistance = lastKnownCenter.DistanceTo(closestFeature.GetCenter());

                for (int i = 1; i < nonStarNearbyFeature.Count; i++)
                {
                    var distance = lastKnownCenter.DistanceTo(nonStarNearbyFeature[i].GetCenter());
                    if (distance < smallestDistance)
                    {
                        smallestDistance = distance;
                        closestFeature   = nonStarNearbyFeature[i];
                    }
                }

                if (closestFeature != null)
                {
                    var center = closestFeature.GetCenter();
                    AstrometryContext.Current.StarMap.GetPSFFit(center.X, center.Y, PSFFittingMethod.NonLinearFit, out psfFit);
                }
            }

            if (psfFit == null)
            {
                // The expected location cannot be matched with any brighter feature so it is likely a faint object
                // with no brighter objects around. Lets find the brightest (faint) object in the are and use it
                ImagePixel centroid = AstrometryContext.Current.StarMap.GetCentroid(startingX, startingY, searchRadius);

                if (centroid != null)
                {
                    AstrometryContext.Current.StarMap.GetPSFFit(centroid.X, centroid.Y, PSFFittingMethod.NonLinearFit, out psfFit);
                }
            }

            if (psfFit != null)
            {
                double ra, de;
                astrometricFit.GetRADEFromImageCoords(psfFit.XCenter, psfFit.YCenter, out ra, out de);

                double maxPosDiffArcSec =
                    astrometricFit.GetDistanceInArcSec(astrometricFit.Image.CenterXImage, astrometricFit.Image.CenterYImage,
                                                       astrometricFit.Image.CenterXImage + Math.Max(m_LastMovementPixels, CoreAstrometrySettings.Default.MaxAllowedDefaultMotionInPixels), astrometricFit.Image.CenterYImage);

                if (!double.IsNaN(TrackedObject.RAHours))
                {
                    double posDif = 3600 * AngleUtility.Elongation(15 * TrackedObject.RAHours, TrackedObject.DEDeg, ra, de);
                    if (posDif > maxPosDiffArcSec)
                    {
                        // NOTE: Not a valid measurement
                        Trace.WriteLine(string.Format("The target position is too far from the last measured position", posDif));
                        return;
                    }
                }

                TrackedObject.RAHours = ra / 15.0;
                TrackedObject.DEDeg   = de;

                if (TrackedObject.PSFFit != null)
                {
                    m_LastMovementPixels = 1.2 * ImagePixel.ComputeDistance(TrackedObject.LastKnownX, psfFit.XCenter, TrackedObject.LastKnownY, psfFit.YCenter);
                }
                TrackedObject.LastKnownX = psfFit.XCenter;
                TrackedObject.LastKnownY = psfFit.YCenter;
                TrackedObject.PSFFit     = psfFit;

                var lastKnownCenter = new ImagePixel(TrackedObject.LastKnownX, TrackedObject.LastKnownY);
                var thisFrameStars  = astrometricFit.FitInfo.AllStarPairs.Where(x => lastKnownCenter.DistanceTo(x.x, x.y) > 2 * psfFit.FWHM).ToList();
                if (thisFrameStars.Count > 0)
                {
                    m_LastFrameStars = thisFrameStars.Select(x => new Star(x.StarNo, x.RADeg, x.DEDeg, x.Mag) as IStar).ToList();
                }

                IsTrackedSuccessfully = true;
            }

            if (psfFit != null && psfFit.XCenter > 0 && psfFit.YCenter > 0)
            {
                m_PastFramePosX.Add(psfFit.XCenter);
                m_PastFramePosY.Add(psfFit.YCenter);
                m_PastFrameNos.Add(frameNo);
            }
        }
Exemple #8
0
        public override void MouseMove(Point location)
        {
            IStarMap map = AstrometryContext.Current.StarMap;

            if (map == null)
            {
                return;
            }

            bool nearbyStarFound = false;

            AstrometricState state = AstrometryContext.Current.AstrometricState;

            if (state != null)
            {
                if (state.AstrometricFit != null)
                {
                    for (int radius = 1; radius < 8; radius++)
                    {
                        ImagePixel centroid = map.GetCentroid(location.X, location.Y, radius);
                        if (centroid == null)
                        {
                            continue;
                        }


                        foreach (PlateConstStarPair star in state.AstrometricFit.FitInfo.AllStarPairs)
                        {
                            if (Math.Abs(star.x - centroid.XDouble) < radius &&
                                Math.Abs(star.y - centroid.YDouble) < radius)
                            {
                                m_Object = star;

                                nearbyStarFound = true;
                                break;
                            }
                        }

                        if (nearbyStarFound)
                        {
                            break;
                        }
                    }

                    if (!nearbyStarFound)
                    {
                        m_State = SelectObjectState.NoObject;
                    }
                    else
                    {
                        m_State = SelectObjectState.ObjectLocked;
                    }


                    if (m_AstrometricState.MeasuringState == AstrometryInFramesState.Ready)
                    {
                        double ra, de;
                        state.AstrometricFit.GetRADEFromImageCoords(location.X, location.Y, out ra, out de);

                        string moreInfo = string.Format("RA={0} DE={1}", AstroConvert.ToStringValue(ra / 15, "HHhMMmSS.Ts"), AstroConvert.ToStringValue(de, "+DD°MM'SS\""));
                        m_VideoController.DisplayCursorPositionDetails(location, moreInfo);
                    }
                }
                else
                {
                    StarMapFeature nearbyFeature = map.GetFeatureInRadius(location.X, location.Y, 8);
                    nearbyStarFound = nearbyFeature != null && nearbyFeature.PixelCount > 4;
                }

                m_VideoController.SetPictureBoxCursor(nearbyStarFound ? Cursors.Hand : (state.ManualStarIdentificationMode ? Cursors.Cross : Cursors.Default));
            }
        }
 public void InitNewFrame(IStarMap imageFeatures)
 {
     m_StarMap = imageFeatures;
     // NOTE: The m_Solution will be reused until the DistanceBasedAstrometrySolver is recreated
 }
		internal FieldAlignmentResult DoFieldAlignmentFitFocalLength(IStarMap starMap)
		{
			return DoFieldAlignment(starMap, (FitInfo)null, true);
		}
		private LeastSquareFittedAstrometry SolveStarPairs(
			IStarMap starMap,
			Dictionary<ImagePixel, IStar> matchedPairs,
			Dictionary<int, ulong> matchedFeatureIdToStarIdIndexes,
			ThreeStarFit.StarPair pair_i,
			ThreeStarFit.StarPair pair_j,
			ThreeStarFit.StarPair pair_k,
			double fittedFocalLength,
			PyramidEntry pyramidLog, 
            int? minMatchedStars = null)
		{
			double RA0Deg, DE0Deg;

			ThreeStarFit coarseFit = new ThreeStarFit(m_PlateConfig, pair_i, pair_j, pair_k);
			if (!coarseFit.IsSolved)
			{
				if (coarseFit.IsSingularity)
				{
                    if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
					    Trace.WriteLine("ThreeStarFit.Var1 - Singularity");

					Dictionary<ImagePixel, IStar> threeStarDict = new Dictionary<ImagePixel, IStar>();

				    try
				    {
                        threeStarDict.Add(ImagePixel.CreateImagePixelWithFeatureId(0, 255, pair_i.XImage, pair_i.YImage), pair_i.Star);
                        threeStarDict.Add(ImagePixel.CreateImagePixelWithFeatureId(1, 255, pair_j.XImage, pair_j.YImage), pair_j.Star);
                        threeStarDict.Add(ImagePixel.CreateImagePixelWithFeatureId(2, 255, pair_k.XImage, pair_k.YImage), pair_k.Star);
				    }
                    catch(ArgumentException)
                    {
                        if (pyramidLog != null) pyramidLog.FailureReason = PyramidEntryFailureReason.ThreeStarFitFailed;

                        if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
                            Trace.WriteLine("ThreeStarFit.Var2 - Failed with ArgumentException");

                        return null;
                    }

					DirectTransRotAstrometry threeStarSolution = 
						DirectTransRotAstrometry.SolveByThreeStars(m_PlateConfig, threeStarDict, 2);

					if (threeStarSolution == null)
					{
						if (pyramidLog != null) pyramidLog.FailureReason = PyramidEntryFailureReason.ThreeStarFitFailed;

                        if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
						    Trace.WriteLine("ThreeStarFit.Var2 - Failed");
						return null;						
					}
					else
					{
						RA0Deg = threeStarSolution.RA0Deg;
						DE0Deg = threeStarSolution.DE0Deg;
					}
				}
				else
				{
					if (pyramidLog != null) pyramidLog.FailureReason = PyramidEntryFailureReason.ThreeStarFitFailed;

                    if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
					    Trace.WriteLine("ThreeStarFit.Var1 - Failed");
					return null;					
				}
			}
			else
			{
				if (pyramidLog != null) pyramidLog.RegisterThreeStarFit(coarseFit);
				RA0Deg = coarseFit.RA0Deg;
				DE0Deg = coarseFit.DE0Deg;
			}
			

#if DEBUG || PYRAMID_DEBUG
		    if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
		    {
                foreach (int key in matchedFeatureIdToStarIdIndexes.Keys)
#if PYRAMID_DEBUG
                    Trace
#else
                    Debug
#endif
                    .WriteLine(string.Format("Star({0}) -> Feature({1})", matchedFeatureIdToStarIdIndexes[key], key));
		    }
#endif

            PlateConstantsSolver solver = new PlateConstantsSolver(m_PlateConfig);
			solver.InitPlateSolution(RA0Deg, DE0Deg);
		    foreach (ImagePixel feature in matchedPairs.Keys)
		    {
		        IStar star = matchedPairs[feature];
		        var kvp = matchedFeatureIdToStarIdIndexes.Single(x => x.Value == star.StarNo);
		        int featureId = kvp.Key;
                solver.AddStar(feature, star, featureId);
		    }

		    LeastSquareFittedAstrometry leastSquareFittedAstrometry = null;
			LeastSquareFittedAstrometry firstFit = null;
			try
			{
				// This is a linear regression when doing simple field alignment. We always use a Linear Fit
				leastSquareFittedAstrometry = solver.SolveWithLinearRegression(
					FitOrder.Linear,
				 	CorePyramidConfig.Default.MinPyramidAlignedStars,
					m_MaxLeastSquareResidual, 
					out firstFit);
			}
			catch (DivideByZeroException)
			{ }

			if (leastSquareFittedAstrometry != null)
			{
				if (pyramidLog != null) pyramidLog.RegisterLinearFit(leastSquareFittedAstrometry);

                if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
				    Trace.WriteLine("Checking possible solution. ");

				List<ulong> usedStarIds = leastSquareFittedAstrometry.FitInfo.AllStarPairs
					.Where(p => p.FitInfo.UsedInSolution)
					.Select(p => p.StarNo)
					.ToList();

				int usedStars = usedStarIds.Count;

				matchedFeatureIdToStarIdIndexes = matchedFeatureIdToStarIdIndexes
					.Where(kvp => usedStarIds.Contains(kvp.Value))
					.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

				List<double> residuals = 
					leastSquareFittedAstrometry.FitInfo.AllStarPairs
						.Where(p => !p.FitInfo.ExcludedForHighResidual)
						.Select(p => p.FitInfo.ResidualArcSec)
						.ToList();


				double secondLargeResidual = 0;

				if (residuals.Count > 0)
				{
					residuals = residuals.OrderByDescending(r => r).ToList();
					secondLargeResidual = residuals.Count > 1 ? residuals[1] : residuals[0];
				}

				double onePixDistArcSec = m_PlateConfig.GetDistanceInArcSec(0, 0, 1, 1);
				if (secondLargeResidual > onePixDistArcSec * CorePyramidConfig.Default.MaxAllowedResidualInPixelsInSuccessfulFit)
				{
					if (pyramidLog != null) pyramidLog.FailureReason = PyramidEntryFailureReason.SecondLargestResidualIsTooLarge;

                    if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
					    Trace.WriteLine(string.Format(
						    "Failing preliminary solution because the second largest residual {0}\" is larger than {1}px",
						    secondLargeResidual.ToString("0.0"), CorePyramidConfig.Default.MaxAllowedResidualInPixelsInSuccessfulFit));

					return null;
				}

                if (minMatchedStars.HasValue)
				{
                    if (usedStars < minMatchedStars.Value)
					{
						if (pyramidLog != null) pyramidLog.FailureReason = PyramidEntryFailureReason.InsufficientStarsForCalibration;

                        if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
						    Trace.WriteLine(string.Format(
						    "Failing preliminary solution because on {0} stars are used but {1} are required as a minimum for calibration.",
						    usedStars, CorePyramidConfig.Default.MinMatchedStarsForCalibration));

						return null;
					}
				}
			}
			else
			{
				if (pyramidLog != null) pyramidLog.FailureReason = PyramidEntryFailureReason.LinearFitFailed;

			    if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
			    {
                    Debug.WriteLine("DistanceBasedContext.LeastSquareFittedAstrometry Failed!");

                    foreach (PlateConstStarPair pair in solver.Pairs)
                    {
#if PYRAMID_DEBUG
                        Trace
#else
                        Debug
#endif
                        .WriteLine(string.Format("{0} ({1}) -> Residuals: {2}\", {3}\"", pair.StarNo,
                                                      pair.FitInfo.UsedInSolution ? "Included" : "Excluded",
                                                      pair.FitInfo.ResidualRAArcSec.ToString("0.00"),
                                                      pair.FitInfo.ResidualDEArcSec.ToString("0.00")));
                    }

			    }
			}

			if (leastSquareFittedAstrometry != null)
			{
				leastSquareFittedAstrometry.FitInfo.FittedFocalLength = fittedFocalLength;
				if (pyramidLog != null) pyramidLog.RegisterFocalLength(fittedFocalLength);
			}

			return leastSquareFittedAstrometry;
		}
		internal IAstrometricFit IsSuccessfulMatch(
			IStarMap starMap,
			int i, int j, int k,
			DistanceEntry ijEntry, DistanceEntry ikEntry, DistanceEntry jkEntry,
            ulong iStarNo, ulong starNo2, ulong starNo3, double fittedFocalLength, bool isRatioFittedFocalLength, double toleranceInArcSec)
		{
			i--;
			j--;
			k--;
			ImagePixel feature_i = GetCenterOfFeature(starMap.GetFeatureById(i), starMap);
			ImagePixel feature_j = GetCenterOfFeature(starMap.GetFeatureById(j), starMap);
			ImagePixel feature_k = GetCenterOfFeature(starMap.GetFeatureById(k), starMap);

			#region find the numbers of the three stars: i, j, k
#if ASTROMETRY_DEBUG
			Trace.Assert(ijEntry.Star1.StarNo == iStarNo || ijEntry.Star2.StarNo == iStarNo);
#endif

            ulong jStarNo, kStarNo;
			if (ijEntry.Star1.StarNo == iStarNo)
			{
				jStarNo = ijEntry.Star2.StarNo;
				if (ijEntry.Star2.StarNo == starNo2) kStarNo = starNo3; else kStarNo = starNo2;
			}
			else
			{
				jStarNo = ijEntry.Star1.StarNo;
				if (ijEntry.Star1.StarNo == starNo2) kStarNo = starNo3; else kStarNo = starNo2;
			}

#if ASTROMETRY_DEBUG
			Trace.Assert(ikEntry.Star1.StarNo == kStarNo || ikEntry.Star2.StarNo == kStarNo);
			Trace.Assert(jkEntry.Star1.StarNo == kStarNo || jkEntry.Star2.StarNo == kStarNo);
#endif

			#endregion

			//if (DebugResolvedStars != null)
			//{
			//    uint ii = 0, jj = 0, kk = 0;
			//    if (DebugResolvedStars.TryGetValue(i + 1, out ii) &&
			//        DebugResolvedStars.TryGetValue(j + 1, out jj) &&
			//        DebugResolvedStars.TryGetValue(k + 1, out kk))
			//    {
			//        if (ii == iStarNo && jj == starNo2 && kk == starNo3)
			//        {
			//            Debugger.Break();
			//        }
			//        else
			//            Trace.WriteLine(string.Format("PYRAMID: {0} = {1}, {2} = {3}, {4} = {5}",
			//                                        i, i == ii ? "YES" : "NO"
			//                                      , j, j == jj ? "YES" : "NO"
			//                                      , k, k == kk ? "YES" : "NO"));
			//    }
			//    else
			//        Trace.WriteLine(string.Format("PYRAMID: {0} = {1}, {2} = {3}, {4} = {5}",
			//                                        i, i == ii ? "YES" : "MISSING"
			//                                      , j, j == jj ? "YES" : "MISSING"
			//                                      , k, k == kk ? "YES" : "MISSING"));
			//}

#if ASTROMETRY_DEBUG
            PyramidEntry pyramidLog = new PyramidEntry(i, j, k, feature_i, feature_j, feature_k, iStarNo, jStarNo, kStarNo);
#endif
            //// Note this is actually cheap way to confirm whether the 3 stars are good or not.
            //List<IStar> threeStars = m_CelestialAllStars.FindAll(s => s.StarNo == iStarNo || s.StarNo == jStarNo || s.StarNo == kStarNo);
            //if (threeStars.Count == 3)
            //{
            //    Dictionary<AstroPixel, IStar> threeStarDict = new Dictionary<AstroPixel, IStar>();

            //    IStar stari = threeStars.Find(s => s.StarNo == iStarNo);
            //    threeStarDict.Add(feature_i, stari);

            //    IStar starj = threeStars.Find(s => s.StarNo == jStarNo);
            //    threeStarDict.Add(feature_j, starj);

            //    IStar stark = threeStars.Find(s => s.StarNo == kStarNo);
            //    threeStarDict.Add(feature_k, stark);

            //    DirectTransRotAstrometry solution = DirectTransRotAstrometry.SolveByThreeStars(m_PlateConfig, threeStarDict);
            //    if (solution != null)
            //    {
            //        pyramidLog.RegisterPreliminaryThreeStarFit(solution);
            //    }
            //}

		    int locatedStars = 3;
			m_MatchedPairs.Clear();
			m_AmbiguousMatches.Clear();
			m_MatchedFeatureIdToStarIdIndexes.Clear();

            List<ulong> usedPyramidAngles = new List<ulong>();

			foreach (StarMapFeature feature in starMap.Features)
			{
				if (feature.FeatureId == i) continue;
				if (feature.FeatureId == j) continue;
				if (feature.FeatureId == k) continue;

				long idx_ix = ((long)i << 32) + (long)feature.FeatureId;

				double dist_ix;
				ImagePixel feature_x = GetCenterOfFeature(feature, starMap);

				if (m_MatchedPairs.ContainsKey(feature_x)) continue;

				if (isRatioFittedFocalLength || !m_FeaturesDistanceCache.TryGetValue(idx_ix, out dist_ix))
				{
					dist_ix = m_PlateConfig.GetDistanceInArcSec(feature_i.X, feature_i.Y, feature_x.X, feature_x.Y, fittedFocalLength);
					long idx_xi = ((long)feature.FeatureId << 32) + (long)i;

					if (!isRatioFittedFocalLength)
					{
						m_FeaturesDistanceCache.Add(idx_ix, dist_ix);
						m_FeaturesDistanceCache.Add(idx_xi, dist_ix);
					}
				}

                 
                Dictionary<ulonglong, DistanceEntry> iStarDists = m_StarsDistanceCache[iStarNo];
                foreach (ulonglong key in iStarDists.Keys)
				{
					// We have found a distance that matches the current feature
				    ulong xStarNo = key.Lo;
					if (usedPyramidAngles.IndexOf(xStarNo) != -1) continue;

					DistanceEntry entry_ix = iStarDists[key];
					if (entry_ix.DistanceArcSec + toleranceInArcSec < dist_ix) continue;
					if (entry_ix.DistanceArcSec - toleranceInArcSec > dist_ix) continue;

                    Dictionary<ulonglong, DistanceEntry> xStarDists = m_StarsDistanceCache[xStarNo];

					#region Test the J-X pair
                    ulonglong jxKey = new ulonglong(xStarNo , jStarNo);

					DistanceEntry entry_jx;
					if (!xStarDists.TryGetValue(jxKey, out entry_jx)) continue;

					long idx_jx = ((long)j << 32) + (long)feature.FeatureId;
					double dist_jx;
					if (isRatioFittedFocalLength || !m_FeaturesDistanceCache.TryGetValue(idx_jx, out dist_jx))
					{
						dist_jx = m_PlateConfig.GetDistanceInArcSec(feature_j.X, feature_j.Y, feature_x.X, feature_x.Y, fittedFocalLength);
						long idx_xj = ((long)feature.FeatureId << 32) + (long)j;

						if (!isRatioFittedFocalLength)
						{
							m_FeaturesDistanceCache.Add(idx_jx, dist_jx);
							m_FeaturesDistanceCache.Add(idx_xj, dist_jx);
						}
					}

					if (entry_jx.DistanceArcSec + toleranceInArcSec < dist_jx) continue;
					if (entry_jx.DistanceArcSec - toleranceInArcSec > dist_jx) continue;
					#endregion

					#region Test the K-X pair
                    ulonglong kxKey = new ulonglong(xStarNo, kStarNo);
					DistanceEntry entry_kx;
					if (!xStarDists.TryGetValue(kxKey, out entry_kx)) continue;

					long idx_kx = ((long)k << 32) + (long)feature.FeatureId;
					double dist_kx;
					if (isRatioFittedFocalLength || !m_FeaturesDistanceCache.TryGetValue(idx_kx, out dist_kx))
					{
						dist_kx = m_PlateConfig.GetDistanceInArcSec(feature_k.X, feature_k.Y, feature_x.X, feature_x.Y, fittedFocalLength);
						long idx_xk = ((long)feature.FeatureId << 32) + (long)k;

						if (!isRatioFittedFocalLength)
						{
							m_FeaturesDistanceCache.Add(idx_kx, dist_kx);
							m_FeaturesDistanceCache.Add(idx_xk, dist_kx);
						}
					}

					if (entry_kx.DistanceArcSec + toleranceInArcSec < dist_kx) continue;
					if (entry_kx.DistanceArcSec - toleranceInArcSec > dist_kx) continue;
					#endregion

					// If we are here, then we have found another star

					locatedStars++;
					IStar xStar = entry_kx.Star1.StarNo == xStarNo ? entry_kx.Star1 : entry_kx.Star2;

#if ASTROMETRY_DEBUG
					Trace.Assert(xStar.StarNo != iStarNo);
					Trace.Assert(xStar.StarNo != jStarNo);
					Trace.Assert(xStar.StarNo != kStarNo);
#endif

					if (RegisterRecognizedPair(feature_x, xStar, feature.FeatureId))
					{
						usedPyramidAngles.Add(xStar.StarNo);
					}

					//Console.WriteLine(string.Format("      {0} ({1}) {2}\" {3}\" {4}\"", xStarNo, feature.FeatureId, dist_ix.ToString("0.0"), dist_jx.ToString("0.0"), dist_kx.ToString("0.0")));
				}
			}

			if (locatedStars >= CorePyramidConfig.Default.MinPyramidAlignedStars)
			{
				ThreeStarFit.StarPair pair_i = new ThreeStarFit.StarPair(feature_i.X, feature_i.Y);
				ThreeStarFit.StarPair pair_j = new ThreeStarFit.StarPair(feature_j.X, feature_j.Y);
				ThreeStarFit.StarPair pair_k = new ThreeStarFit.StarPair(feature_k.X, feature_k.Y);

				if (ijEntry.Star1.StarNo == iStarNo)
				{
					pair_i.RADeg = ijEntry.Star1.RADeg;
					pair_i.DEDeg = ijEntry.Star1.DEDeg;
					pair_i.Star = ijEntry.Star1;

					pair_j.RADeg = ijEntry.Star2.RADeg;
					pair_j.DEDeg = ijEntry.Star2.DEDeg;
					pair_j.Star = ijEntry.Star2;

#if ASTROMETRY_DEBUG
					Trace.Assert(ijEntry.Star1.StarNo == iStarNo);
					Trace.Assert(ijEntry.Star2.StarNo == jStarNo);
#endif

					RegisterRecognizedPair(feature_i, ijEntry.Star1, i);
					RegisterRecognizedPair(feature_j, ijEntry.Star2, j);
				}
				else
				{
					pair_i.RADeg = ijEntry.Star2.RADeg;
					pair_i.DEDeg = ijEntry.Star2.DEDeg;
					pair_i.Star = ijEntry.Star2;

					pair_j.RADeg = ijEntry.Star1.RADeg;
					pair_j.DEDeg = ijEntry.Star1.DEDeg;
					pair_j.Star = ijEntry.Star1;

#if ASTROMETRY_DEBUG
					Trace.Assert(ijEntry.Star2.StarNo == iStarNo);
					Trace.Assert(ijEntry.Star1.StarNo == jStarNo);
#endif

					RegisterRecognizedPair(feature_i, ijEntry.Star2, i);
					RegisterRecognizedPair(feature_j, ijEntry.Star1, j);
				}

				if (ikEntry.Star1.StarNo == kStarNo)
				{
					pair_k.RADeg = ikEntry.Star1.RADeg;
					pair_k.DEDeg = ikEntry.Star1.DEDeg;
					pair_k.Star = ikEntry.Star1;

#if ASTROMETRY_DEBUG
					Trace.Assert(ikEntry.Star1.StarNo == kStarNo);
#endif
					RegisterRecognizedPair(feature_k, ikEntry.Star1, k);
				}
				else
				{
					pair_k.RADeg = ikEntry.Star2.RADeg;
					pair_k.DEDeg = ikEntry.Star2.DEDeg;
					pair_k.Star = ikEntry.Star2;

#if ASTROMETRY_DEBUG
					Trace.Assert(ikEntry.Star2.StarNo == kStarNo);
#endif

					RegisterRecognizedPair(feature_k, ikEntry.Star2, k);
				}

				if (m_AmbiguousMatches.Count > 0 &&
					locatedStars - m_AmbiguousMatches.Count >= CorePyramidConfig.Default.MinPyramidAlignedStars)
				{
					// If we have sufficient number of stars and ambiguous stars (close doubles that satisfy more than one solution)
					// then remove all ambiguous stars before proceeding
					foreach (ImagePixel matchedPixel in m_AmbiguousMatches)
					{
						IStar matchedStar = m_MatchedPairs[matchedPixel];

						int featureToRemove = -1;
						foreach (int featureId in m_MatchedFeatureIdToStarIdIndexes.Keys)
						{
							if (m_MatchedFeatureIdToStarIdIndexes[featureId] == matchedStar.StarNo)
							{
								featureToRemove = featureId;
								break;
							}
						}

						m_MatchedFeatureIdToStarIdIndexes.Remove(featureToRemove);
						m_MatchedPairs.Remove(matchedPixel);
					}
				}

                if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
				    Debug.WriteLine(string.Format("Attempting DistanceBasedContext.LeastSquareFittedAstrometry ({0}={1}; {2}={3}; {4}={5})", i, iStarNo, j, jStarNo, k, kStarNo));
				
                return SolveStarPairs(
					starMap, m_MatchedPairs, m_MatchedFeatureIdToStarIdIndexes, pair_i, pair_j, pair_k,					
					fittedFocalLength, 
#if ASTROMETRY_DEBUG
					pyramidLog
#else
					null
#endif
					);
			}
			else
			{
#if PYRAMID_DEBUG || DEBUG

                foreach(ImagePixel pixel in m_MatchedPairs.Keys)
                {
                    IStar star = m_MatchedPairs[pixel];
                    foreach(int featureId in m_MatchedFeatureIdToStarIdIndexes.Keys)
                    {
                        if (m_MatchedFeatureIdToStarIdIndexes[featureId] == star.StarNo)
                        {
                            if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
                            {
#if DEBUG
                                Debug
#endif
#if PYRAMID_DEBUG
                                Trace 
#endif
                                .WriteLine(string.Format("({0}, {1}) - StarNo: {2}; FeatureId: {3}", pixel.X, pixel.Y, star.StarNo, featureId));
                            }

                            break;
                        }
                    }
                }
#endif

#if ASTROMETRY_DEBUG
				pyramidLog.FailBecauseOfTooFiewLocatedStars(locatedStars);
				AstrometricFitDebugger.RegisterFailedPyramid(pyramidLog);
#endif
			}

			return null;
		}
		internal IAstrometricFit IsSuccessfulMatch(
			IStarMap starMap,
			int i, int j, int k,
			DistanceEntry ijEntry, DistanceEntry ikEntry, DistanceEntry jkEntry,
            ulong iStarNo, ulong starNo2, ulong starNo3, double toleranceInArcSec)
		{
			return IsSuccessfulMatch(
				starMap, i, j, k, 
				ijEntry, ikEntry, jkEntry, 
				iStarNo, starNo2, starNo3,
				m_PlateConfig.EffectiveFocalLength, false, toleranceInArcSec);
		}
		private ImagePixel GetCenterOfFeature(StarMapFeature feature, IStarMap starMap)
		{
			ImagePixel center = feature.GetCenter();
            if (center == null)
				return ImagePixel.Unspecified;

            return starMap.GetCentroid(center.X, center.Y, (int)CoreAstrometrySettings.Default.SearchArea);
		}
		internal FieldAlignmentResult DoFieldAlignment(IStarMap starMap, Dictionary<StarMapFeature, IStar> manualPairs)
		{
			m_ManualPairs = manualPairs;
			return DoFieldAlignment(starMap, (FitInfo)null, false);
		}
		internal FieldAlignmentResult DoFieldAlignment(IStarMap starMap, double fittedFocalLength)
		{
			m_PlateConfig.EffectiveFocalLength = fittedFocalLength;
			return DoFieldAlignment(starMap, (FitInfo)null, false);
		}
		internal bool LoopThroghFeatureTriangles(IStarMap starMap, double toleranceInArcSec, CheckTriangleWithRatiosCallback callback)
		{
			return LoopThroghFeatureTriangles(starMap, toleranceInArcSec, null, callback);
		}
		internal FieldAlignmentResult DoFieldAlignment(
			IStarMap starMap, 
			FitInfo previousFit, 
			bool fitFocalLength)
		{
#if ASTROMETRY_DEBUG
			Trace.Assert(m_CelestialPyramidStars != null, "m_CelestialPyramidStars hasn't been set! Initialize() not called or called twice?");
			Trace.Assert(m_Settings.PyramidDistanceToleranceInPixels > 0);
#endif
			double toleranceInArcSec = m_PlateConfig.GetDistanceInArcSec(m_Settings.PyramidDistanceToleranceInPixels);
			m_PreviousFit = previousFit;

			m_FeaturesDistanceCache.Clear();
			m_MatchedPairs.Clear();
			m_AmbiguousMatches.Clear();
			m_MatchedFeatureIdToStarIdIndexes.Clear();
			m_StarMap = starMap;

			if (m_DetermineAutoLimitMagnitude)
			{
#if ASTROMETRY_DEBUG
				Trace.Assert(previousFit == null || previousFit.DetectedLimitingMagnitude != 0);
#endif
				double detectedLimitingMagnitude = double.NaN;
				if (previousFit != null &&
					!double.IsNaN(previousFit.DetectedLimitingMagnitude))
				{
					detectedLimitingMagnitude = previousFit.DetectedLimitingMagnitude;
				}

				if (!(double.IsNaN(detectedLimitingMagnitude) && double.IsNaN(m_DetectedLimitingMagnitude)) &&
					detectedLimitingMagnitude != m_DetectedLimitingMagnitude)
				{
					m_DetectedLimitingMagnitude = detectedLimitingMagnitude;
					if (double.IsNaN(m_DetectedLimitingMagnitude))
					{						
						m_CelestialPyramidStars = m_CelestialAllStars;

                        if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
						    Trace.WriteLine(string.Format("Now using all {0} loaded stars as alignment stars", m_CelestialPyramidStars.Count));
					}
					else
					{
						m_CelestialPyramidStars = m_CelestialAllStars
							.Where(s => s.Mag <= m_DetectedLimitingMagnitude)
							.ToList();

                        if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
						    Trace.WriteLine(string.Format("Limitting alignment stars to {0} start up to mag {1:0.0}", m_CelestialPyramidStars.Count, m_DetectedLimitingMagnitude));
					}
					InitializePyramidMatching();
				}				
			}

			m_AbortSearch = false;
			try
			{
                m_OperationNotifier.SendNotification(new OperationNotifications(NotificationType.SearchStarted, null));
			
				if (CorePyramidConfig.Default.ForceAlwaysUsePyramidWithRatios ||
					fitFocalLength)
				{

                    if (CoreAstrometrySettings.Default.UseQuickAlign)
                    {
                        if (LoopThroghFeatureTriangles(starMap, toleranceInArcSec, CheckTrianglesWithRatiosByMagnitude))
                        {
                            return new FieldAlignmentResult() { Solution = m_Solution, ImprovedSolution = m_ImprovedSolution, Solver = m_SolutionSolver, MatchedTriangle = m_MatchedTriangle };
                        }                        
                    }
                    else
                    {
                        if (LoopThroghFeatureTriangles(starMap, toleranceInArcSec, CheckTriangleWithRatios))
                        {
                            return new FieldAlignmentResult() { Solution = m_Solution, ImprovedSolution = m_ImprovedSolution, Solver = m_SolutionSolver, MatchedTriangle = m_MatchedTriangle };
                        }                        
                    }

				}
				else
				{
                    if (CoreAstrometrySettings.Default.UseQuickAlign)
                    {
                        if (LoopThroghFeatureTriangles(starMap, toleranceInArcSec, CheckTriangleByMagnitude))
                        {
                            return new FieldAlignmentResult() { Solution = m_Solution, ImprovedSolution = m_ImprovedSolution, Solver = m_SolutionSolver, MatchedTriangle = m_MatchedTriangle };
                        }	
                    }
                    else
                    {
                        if (LoopThroghFeatureTriangles(starMap, toleranceInArcSec, CheckTriangle))
                        {
                            return new FieldAlignmentResult() { Solution = m_Solution, ImprovedSolution = m_ImprovedSolution, Solver = m_SolutionSolver, MatchedTriangle = m_MatchedTriangle };
                        }	                        
                    }
				}

				return null;
			}
			finally
			{
                m_OperationNotifier.SendNotification(new OperationNotifications(NotificationType.SearchFinished, null));
			}
		}
		private bool LoopThroghFeatureTriangles(
			IStarMap starMap, 
			double toleranceInArcSec, 
			CheckTriangleCallback callback, 
			CheckTriangleWithRatiosCallback callbackWithRatios)
		{
			if (m_PreviousFit != null)
			{
			    if (TryMatchingPairsFromPreviousFit(starMap))
			    {
                    // Successfull fit from star to feature matching inferred from the previous fit was successfull
                    if (ImproveAndRetestSolution(0, 0, 0, true))
                        return true;
                    else
                    {
                        Trace.WriteLine("Could improve solution from previous fit");
                    }
			    }
			    else
			    {
			        Trace.WriteLine("Could not match stars from previous fit");
			    }
			}

			if (m_ManualPairs != null &&
				TryMatchingPairsFromManualPairs(starMap))
			{
				// Successfull fit from star to feature matching inferred from the previous fit was successfull
                if (ImproveAndRetestSolution(0, 0, 0))
					return true;
			}

			int n = starMap.FeaturesCount;

			if (m_Settings.PyramidOptimumStarsToMatch < n)
			{
				// TODO: Need to extract only the largest m_Settings.PyramidOptimumStarsToMatch features.
			}

			int total = n * (n - 1) * (n - 2) / 6;
			int counter = 0;
			int maxCombinationsBeforeFail = CorePyramidConfig.Default.MaxNumberOfCombinations;

			Stopwatch timeTaken = new Stopwatch();
			timeTaken.Start();

			bool delayWarningSent = false;

//            if (DebugResolvedStars != null)
//            {
//                int numInmagRegion, numPyramidStars;

//                GetNumStarsInRegionAndPyramidSet(
//                    m_PyramidMinMag, m_PyramidMaxMag,
//                    out numInmagRegion, out numPyramidStars);

//#if ASTROMETRY_DEBUG
//                Trace.Assert(numInmagRegion > 3);
//                Trace.Assert(numPyramidStars > 3); 			
//#endif
//            }

			if (m_ManualPairs != null && m_ManualPairs.Count <= 3)
			{
				if (m_ManualPairs.Count == 1)
				{
                    int fixedFeatureIndex = m_ManualPairs.Keys.ToList()[0].FeatureId + 1;
					for (int k = 2; k <= n; k++)
					{
						for (int j = 1; j < k; j++)
						{
                            var rv = CheckCombination(fixedFeatureIndex, j, k, starMap, toleranceInArcSec, callback, callbackWithRatios,
								timeTaken, ref counter, ref delayWarningSent, maxCombinationsBeforeFail, total, n);
							if (rv != null) return rv.Value;
						}
					}
				}
				else if (m_ManualPairs.Count == 2)
				{
					int fixedFeatureIndex1 = m_ManualPairs.Keys.ToList()[0].FeatureId + 1;
                    int fixedFeatureIndex2 = m_ManualPairs.Keys.ToList()[1].FeatureId + 1;
					for (int k = 1; k <= n; k++)
					{
                        var rv = CheckCombination(fixedFeatureIndex1, fixedFeatureIndex2, k, starMap, toleranceInArcSec, callback, callbackWithRatios,
							timeTaken, ref counter, ref delayWarningSent, maxCombinationsBeforeFail, total, n);
						if (rv != null) return rv.Value;
					}
				}
                else if (m_ManualPairs.Count == 3)
                {
                    var m_FeatureId_i = m_ManualPairs.Keys.ToList()[0].FeatureId;
                    var m_FeatureId_j = m_ManualPairs.Keys.ToList()[1].FeatureId;
                    var m_FeatureId_k = m_ManualPairs.Keys.ToList()[2].FeatureId;
                    ulong starNo1 = m_ManualPairs.Values.ToList()[0].StarNo;
                    ulong starNo2 = m_ManualPairs.Values.ToList()[1].StarNo;
                    ulong starNo3 = m_ManualPairs.Values.ToList()[2].StarNo;

                    int fixedFeatureIndex1 = m_FeatureId_i + 1;
                    int fixedFeatureIndex2 = m_FeatureId_j + 1;
                    int fixedFeatureIndex3 = m_FeatureId_k + 1;


                    var ijEntry = m_DistancesByMagnitude.FirstOrDefault(x => (x.Star1.StarNo == starNo1 && x.Star2.StarNo == starNo2) || (x.Star1.StarNo == starNo2 && x.Star2.StarNo == starNo1));
                    var ikEntry = m_DistancesByMagnitude.FirstOrDefault(x => (x.Star1.StarNo == starNo1 && x.Star2.StarNo == starNo3) || (x.Star1.StarNo == starNo3 && x.Star2.StarNo == starNo1));
                    var jkEntry = m_DistancesByMagnitude.FirstOrDefault(x => (x.Star1.StarNo == starNo3 && x.Star2.StarNo == starNo2) || (x.Star1.StarNo == starNo2 && x.Star2.StarNo == starNo3));

                    m_Solution = IsSuccessfulMatch(m_StarMap, fixedFeatureIndex1, fixedFeatureIndex2, fixedFeatureIndex3, ijEntry, ikEntry, jkEntry,
                               starNo1, starNo2, starNo3, toleranceInArcSec);

                    if (m_Solution != null)
                    {
                        if (ImproveAndRetestSolution(fixedFeatureIndex1, fixedFeatureIndex2, fixedFeatureIndex3))
                        {
                            m_MatchedTriangle = string.Format("{0}-{1}-{2}:{5}:[{3}/{4}]", fixedFeatureIndex1, fixedFeatureIndex2, fixedFeatureIndex3, counter, total, n);
                            return true;
                        }
                    }
                }
			}

		    for (int k = 3; k <= n; k++)
			{
				for (int j = 2; j < k; j++)
				{
					for (int i = 1; i < j; i++)
					{
						var rv = CheckCombination(i, j, k, starMap, toleranceInArcSec, callback, callbackWithRatios, 
							timeTaken, ref counter, ref delayWarningSent, maxCombinationsBeforeFail, total, n);
						if (rv != null) return rv.Value;
					}
				}
			}

			return false;
		}
        public void InitNewMatch(IStarMap imageFeatures, PyramidMatchType matchType, Dictionary<PSFFit, IStar> manualStars)
        {
            m_StarMap = imageFeatures;

            if (manualStars != null)
                SetManuallyIdentifiedHints(manualStars);

            matchType = PyramidMatchType.PlateSolve;

            m_IsCalibration = matchType == PyramidMatchType.ConfigCalibration;

            Context.Initialize(m_CelestialStars, m_PyramidMinMag, m_PyramidMaxMag, m_DetermineAutoLimitMagnitude, m_ManualStarMatch);

            #if ASTROMETRY_DEBUG
            AstrometricFitDebugger.Init(m_FitSettings, m_PyramidMinMag, m_PyramidMaxMag, m_AstrometryMinMag, m_AstrometryMaxMag);
            #endif
        }
		private bool? CheckCombination(int i, int j, int k, 
			IStarMap starMap,
			double toleranceInArcSec,
			CheckTriangleCallback callback,
			CheckTriangleWithRatiosCallback callbackWithRatios,
			Stopwatch timeTaken,
			ref int counter,
			ref bool delayWarningSent,
			int maxCombinationsBeforeFail,
			int total,
			int n)
		{
			if (i == j || i == k || j == k) return null;

			counter++;

			if (DebugResolvedStars != null)
			{
				if (!DebugResolvedStars.ContainsKey(i)) return null;
				if (!DebugResolvedStars.ContainsKey(j)) return null;
				if (!DebugResolvedStars.ContainsKey(k)) return null;

				if (DebugExcludeStars != null)
				{
					if (DebugExcludeStars.ContainsKey(i)) return null;
					if (DebugExcludeStars.ContainsKey(j)) return null;
					if (DebugExcludeStars.ContainsKey(k)) return null;
				}
			}
#if PYRAMID_DEBUG
                        Trace.WriteLine(string.Format("Trying triangle {0}-{1}-{2}", i, j, k));
#endif

			if (counter >= maxCombinationsBeforeFail)
				return false;

			if (m_AbortSearch) return false;

			if (counter % 5000 == 0)
                m_OperationNotifier.SendNotification(new OperationNotifications(NotificationType.SearchProgressed, null));

			if (!delayWarningSent &&
				timeTaken.ElapsedMilliseconds > m_Settings.PyramidTimeoutInSeconds * 100 &&
				timeTaken.ElapsedMilliseconds > 5000)
			{
                m_OperationNotifier.SendNotification(new OperationNotifications(NotificationType.SearchTakingLonger, timeTaken.ElapsedMilliseconds));
				delayWarningSent = true;
			}

			if (timeTaken.ElapsedMilliseconds > m_Settings.PyramidTimeoutInSeconds * 1000)
			{
				if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceError())
					Trace.WriteLine(string.Format("Timeout of {0}sec reached at {1}-{2}-{3}", m_Settings.PyramidTimeoutInSeconds, i, j, k));
				return false;
			}

			if (callbackWithRatios != null)
			{
				if (CheckTriangleWithRatios(starMap, callbackWithRatios, i, j, k, toleranceInArcSec))
				{
					if (ImproveAndRetestSolution(i, j, k))
					{
						m_MatchedTriangle = string.Format("{0}-{1}-{2}:{5}:[{3}/{4}]", i, j, k, counter, total, n);
						return true;
					}
				}

			}
			else
			{
				if (CheckTriangle(starMap, callback, i, j, k, toleranceInArcSec))
				{
					if (ImproveAndRetestSolution(i, j, k))
					{
						m_MatchedTriangle = string.Format("{0}-{1}-{2}:{5}:[{3}/{4}]", i, j, k, counter, total, n);
						return true;
					}
				}
			}

			return null;
		}
        public void NextFrame(int frameNo, IAstroImage astroImage, IStarMap starMap, LeastSquareFittedAstrometry astrometricFit)
        {
            IsTrackedSuccessfully = false;

            PSFFit psfFit = null;

            if (m_RepeatedIntergationPositions * 4 < m_PastFrameNos.Count)
            {
                var expectedPos = GetExpectedPosition(frameNo);
                if (expectedPos != null)
                    AstrometryContext.Current.StarMap.GetPSFFit(expectedPos.X, expectedPos.Y, PSFFittingMethod.NonLinearFit, out psfFit);
            }

            if (psfFit == null)
            {
                var brightestFeature = starMap.GetFeatureInRadius((int)TrackedObject.LastKnownX, (int)TrackedObject.LastKnownY, CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius);

                if (brightestFeature != null)
                {
                    var center = brightestFeature.GetCenter();
                    var referenceStarFeatures = astrometricFit.FitInfo.AllStarPairs.Where(x => x.FitInfo.UsedInSolution).ToList();
                    var refStar = referenceStarFeatures.FirstOrDefault(s => Math.Sqrt((s.x - center.X) * (s.x - center.X) + (s.y - center.Y) * (s.y - center.Y)) < 2);
                    if (refStar == null)
                        // The brightest feature is not a reference star, so we assume it is our object
                        AstrometryContext.Current.StarMap.GetPSFFit(center.X, center.Y, PSFFittingMethod.NonLinearFit, out psfFit);
                }
            }

            if (psfFit == null)
            {
                ImagePixel centroid = AstrometryContext.Current.StarMap.GetCentroid(
                    (int)TrackedObject.LastKnownX,
                    (int)TrackedObject.LastKnownY,
                    CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius);

                if (centroid != null)
                    AstrometryContext.Current.StarMap.GetPSFFit(centroid.X, centroid.Y, PSFFittingMethod.NonLinearFit, out psfFit);
            }

            if (psfFit != null)
            {
                double ra, de;
                astrometricFit.GetRADEFromImageCoords(psfFit.XCenter, psfFit.YCenter, out ra, out de);

                double maxPosDiffArcSec =
                        astrometricFit.GetDistanceInArcSec(astrometricFit.Image.CenterXImage, astrometricFit.Image.CenterYImage,
                        astrometricFit.Image.CenterXImage + CoreAstrometrySettings.Default.PreMeasureSearchCentroidRadius, astrometricFit.Image.CenterYImage);

                if (!double.IsNaN(TrackedObject.RAHours))
                {
                    double posDif = 3600 * AngleUtility.Elongation(15 * TrackedObject.RAHours, TrackedObject.DEDeg, ra, de);
                    if (posDif > maxPosDiffArcSec)
                    {
                        // NOTE: Not a valid measurement
                        Trace.WriteLine(string.Format("The target position is too far from the last measured position", posDif));
                        return;
                    }
                }

                TrackedObject.RAHours = ra / 15.0;
                TrackedObject.DEDeg = de;
                TrackedObject.LastKnownX = psfFit.XCenter;
                TrackedObject.LastKnownY = psfFit.YCenter;
                TrackedObject.PSFFit = psfFit;

                IsTrackedSuccessfully = true;
            }

            if (psfFit != null && psfFit.XCenter > 0 && psfFit.YCenter > 0)
            {
                m_PastFramePosX.Add(psfFit.XCenter);
                m_PastFramePosY.Add(psfFit.YCenter);
                m_PastFrameNos.Add(frameNo);
            }
        }
		private bool TryMatchingPairsFromPreviousFit(IStarMap starMap)
		{
			ThreeStarFit.StarPair[] pairs = new ThreeStarFit.StarPair[3];
			Dictionary<ImagePixel, IStar> matchedPairs = new Dictionary<ImagePixel, IStar>();
            Dictionary<int, ulong> matchedFeatureIdToStarIdIndexes = new Dictionary<int, ulong>();

			int idx = 0;
			foreach (PlateConstStarPair pair in m_PreviousFit.AllStarPairs)
			{
				if (pair.FitInfo.ExcludedForHighResidual) continue;
                StarMapFeature ftr = starMap.GetFeatureInRadius((int)pair.x, (int)pair.y, (int)CoreAstrometrySettings.Default.SearchArea);
				if (ftr != null)
				{
					ImagePixel center = starMap.GetCentroid(
                        (int)pair.x, (int)pair.y, (int)CoreAstrometrySettings.Default.SearchArea);
					IStar star = null;

					foreach (IStar s in m_CelestialPyramidStars)
					{
						if (s.StarNo == pair.StarNo)
						{
							star = s;
							break;
						}
					}

					if (star != null &&
						center != null &&
						!matchedFeatureIdToStarIdIndexes.ContainsKey(ftr.FeatureId))
					{
						if (idx < 3)
						{
							pairs[idx] = new ThreeStarFit.StarPair(center.X, center.Y);
							pairs[idx].RADeg = star.RADeg;
							pairs[idx].DEDeg = star.DEDeg;
							pairs[idx].Star = star;
							idx++;
						}

						matchedPairs.Add(center, star);
						matchedFeatureIdToStarIdIndexes.Add(ftr.FeatureId, star.StarNo);
					}
				}
			}

			// Shortcurcuit to FeautreId - StarNo detection
			if (matchedPairs.Count >= m_Settings.MinimumNumberOfStars)
			{
                // When there was a previous fit and we have sufficient stars from the current star map
                // then don't require % of the bright features to approve the solution. Do it as a calibration fit (not too precise)
                LeastSquareFittedAstrometry fit = SolveStarPairs(
                    starMap, matchedPairs,
                    matchedFeatureIdToStarIdIndexes,
                    pairs[0], pairs[1], pairs[2], m_PreviousFit.FittedFocalLength, null, 
                    TangraConfig.Settings.Astrometry.MinimumNumberOfStars);

                if (fit != null)
                {
                    m_Solution = fit;
                    m_MatchedPairs = matchedPairs;
                    m_MatchedFeatureIdToStarIdIndexes = matchedFeatureIdToStarIdIndexes;
                    return true;
                }
            }

			return false;
		}
Exemple #24
0
 public void InitNewFrame(IStarMap imageFeatures)
 {
     m_StarMap = imageFeatures;
     // NOTE: The m_Solution will be reused until the DistanceBasedAstrometrySolver is recreated
 }
		private bool TryMatchingPairsFromManualPairs(IStarMap starMap)
		{
			ThreeStarFit.StarPair[] pairs = new ThreeStarFit.StarPair[3];
			Dictionary<ImagePixel, IStar> matchedPairs = new Dictionary<ImagePixel, IStar>();
            Dictionary<int, ulong> matchedFeatureIdToStarIdIndexes = new Dictionary<int, ulong>();

			int idx = 0;
			foreach (StarMapFeature feature in m_ManualPairs.Keys)
			{
				IStar star = m_ManualPairs[feature];
				ImagePixel center = feature.GetCenter();

                StarMapFeature ftr = starMap.GetFeatureInRadius((int)center.X, (int)center.Y, (int)CoreAstrometrySettings.Default.SearchArea);
				if (ftr != null)
				{
					if (!matchedFeatureIdToStarIdIndexes.ContainsKey(ftr.FeatureId))
					{
						if (idx < 3)
						{
							pairs[idx] = new ThreeStarFit.StarPair(center.X, center.Y);
							pairs[idx].RADeg = star.RADeg;
							pairs[idx].DEDeg = star.DEDeg;
							pairs[idx].Star = star;
							idx++;
						}

						matchedPairs.Add(center, star);
						matchedFeatureIdToStarIdIndexes.Add(ftr.FeatureId, star.StarNo);
					}
				}
			}

			if (matchedPairs.Count >= m_Settings.MinimumNumberOfStars)
			{
                // When there was a previous fit and we have sufficient stars from the current star map
                // then don't require % of the bright features to approve the solution. Do it as a calibration fit (not too precise)
                LeastSquareFittedAstrometry fit = SolveStarPairs(
                    starMap, matchedPairs,
                    matchedFeatureIdToStarIdIndexes,
                    pairs[0], pairs[1], pairs[2], m_PlateConfig.EffectiveFocalLength, null, 
                    TangraConfig.Settings.Astrometry.MinimumNumberOfStars);

                if (fit != null)
                {
                    m_Solution = fit;
                    m_MatchedPairs = matchedPairs;
                    m_MatchedFeatureIdToStarIdIndexes = matchedFeatureIdToStarIdIndexes;
                    return true;
                }
            }

			return false;
		}