kopia lustrzana https://github.com/dl2alf/AirScout
367 wiersze
19 KiB
C#
367 wiersze
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using AirScout.Core;
|
|
using AirScout.Aircrafts;
|
|
using ScoutBase.Core;
|
|
using ScoutBase.Stations;
|
|
using ScoutBase.Elevation;
|
|
using ScoutBase.Propagation;
|
|
using AirScout.AircraftPositions;
|
|
using AirScout.Signals;
|
|
|
|
namespace AirScout
|
|
{
|
|
public partial class CrossingHistoryDlg : Form
|
|
{
|
|
|
|
DateTime From;
|
|
DateTime To;
|
|
int Stepwidth;
|
|
List<AircraftPositionDesignator> AllPositions;
|
|
List<PlaneInfo> NearestPositions = new List<PlaneInfo>();
|
|
|
|
PropagationPathDesignator PPath;
|
|
|
|
const int TOOLTIP_XOFFSET =3;
|
|
const int TOOLTIP_YOFFSET = 3;
|
|
|
|
List<PlaneInfo> Crossings = new List<PlaneInfo>();
|
|
|
|
public CrossingHistoryDlg(DateTime from, DateTime to, int stepwidth, ref List<AircraftPositionDesignator> alllpositions)
|
|
{
|
|
InitializeComponent();
|
|
From = new DateTime(from.Year, from.Month, from.Day, 0, 0, 0, 0, from.Kind);
|
|
DateTime oldest = AircraftPositionData.Database.AircraftPositionOldestEntry();
|
|
if (From < oldest)
|
|
From = oldest;
|
|
To = to;
|
|
// check bounds
|
|
if (From > To)
|
|
From = To;
|
|
Stepwidth = stepwidth;
|
|
AllPositions = alllpositions;
|
|
// set minimum stepwidth to 1sec
|
|
if (Stepwidth <= 0)
|
|
Stepwidth = 1;
|
|
this.Text = "Path Crossing History: " + Properties.Settings.Default.MyCall + " >>> " + Properties.Settings.Default.DXCall;
|
|
btn_History_Export.Enabled = false;
|
|
|
|
}
|
|
|
|
private void bw_History_DoWork(object sender, DoWorkEventArgs e)
|
|
{
|
|
// check that Stepwidth ist positive in any case
|
|
if (Properties.Settings.Default.Path_History_StepWidth <= 0)
|
|
Properties.Settings.Default.Path_History_StepWidth = 1;
|
|
bw_History.ReportProgress(0, "Calculating Path....");
|
|
LocationDesignator mycall = StationData.Database.LocationFindOrCreate(Properties.Settings.Default.MyCall, MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3));
|
|
QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(mycall.Call, mycall.Loc, Properties.Settings.Default.Band);
|
|
// set qrv defaults if zero
|
|
if (myqrv.AntennaHeight == 0)
|
|
myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band);
|
|
if (myqrv.AntennaGain == 0)
|
|
myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band);
|
|
if (myqrv.Power == 0)
|
|
myqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band);
|
|
if (Properties.Settings.Default.Path_BestCaseElevation)
|
|
{
|
|
if (!MaidenheadLocator.IsPrecise(mycall.Lat, mycall.Lon, 3))
|
|
{
|
|
ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(mycall.Loc, Properties.Settings.Default.ElevationModel);
|
|
if (maxinfo != null)
|
|
{
|
|
mycall.Lat = maxinfo.MaxLat;
|
|
mycall.Lon = maxinfo.MaxLon;
|
|
}
|
|
}
|
|
}
|
|
LocationDesignator dxcall = StationData.Database.LocationFindOrCreate(Properties.Settings.Default.DXCall, MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3));
|
|
QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxcall.Call, dxcall.Loc, Properties.Settings.Default.Band);
|
|
// set qrv defaults if zero
|
|
if (dxqrv.AntennaHeight == 0)
|
|
dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band);
|
|
if (dxqrv.AntennaGain == 0)
|
|
dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band);
|
|
if (dxqrv.Power == 0)
|
|
dxqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band);
|
|
if (Properties.Settings.Default.Path_BestCaseElevation)
|
|
{
|
|
if (!MaidenheadLocator.IsPrecise(dxcall.Lat, dxcall.Lon, 3))
|
|
{
|
|
ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(dxcall.Loc, Properties.Settings.Default.ElevationModel);
|
|
if (maxinfo != null)
|
|
{
|
|
dxcall.Lat = maxinfo.MaxLat;
|
|
dxcall.Lon = maxinfo.MaxLon;
|
|
}
|
|
}
|
|
}
|
|
|
|
// find local obstruction, if any
|
|
LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(mycall.Lat, mycall.Lon, Properties.Settings.Default.ElevationModel);
|
|
double mybearing = LatLon.Bearing(mycall.Lat, mycall.Lon, dxcall.Lat, dxcall.Lon);
|
|
double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue;
|
|
|
|
// try to find propagation path in database or create new one and store
|
|
PPath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon(
|
|
bw_History,
|
|
mycall.Lat,
|
|
mycall.Lon,
|
|
ElevationData.Database[mycall.Lat, mycall.Lon,Properties.Settings.Default.ElevationModel] + myqrv.AntennaHeight,
|
|
dxcall.Lat,
|
|
dxcall.Lon,
|
|
ElevationData.Database[dxcall.Lat, dxcall.Lon,Properties.Settings.Default.ElevationModel] + dxqrv.AntennaHeight,
|
|
Bands.ToGHz(Properties.Settings.Default.Band),
|
|
LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor,
|
|
Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance,
|
|
ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel),
|
|
Properties.Settings.Default.ElevationModel,
|
|
myobstr);
|
|
DateTime time = From;
|
|
lock (Crossings)
|
|
{
|
|
Crossings.Clear();
|
|
}
|
|
lock(NearestPositions)
|
|
{
|
|
NearestPositions.Clear();
|
|
}
|
|
// pre-select nearest positions only
|
|
bw_History.ReportProgress(0, "Pre-selecting nearest positions...");
|
|
LatLon.GPoint midpoint = PPath.GetMidPoint();
|
|
double maxdist = PPath.Distance / 2;
|
|
foreach (AircraftPositionDesignator ap in AllPositions)
|
|
{
|
|
if ((ap.LastUpdated >= From) && (ap.LastUpdated <= To) && (LatLon.Distance(ap.Lat, ap.Lon, midpoint.Lat, midpoint.Lon) <= maxdist))
|
|
{
|
|
AircraftDesignator ac = null;
|
|
AircraftTypeDesignator at = null;
|
|
ac = AircraftData.Database.AircraftFindByHex(ap.Hex);
|
|
if (ac != null)
|
|
at = AircraftData.Database.AircraftTypeFindByICAO(ac.TypeCode);
|
|
PlaneInfo plane = new PlaneInfo(ap.LastUpdated, ap.Call, ((ac != null) && (!String.IsNullOrEmpty(ac.TypeCode)))? ac.Reg : "[unknown]", ap.Hex, ap.Lat, ap.Lon, ap.Track, ap.Alt, ap.Speed, (ac != null) && (!String.IsNullOrEmpty(ac.TypeCode)) ? ac.TypeCode : "[unkomwn]", ((at != null) && (!String.IsNullOrEmpty(at.Manufacturer))) ? at.Manufacturer : "[unknown]", ((at != null) && (!String.IsNullOrEmpty(at.Model))) ? at.Model : "[unknown]", (at != null) ? at.Category : PLANECATEGORY.NONE);
|
|
lock (NearestPositions)
|
|
{
|
|
NearestPositions.Add(plane);
|
|
}
|
|
if (NearestPositions.Count % 1000 == 0)
|
|
bw_History.ReportProgress(0, "Pre-selecting nearest positions..." + "[" + NearestPositions.Count.ToString() + "]");
|
|
}
|
|
if (bw_History.CancellationPending)
|
|
break;
|
|
}
|
|
bw_History.ReportProgress(0, "Pre-selecting nearest positions finished, " + NearestPositions.Count.ToString() + " positions.");
|
|
// return if no positions left over
|
|
if (NearestPositions.Count == 0)
|
|
return;
|
|
int startindex = 0;
|
|
// set timeline to first reported position
|
|
time = NearestPositions[0].Time;
|
|
while ((!bw_History.CancellationPending) && (time <= To))
|
|
{
|
|
if (Crossings.Count % 1000 == 0)
|
|
bw_History.ReportProgress(0, "Calculating at " + time.ToString("yyyy-MM-dd HH:mm:ss") + ", " + Crossings.Count.ToString() + " crossings so far.");
|
|
// calculate from timestamp
|
|
DateTime from = time.AddMinutes(-Properties.Settings.Default.Planes_Position_TTL);
|
|
// fill plane position cache
|
|
PlaneInfoCache ac = new PlaneInfoCache();
|
|
int i = startindex;
|
|
startindex = -1;
|
|
while ((!bw_History.CancellationPending) && (i < NearestPositions.Count))
|
|
{
|
|
// update ap in cache if relevant
|
|
if (NearestPositions[i].Time >= from)
|
|
{
|
|
// store first index as startindex for next iteration
|
|
if (startindex == -1)
|
|
startindex = i;
|
|
lock (ac)
|
|
{
|
|
ac.InsertOrUpdateIfNewer(NearestPositions[i]);
|
|
}
|
|
}
|
|
// stop if position is newer than current time
|
|
if (NearestPositions[i].Time > time)
|
|
break;
|
|
i++;
|
|
}
|
|
List<PlaneInfo> allplanes = ac.GetAll(time, Properties.Settings.Default.Planes_Position_TTL);
|
|
// get nearest planes
|
|
List<PlaneInfo> nearestplanes = AircraftData.Database.GetNearestPlanes(time, PPath, allplanes, Properties.Settings.Default.Planes_Filter_Max_Circumcircle, Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance, Properties.Settings.Default.Planes_MaxAlt);
|
|
if ((nearestplanes != null) && (nearestplanes.Count() > 0))
|
|
{
|
|
// get all planes crossing the path
|
|
foreach (PlaneInfo plane in nearestplanes)
|
|
{
|
|
if (plane.IntQRB <= Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance)
|
|
{
|
|
// check if level value is available
|
|
SignalLevelDesignator ad = SignalData.Database.SignalLevelFind(plane.Time);
|
|
if (ad != null)
|
|
plane.SignalStrength = ad.Level;
|
|
else
|
|
plane.SignalStrength = double.MinValue;
|
|
lock (Crossings)
|
|
{
|
|
if (!Properties.Settings.Default.Analysis_CrossingHistory_WithSignalLevel || (ad != null))
|
|
Crossings.Add(plane);
|
|
}
|
|
}
|
|
bw_History.ReportProgress(0, "Calculating at " + time.ToString("yyyy-MM-dd HH:mm:ss") + ", " + Crossings.Count.ToString() + " crossings so far.");
|
|
}
|
|
}
|
|
time = time.AddSeconds(Stepwidth);
|
|
}
|
|
bw_History.ReportProgress(100, "Calculation done.");
|
|
}
|
|
|
|
private void bw_History_ProgressChanged(object sender, ProgressChangedEventArgs e)
|
|
{
|
|
if (e.ProgressPercentage == 0)
|
|
{
|
|
tsl_Main.Text = (string)e.UserState;
|
|
// ss_Main.Refresh();
|
|
}
|
|
else if (e.ProgressPercentage == 100)
|
|
{
|
|
tsl_Main.Text = (string)e.UserState;
|
|
// this.Refresh();
|
|
ch_Crossing_History.Show();
|
|
}
|
|
}
|
|
|
|
private void bw_History_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
|
{
|
|
if (!bw_History.CancellationPending)
|
|
btn_History_Export.Enabled = true;
|
|
}
|
|
|
|
private void btn_History_Calculate_Click(object sender, EventArgs e)
|
|
{
|
|
btn_History_Calculate.Enabled = false;
|
|
// drwaing charts
|
|
ch_Crossing_History.Series.Clear();
|
|
ch_Crossing_History.Series.Add("Count_Lo");
|
|
ch_Crossing_History.Series["Count_Lo"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
|
|
ch_Crossing_History.Series["Count_Lo"].Color = Color.Blue;
|
|
ch_Crossing_History.Series["Count_Lo"].BorderWidth = 2;
|
|
ch_Crossing_History.Series["Count_Lo"].BackSecondaryColor = Color.Blue;
|
|
ch_Crossing_History.Series["Count_Lo"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
|
|
ch_Crossing_History.Series["Count_Lo"]["PointWidth"] = "1";
|
|
ch_Crossing_History.Series.Add("Count_Hi");
|
|
ch_Crossing_History.Series["Count_Hi"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
|
|
ch_Crossing_History.Series["Count_Hi"].Color = Color.Red;
|
|
ch_Crossing_History.Series["Count_Hi"].BorderWidth = 2;
|
|
ch_Crossing_History.Series["Count_Hi"].BackSecondaryColor = Color.Red;
|
|
ch_Crossing_History.Series["Count_Hi"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
|
|
ch_Crossing_History.Series["Count_Hi"]["PointWidth"] = "1";
|
|
|
|
ch_Crossing_History.ChartAreas[0].AxisX.LabelStyle.Format = "yyyy-MM-dd\nHH:mm:ss";
|
|
bw_History.RunWorkerAsync();
|
|
}
|
|
|
|
private void btn_History_Export_Click(object sender, EventArgs e)
|
|
{
|
|
if (PPath == null)
|
|
return;
|
|
if (Crossings == null)
|
|
return;
|
|
if (Crossings.Count() <= 0)
|
|
return;
|
|
tsl_Main.Text = "Checking ambiguous crossings...";
|
|
int start = 0;
|
|
Dictionary<string, PlaneInfo> p = new Dictionary<string, PlaneInfo>();
|
|
for (int i = 1; i < Crossings.Count; i++)
|
|
{
|
|
// if ((Crossings[i].Call == "DLH2LJ") && (Crossings[i].Time.Day == 26))
|
|
// Console.WriteLine("");
|
|
Crossings[i].Ambiguous = false;
|
|
PlaneInfo pl = null;
|
|
if (!p.TryGetValue(Crossings[i].Hex, out pl))
|
|
p.Add(Crossings[i].Hex, Crossings[i]);
|
|
if (((Crossings[i].Time - Crossings[i - 1].Time).TotalSeconds > (double)Properties.Settings.Default.Analysis_CrossingHistory_AmbigousGap) || (i >= Crossings.Count - 1))
|
|
{
|
|
// gap detected
|
|
if (p.Count > 1)
|
|
{
|
|
// multiple planes detected --> mark all as ambiguous
|
|
for (int j = start; j <= i - 1; j++)
|
|
Crossings[j].Ambiguous = true;
|
|
}
|
|
// reset all and start new crossing
|
|
p.Clear();
|
|
start = i;
|
|
}
|
|
}
|
|
// mark last plane as ambiguous in any case
|
|
Crossings[Crossings.Count - 1].Ambiguous = true;
|
|
CultureInfo uiculture = CultureInfo.CurrentUICulture;
|
|
SaveFileDialog Dlg = new SaveFileDialog();
|
|
Dlg.AddExtension = true;
|
|
Dlg.DefaultExt = "csv";
|
|
Dlg.FileName = "Path Crossing History_" + Properties.Settings.Default.MyCall + "_" + Properties.Settings.Default.DXCall + "_" + From.ToString("yyyyMMdd_HHmmss") + "_to_" + To.ToString("yyyyMMdd_HHmmss");
|
|
// Dlg.InitialDirectory = Application.StartupPath;
|
|
Dlg.OverwritePrompt = true;
|
|
if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
|
{
|
|
using (StreamWriter sw = new StreamWriter(Dlg.FileName))
|
|
{
|
|
sw.WriteLine("time[utc];call;lat[deg];lon[deg];type;category;potential;track[deg];alt[m];altdiff[m];dist[km];crossangle[deg];squint[deg];dist1[km];dist2[km];eps1[deg];eps2[deg];theta1[deg];theta2[deg];signal_strength[dB];ambiguous");
|
|
foreach (PlaneInfo plane in Crossings)
|
|
{
|
|
double a1 = LatLon.Distance(PPath.Lat1, PPath.Lon1, plane.Lat, plane.Lon);
|
|
double a2 = LatLon.Distance(PPath.Lat2, PPath.Lon2, plane.Lat, plane.Lon);
|
|
double sl = plane.SignalStrength;
|
|
if (sl == double.MinValue)
|
|
sl = -255;
|
|
sw.WriteLine(plane.Time.ToString("yyyy-MM-dd HH:mm:ss") + ";" +
|
|
plane.Call + ";" +
|
|
plane.Lat.ToString("F8", uiculture) + ";" +
|
|
plane.Lon.ToString("F8", uiculture) + ";" +
|
|
plane.Type + ";" +
|
|
plane.Category.ToString() + ";" +
|
|
plane.Potential.ToString() + ";" +
|
|
plane.Track.ToString("F8", uiculture) + ";" +
|
|
plane.Alt_m.ToString("F8", uiculture) + ";" +
|
|
plane.AltDiff.ToString("F8", uiculture) + ";" +
|
|
plane.IntQRB.ToString("F8", uiculture) + ";" +
|
|
(plane.Angle / Math.PI * 180.0).ToString("F8", uiculture) + ";" +
|
|
(plane.Squint / Math.PI * 180.0).ToString("F8", uiculture) + ";" +
|
|
a1.ToString("F8", uiculture) + ";" +
|
|
a2.ToString("F8", uiculture) + ";" +
|
|
(plane.Eps1 / Math.PI * 180.0).ToString("F8", uiculture) + ";" +
|
|
(plane.Eps2 / Math.PI * 180.0).ToString("F8", uiculture) + ";" +
|
|
(plane.Theta1 / Math.PI * 180.0).ToString("F8", uiculture) + ";" +
|
|
(plane.Theta2 / Math.PI * 180.0).ToString("F8", uiculture) + ";" +
|
|
sl.ToString("F2", uiculture) + ";" +
|
|
plane.Ambiguous.ToString());
|
|
}
|
|
}
|
|
tsl_Main.Text = "Export done.";
|
|
}
|
|
}
|
|
|
|
private void CrossingHistoryDlg_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
bw_History.CancelAsync();
|
|
}
|
|
|
|
private void btn_History_Cancel_Click(object sender, EventArgs e)
|
|
{
|
|
this.Close();
|
|
}
|
|
|
|
private void ch_Crossing_History_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
}
|
|
}
|
|
}
|