public Ephemerides GetObservationDetails(PlanningFilter filter, CelestialObject body) { var ctx = new SkyContext(filter.JulianDayMidnight, filter.ObserverLocation, preferFast: true); ctx.MinBodyAltitudeForVisibilityCalculations = filter.MinBodyAltitude; ctx.MaxSunAltitudeForVisibilityCalculations = filter.MaxSunAltitude; return(GetObservationDetails(filter, ctx, body, force: true)); }
public ICollection <Ephemerides> CreatePlan(PlanningFilter filter, CancellationToken?token = null, IProgress <double> progress = null) { double timeFrom = filter.TimeFrom / 24.0; double timeTo = filter.TimeTo / 24.0; double obsDuration = timeTo < timeFrom ? timeTo - timeFrom + 1 : timeTo - timeFrom; int countLimit = filter.CountLimit ?? 1000; // Planned observation range, expressed as circular sector (angles) // It represents a timeframe to be compared with each celestial body visibility conditions. AngleRange obsRange = new AngleRange(timeFrom * 360, obsDuration * 360); // Resulting list of ephemerides. Each item is a planned observation of a celestial body. List <Ephemerides> ephemerides = new List <Ephemerides>(); // Context used for calculations. We use local midnight as a time reference to obtain // general conditions of visibility of celestial bodies, // and after that apply desired timeframe to find intersections. var context = new SkyContext(filter.JulianDayMidnight, filter.ObserverLocation, true); context.MinBodyAltitudeForVisibilityCalculations = filter.MinBodyAltitude; context.MaxSunAltitudeForVisibilityCalculations = filter.MaxSunAltitude; IEnumerable <CelestialObject> celestialObjects = null; // create plan from predefined list of objects if (filter.CelestialObjects.Any()) { celestialObjects = filter.CelestialObjects; } // create plan from objects types filter else if (filter.CelestialObjectsTypes.Any()) { celestialObjects = sky.CelestialObjects.Where(b => filter.CelestialObjectsTypes.Contains(b.Type)); } // filter is uncomplete! else { throw new ArgumentException("Filter must contain either celestial objects list or celestial objects types list."); } // Total objects count double objectsCount = filter.CountLimit ?? celestialObjects.Count(); int counter = 0; foreach (CelestialObject body in celestialObjects) { if (token.HasValue && token.Value.IsCancellationRequested) { ephemerides.Clear(); break; } if (ephemerides.Count >= countLimit) { break; } progress?.Report(counter++ / objectsCount * 100); var e = GetObservationDetails(filter, context, body, !filter.ApplyFilters); if (e != null) { ephemerides.Add(e); } } return(ephemerides); }
private Ephemerides GetObservationDetails(PlanningFilter filter, SkyContext context, CelestialObject body, bool force = false) { float? magLimit = filter.MagLimit; double timeFrom = filter.TimeFrom / 24.0; double timeTo = filter.TimeTo / 24.0; double obsDuration = timeTo < timeFrom ? timeTo - timeFrom + 1 : timeTo - timeFrom; double?durationLimit = filter.DurationLimit != null ? filter.DurationLimit / 60.0 : null; // Planned observation range, expressed as circular sector (angles) // It represents a timeframe to be compared with each celestial body visibility conditions. AngleRange obsRange = new AngleRange(timeFrom * 360, obsDuration * 360); // Ephemerides for particular celestial body Ephemerides bodyEphemerides = new Ephemerides(body); // Ephemerides available for the body var categories = sky.GetEphemerisCategories(body); var visibilityEphems = sky.GetEphemerides(body, context, new[] { "Visibility.Begin", "Visibility.End", "Visibility.Duration", "RTS.Rise", "RTS.Transit", "RTS.Set", "RTS.RiseAzimuth", "RTS.TransitAltitude", "RTS.SetAzimuth", "Magnitude" }); bodyEphemerides.AddRange(visibilityEphems); // Body does not have magnitude if (!categories.Contains("Magnitude")) { if (!force && filter.SkipUnknownMagnitude) { return(null); } // Apply "skip unknown mag" filter bodyEphemerides.Add(new Ephemeris("Magnitude", null, Formatters.Magnitude)); } if (!force && filter.MagLimit != null) { float?mag = bodyEphemerides.GetValue <float?>("Magnitude"); // Apply magnitude filter if (mag > magLimit) { return(null); } } // If body has visibility ephemerides, it can be planned. if (categories.Contains("Visibility.Duration")) { double visDuration = visibilityEphems.GetValue <double>("Visibility.Duration"); if (visDuration > 0) { Date visBegin = visibilityEphems.GetValue <Date>("Visibility.Begin"); AngleRange visRange = new AngleRange(visBegin.Time * 360, visDuration / 24 * 360); var ranges = visRange.Overlaps(obsRange); if (ranges.Any()) { double beginTime = ranges.First().Start / 360; if (beginTime < timeFrom) { beginTime += 1; } double endTime = beginTime + ranges.First().Range / 360; // Begin of body observation, limited by desired observation begin and end time, // and expressed in Date Date bodyObsBegin = new Date(context.JulianDayMidnight + beginTime, context.GeoLocation.UtcOffset); // Real duration of body observation, limited by desired observation begin and end time, // and expressed in hours (convert from angle range expressed in degrees): double bodyObsDuration = ranges.First().Range / 360 * 24; // Apply duration filter if (!force && durationLimit > bodyObsDuration) { return(null); } // End of body observation, limited by desired observation begin and end time, // and expressed in Date Date bodyObsEnd = new Date(context.JulianDayMidnight + endTime, context.GeoLocation.UtcOffset); bodyEphemerides.Add(new Ephemeris("Observation.Begin", bodyObsBegin, Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.Duration", bodyObsDuration, Formatters.VisibilityDuration)); bodyEphemerides.Add(new Ephemeris("Observation.End", bodyObsEnd, Formatters.Time)); // best time of observation (in the expected time range) { var transit = visibilityEphems.GetValue <Date>("RTS.Transit"); var transitAlt = visibilityEphems.GetValue <double>("RTS.TransitAltitude"); AngleRange bodyObsRange = new AngleRange(bodyObsBegin.Time * 360, bodyObsDuration / 24 * 360); AngleRange tranRange = new AngleRange(transit.Time * 360, 1e-6); if (bodyObsRange.Overlaps(tranRange).Any()) { bodyEphemerides.Add(new Ephemeris("Observation.Best", transit, Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.BestAltitude", transitAlt, Formatters.Altitude)); bodyEphemerides.Add(new Ephemeris("Observation.BestAzimuth", 0.0, Formatters.Azimuth)); var ctxBest = new SkyContext(transit.ToJulianEphemerisDay(), context.GeoLocation, preferFast: true); var bestEphemerides = sky.GetEphemerides(body, ctxBest, new[] { "Equatorial.Alpha", "Equatorial.Delta", "Constellation" }); bodyEphemerides.AddRange(bestEphemerides); } else { var ctxBegin = new SkyContext(bodyObsBegin.ToJulianEphemerisDay(), context.GeoLocation, preferFast: true); var beginEphemerides = sky.GetEphemerides(body, ctxBegin, new[] { "Horizontal.Altitude", "Horizontal.Azimuth", "Equatorial.Alpha", "Equatorial.Delta", "Constellation" }); double altBegin = beginEphemerides.GetValue <double>("Horizontal.Altitude"); double aziBegin = beginEphemerides.GetValue <double>("Horizontal.Azimuth"); var ctxEnd = new SkyContext(bodyObsEnd.ToJulianEphemerisDay(), context.GeoLocation, preferFast: true); var endEphemerides = sky.GetEphemerides(body, ctxEnd, new[] { "Horizontal.Altitude", "Horizontal.Azimuth", "Equatorial.Alpha", "Equatorial.Delta", "Constellation" }); double altEnd = endEphemerides.GetValue <double>("Horizontal.Altitude"); double aziEnd = endEphemerides.GetValue <double>("Horizontal.Azimuth"); if (altBegin >= altEnd) { bodyEphemerides.Add(new Ephemeris("Observation.Best", bodyObsBegin, Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.BestAltitude", altBegin, Formatters.Altitude)); bodyEphemerides.Add(new Ephemeris("Observation.BestAzimuth", aziBegin, Formatters.Azimuth)); bodyEphemerides.AddRange(beginEphemerides); } else { bodyEphemerides.Add(new Ephemeris("Observation.Best", bodyObsEnd, Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.BestAltitude", altEnd, Formatters.Altitude)); bodyEphemerides.Add(new Ephemeris("Observation.BestAzimuth", aziEnd, Formatters.Azimuth)); bodyEphemerides.AddRange(endEphemerides); } } } return(bodyEphemerides); } } } // body observation duration does not match with desired time // or body does not observable at all, // or no info provided about observation if (force) { bodyEphemerides.Add(new Ephemeris("Observation.Begin", new Date(double.NaN), Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.Duration", double.NaN, Formatters.VisibilityDuration)); bodyEphemerides.Add(new Ephemeris("Observation.End", new Date(double.NaN), Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.Best", new Date(double.NaN), Formatters.Time)); bodyEphemerides.Add(new Ephemeris("Observation.BestAltitude", double.NaN, Formatters.Altitude)); bodyEphemerides.Add(new Ephemeris("Observation.BestAzimuth", double.NaN, Formatters.Azimuth)); bodyEphemerides.AddRange(visibilityEphems); var ctx = new SkyContext(filter.JulianDayMidnight, filter.ObserverLocation, preferFast: true); bodyEphemerides.AddRange(sky.GetEphemerides(body, ctx, new[] { "Horizontal.Altitude", "Horizontal.Azimuth", "Equatorial.Alpha", "Equatorial.Delta", "Constellation" })); // final check: add fictive/empty ephemerides as it needed by planner AddEphemIfMissing(bodyEphemerides, "Constellation", null); AddEphemIfMissing(bodyEphemerides, "Equatorial.Alpha", double.NaN); AddEphemIfMissing(bodyEphemerides, "Equatorial.Delta", double.NaN); AddEphemIfMissing(bodyEphemerides, "Horizontal.Altitude", double.NaN); AddEphemIfMissing(bodyEphemerides, "Horizontal.Azimuth", double.NaN); AddEphemIfMissing(bodyEphemerides, "Constellation", null); AddEphemIfMissing(bodyEphemerides, "RTS.RiseAzimuth", double.NaN); AddEphemIfMissing(bodyEphemerides, "RTS.SetAzimuth", double.NaN); AddEphemIfMissing(bodyEphemerides, "RTS.TransitAltitude", double.NaN); return(bodyEphemerides); } return(null); }