Skip to content

Commit

Permalink
Merge pull request #25 from tcpalmer/development/nina3-pass2
Browse files Browse the repository at this point in the history
Development/nina3 pass2
  • Loading branch information
tcpalmer authored Aug 2, 2023
2 parents 867a72b + 3e8e616 commit 8883ca6
Show file tree
Hide file tree
Showing 58 changed files with 13,864 additions and 829 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Target Planning

## 3.2.1.0 - 2023-08-02
* Ported to NINA 3. Future updates will only apply to the NINA 3 version.

## 1.2.1.0 - 2023-01-13
* Fixed problem with twilight time calculations for high latitudes
* Added 'Send coordinates to Framing Wizard' button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<UserControl
x:Class="TargetPlanning.NINAPlugin.AnnualChart.AnnualPlanningChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"
xmlns:util="clr-namespace:NINA.Core.Utility;assembly=NINA.Core"
x:Name="UC"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/NINA.WPF.Base;component/Resources/StaticResources/ProfileService.xaml" />
<ResourceDictionary Source="/NINA.WPF.Base;component/Resources/StaticResources/SVGDictionary.xaml" />
<ResourceDictionary Source="/NINA.WPF.Base;component/Resources/StaticResources/Brushes.xaml" />
<ResourceDictionary Source="/NINA.WPF.Base;component/Resources/StaticResources/Converters.xaml" />
<ResourceDictionary Source="/NINA.WPF.Base;component/Resources/Styles/ToggleButton.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.Resources>
<util:BindingProxy x:Key="proxy" Data="{Binding ElementName=UC, Path=AnnualPlanningChartModel}" />
</Grid.Resources>

<StackPanel Orientation="Vertical">
<oxy:Plot
MinHeight="410"
VerticalAlignment="Stretch"
Background="{StaticResource BackgroundBrush}"
PlotAreaBackground="{StaticResource BackgroundBrush}"
PlotAreaBorderColor="{Binding Path=Color, Source={StaticResource BorderBrush}}"
TextColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}">

<oxy:Plot.Axes>
<oxy:DateTimeAxis
AxislineColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}"
IntervalType="Months"
IsPanEnabled="False"
IsZoomEnabled="False"
MajorGridlineColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}, Converter={StaticResource SetAlphaToColorConverter}, ConverterParameter=60}"
MajorGridlineStyle="LongDash"
MinorIntervalType="Weeks"
Position="Bottom"
StringFormat="MMM"
TextColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}"
TicklineColor="{Binding Path=Color, Source={StaticResource SecondaryBrush}}" />
<oxy:LinearAxis
AxislineColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}"
EndPosition="1"
IntervalLength="30"
IsPanEnabled="False"
IsZoomEnabled="False"
MajorGridlineColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}, Converter={StaticResource SetAlphaToColorConverter}, ConverterParameter=60}"
MajorGridlineStyle="LongDash"
MajorStep="30"
Maximum="90"
Minimum="0"
Position="Left"
StartPosition="0"
TextColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}"
TicklineColor="{Binding Path=Color, Source={StaticResource SecondaryBrush}}" />
</oxy:Plot.Axes>
<oxy:Plot.Series>
<oxy:LineSeries
Title="{Binding Source={StaticResource proxy}, Path=Data.TargetName}"
ItemsSource="{Binding Source={StaticResource proxy}, Path=Data.TargetAltitudes}"
TrackerFormatString="Target: {2:MM/dd/yyyy}&#x0a;Alt: {4:0.00}&#176;"
Color="{Binding Path=Color, Source={StaticResource PrimaryBrush}}" />
<oxy:LineSeries
Title="Moon"
ItemsSource="{Binding Source={StaticResource proxy}, Path=Data.MoonAltitudes}"
TrackerFormatString="Moon: {2:MM/dd/yyyy}&#x0a;Alt: {4:0.00}&#176;"
Color="{Binding Source={StaticResource proxy}, Path=Data.MoonColor, Converter={StaticResource SetAlphaToColorConverter}, ConverterParameter=90}" />
</oxy:Plot.Series>
<oxy:Plot.Annotations>
<oxy:LineAnnotation
MaximumY="90"
Text="Start Date"
TextColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}"
Type="Vertical"
X="{Binding Source={StaticResource proxy}, Path=Data.StartDate}"
Color="{Binding Path=Color, Source={StaticResource PrimaryBrush}}" />
<oxy:TextAnnotation
Background="{Binding Path=Color, Source={StaticResource BackgroundBrush}}"
Stroke="{Binding Path=Color, Source={StaticResource BorderBrush}}"
StrokeThickness="2"
Text="{Binding Source={StaticResource proxy}, Path=Data.AnnoText}"
TextColor="{Binding Path=Color, Source={StaticResource PrimaryBrush}}"
TextPosition="{Binding Source={StaticResource proxy}, Path=Data.AnnoPoint}" />
</oxy:Plot.Annotations>

