internal void SaveContext(string fileName) { PlateConstantsSolver oldValue = m_Context.ConstantsSolver; try { m_Context.ConstantsSolver = m_ConstantsSolver; byte[] data = m_Context.Serialize(); System.IO.File.WriteAllBytes(fileName, data); } finally { m_Context.ConstantsSolver = oldValue; } }
public PerformMatchResult PerformMatch( out LeastSquareFittedAstrometry distanceBasedSolution, out LeastSquareFittedAstrometry improvedSolution, out PlateConstantsSolver solver) { DistanceBasedContext.FieldAlignmentResult alignmentResult = null; improvedSolution = null; #if UNIT_TESTS Flags.Reset(); #endif m_PerformanceWatch.Reset(); m_PerformanceWatch.Start(); try { if (m_StarMap.FeaturesCount < TangraConfig.Settings.Astrometry.MinimumNumberOfStars) { distanceBasedSolution = null; solver = null; return(PerformMatchResult.FieldAlignmentFailed); } if (!m_IsCalibration && m_Solution != null) { // Try using the cache from the last time alignmentResult = Context.DoFieldAlignment(m_StarMap, m_Solution.FitInfo, false); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.CacheFromLastTimeAttempted = true; Flags.CacheFromLastTimeSuccessful = m_Solution != null; #endif } if (m_Solution == null && !m_IsCalibration && m_ManualStarMatch != null) { // Try using the manual star match alignmentResult = Context.DoFieldAlignment(m_StarMap, m_ManualStarMatch); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.ManualMatchAttempted = true; Flags.ManualMatchSuccessful = m_Solution != null; #endif } if (m_Solution == null && !m_RatioBasedFittedFocalLengthIsDerived && !m_FitSettings.PyramidForceFixedFocalLength && !m_IsCalibration) { m_OperationNotifier.NotifyBeginLongOperation("Performing a plate solve ..."); try { // If this is the first fit, then fit a focal length alignmentResult = Context.DoFieldAlignmentFitFocalLength(m_StarMap); m_Solution = GetSolution(alignmentResult); if (m_Solution != null) { m_RatioBasedFittedFocalLengthIsDerived = true; m_RatioBasedFittedFocalLength = m_Solution.FitInfo.FittedFocalLength; } else { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceInfo()) { Trace.WriteLine(string.Format("No solution after reaching combination {0} with {1} features.", Context.CurrentCombination, m_StarMap.FeaturesCount)); } } } finally { m_OperationNotifier.NotifyEndLongOperation(); } #if UNIT_TESTS Flags.RatioBasedFocalLengthFitAttempted = true; Flags.RatioBasedFocalLengthFitSuccessful = m_RatioBasedFittedFocalLengthIsDerived; #endif } else { if (m_Solution == null) { if (m_RatioBasedFittedFocalLengthIsDerived && !m_IsCalibration) { alignmentResult = Context.DoFieldAlignment(m_StarMap, m_RatioBasedFittedFocalLength); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.FitWithPreFittedFocalLengthAttempted = true; Flags.FitWithPreFittedFocalLengthSuccessful = m_Solution != null; #endif } else { alignmentResult = Context.DoFieldAlignment(m_StarMap); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.FitWithFixedFocalLengthAttempted = true; Flags.FitWithFixedFocalLengthSuccessful = m_Solution != null; #endif } } } m_SearchAborted = Context.m_AbortSearch; if (alignmentResult != null) { distanceBasedSolution = (LeastSquareFittedAstrometry)alignmentResult.Solution; improvedSolution = (LeastSquareFittedAstrometry)alignmentResult.ImprovedSolution; m_Solution = GetSolution(alignmentResult); solver = alignmentResult.Solver; } else { distanceBasedSolution = m_Solution; solver = null; } if (m_SearchAborted) { return(PerformMatchResult.SearchAborted); } #if ASTROMETRY_DEBUG if (m_Solution != null) { // No need to keep the debug log any longer when the fit is successful AstrometricFitDebugger.Reset(); } #endif if (m_Solution != null) { // Clear manually identified starting position in a case of a successful plate solve m_ManualStarMatch = null; } return(m_Solution != null ? PerformMatchResult.FitSucceessfull : PerformMatchResult.FitImprovementFailed); } finally { // Reset the manually matched stars after every fit attempt m_ManualStarMatch = null; m_PerformanceWatch.Stop(); if (m_Solution != null) { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceError()) { Trace.WriteLine(string.Format( "Pyramid Match Successful: {0} ms, {1} stars total, aligned on {2} stars, {3} stars matched {4}. Combination: {5}", m_PerformanceWatch.ElapsedMilliseconds, m_CelestialStars.Count, alignmentResult != null ? (alignmentResult.Solution as LeastSquareFittedAstrometry).FitInfo.NumberOfStarsUsedInSolution() : 0, improvedSolution != null ? improvedSolution.FitInfo.NumberOfStarsUsedInSolution().ToString() : "N/A", improvedSolution != null ? string.Format(" ({0}-{1} mag)", Context.ImprovedSolutionIncludedMinMag.ToString("0.00"), Context.ImprovedSolutionIncludedMaxMag.ToString("0.00")) : null, alignmentResult != null ? alignmentResult.MatchedTriangle : null)); } } else { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceError()) { Trace.WriteLine(string.Format("Pyramid Match Failed: {0} ms, {1} stars total, NO MATCH", m_PerformanceWatch.ElapsedMilliseconds, m_CelestialStars.Count)); } if (Context.DebugResolvedStars != null) { foreach (DistanceBasedContext.DebugTripple tripple in Context.m_DebugTripples) { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose()) { Trace.WriteLine(string.Format("DEBUG ALIGN: Missed alignment on {0}-{1}-{2}", tripple.Id1, tripple.Id2, tripple.Id3)); } } } } #if ASTROMETRY_DEBUG Trace.Assert(!m_DetermineAutoLimitMagnitude || m_Solution == null || m_Solution.FitInfo.DetectedLimitingMagnitude != 0); #endif } }
public PerformMatchResult PerformMatch( out LeastSquareFittedAstrometry distanceBasedSolution, out LeastSquareFittedAstrometry improvedSolution, out PlateConstantsSolver solver) { DistanceBasedContext.FieldAlignmentResult alignmentResult = null; improvedSolution = null; #if UNIT_TESTS Flags.Reset(); #endif m_PerformanceWatch.Reset(); m_PerformanceWatch.Start(); try { if (m_StarMap.FeaturesCount < TangraConfig.Settings.Astrometry.MinimumNumberOfStars) { distanceBasedSolution = null; solver = null; return PerformMatchResult.FieldAlignmentFailed; } if (!m_IsCalibration && m_Solution != null) { // Try using the cache from the last time alignmentResult = Context.DoFieldAlignment(m_StarMap, m_Solution.FitInfo, false); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.CacheFromLastTimeAttempted = true; Flags.CacheFromLastTimeSuccessful = m_Solution != null; #endif } if (m_Solution == null && !m_IsCalibration && m_ManualStarMatch != null) { // Try using the manual star match alignmentResult = Context.DoFieldAlignment(m_StarMap, m_ManualStarMatch); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.ManualMatchAttempted = true; Flags.ManualMatchSuccessful = m_Solution != null; #endif } if (m_Solution == null && !m_RatioBasedFittedFocalLengthIsDerived && !m_FitSettings.PyramidForceFixedFocalLength && !m_IsCalibration) { m_OperationNotifier.NotifyBeginLongOperation("Performing a plate solve ..."); try { // If this is the first fit, then fit a focal length alignmentResult = Context.DoFieldAlignmentFitFocalLength(m_StarMap); m_Solution = GetSolution(alignmentResult); if (m_Solution != null) { m_RatioBasedFittedFocalLengthIsDerived = true; m_RatioBasedFittedFocalLength = m_Solution.FitInfo.FittedFocalLength; } else { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceInfo()) Trace.WriteLine("No Solution."); } } finally { m_OperationNotifier.NotifyEndLongOperation(); } #if UNIT_TESTS Flags.RatioBasedFocalLengthFitAttempted = true; Flags.RatioBasedFocalLengthFitSuccessful = m_RatioBasedFittedFocalLengthIsDerived; #endif } else { if (m_Solution == null) { if (m_RatioBasedFittedFocalLengthIsDerived && !m_IsCalibration) { alignmentResult = Context.DoFieldAlignment(m_StarMap, m_RatioBasedFittedFocalLength); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.FitWithPreFittedFocalLengthAttempted = true; Flags.FitWithPreFittedFocalLengthSuccessful = m_Solution != null; #endif } else { alignmentResult = Context.DoFieldAlignment(m_StarMap); m_Solution = GetSolution(alignmentResult); #if UNIT_TESTS Flags.FitWithFixedFocalLengthAttempted = true; Flags.FitWithFixedFocalLengthSuccessful = m_Solution != null; #endif } } } m_SearchAborted = Context.m_AbortSearch; if (alignmentResult != null) { distanceBasedSolution = (LeastSquareFittedAstrometry)alignmentResult.Solution; improvedSolution = (LeastSquareFittedAstrometry)alignmentResult.ImprovedSolution; m_Solution = GetSolution(alignmentResult); solver = alignmentResult.Solver; } else { distanceBasedSolution = m_Solution; solver = null; } if (m_SearchAborted) return PerformMatchResult.SearchAborted; #if ASTROMETRY_DEBUG if (m_Solution != null) // No need to keep the debug log any longer when the fit is successful AstrometricFitDebugger.Reset(); #endif if (m_Solution != null) // Clear manually identified starting position in a case of a successful plate solve m_ManualStarMatch = null; return m_Solution != null ? PerformMatchResult.FitSucceessfull : PerformMatchResult.FitImprovementFailed; } finally { // Reset the manually matched stars after every fit attempt m_ManualStarMatch = null; m_PerformanceWatch.Stop(); if (m_Solution != null) { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceError()) { Trace.WriteLine(string.Format( "Pyramid Match Successful: {0} ms, {1} stars total, aligned on {2} stars, {3} stars matched {4}. Combination: {5}", m_PerformanceWatch.ElapsedMilliseconds, m_CelestialStars.Count, alignmentResult != null ? (alignmentResult.Solution as LeastSquareFittedAstrometry).FitInfo.NumberOfStarsUsedInSolution() : 0, improvedSolution != null ? improvedSolution.FitInfo.NumberOfStarsUsedInSolution().ToString() : "N/A", improvedSolution != null ? string.Format(" ({0}-{1} mag)", Context.ImprovedSolutionIncludedMinMag.ToString("0.00"), Context.ImprovedSolutionIncludedMaxMag.ToString("0.00")) : null, alignmentResult != null ? alignmentResult.MatchedTriangle : null)); } } else { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceError()) Trace.WriteLine(string.Format("Pyramid Match Failed: {0} ms, {1} stars total, NO MATCH", m_PerformanceWatch.ElapsedMilliseconds, m_CelestialStars.Count)); if (Context.DebugResolvedStars != null) { foreach(DistanceBasedContext.DebugTripple tripple in Context.m_DebugTripples) { if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceVerbose()) Trace.WriteLine(string.Format("DEBUG ALIGN: Missed alignment on {0}-{1}-{2}", tripple.Id1, tripple.Id2, tripple.Id3)); } } } #if ASTROMETRY_DEBUG Trace.Assert(!m_DetermineAutoLimitMagnitude || m_Solution == null || m_Solution.FitInfo.DetectedLimitingMagnitude != 0); #endif } }
private void ImproveSolution(LeastSquareFittedAstrometry fit, double coeff, int i, int j, int k) { m_SolutionSolver = new PlateConstantsSolver(m_PlateConfig); double ra0, de0; fit.GetRADEFromImageCoords(m_PlateConfig.CenterXImage, m_PlateConfig.CenterXImage, out ra0, out de0); m_SolutionSolver.InitPlateSolution(ra0, de0); List<IStar> consideredStars = new List<IStar>(); List<ulong> nonStellarStars = new List<ulong>(); foreach (IStar star in m_CelestialAllStars) { if (star.Mag < m_MinMag || star.Mag > m_MaxMag) continue; if (m_DetermineAutoLimitMagnitude && !double.IsNaN(m_DetectedLimitingMagnitude) && star.Mag > m_DetectedLimitingMagnitude) { #if ASTROMETRY_DEBUG Trace.Assert(false); #endif continue; } double x, y; fit.GetImageCoordsFromRADE(star.RADeg, star.DEDeg, out x, out y); if (x < 0 || x > m_PlateConfig.ImageWidth || y < 0 || y > m_PlateConfig.ImageHeight) continue; ImagePixel pixel = null; PSFFit psfFit; PSFFit asymPsfFit = null; //if (m_FitSettings.CenterDetectionMethod == StarCenterDetection.PSFFit) { if (m_Settings.PyramidRemoveNonStellarObject) m_StarMap.GetPSFFit((int)x, (int)y, PSFFittingMethod.NonLinearAsymetricFit, out asymPsfFit); pixel = m_StarMap.GetPSFFit((int)x, (int)y, PSFFittingMethod.NonLinearFit, out psfFit); } //else if (m_FitSettings.CenterDetectionMethod == StarCenterDetection.Centroid) { // NOTE: Centroid detection is way faster and PSF will not lead to big improvement considering the threshold for star matching //pixel = m_StarMap.GetCentroid((int)x, (int)y, (int)Math.Ceiling(m_Settings.SearchArea)); } if (pixel != null && psfFit.Certainty >= CorePyramidConfig.Default.MinDetectionLimitForSolutionImprovement / coeff) { consideredStars.Add(star); double distance = fit.GetDistanceInArcSec(pixel.XDouble, pixel.YDouble, x, y); if (distance < CorePyramidConfig.Default.MaxPreliminaryResidualForSolutionImprovement / coeff) { #if ASTROMETRY_DEBUG Trace.Assert(!double.IsNaN(pixel.XDouble)); Trace.Assert(!double.IsNaN(pixel.YDouble)); #endif if ( Math.Sqrt((x - pixel.XDouble) * (x - pixel.XDouble) + (y - pixel.YDouble) * (y - pixel.YDouble)) > CoreAstrometrySettings.Default.SearchArea) continue; pixel.SignalNoise = psfFit.Certainty; pixel.Brightness = psfFit.Brightness; m_SolutionSolver.AddStar(pixel, star); if (m_Settings.PyramidRemoveNonStellarObject && ( asymPsfFit.FWHM < m_Settings.MinReferenceStarFWHM || asymPsfFit.FWHM > m_Settings.MaxReferenceStarFWHM || asymPsfFit.ElongationPercentage > m_Settings.MaximumPSFElongation) ) { nonStellarStars.Add(star.StarNo); } } } } double ffl = fit.FitInfo.FittedFocalLength; m_ImprovedSolution = m_SolutionSolver.SolveWithLinearRegression(m_Settings, out m_FirstImprovedSolution); //if (m_ImprovedSolution != null && m_ImprovedSolution.FitInfo.AllStarPairs.Count < 12) //{ // // Attempt to reject errorous solutions with small number of fitted stars // int totalMatched = 0; // var largeFeatures = m_StarMap.Features.Where(x => x.MaxBrightnessPixels > 1).ToList(); // if (largeFeatures.Count > 5) // { // foreach (var feature in largeFeatures) // { // var ftrCenter = feature.GetCenter(); // // TODO: PFS Fit on the feature? // var matchedFittedStar = m_ImprovedSolution.FitInfo.AllStarPairs.FirstOrDefault( // x => // Math.Sqrt(Math.Pow(x.x - ftrCenter.XDouble, 2) + Math.Pow(x.x - ftrCenter.XDouble, 2)) < // 2); // if (matchedFittedStar != null) totalMatched++; // } // double percentLargeFeaturesMatched = 1.0*totalMatched/largeFeatures.Count; // if (percentLargeFeaturesMatched < 0.75) // { // if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceInfo()) // Trace.WriteLine(string.Format("Only {0:0.0}% ({1} features) of the large {2} features have been matched, where 75% is required.", percentLargeFeaturesMatched*100, totalMatched, largeFeatures.Count)); // // At least 75% of the bright features from the video need to be matched to stars for the solution to be accepted // m_ImprovedSolution = null; // } // } //} if (m_ImprovedSolution != null) { m_ImprovedSolution.FitInfo.FittedFocalLength = ffl; if (TangraConfig.Settings.TraceLevels.PlateSolving.TraceWarning()) Trace.WriteLine(string.Format("Improved solution: {0} considered stars, UsedInSolution: {1}, ExcludedForHighResidual: {2}", m_ImprovedSolution.FitInfo.AllStarPairs.Count(), m_ImprovedSolution.FitInfo.AllStarPairs.Count(x => x.FitInfo.UsedInSolution), m_ImprovedSolution.FitInfo.AllStarPairs.Count(x => x.FitInfo.ExcludedForHighResidual))); // Fit was successful, exclude all unused non stellar objects so they // don't interfere with the included/excluded stars improved solution tests m_ImprovedSolution.FitInfo.AllStarPairs.RemoveAll(p => (p.FitInfo.ExcludedForHighResidual || !p.FitInfo.UsedInSolution) && nonStellarStars.Contains(p.StarNo)); // NOTE: How excluding stars for FWHM/Elongation may lead to incorrectly accepted solutions that include small number of stars // because the majority haven't been used for the fit. This is why we have another solution check here. if (m_ImprovedSolution.FitInfo.AllStarPairs.Count > 3) { List<PlateConstStarPair> usedStarPairs = m_ImprovedSolution.FitInfo.AllStarPairs.Where(p => p.FitInfo.UsedInSolution).ToList(); double maxIncludedMag = usedStarPairs.Max(p => p.Mag); int nonIncludedConsideredStars = consideredStars.Count(s => s.Mag <= maxIncludedMag) - usedStarPairs.Count; if (nonIncludedConsideredStars > CorePyramidConfig.Default.MaxFWHMExcludedImprovemntStarsCoeff * usedStarPairs.Count) { LogUnsuccessfulFitImage(m_ImprovedSolution, i, j, k, string.Format("More than {0:0.0}% of the stars ({1} stars) down to mag {2:0.00} have not been matched. Attempted stars: {3}, Coeff: {4:0.00}", CorePyramidConfig.Default.MaxFWHMExcludedImprovemntStarsCoeff * 100, nonIncludedConsideredStars, maxIncludedMag, m_SolutionSolver.Pairs.Count, nonIncludedConsideredStars / m_SolutionSolver.Pairs.Count)); m_ImprovedSolution = null; return; } List<double> intensities = usedStarPairs.Select(s => (double)s.Intensity).ToList(); List<double> mags = usedStarPairs.Select(s => s.Mag).ToList(); if (usedStarPairs.Count > 3) { LinearRegression reg = new LinearRegression(); int pointsAdded = 0; for (int ii = 0; ii < intensities.Count; ii++) { if (intensities[ii] > 0) { reg.AddDataPoint(intensities[ii], Math.Log10(mags[ii])); pointsAdded++; } } if (pointsAdded > 3) { reg.Solve(); if (Math.Pow(10, reg.StdDev) > CorePyramidConfig.Default.MagFitTestMaxStdDev || reg.A > CorePyramidConfig.Default.MagFitTestMaxInclination) { LogUnsuccessfulFitImage(m_ImprovedSolution, i, j, k, string.Format("Failing solution for failed magnitude fit. Intensity(Log10(Magntude)) = {1} * x + {2}, StdDev = {0:0.0000}, ChiSquare = {3:0.000}", Math.Pow(10, reg.StdDev), reg.A, reg.B, reg.ChiSquare)); m_ImprovedSolution = null; return; } } } } } }
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; }
/// <summary> /// Used to calibrate a plate. Does the more precise astrometric fit /// Precondition: A coarser (m_PreliminaryFit) is required where the user has visually roughly fitted the stars to the plate /// </summary> /// <param name="limitMag">The faintest magnitude of a star to be used for the fit</param> /// <param name="fineFit">Used to determine the search area for the stars. When the fit is not a 'fine' fit then 2.5 times /// larger are is used for the fit</param> public bool SolvePlateConstantsPhase1(double limitMag, bool fineFit) { int searchDistance = 10; IAstrometricFit previousFit; if (!fineFit) { searchDistance = ((int)Math.Ceiling(2.5 * CoreAstrometrySettings.Default.SearchArea) + 1); previousFit = m_Context.PreliminaryFit; } else { searchDistance = (int)Math.Ceiling(CoreAstrometrySettings.Default.SearchArea) + 1; previousFit = m_Context.FirstAstrometricFit; } m_ConstantsSolver = new PlateConstantsSolver(m_Context.PlateConfig); // NOTE: Get the coordinates of the center of the plate. double ra0deg, de0Deg; previousFit.GetRADEFromImageCoords( m_Context.PlateConfig.CenterXImage, m_Context.PlateConfig.CenterYImage, out ra0deg, out de0Deg); m_ConstantsSolver.InitPlateSolution(ra0deg, de0Deg); #region PASS 1 - Only using the stars down to limitMag int idx = -1; foreach (IStar star in m_Context.FieldSolveContext.CatalogueStars) { idx++; double x, y; previousFit.GetImageCoordsFromRADE(star.RADeg, star.DEDeg, out x, out y); if (m_Context.FitExcludeArea.Contains((int)x, (int)y)) { continue; } if (x < 0 || x > m_Context.PlateConfig.ImageWidth || y < 0 || y > m_Context.PlateConfig.ImageHeight) { continue; } if (limitMag < star.Mag) { continue; } StarMapFeature feature = m_Context.StarMap.GetFeatureInRadius((int)x, (int)y, searchDistance); Trace.WriteLine(string.Format("Searching for {0} ({1:0.0} mag) at ({2:0.0},{3:0.0}).{4}Found!", star.GetStarDesignation(0), star.Mag, x, y, feature != null ? "" : "NOT ")); if (fineFit) { ImagePixel centroid = m_Context.StarMap.GetCentroid((int)x, (int)y, searchDistance); if (centroid != null) { if (centroid.SignalNoise >= m_AstrometrySettings.DetectionLimit) { if (!m_Context.FitExcludeArea.Contains(centroid.X, centroid.Y)) { // NOTE: Don't add a star if outside the selected area m_ConstantsSolver.AddStar(centroid, star, feature); } } } else { //Trace.WriteLine("m_SolvePlateConts.NoMatch:" + star.StarNo); } } else if (feature != null) { ImagePixel plateStar = feature.GetCenter(); if (!m_Context.FitExcludeArea.Contains(plateStar.X, plateStar.Y)) { // NOTE: Don't add a star if outside the selected area m_ConstantsSolver.AddStar(plateStar, star, feature); } } else { //Trace.WriteLine("m_SolvePlateConts.NoMatch:" + star.StarNo); } } // TODO: Shouldn't this be comming from the settings?? return(m_ConstantsSolver.Pairs.Count >= 4); #endregion }