Esempio n. 1
0
        public bool Equals(ulonglong p)
        {
            // If parameter is null return false:
            if ((object)p == null)
            {
                return(false);
            }

            // Return true if the fields match:
            return
                (Lo == p.Lo &&
                 Hi == p.Hi);
        }
Esempio n. 2
0
        public bool Equals(ulonglong p)
        {
            // If parameter is null return false:
            if ((object)p == null)
            {
                return false;
            }

            // Return true if the fields match:
            return
                Lo == p.Lo &&
                Hi == p.Hi;
        }
Esempio n. 3
0
        public override bool Equals(System.Object obj)
        {
            // If parameter is null return false.
            if (obj == null)
            {
                return(false);
            }

            // If parameter cannot be cast to ulonglong return false.
            ulonglong p = obj as ulonglong;

            if ((System.Object)p == null)
            {
                return(false);
            }

            // Return true if the fields match:
            return
                (Lo == p.Lo &&
                 Hi == p.Hi);
        }
Esempio n. 4
0
		private void BuildPiramidMatchingUsingOldKVector()
		{
			#region The old way of doing it
			// The distance base "Pyramid" matching only uses limited number of stars
			m_CelestialPyramidStars = m_CelestialAllStars.FindAll((star) => star.Mag >= m_PyramidMinMag && star.Mag <= m_PyramidMaxMag);

            if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
			    Trace.WriteLine(string.Format("Building Pyramid Alignment Dataset of {0}/{1} stars in range {2}m - {3}m", m_CelestialPyramidStars.Count, m_CelestialAllStars.Count, m_PyramidMinMag.ToString("0.0"), m_PyramidMaxMag.ToString("0.0")));

			m_Distances.Clear();
			m_Entries.Clear();
			m_StarsDistanceCache.Clear();
			m_IndexLower.Clear();
			m_IndexUpper.Clear();

			// Build a list of distances 
			double maxFovInDeg = m_PlateConfig.GetMaxFOVInArcSec() / 3600.0;
			for (int i = 0; i < m_CelestialPyramidStars.Count; i++)
			{
				IStar star1 = m_CelestialPyramidStars[i];

				for (int j = i + 1; j < m_CelestialPyramidStars.Count; j++)
				{
					IStar star2 = m_CelestialPyramidStars[j];

					double dist = AngleUtility.Elongation(star1.RADeg, star1.DEDeg, star2.RADeg, star2.DEDeg);
					if (dist > maxFovInDeg) continue;

					dist *= 3600.0;

					m_Distances.Add(dist);
					DistanceEntry entry = new DistanceEntry(star1, star2, dist);
					m_Entries.Add(entry);

					#region PerStar distance cache
                    ulonglong id1 = new ulonglong(star1.StarNo, star2.StarNo);
                    ulonglong id2 = new ulonglong(star2.StarNo, star1.StarNo);

					Dictionary<ulonglong, DistanceEntry> map;
					if (!m_StarsDistanceCache.TryGetValue(star1.StarNo, out map))
					{
                        map = new Dictionary<ulonglong, DistanceEntry>();
						m_StarsDistanceCache.Add(star1.StarNo, map);
					}
					map.Add(id1, entry);

					if (!m_StarsDistanceCache.TryGetValue(star2.StarNo, out map))
					{
                        map = new Dictionary<ulonglong, DistanceEntry>();
						m_StarsDistanceCache.Add(star2.StarNo, map);
					}
					map.Add(id2, entry);
					#endregion
				}
			}


			// Build a K-Vector

			double[] distArr = m_Distances.ToArray();
			DistanceEntry[] entryArr = m_Entries.ToArray();
			Array.Sort(distArr, entryArr);

			m_Distances = new List<double>(distArr);
			m_Entries = new List<DistanceEntry>(entryArr);

			//Console.WriteLine(m_Distances.Count.ToString() + " match distances.");
			double errorInArcSec = m_PlateConfig.GetDistanceInArcSec(m_Settings.PyramidDistanceToleranceInPixels);

			errorInArcSec = 0; /* the error will be factored in later on */

			int maxIdx = (int)Math.Round(m_PlateConfig.GetMaxFOVInArcSec() + 2 * errorInArcSec) + 1;
			for (int i = 0; i < maxIdx; i++)
			{
				m_IndexLower.Add(i, -1);
				m_IndexUpper.Add(i, -1);
			}

			int lower = -1;
			int upper = -1;
			for (int i = 0; i < m_Distances.Count; i++)
			{
				int dstL = Math.Max((int)(m_Distances[i] - errorInArcSec), (int)errorInArcSec);

				if (dstL > lower)
				{
					lower = dstL;
					for (int j = lower; j >= 0 && m_IndexLower[j] == -1; j--)
						m_IndexLower[j] = Math.Max(i - 1, 0);
				}

				int dstU = Math.Max((int)Math.Round(m_Distances[i] + errorInArcSec), (int)errorInArcSec);
				if (dstU > upper)
				{
					upper = dstU;
					for (int j = upper; j >= 0 && m_IndexUpper[j] == -1; j--)
						m_IndexUpper[j] = Math.Min(i, m_Distances.Count - 1);
				}
			}

			for (int j = m_IndexLower.Count - 1; j >= 0 && m_IndexLower[j] == -1; j--)
				m_IndexLower[j] = m_Distances.Count - 1;

			for (int j = m_IndexUpper.Count - 1; j >= 0 && m_IndexUpper[j] == -1; j--)
				m_IndexUpper[j] = m_Distances.Count - 1;

			#endregion			
		}
