using System; using System.ComponentModel; using System.Windows.Forms; using System.Net; using System.IO; using System.Threading; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Globalization; using System.Drawing; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using ScoutBase.Core; using ScoutBase.Stations; using ScoutBase.Elevation; using ScoutBase.Propagation; using ScoutBase; using System.Data.SQLite; namespace AirScout { public partial class MapDlg : Form { #region PathCalculator // Background worker for path calculation [DefaultPropertyAttribute("Name")] public class PathCalculator : BackgroundWorker { ELEVATIONMODEL Model = ELEVATIONMODEL.NONE; string Name = ""; double StepWidth = 1000; public PathCalculator(ELEVATIONMODEL model) : base() { this.WorkerReportsProgress = true; this.WorkerSupportsCancellation = true; this.Model = model; } private DateTime GetDatabaseTimeStamp() { string filename = StationData.Database.GetDBLocation(); DateTime dt = File.GetLastWriteTimeUtc(filename).ToUniversalTime(); // convert to YYYY:MM:DD hh:mm:ss only as this is stored in settings dt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, DateTimeKind.Utc); return dt; } private DateTime GetSavedDatabaseTimeStamp() { DateTime dt = DateTime.MinValue; dt = Properties.Settings.Default.StationsDatabase_TimeStamp; // change kind to UTC as it is not specified in settings dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc); return dt; } private void SaveDatabaseTimeStamp() { Properties.Settings.Default.StationsDatabase_TimeStamp = GetDatabaseTimeStamp(); } private bool HasDatabaseChanged() { try { DateTime dt1 = GetSavedDatabaseTimeStamp(); DateTime dt2 = GetDatabaseTimeStamp(); return dt1 != dt2; } catch { // do nothing } return true; } protected override void OnDoWork(DoWorkEventArgs e) { // get all parameters BACKGROUNDUPDATERSTARTOPTIONS Options = (BACKGROUNDUPDATERSTARTOPTIONS)e.Argument; // get update interval int interval = (int)Properties.Settings.Default.Background_Update_Period * 60; do { if (Properties.Settings.Default.Background_Calculations_Enable) { // get all parameters // set name and stepwidth according to model switch (Model) { case ELEVATIONMODEL.GLOBE: Name = "GLOBE"; StepWidth = ElevationData.Database.GetDefaultStepWidth(ELEVATIONMODEL.GLOBE); break; case ELEVATIONMODEL.SRTM3: Name = "SRTM3"; StepWidth = ElevationData.Database.GetDefaultStepWidth(ELEVATIONMODEL.SRTM3); break; case ELEVATIONMODEL.SRTM1: Name = "SRTM1"; StepWidth = ElevationData.Database.GetDefaultStepWidth(ELEVATIONMODEL.SRTM1); break; } // name the thread for debugging if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) Thread.CurrentThread.Name = Name + "_" + this.GetType().Name; this.ReportProgress(0, Name + " started."); try { // iterate through all locations in the database and calculate the propagation path // chek if databases are ready and changes reported first while (!ElevationData.Database.GetDBStatusBit(Model, DATABASESTATUS.UPTODATE) || !StationData.Database.GetDBStatusBit(DATABASESTATUS.UPTODATE) || !HasDatabaseChanged()) { // sleep 10 sec int i = 0; while (!this.CancellationPending && (i < 10)) { Thread.Sleep(1000); i++; if (this.CancellationPending) break; } if (this.CancellationPending) break; } if (this.CancellationPending) break; this.ReportProgress(0, Name + " getting locations..."); // get all locations in covered area but don't report progress this.WorkerReportsProgress = false; List lds = StationData.Database.LocationGetAll(this, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); this.WorkerReportsProgress = true; // start over again with main loog when lds = null for some reason if (lds == null) continue; // iterate through locations QRVDesignator myqrv = null; QRVDesignator dxqrv = null; this.ReportProgress(0, Name + " checking locations..."); foreach (LocationDesignator ld in lds) { Stopwatch st = new Stopwatch(); st.Start(); try { // leave the iteration when something went wrong --> start new if (ld == null) break; // check lat/lon if valid if (!GeographicalPoint.Check(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon)) continue; // check lat/lon if valid if (!GeographicalPoint.Check(ld.Lat, ld.Lon)) continue; // chek for path < MaxDistance double dist = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, ld.Lat, ld.Lon); if (dist <= Properties.Settings.Default.Path_MaxLength) { // start calculation for each band foreach (BAND band in Bands.GetValuesExceptNoneAndAll()) { PropagationPathDesignator pp; // get my lat/lon from settings string mycall = Properties.Settings.Default.MyCall; double mylat = Properties.Settings.Default.MyLat; double mylon = Properties.Settings.Default.MyLon; string myloc = MaidenheadLocator.LocFromLatLon(mylat, mylon, false, 3); double myelv = ElevationData.Database[mylat, mylon, Model]; // modify location in case of best case elevation is selected --> but do not store in database or settings! if (Properties.Settings.Default.Path_BestCaseElevation) { if (!MaidenheadLocator.IsPrecise(mylat, mylon, 3)) { ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(myloc, Model); if (maxinfo != null) { mylat = maxinfo.MaxLat; mylon = maxinfo.MaxLon; myelv = maxinfo.MaxElv; } } } myqrv = StationData.Database.QRVFind(mycall, myloc, band); double myheight = ((myqrv != null) && (myqrv.AntennaHeight != 0)) ? myqrv.AntennaHeight : StationData.Database.QRVGetDefaultAntennaHeight(band); string dxcall = ld.Call; // get my lat/lon from settings double dxlat = ld.Lat; double dxlon = ld.Lon; string dxloc = MaidenheadLocator.LocFromLatLon(dxlat, dxlon, false, 3); double dxelv = ElevationData.Database[dxlat, dxlon, Model]; // modify location in case of best case elevation is selected --> but do not store in database or settings! if (Properties.Settings.Default.Path_BestCaseElevation) { if (!MaidenheadLocator.IsPrecise(dxlat, dxlon, 3)) { ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(dxloc, Model); if (maxinfo != null) { dxlat = maxinfo.MaxLat; dxlon = maxinfo.MaxLon; dxelv = maxinfo.MaxElv; } } } dxqrv = StationData.Database.QRVFind(dxcall, dxloc, band); double dxheight = ((dxqrv != null) && (dxqrv.AntennaHeight != 0)) ? dxqrv.AntennaHeight : StationData.Database.QRVGetDefaultAntennaHeight(band); LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(mylat, mylon, Model); double myobstr = (o != null) ? o.GetObstruction(myheight, LatLon.Bearing(mylat, mylon, dxlat, dxlon)) : double.MinValue; pp = PropagationData.Database.PropagationPathFind( mylat, mylon, myelv + myheight, dxlat, dxlon, dxelv + dxheight, Bands.ToGHz(band), LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[band].K_Factor, Properties.Settings.Default.Path_Band_Settings[band].F1_Clearance, ElevationData.Database.GetDefaultStepWidth(Model), Model, myobstr); // skip if path already in database if (pp != null) { Thread.Sleep(Properties.Settings.Default.Background_Calculations_ThreadWait); continue; } // create new propagation path pp = PropagationData.Database.PropagationPathCreateFromLatLon( this, mylat, mylon, myelv + myheight, dxlat, dxlon, dxelv + dxheight, Bands.ToGHz(band), LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[band].K_Factor, Properties.Settings.Default.Path_Band_Settings[band].F1_Clearance, ElevationData.Database.GetDefaultStepWidth(Model), Model, myobstr); st.Stop(); this.ReportProgress(0, Name + " calculating path[ " + Bands.GetStringValue(band) + "]: " + Properties.Settings.Default.MyCall + "<>" + ld.Call + ", " + st.ElapsedMilliseconds.ToString() + " ms."); } if (this.CancellationPending) break; } if (this.CancellationPending) break; } catch (Exception ex) { Log.WriteMessage(Name + " error processing call [" + ld.Call + "]: " + ex.ToString()); } // keep cpu load low --> TODO: find better solution here Thread.Sleep(100); } // save station database timestamp SaveDatabaseTimeStamp(); // wait to keep cpu load low Thread.Sleep(Properties.Settings.Default.Background_Calculations_ThreadWait); this.ReportProgress(0, Name + " finished."); } catch (Exception ex) { Log.WriteMessage(ex.ToString(), LogLevel.Error); } } // sleep when running periodically if (Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY) { int i = 0; while (!this.CancellationPending && (i < interval)) { Thread.Sleep(1000); i++; } } } while (Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY); if (this.CancellationPending) { this.ReportProgress(0, Name + " cancelled."); Log.WriteMessage(Name + " cancelled."); } else { this.ReportProgress(0, Name + " finished."); Log.WriteMessage(Name + " finished."); } } #endregion } } }