</oxy:Plot>
</StackPanel>
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Windows;
using System.Windows.Controls;

namespace TargetPlanning.NINAPlugin.AnnualChart {

/// <summary>
/// Interaction logic for AnnualPlanningChart.xaml
/// </summary>
public partial class AnnualPlanningChart : UserControl {

public AnnualPlanningChart() {
InitializeComponent();
}

public static DependencyProperty AnnualPlanningChartProperty = DependencyProperty.Register("AnnualPlanningChartModel", typeof(AnnualPlanningChartModel), typeof(AnnualPlanningChart));

public AnnualPlanningChartModel AnnualPlanningChartModel {
get => (AnnualPlanningChartModel)GetValue(AnnualPlanningChartProperty);
set {
SetValue(AnnualPlanningChartProperty, value);
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using NINA.Astrometry;
using OxyPlot;
using OxyPlot.Axes;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Media;
using TargetPlanning.NINAPlugin.Astrometry;

namespace TargetPlanning.NINAPlugin.AnnualChart {

public class AnnualPlanningChartModel {

public string TargetName { get; private set; }
public IList<DataPoint> TargetAltitudes { get; private set; }
public IList<DataPoint> MoonAltitudes { get; private set; }

public Color MoonColor { get; private set; }
public double StartDate { get; private set; }
public string AnnoText { get; private set; }
public DataPoint AnnoPoint { get; private set; }

public AnnualPlanningChartModel(ObserverInfo location, DeepSkyObject target, DateTime startTime, CancellationToken token) {

MoonColor = Color.FromRgb(Colors.Gold.R, Colors.Gold.G, Colors.Gold.B);
AnnoText = startTime.Year.ToString();
DateTime dt = new DateTime(startTime.Year, 1, 12);
AnnoPoint = new DataPoint(DateTimeAxis.ToDouble(dt), 82);
StartDate = DateTimeAxis.ToDouble(startTime);

TargetName = target.Name != null ? target.Name : "User coords";
TargetAltitudes = new List<DataPoint>(366);
MoonAltitudes = new List<DataPoint>(366);

int daysInYear = DateTime.IsLeapYear(startTime.Year) ? 366 : 365;
DateTime dateTime = new DateTime(startTime.Year, 1, 1, 0, 0, 0, DateTimeKind.Local);
for (int i = 0; i < daysInYear; i++) {

if (token.IsCancellationRequested) {
throw new OperationCanceledException();
}

HorizontalCoordinate hc = AstrometryUtils.GetHorizontalCoordinates(location, target.Coordinates, dateTime);
TargetAltitudes.Add(new DataPoint(DateTimeAxis.ToDouble(dateTime), hc.Altitude));
dateTime = dateTime.AddDays(1);
}

dateTime = new DateTime(startTime.Year, 1, 1, 0, 0, 0, DateTimeKind.Local);
for (int i = 0; i < daysInYear; i++) {

if (token.IsCancellationRequested) {
throw new OperationCanceledException();
}

double altitude = AstroUtil.GetMoonAltitude(dateTime, location);
MoonAltitudes.Add(new DataPoint(DateTimeAxis.ToDouble(dateTime), altitude));
dateTime = dateTime.AddDays(1);
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace TargetPlanning.NINAPlugin.Astrometry {

public class Altitudes {

public List<AltitudeAtTime> AltitudeList { get; private set; }
public DateTime StartTime { get; private set; }
public DateTime EndTime { get; private set; }

public Altitudes(List<AltitudeAtTime> altitudes) {
Validate.Assert.notNull(altitudes, "altitudes cannot be null");
Validate.Assert.isTrue(altitudes.Count > 1, "altitudes must have at least two values");

this.AltitudeList = altitudes;
this.StartTime = altitudes[0].AtTime;
this.EndTime = altitudes[altitudes.Count - 1].AtTime;

Validate.Assert.isTrue(StartTime < EndTime, "startTime must be before endTime");

DateTime cmp = StartTime.AddSeconds(-1);
foreach (AltitudeAtTime altitude in altitudes) {
Validate.Assert.isTrue(cmp < altitude.AtTime, "time is not always increasing");
cmp = altitude.AtTime;
}
}

public int GetIntervalSeconds() {
return (int)EndTime.Subtract(StartTime).TotalSeconds;
}

public bool IsRisingAtEnd() {
int lastPos = AltitudeList.Count - 1;
return AltitudeList[lastPos].Altitude > AltitudeList[lastPos - 1].Altitude;
}

public Tuple<int, AltitudeAtTime> FindMaximumAltitude() {
double alt = double.MinValue;
AltitudeAtTime max = null;
int pos = -1;

for (int i = 0; i < AltitudeList.Count; i++) {
if (AltitudeList[i].Altitude > alt) {
max = AltitudeList[i];
alt = AltitudeList[i].Altitude;
pos = i;
}
}

return Tuple.Create(pos, max);
}

public Tuple<int, AltitudeAtTime> FindMinimumAltitude() {
double alt = double.MaxValue;
AltitudeAtTime min = null;
int pos = -1;

for (int i = 0; i < AltitudeList.Count; i++) {
if (AltitudeList[i].Altitude < alt) {
min = AltitudeList[i];
alt = AltitudeList[i].Altitude;
pos = i;
}
}

return Tuple.Create(pos, min);
}

/// <summary>
/// Remove any leading ascending samples. For daylight checks, a set of altitudes may run from local noon until the next noon.
/// If local noon is before solar noon, then the altitudes will still be climbing for that span (which we don't want).
/// </summary>
/// <param name="altitudes"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public Altitudes ClipAscendingStart() {

List<AltitudeAtTime> alts = AltitudeList;
if (alts[0].Altitude > alts[1].Altitude) {
return this;
}

for (int i = 0; i < alts.Count - 1; i++) {
if (alts[i].Altitude > alts[i + 1].Altitude) {
return new Altitudes(AltitudeList.GetRange(i, alts.Count - i));
}
}

throw new ArgumentException("altitude list is unexpectedly always ascending");
}

/// <summary>
/// Find the time span containing the target altitude. If descending is true, it will detect the altitude in the
/// region where the sequence is descending, otherwise where ascending. The motivation is finding altitudes of interest
/// such as horizon crossings.
/// </summary>
/// <param name="targetAltitude"></param>
/// <param name="descending"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public Altitudes FindSpan(double targetAltitude, bool descending) {
List<AltitudeAtTime> alts = AltitudeList;

// If descending, start from beginning
if (descending) {
for (int i = 0; i < alts.Count; i++) {
if (alts[i].Altitude < targetAltitude) {
List<AltitudeAtTime> span = new List<AltitudeAtTime>(2) {
alts[i-1],
alts[i]
};
return new Altitudes(span);
}
}
}
else {
// If ascending, find minimum and start from there
Tuple<int, AltitudeAtTime> min = FindMinimumAltitude();

// If the minimum is above the target, then the target is not present in this set of samples
if (min.Item2.Altitude > targetAltitude) {
return null;
}

for (int i = min.Item1; i < alts.Count; i++) {
if (alts[i].Altitude > targetAltitude) {
List<AltitudeAtTime> span = new List<AltitudeAtTime>(2) {
alts[i-1],
alts[i]
};
return new Altitudes(span);
}
}
}

// If the span can't be found, it might be after further refinement ...
return null;
}

public override string ToString() {
StringBuilder sb = new StringBuilder();

for (int i = 0; i < AltitudeList.Count; i++) {
AltitudeAtTime altitude = AltitudeList[i];
sb.Append(String.Format("{0,2:F0} {1,9:F2} {2,9:F2} ", i, altitude.Altitude, altitude.Azimuth));
sb.Append(altitude.AtTime.ToString("MM/dd/yyyy HH:mm:ss"));
sb.Append("\n");
}

return sb.ToString();
}
}

public class AltitudeAtTime {

public double Altitude { get; private set; }
public double Azimuth { get; private set; }
public DateTime AtTime { get; private set; }

public AltitudeAtTime(double altitude, double azimuth, DateTime atTime) {
Validate.Assert.isTrue(altitude <= 90 && altitude >= -90, "altitude must be <= 90 and >= -90");
Validate.Assert.isTrue(azimuth >= 0 && azimuth <= 360, "azimuth must be >= 0 and <= 360");

this.Altitude = altitude;
this.Azimuth = azimuth;
this.AtTime = atTime;
}

public override string ToString() {
return "AltitudeAtTime{" + "altitude=" + Altitude + ", azimuth=" + Azimuth + ", atTime=" + AtTime + '}';
}
}

}
Loading

0 comments on commit 8883ca6

Please sign in to comment.