Esempio n. 5
0
        public static PyramidStarsDensityDistributor BuildPyramidMatchingByMagnitude(
            List<IStar> pyramidStars, 
			AstroPlate image, 
			IAstrometrySettings settings,
            Dictionary<int, ulong> debugResolvedStarsWithAppliedExclusions,
            List<ulong> alwaysIncludeStars,
            out List<DistanceEntry> distancesByMagnitude,
            out Dictionary<ulong, Dictionary<ulonglong, DistanceEntry>> starsDistanceCache)
		{
			// 1) This should be the first N brightest (or all stars) 
			// 2) The memory structure should be a dictionary of Pairs with values all other pairs that include one of the pair
			// 3) The dictionary should be sorted by brightness i.e. brightest pairs should be on the top/ NOTE: Exclude the brightest
			//    stars until the mag difference between the next 2 bright stars becomes less than 1 mag
			// 4) Matching should be done by searching the distance match checking the brightest pairs first. 

            distancesByMagnitude = new List<DistanceEntry>();
            starsDistanceCache = new Dictionary<ulong, Dictionary<ulonglong, DistanceEntry>>();

            if (pyramidStars.Count == 0)
				return null;

            PyramidStarsDensityDistributor distributor;

            pyramidStars.Sort((s1, s2) => s1.Mag.CompareTo(s2.Mag));

            int n = pyramidStars.Count;
            double maxFovInDeg = image.GetMaxFOVInArcSec() / 3600.0;

			distributor = new PyramidStarsDensityDistributor(pyramidStars, image, settings);
            distributor.DebugResolvedStarsWithAppliedExclusions = debugResolvedStarsWithAppliedExclusions;
            distributor.Initialize(alwaysIncludeStars);

            distancesByMagnitude.Clear();
            starsDistanceCache.Clear();

            List<ulong> resolvedDebugStarsNos = null;
            if (debugResolvedStarsWithAppliedExclusions != null)
            {

				// This is disabled at the moment
                resolvedDebugStarsNos = debugResolvedStarsWithAppliedExclusions.Values.ToList();
                int pyramidStarsLocated = 0;
                for (int i = resolvedDebugStarsNos.Count - 1; i >= 0 ; i--)
                {
                    ulong starNo = resolvedDebugStarsNos[i];

                    IStar star = pyramidStars.FirstOrDefault(s => s.StarNo == starNo);
                    if (star != null)
                        pyramidStarsLocated++;
                    else
                        resolvedDebugStarsNos.Remove(starNo);
                    Trace.Assert(star != null, string.Format("Debug Star {0} not found in the pyramid stars!", starNo));
                }

                if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose())
                    Trace.WriteLine(
					    string.Format("DEBUG ALIGN: {0} out of {1} Debug Stars found among the pyramid stars ({2})",
					    pyramidStarsLocated, resolvedDebugStarsNos.Count,
					    resolvedDebugStarsNos.Count > 1 
						    ? resolvedDebugStarsNos.Select(s => s.ToString()).Aggregate((a, b) => string.Concat(a, " ", b))
						    : ""));
            }

			// Start building the pairs
			for (int j = 0; j < n; j++)
			{
                IStar jStar = pyramidStars[j];
                if (!distributor.CheckStar(jStar))
                {
                    if (resolvedDebugStarsNos != null)
                    {
                        if (resolvedDebugStarsNos.Contains(jStar.StarNo))
                        {
                            //Trace.Assert(false, "DebugResolved star pair not added to the pyramid areas because the distributor rejected it.");
                        }
                    }
                    continue;
                }

				for (int i = j + 1; i < n; i++)
				{
                    IStar iStar = pyramidStars[i];
						
					double distDeg = AngleUtility.Elongation(iStar.RADeg, iStar.DEDeg, jStar.RADeg, jStar.DEDeg);

					if (distDeg > maxFovInDeg)
					{
                        if (resolvedDebugStarsNos != null)
                        {
                            if (resolvedDebugStarsNos.Contains(iStar.StarNo) &&
                                resolvedDebugStarsNos.Contains(jStar.StarNo))
                            {
                                //Trace.Assert(false, "DebugResolved star pair not added to the pyramid areas because the distance is too large.");
                            }
                        }
					    continue;
					}

				    if (!distributor.CheckStar(iStar))
				    {
                        if (resolvedDebugStarsNos != null)
                        {
                            if (resolvedDebugStarsNos.Contains(iStar.StarNo))
                            {
                                //Trace.Assert(false, string.Format("DebugResolved star {0} not added to the pyramid areas because the distributor rejected it.", iStar.StarNo));
                            }
                        }
				        continue;
				    }

					distributor.MarkStar(iStar);
					distributor.MarkStar(jStar);
						
					DistanceEntry entry = new DistanceEntry(iStar, jStar, distDeg * 3600);
                    distancesByMagnitude.Add(entry);

					#region PerStar distance cache
                    ulonglong id1 = new ulonglong(iStar.StarNo, jStar.StarNo);
                    ulonglong id2 = new ulonglong(jStar.StarNo, iStar.StarNo);

					Dictionary<ulonglong, DistanceEntry> map;
                    if (!starsDistanceCache.TryGetValue(iStar.StarNo, out map))
					{
                        map = new Dictionary<ulonglong, DistanceEntry>();
                        starsDistanceCache.Add(iStar.StarNo, map);
					}
					map.Add(id1, entry);

                    if (!starsDistanceCache.TryGetValue(jStar.StarNo, out map))
					{
                        map = new Dictionary<ulonglong, DistanceEntry>();
                        starsDistanceCache.Add(jStar.StarNo, map);
					}
					map.Add(id2, entry);
					#endregion
				}
			}

			//if (resolvedDebugStarsNos != null)
			//{
			//    foreach(uint starNo in resolvedDebugStarsNos)
			//    {
			//        DensityArea area = distributor.m_Areas.FirstOrDefault(a => a.m_IncludedStarNos.Contains(starNo));
			//        if (area != null)
			//            Trace.WriteLine(string.Format("DEBUG ALIGN: Star {0} located to area [{1:0.00}, {2:0.0}]", starNo, area.XMiddle, area.YMiddle));
			//        Trace.Assert(area != null);
			//    }
			//}

            return distributor;
		}
Esempio n. 6
0
		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;
		}