kopia lustrzana https://github.com/dl2alf/AirScout
492 wiersze
19 KiB
C#
492 wiersze
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.ComponentModel;
|
|
using System.Windows;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.IO;
|
|
using AirScout.Core;
|
|
using AirScout.Aircrafts;
|
|
using AirScout.PlaneFeeds.Generic;
|
|
using LibADSB;
|
|
using ScoutBase.Core;
|
|
|
|
namespace AirScout.PlaneFeeds
|
|
{
|
|
public class ADSBMessage
|
|
{
|
|
public string RawMessage = "";
|
|
public DateTime TimeStamp = DateTime.UtcNow;
|
|
public int SignalStrength = 0;
|
|
}
|
|
|
|
public class PlaneFeedSettings_ADSB
|
|
{
|
|
[Browsable(true)]
|
|
[DescriptionAttribute("Server address for raw ADS-B data.\nUse localhost for running on the same machine.")]
|
|
public virtual string Server
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Server;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_Server = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(true)]
|
|
[DescriptionAttribute("Server port for raw ADS-B data.\nADSBSharp.exe: Port 47806\nRTL1090: Port 31001")]
|
|
public virtual int Port
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Port;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_Port = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(true)]
|
|
[DescriptionAttribute("Interval for updating ADS-B data [sec].")]
|
|
public virtual int Interval
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Interval;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_Interval = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(true)]
|
|
[DescriptionAttribute("Use binary data format for ADS-B data.\nTrue: Use binary format (ADS Beast with MLAT)\nFalse: Use ASCII format (AVR with/without MLAT)")]
|
|
public virtual bool Binary
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Binary;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_Binary = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(true)]
|
|
[DescriptionAttribute("Report ADS-B messages and show in status line.")]
|
|
public virtual bool ReportMessages
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Report_Messages;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_Report_Messages = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(true)]
|
|
[DescriptionAttribute("Marks locally received aircrafts by adding '@' to the call sign")]
|
|
public virtual bool MarkLocal
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_MarkLocal;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_MarkLocal = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PlaneFeed_ADSB : PlaneFeed
|
|
{
|
|
[Browsable(false)]
|
|
public override string Name
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Name; ;
|
|
}
|
|
protected set
|
|
{
|
|
Properties.Settings.Default.ADSB_Name = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public override string Disclaimer
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Disclaimer;
|
|
}
|
|
protected set
|
|
{
|
|
Properties.Settings.Default.ADSB_Disclaimer = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public override string DisclaimerAccepted
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Disclaimer_Accepted;
|
|
}
|
|
set
|
|
{
|
|
Properties.Settings.Default.ADSB_Disclaimer_Accepted = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public override string Info
|
|
{
|
|
get
|
|
{
|
|
return Properties.Settings.Default.ADSB_Info;
|
|
}
|
|
protected set
|
|
{
|
|
Properties.Settings.Default.ADSB_Info = value;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
}
|
|
|
|
public new PlaneFeedSettings_ADSB FeedSettings = new PlaneFeedSettings_ADSB();
|
|
|
|
public PlaneFeed_ADSB()
|
|
: base ()
|
|
{
|
|
HasSettings = true;
|
|
}
|
|
|
|
private ADSBMessage ReceiveBinaryMsg(Stream stream)
|
|
{
|
|
// read Mode-S beast binary input
|
|
string ADSB = null;
|
|
int signal_strength = 0;
|
|
long nanosec = 0;
|
|
long daysec = 0;
|
|
DateTime timestamp = DateTime.UtcNow;
|
|
byte[] buffer = new byte[23];
|
|
// wait for escape character
|
|
DateTime start = DateTime.UtcNow;
|
|
DateTime stop = DateTime.Now;
|
|
do
|
|
{
|
|
stream.Read(buffer, 0, 1);
|
|
// System.Console.WriteLine(BitConverter.ToString(buffer,0,1));
|
|
if (buffer[0] == 0x1A)
|
|
{
|
|
// read next character
|
|
stream.Read(buffer, 1, 1);
|
|
switch (buffer[1])
|
|
{
|
|
case 0x31:
|
|
// do not decode
|
|
ADSB = null;
|
|
break;
|
|
case 0x32:
|
|
// 7 byte short frame
|
|
// read timestamp
|
|
stream.Read(buffer, 2, 6);
|
|
nanosec = ((buffer[4] & 0x3f) << 24) |
|
|
(buffer[5] << 16) |
|
|
(buffer[6] << 8) |
|
|
(buffer[7]);
|
|
daysec = (buffer[2] << 10) |
|
|
(buffer[3] << 2) |
|
|
(buffer[4] >> 6);
|
|
timestamp = DateTime.Today.AddSeconds(daysec);
|
|
timestamp = timestamp.AddMilliseconds(nanosec / 1000);
|
|
// plausibility check
|
|
if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10)
|
|
{
|
|
// time difference > 10sec --> discard timestamp
|
|
timestamp = DateTime.UtcNow;
|
|
}
|
|
// read signal strength
|
|
stream.Read(buffer, 8, 1);
|
|
// plausibility check
|
|
if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10)
|
|
{
|
|
// time difference > 10sec --> discard timestamp
|
|
timestamp = DateTime.UtcNow;
|
|
}
|
|
signal_strength = buffer[8];
|
|
// read frame
|
|
stream.Read(buffer, 9, 7);
|
|
// convert to AVR string
|
|
ADSB = BitConverter.ToString((byte[])buffer, 9, 7).Replace("-", String.Empty);
|
|
ADSB = "*" + ADSB + ";";
|
|
break;
|
|
case 0x33:
|
|
// 14 byte long frame
|
|
// read timestamp
|
|
stream.Read(buffer, 2, 6);
|
|
nanosec = ((buffer[4] & 0x3f) << 24) |
|
|
(buffer[5] << 16) |
|
|
(buffer[6] << 8) |
|
|
(buffer[7]);
|
|
|
|
daysec = (buffer[2] << 10) |
|
|
(buffer[3] << 2) |
|
|
(buffer[4] >> 6);
|
|
timestamp = DateTime.Today.AddSeconds(daysec);
|
|
timestamp = timestamp.AddMilliseconds(nanosec / 1000);
|
|
// plausibility check
|
|
if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10)
|
|
{
|
|
// time difference > 10sec --> discard timestamp
|
|
timestamp = DateTime.UtcNow;
|
|
}
|
|
// read signal strength
|
|
stream.Read(buffer, 8, 1);
|
|
signal_strength = buffer[8];
|
|
// read frame
|
|
stream.Read(buffer, 9, 14);
|
|
// convert to AVR string
|
|
ADSB = BitConverter.ToString((byte[])buffer, 9, 14).Replace("-", String.Empty);
|
|
ADSB = "*" + ADSB + ";";
|
|
break;
|
|
default:
|
|
// false decode
|
|
ADSB = null;
|
|
break;
|
|
}
|
|
}
|
|
// check for timeout 10sec
|
|
stop = DateTime.UtcNow;
|
|
if (stop - start > new TimeSpan(0, 0, 10))
|
|
throw new TimeoutException();
|
|
}
|
|
while ((ADSB == null) && !this.CancellationPending);
|
|
if (ADSB == null)
|
|
return null;
|
|
ADSBMessage msg = new ADSBMessage();
|
|
msg.RawMessage = ADSB;
|
|
msg.TimeStamp = timestamp;
|
|
msg.SignalStrength = signal_strength;
|
|
return msg;
|
|
}
|
|
|
|
private ADSBMessage ReceiveAVRMsg ( StreamReader sr)
|
|
{
|
|
ADSBMessage msg = new ADSBMessage();
|
|
// read AVR format input
|
|
msg.RawMessage = sr.ReadLine();
|
|
if (msg.RawMessage.StartsWith("*"))
|
|
{
|
|
// standard AVR message
|
|
// no timestamp in telegram --> set timestamp after reading
|
|
msg.TimeStamp = DateTime.UtcNow;
|
|
msg.SignalStrength = 0;
|
|
}
|
|
else if (msg.RawMessage.StartsWith("@"))
|
|
{
|
|
// extended AVR message wit MLAT
|
|
// convert into standard message format
|
|
// extract time string
|
|
string time = msg.RawMessage.Substring(1, 12);
|
|
// TODO: interprete the MLAT timestamp!
|
|
msg.TimeStamp = DateTime.UtcNow;
|
|
msg.RawMessage = "*" + msg.RawMessage.Remove(0, 13);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
protected override void OnDoWork(DoWorkEventArgs e)
|
|
{
|
|
Log.WriteMessage("Started.");
|
|
PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument;
|
|
// set directories
|
|
AppDirectory = args.AppDirectory;
|
|
AppDataDirectory = args.AppDataDirectory;
|
|
LogDirectory = args.LogDirectory;
|
|
TmpDirectory = args.TmpDirectory;
|
|
DatabaseDirectory = args.DatabaseDirectory;
|
|
// set boundaries from arguments
|
|
MaxLat = args.MaxLat;
|
|
MinLon = args.MinLon;
|
|
MinLat = args.MinLat;
|
|
MaxLon = args.MaxLon;
|
|
MyLat = args.MyLat;
|
|
MyLon = args.MyLon;
|
|
DXLat = args.DXLat;
|
|
DXLon = args.DXLon;
|
|
MinAlt = args.MinAlt;
|
|
MaxAlt = args.MaxAlt;
|
|
|
|
// keep history settings from arguments
|
|
KeepHistory = args.KeepHistory;
|
|
|
|
// check boundaries
|
|
if ((MaxLat <= MinLat) || (MaxLon <= MinLon))
|
|
{
|
|
Status = STATUS.ERROR;
|
|
this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!");
|
|
}
|
|
else
|
|
{
|
|
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
|
Status = STATUS.OK;
|
|
ADSBDecoder decoder = new ADSBDecoder();
|
|
DateTime lastreported = DateTime.UtcNow;
|
|
StreamReader sr = null;
|
|
TcpClient client = null;
|
|
ADSBMessage msg = null;
|
|
// outer loop
|
|
do
|
|
{
|
|
try
|
|
{
|
|
// setup TCP listener
|
|
client = new TcpClient();
|
|
client.Connect(Properties.Settings.Default.ADSB_Server, Properties.Settings.Default.ADSB_Port);
|
|
sr = new StreamReader(client.GetStream());
|
|
// inner loop
|
|
// receive messages in a loop
|
|
do
|
|
{
|
|
if (FeedSettings.Binary)
|
|
msg = ReceiveBinaryMsg(sr.BaseStream);
|
|
else
|
|
msg = ReceiveAVRMsg(sr);
|
|
// decode the message
|
|
{
|
|
Console.Write(msg.RawMessage + " --> ");
|
|
// try to decode the message
|
|
string info = "";
|
|
try
|
|
{
|
|
info = decoder.DecodeMessage(msg.RawMessage, msg.TimeStamp);
|
|
// report messages to main window if activated
|
|
if (FeedSettings.ReportMessages && (info.StartsWith("[")))
|
|
this.ReportProgress((int)PROGRESS.STATUS, info);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex.Message);
|
|
}
|
|
Console.WriteLine(info);
|
|
}
|
|
DateTime stop = DateTime.UtcNow;
|
|
// check if update report is necessary
|
|
if ((DateTime.UtcNow - lastreported).TotalSeconds > FeedSettings.Interval)
|
|
{
|
|
lastreported = DateTime.UtcNow;
|
|
// time to report planes
|
|
ArrayList list = decoder.GetPlanes();
|
|
if (list.Count > 0)
|
|
{
|
|
// convert to plane info list
|
|
List<PlaneInfo> planes = new List<PlaneInfo>();
|
|
foreach (ADSBInfo info in list)
|
|
{
|
|
PlaneInfo planeinfo = new PlaneInfo();
|
|
planeinfo.Time = info.Timestamp;
|
|
planeinfo.Hex = info.ICAO24;
|
|
// mark call with "@" if option is enabled
|
|
planeinfo.Call = (FeedSettings.MarkLocal) ? "@" + info.Call : info.Call;
|
|
planeinfo.Lat = info.Lat;
|
|
planeinfo.Lon = info.Lon;
|
|
planeinfo.Alt = info.Alt;
|
|
planeinfo.Speed = info.Speed;
|
|
planeinfo.Track = info.Heading;
|
|
planeinfo.Reg = "[unknown]";
|
|
planeinfo.Type = "[unknown]";
|
|
planeinfo.Manufacturer = "[unknown]";
|
|
planeinfo.Model = "[unknown]";
|
|
planeinfo.Category = PLANECATEGORY.NONE;
|
|
// try to get the registration and type
|
|
AircraftDesignator aircraft = AircraftData.Database.AircraftFindByHex(planeinfo.Hex);
|
|
if (aircraft != null)
|
|
{
|
|
planeinfo.Reg = aircraft.Reg;
|
|
planeinfo.Type = aircraft.TypeCode;
|
|
// try to get the type
|
|
AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(planeinfo.Type);
|
|
if (planeinfo != null)
|
|
{
|
|
planeinfo.Manufacturer = type.Manufacturer;
|
|
planeinfo.Model = type.Model;
|
|
planeinfo.Category = type.Category;
|
|
}
|
|
}
|
|
planes.Add(planeinfo);
|
|
}
|
|
ReportProgress((int)PROGRESS.PLANES, planes);
|
|
AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes);
|
|
string message = "[" + lastreported.ToString("HH:mm:ss") + "] " +
|
|
decoder.Count.ToString() + " Positions updated from local ADS-B receiver.";
|
|
this.ReportProgress((int)PROGRESS.STATUS, message);
|
|
}
|
|
}
|
|
}
|
|
while ((msg != null) && !CancellationPending);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.WriteMessage(ex.Message);
|
|
this.ReportProgress((int)PROGRESS.ERROR, "Error reading from TCP connection: " + ex.Message);
|
|
Thread.Sleep(10000);
|
|
}
|
|
finally
|
|
{
|
|
// try to close the stream and TCP client
|
|
try
|
|
{
|
|
if (sr != null)
|
|
sr.Close();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
try
|
|
{
|
|
if (client != null)
|
|
client.Close();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
while (!this.CancellationPending || (Status != STATUS.OK));
|
|
}
|
|
this.ReportProgress((int)PROGRESS.FINISHED);
|
|
Log.WriteMessage("Finished.");
|
|
}
|
|
|
|
public override Object GetFeedSettings()
|
|
{
|
|
return FeedSettings;
|
|
}
|
|
|
|
}
|
|
}
|