public async Task <PlateSolveResult> SolveAsync(IImageData source, PlateSolveParameter parameter, IProgress <ApplicationStatus> progress, CancellationToken canceltoken) { EnsureSolverValid(parameter); var imageProperties = PlateSolveImageProperties.Create(parameter, source); return(await SolveAsyncImpl(source, parameter, imageProperties, progress, canceltoken)); }
protected override async Task <PlateSolveResult> SolveAsyncImpl( IImageData source, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties, IProgress <ApplicationStatus> progress, CancellationToken cancelToken) { PlateSolveResult result = new PlateSolveResult(); try { progress.Report(new ApplicationStatus() { Status = "Authenticating to Astrometery.net..." }); var session = await GetAuthenticationToken(cancelToken); progress.Report(new ApplicationStatus() { Status = "Uploading image to Astrometry.net..." }); var jobId = await SubmitImageJob(progress, source, session, cancelToken); progress.Report(new ApplicationStatus() { Status = $"Getting result for Astrometry.net job {jobId}..." }); JobResult jobinfo = await GetJobResult(jobId, cancelToken); result.Orientation = jobinfo.calibration.orientation; /* The orientation is mirrored on the x-axis */ result.Flipped = jobinfo.calibration.parity < 0; result.Orientation = 180 - result.Orientation + 360; result.Pixscale = jobinfo.calibration.pixscale; result.Coordinates = new Utility.Astrometry.Coordinates(jobinfo.calibration.ra, jobinfo.calibration.dec, Utility.Astrometry.Epoch.J2000, Utility.Astrometry.Coordinates.RAType.Degrees); result.Radius = jobinfo.calibration.radius; } catch (OperationCanceledException) { result.Success = false; } catch (Exception ex) { result.Success = false; Notification.ShowError($"Error plate solving with Astrometry.net. {ex.Message}"); } if (result.Success) { progress.Report(new ApplicationStatus() { Status = "Solved" }); } else { progress.Report(new ApplicationStatus() { Status = "Solve failed" }); } return(result); }
protected override void EnsureSolverValid(PlateSolveParameter parameter) { if (string.IsNullOrWhiteSpace(_apikey)) { throw new ArgumentException("Astrometry.net API key is not configured"); } if (string.IsNullOrWhiteSpace(_apiurl)) { throw new ArgumentException("Astrometry.net API URL is not configured"); } // Trailing spaces on the API key text sometimes sneak in if it has been copy and pasted if (Regex.IsMatch(_apikey, @"\s")) { throw new ArgumentException("Astrometry.net API key contains an invalid space character"); } }
/// <summary> /// Gets start arguments for Platesolve2 out of RA,Dec, ArcDegWidth, ArcDegHeight and ImageFilePath /// </summary> /// <returns></returns> protected override string GetArguments( string imageFilePath, string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { var args = new string[] { Astrometry.ToRadians(parameter.Coordinates.RADegrees).ToString(CultureInfo.InvariantCulture), Astrometry.ToRadians(parameter.Coordinates.Dec).ToString(CultureInfo.InvariantCulture), Astrometry.ToRadians(imageProperties.FoVW).ToString(CultureInfo.InvariantCulture), Astrometry.ToRadians(imageProperties.FoVH).ToString(CultureInfo.InvariantCulture), parameter.Regions.ToString(), imageFilePath, "0" }; return(string.Join(",", args)); }
protected override async Task <PlateSolveResult> SolveAsyncImpl( IImageData source, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties, IProgress <ApplicationStatus> progress, CancellationToken cancelToken) { var result = new PlateSolveResult() { Success = false }; string imagePath = null, outputPath = null; try { //Copy Image to local app data imagePath = await PrepareAndSaveImage(source, cancelToken); progress.Report(new ApplicationStatus() { Status = Locale.Loc.Instance["LblSolving"] }); outputPath = GetOutputPath(imagePath); await StartCLI(imagePath, outputPath, parameter, imageProperties, progress, cancelToken); //Extract solution coordinates result = ReadResult(outputPath, parameter, imageProperties); } finally { progress.Report(new ApplicationStatus() { Status = string.Empty }); if (imagePath != null && File.Exists(imagePath)) { File.Delete(imagePath); } if (outputPath != null && File.Exists(outputPath)) { File.Delete(outputPath); } } return(result); }
/// <summary> /// Creates the arguments to launch ASTAP process /// </summary> /// <returns></returns> /// <remarks>http://www.hnsky.org/astap.htm#astap_command_line</remarks> protected override string GetArguments( string imageFilePath, string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { var args = new List <string>(); //File location to solve args.Add($"-f \"{imageFilePath}\""); //Field height of image var fov = Math.Round(imageProperties.FoVH, 6); args.Add($"-fov {fov.ToString(CultureInfo.InvariantCulture)}"); //Downsample factor args.Add($"-z {parameter.DownSampleFactor}"); //Max number of stars args.Add($"-s {parameter.MaxObjects}"); if (parameter.SearchRadius > 0 && parameter.Coordinates != null) { //Search field radius args.Add($"-r {parameter.SearchRadius}"); var ra = Math.Round(parameter.Coordinates.RA, 6); //Right Ascension in degrees args.Add($"-ra {ra.ToString(CultureInfo.InvariantCulture)}"); var spd = Math.Round(parameter.Coordinates.Dec + 90.0, 6); //South pole distance in degrees args.Add($"-spd {spd.ToString(CultureInfo.InvariantCulture)}"); } else { //Search field radius args.Add($"-r {180}"); } return(string.Join(" ", args)); }
protected override string GetArguments( string imageFilePath, string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { var args = new List <string>(); var imageFilePathArg = imageFilePath.Replace("\\", "/"); //FileName args.Add($"\"{imageFilePathArg}\""); //OutFile args.Add($"\"{outputFilePath}\""); //FocalLength args.Add(parameter.FocalLength.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)); //PixelSize args.Add(parameter.PixelSize.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)); if (parameter.Coordinates != null) { //CurrentRA args.Add(parameter.Coordinates.RADegrees.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)); //CurrentDec args.Add(parameter.Coordinates.Dec.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)); } else { args.Add("0"); args.Add("0"); } //NearRadius args.Add(parameter.SearchRadius.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)); return($"/solvefile {string.Join(" ", args)}"); }
protected override void EnsureSolverValid(PlateSolveParameter parameter) { if (string.IsNullOrWhiteSpace(this.executableLocation)) { throw new ASTAPValidationFailedException($"ASTAP executable location missing! Please enter the location in the platesolver options!"); } if (!File.Exists(this.executableLocation)) { throw new ASTAPValidationFailedException($"ASTAP executable not found at {this.executableLocation}"); } var astapVersionInfo = FileVersionInfo.GetVersionInfo(this.executableLocation); if (astapVersionInfo.FileVersion == null) { // Version below 0.9.1.0 // Only allows downsample in the range of 1 to 4 if (parameter.DownSampleFactor == 0) { throw new ASTAPValidationFailedException($"ASTAP version below 0.9.1.0 does not allow auto downsample factor value of 0! Please update your ASTAP version!"); } } }
protected override PlateSolveResult ReadResult( string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { var result = new PlateSolveResult() { Success = false }; if (File.Exists(outputFilePath)) { string[] lines = File.ReadAllLines(outputFilePath, Encoding.UTF8); if (lines.Length > 0) { if (lines[0] == "OK" && lines.Length >= 8) { var ra = double.Parse(lines[1]); var dec = double.Parse(lines[2]); result.Coordinates = new Coordinates(ra, dec, Epoch.J2000, Coordinates.RAType.Degrees); var fovW = lines[3]; var fovH = lines[4]; result.Pixscale = double.Parse(lines[5]); result.Orientation = double.Parse(lines[6]); /* Due to the way N.I.N.A. writes FITS files, the orientation is mirrored on the x-axis */ result.Orientation = 180 - result.Orientation + 360; var focalLength = lines[7]; result.Success = true; } } } return(result); }
protected async Task StartCLI(string imageFilePath, string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties, IProgress <ApplicationStatus> progress, CancellationToken ct) { if (executableLocation != "cmd.exe" && !File.Exists(executableLocation)) { throw new FileNotFoundException("Executable not found", executableLocation); } System.Diagnostics.Process process = new System.Diagnostics.Process(); System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(); startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal; startInfo.FileName = executableLocation; startInfo.UseShellExecute = false; startInfo.RedirectStandardOutput = true; startInfo.CreateNoWindow = true; startInfo.Arguments = GetArguments(imageFilePath, outputFilePath, parameter, imageProperties); process.StartInfo = startInfo; process.EnableRaisingEvents = true; process.OutputDataReceived += (object sender, System.Diagnostics.DataReceivedEventArgs e) => { progress.Report(new ApplicationStatus() { Status = e.Data }); }; process.ErrorDataReceived += (object sender, System.Diagnostics.DataReceivedEventArgs e) => { progress.Report(new ApplicationStatus() { Status = e.Data }); }; Logger.Debug($"Starting process '{executableLocation}' with args '{startInfo.Arguments}'"); process.Start(); await process.WaitForExitAsync(ct); }
protected override string GetArguments( string imageFilePath, string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { List <string> options = new List <string>(); options.Add("--overwrite"); options.Add("--index-xyls none"); options.Add("--corr none"); options.Add("--rdls none"); options.Add("--match none"); options.Add("--new-fits none"); //options.Add("-C cancel--crpix"); options.Add("-center"); options.Add($"--objs {parameter.MaxObjects}"); options.Add("--no-plots"); options.Add("--resort"); options.Add($"--downsample {parameter.DownSampleFactor}"); var lowArcSecPerPix = imageProperties.ArcSecPerPixel - 0.2; var highArcSecPerPix = imageProperties.ArcSecPerPixel + 0.2; options.Add("--scale-units arcsecperpix"); options.Add(string.Format("-L {0}", lowArcSecPerPix.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture))); options.Add(string.Format("-H {0}", highArcSecPerPix.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture))); if (parameter.SearchRadius > 0 && parameter.Coordinates != null) { options.Add($"--ra {parameter.Coordinates.RADegrees.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)}"); options.Add($"--dec {parameter.Coordinates.Dec.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)}"); options.Add($"--radius {parameter.SearchRadius.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)}"); } return(string.Format("/C \"\"{0}\" --login -c '/usr/bin/solve-field {1} \"{2}\"'\"", bashLocation, string.Join(" ", options), imageFilePath.Replace("\\", "/"))); }
protected abstract string GetArguments( string imageFilePath, string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties);
/// <summary> /// Extract result out of generated .axy file. File consists of three rows /// 1. row: RA,Dec,Code /// 2. row: Scale,Orientation,?,?,Stars /// </summary> /// <returns>PlateSolveResult</returns> protected override PlateSolveResult ReadResult( string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { PlateSolveResult result = new PlateSolveResult() { Success = false }; if (File.Exists(outputFilePath)) { using (var s = new StreamReader(outputFilePath)) { string line; int linenr = 0; while ((line = s.ReadLine()) != null) { string[] resultArr = line.Split(','); if (linenr == 0) { if (resultArr.Length > 2) { double ra, dec; int status; if (resultArr.Length == 5) { /* workaround for when decimal separator is comma instead of point. * won't work when result contains even numbers tho... */ status = int.Parse(resultArr[4]); if (status != 1) { /* error */ result.Success = false; break; } ra = double.Parse(resultArr[0] + "." + resultArr[1], CultureInfo.InvariantCulture); dec = double.Parse(resultArr[2] + "." + resultArr[3], CultureInfo.InvariantCulture); } else { status = int.Parse(resultArr[2]); if (status != 1) { /* error */ result.Success = false; break; } ra = double.Parse(resultArr[0], CultureInfo.InvariantCulture); dec = double.Parse(resultArr[1], CultureInfo.InvariantCulture); } /* success */ result.Success = true; result.Coordinates = new Coordinates(Astrometry.ToDegree(ra), Astrometry.ToDegree(dec), Epoch.J2000, Coordinates.RAType.Degrees); } } if (linenr == 1) { if (resultArr.Length > 2) { if (resultArr.Length > 5) { /* workaround for when decimal separator is comma instead of point. * won't work when result contains even numbers tho... */ result.Pixscale = double.Parse(resultArr[0] + "." + resultArr[1], CultureInfo.InvariantCulture); result.Orientation = double.Parse(resultArr[2] + "." + resultArr[3], CultureInfo.InvariantCulture); result.Flipped = !(double.Parse(resultArr[4] + "." + resultArr[5], CultureInfo.InvariantCulture) < 0); if (result.Flipped) { result.Orientation = result.Orientation - 180; } } else { result.Pixscale = double.Parse(resultArr[0], CultureInfo.InvariantCulture); result.Orientation = double.Parse(resultArr[1], CultureInfo.InvariantCulture); result.Flipped = !(double.Parse(resultArr[2], CultureInfo.InvariantCulture) < 0); if (result.Flipped) { result.Orientation = result.Orientation - 180; } } } } linenr++; } } } return(result); }
protected virtual void EnsureSolverValid(PlateSolveParameter parameter) { }
protected abstract Task <PlateSolveResult> SolveAsyncImpl( IImageData source, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties, IProgress <ApplicationStatus> progress, CancellationToken canceltoken);
protected abstract PlateSolveResult ReadResult( string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties);
protected override PlateSolveResult ReadResult(string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { var result = new PlateSolveResult() { Success = false }; if (File.Exists(outputFilePath)) { var startInfo = new System.Diagnostics.ProcessStartInfo(); startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal; startInfo.FileName = "cmd.exe"; startInfo.UseShellExecute = false; startInfo.RedirectStandardOutput = true; startInfo.CreateNoWindow = true; startInfo.Arguments = string.Format("/C \"\"{0}\" --login -c 'wcsinfo \"{1}\"'\"", bashLocation, outputFilePath.Replace("\\", "/")); using (var process = new System.Diagnostics.Process()) { process.StartInfo = startInfo; process.Start(); Dictionary <string, string> wcsinfo = new Dictionary <string, string>(); while (!process.StandardOutput.EndOfStream) { var line = process.StandardOutput.ReadLine(); if (line != null) { var valuepair = line.Split(' '); if (valuepair != null && valuepair.Length == 2) { wcsinfo[valuepair[0]] = valuepair[1]; } } } if (wcsinfo.ContainsKey("crval0") && wcsinfo.ContainsKey("crval1") && wcsinfo.ContainsKey("crpix0") && wcsinfo.ContainsKey("crpix1") && wcsinfo.ContainsKey("cd11") && wcsinfo.ContainsKey("cd12") && wcsinfo.ContainsKey("cd21") && wcsinfo.ContainsKey("cd22")) { var crval1 = double.Parse(wcsinfo["crval0"]); var crval2 = double.Parse(wcsinfo["crval1"]); var crpix1 = double.Parse(wcsinfo["crpix0"]); var crpix2 = double.Parse(wcsinfo["crpix1"]); var cd11 = double.Parse(wcsinfo["cd11"]); var cd12 = double.Parse(wcsinfo["cd12"]); var cd21 = double.Parse(wcsinfo["cd21"]); var cd22 = double.Parse(wcsinfo["cd22"]); var wcs = new WorldCoordinateSystem( crval1, crval2, crpix1, crpix2, cd11, cd12, cd21, cd22 ); /* Due to the way N.I.N.A. writes FITS files, the orientation is mirrored on the x-axis */ result.Flipped = !wcs.Flipped; } double ra = 0, dec = 0; if (wcsinfo.ContainsKey("ra_center")) { ra = double.Parse(wcsinfo["ra_center"], CultureInfo.InvariantCulture); } if (wcsinfo.ContainsKey("dec_center")) { dec = double.Parse(wcsinfo["dec_center"], CultureInfo.InvariantCulture); } if (wcsinfo.ContainsKey("orientation_center")) { result.Orientation = double.Parse(wcsinfo["orientation_center"], CultureInfo.InvariantCulture); /* Due to the way N.I.N.A. writes FITS files, the orientation is mirrored on the x-axis */ result.Orientation = 180 - result.Orientation + 360; } if (wcsinfo.ContainsKey("pixscale")) { result.Pixscale = double.Parse(wcsinfo["pixscale"], CultureInfo.InvariantCulture); } result.Coordinates = new Coordinates(ra, dec, Epoch.J2000, Coordinates.RAType.Degrees); result.Success = true; } } return(result); }
protected override PlateSolveResult ReadResult( string outputFilePath, PlateSolveParameter parameter, PlateSolveImageProperties imageProperties) { var result = new PlateSolveResult() { Success = false }; if (!File.Exists(outputFilePath)) { Notification.ShowError("ASTAP - Plate solve failed. No output file found."); return(result); } var dict = File.ReadLines(outputFilePath) .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => line.Split(new char[] { '=' }, 2, 0)) .ToDictionary(parts => parts[0], parts => parts[1]); dict.TryGetValue("WARNING", out var warning); if (!dict.ContainsKey("PLTSOLVD") || dict["PLTSOLVD"] != "T") { dict.TryGetValue("ERROR", out var error); Notification.ShowError($"ASTAP - Plate solve failed.{Environment.NewLine}{warning}{Environment.NewLine}{error}"); return(result); } if (!string.IsNullOrWhiteSpace(warning)) { Notification.ShowWarning($"ASTAP - {warning}"); } var wcs = new WorldCoordinateSystem( double.Parse(dict["CRVAL1"], CultureInfo.InvariantCulture), double.Parse(dict["CRVAL2"], CultureInfo.InvariantCulture), double.Parse(dict["CRPIX1"], CultureInfo.InvariantCulture), double.Parse(dict["CRPIX2"], CultureInfo.InvariantCulture), double.Parse(dict["CD1_1"], CultureInfo.InvariantCulture), double.Parse(dict["CD1_2"], CultureInfo.InvariantCulture), double.Parse(dict["CD2_1"], CultureInfo.InvariantCulture), double.Parse(dict["CD2_2"], CultureInfo.InvariantCulture) ); result.Success = true; result.Coordinates = new Coordinates( double.Parse(dict["CRVAL1"], CultureInfo.InvariantCulture), double.Parse(dict["CRVAL2"], CultureInfo.InvariantCulture), Epoch.J2000, Coordinates.RAType.Degrees ); result.Orientation = double.Parse(dict["CROTA2"], CultureInfo.InvariantCulture); /* * CDELT1 and CDELT2 are obsolete. * To calculate pixel scale, we should add the squares of CD1_2 and CD2_2 and take the square root to get degrees. */ if (dict.ContainsKey("CD1_2") && dict.ContainsKey("CD2_2")) { double.TryParse(dict["CD1_2"], NumberStyles.Any, CultureInfo.InvariantCulture, out double cr1y); double.TryParse(dict["CD2_2"], NumberStyles.Any, CultureInfo.InvariantCulture, out double cr2y); result.Pixscale = Astrometry.DegreeToArcsec(Math.Sqrt(Math.Pow(cr1y, 2) + Math.Pow(cr2y, 2))); } /* Due to the way N.I.N.A. writes FITS files, the orientation is mirrored on the x-axis */ result.Orientation = wcs.Rotation - 180; result.Flipped = !wcs.Flipped; return(result); }