kopia lustrzana https://github.com/dl2alf/AirScout
307 wiersze
17 KiB
C#
307 wiersze
17 KiB
C#
|
|
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<LocationDesignator> 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
|
|
}
|
|
}
|
|
}
|