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 AllPositions; List NearestPositions = new List(); PropagationPathDesignator PPath; const int TOOLTIP_XOFFSET =3; const int TOOLTIP_YOFFSET = 3; List Crossings = new List(); public CrossingHistoryDlg(DateTime from, DateTime to, int stepwidth, ref List 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 allplanes = ac.GetAll(time, Properties.Settings.Default.Planes_Position_TTL); // get nearest planes List 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 p = new Dictionary(); 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) { } } }