mirror of
https://github.com/dotMorten/NmeaParser.git
synced 2026-04-21 06:13:58 +00:00
Moved NTRIP classes to NmeaParser lib and introduced new GnssMonitor class for simplified monitoring of GNSS messages
This commit is contained in:
parent
f3f80534f9
commit
73dbdf508f
12 changed files with 662 additions and 109 deletions
28
src/SampleApp.WinDesktop/GnssMonitorView.xaml
Normal file
28
src/SampleApp.WinDesktop/GnssMonitorView.xaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<UserControl x:Class="SampleApp.WinDesktop.GnssMonitorView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SampleApp.WinDesktop"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
<StackPanel DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Message}">
|
||||
<Border Background="LightGray" Padding="10" Margin="-10,-10,-10,5" x:Name="HeaderPanel">
|
||||
<TextBlock Text="{Binding MessageType}" x:Name="typeName" FontSize="20" FontWeight="Bold" Foreground="White" />
|
||||
</Border>
|
||||
<ItemsControl x:Name="Values">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MinWidth="100" Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding Key}" Margin="0,0,5,0" VerticalAlignment="Top" />
|
||||
<TextBlock Text="{Binding Value}" TextWrapping="Wrap" Grid.Column="1" VerticalAlignment="Top" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
76
src/SampleApp.WinDesktop/GnssMonitorView.xaml.cs
Normal file
76
src/SampleApp.WinDesktop/GnssMonitorView.xaml.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using NmeaParser.Gnss;
|
||||
using NmeaParser.Messages;
|
||||
|
||||
namespace SampleApp.WinDesktop
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for GnssMonitorView.xaml
|
||||
/// </summary>
|
||||
public partial class GnssMonitorView : UserControl
|
||||
{
|
||||
public GnssMonitorView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public GnssMonitor Monitor
|
||||
{
|
||||
get { return (GnssMonitor)GetValue(MonitorProperty); }
|
||||
set { SetValue(MonitorProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MonitorProperty =
|
||||
DependencyProperty.Register(nameof(Monitor), typeof(GnssMonitor), typeof(GnssMonitorView), new PropertyMetadata(null, (d,e) => ((GnssMonitorView)d).OnMonitorPropertyChanged(e)));
|
||||
|
||||
private void OnMonitorPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.OldValue is GnssMonitor oldMonitor)
|
||||
{
|
||||
oldMonitor.LocationChanged -= LocationChanged;
|
||||
oldMonitor.LocationLost -= LocationChanged;
|
||||
}
|
||||
if (e.NewValue is GnssMonitor newMonitor)
|
||||
{
|
||||
newMonitor.LocationChanged += LocationChanged;
|
||||
newMonitor.LocationLost += LocationChanged;
|
||||
}
|
||||
UpdateValues();
|
||||
}
|
||||
|
||||
private void LocationChanged(object sender, System.EventArgs e)
|
||||
{
|
||||
Dispatcher.Invoke(UpdateValues);
|
||||
}
|
||||
private void UpdateValues()
|
||||
{
|
||||
if (Monitor == null)
|
||||
Values.ItemsSource = null;
|
||||
else
|
||||
{
|
||||
var props = Monitor.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
List<KeyValuePair<string, object>> values = new List<KeyValuePair<string, object>>();
|
||||
foreach (var prop in props.OrderBy(t => t.Name))
|
||||
{
|
||||
if (prop.Name == nameof(GnssMonitor.AllMessages)) continue;
|
||||
if (prop.PropertyType.IsSubclassOf(typeof(NmeaMessage)))
|
||||
continue;
|
||||
var value = prop.GetValue(Monitor);
|
||||
if (!(value is string) && value is System.Collections.IEnumerable arr)
|
||||
{
|
||||
var str = "[" + string.Join(",", arr.OfType<object>().ToArray()) + "]";
|
||||
if (str.Length == 2)
|
||||
str = "[ ]";
|
||||
value = str;
|
||||
}
|
||||
values.Add(new KeyValuePair<string, object>(prop.Name, value));
|
||||
}
|
||||
Values.ItemsSource = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,10 @@ namespace SampleApp.WinDesktop
|
|||
|
||||
private void OnGsaPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
vehicles.Value = string.Join(",", Message?.SatelliteIDs);
|
||||
if (Message == null)
|
||||
vehicles.Value = null;
|
||||
else
|
||||
vehicles.Value = string.Join(",", Message?.SatelliteIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
</Window.Resources>
|
||||
<Grid>
|
||||
<TabControl>
|
||||
<TabItem Header="GPS Info">
|
||||
<TabItem Header="Messages">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" Background="#FFEEEEEE">
|
||||
<WrapPanel x:Name="MessagePanel">
|
||||
<local:RmcControl x:Name="gprmcView" Style="{StaticResource card}" Visibility="{Binding Message, ElementName=gprmcView, Converter={StaticResource nullConv}}" />
|
||||
|
|
@ -42,13 +42,16 @@
|
|||
<local:SatelliteSnr Grid.Row="1" x:Name="satSnr" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="GNSS Monitor">
|
||||
<local:GnssMonitorView x:Name="gnssMonitorView" />
|
||||
</TabItem>
|
||||
<TabItem Header="Map">
|
||||
<local:View2D x:Name="view2d" />
|
||||
</TabItem>
|
||||
<TabItem Header="3D">
|
||||
<local:View3D x:Name="view3d" />
|
||||
</TabItem>
|
||||
<TabItem Header="Messages">
|
||||
<TabItem Header="NMEA Log">
|
||||
<TextBox x:Name="output"
|
||||
AcceptsReturn="True"
|
||||
IsReadOnly="True"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using NmeaParser.Gnss;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -15,6 +16,7 @@ namespace SampleApp.WinDesktop
|
|||
{
|
||||
private Queue<string> messages = new Queue<string>(101);
|
||||
public static NmeaParser.NmeaDevice currentDevice;
|
||||
|
||||
//Dialog for browsing to nmea log files
|
||||
private Microsoft.Win32.OpenFileDialog nmeaOpenFileDialog = new Microsoft.Win32.OpenFileDialog()
|
||||
{
|
||||
|
|
@ -53,6 +55,7 @@ namespace SampleApp.WinDesktop
|
|||
if (currentDevice.IsOpen)
|
||||
await currentDevice.CloseAsync();
|
||||
currentDevice.Dispose();
|
||||
gnssMonitorView.Monitor = null;
|
||||
}
|
||||
output.Text = "";
|
||||
messages.Clear();
|
||||
|
|
@ -78,6 +81,7 @@ namespace SampleApp.WinDesktop
|
|||
((NmeaParser.SerialPortDevice)device).Port.BaudRate);
|
||||
}
|
||||
await device.OpenAsync();
|
||||
gnssMonitorView.Monitor = new GnssMonitor(device);
|
||||
}
|
||||
|
||||
private void device_MessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs args)
|
||||
|
|
|
|||
|
|
@ -1,120 +1,87 @@
|
|||
using Esri.ArcGISRuntime.Geometry;
|
||||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Esri.ArcGISRuntime.Geometry;
|
||||
using NmeaParser.Gnss;
|
||||
|
||||
namespace SampleApp.WinDesktop
|
||||
{
|
||||
public class NmeaLocationDataSource : Esri.ArcGISRuntime.Location.LocationDataSource
|
||||
{
|
||||
private static SpatialReference wgs84_ellipsoidHeight = SpatialReference.Create(4326, 115700);
|
||||
private readonly NmeaParser.NmeaDevice m_device;
|
||||
private double m_Accuracy = 0;
|
||||
private double m_altitude = double.NaN;
|
||||
private double m_speed = 0;
|
||||
private double m_course = 0;
|
||||
private bool m_startStopDevice;
|
||||
private bool m_supportGNMessages; // If device detect GN* messages, ignore all other Talker ID
|
||||
private bool m_supportGGaMessages; //If device support GGA, ignore RMC for location
|
||||
private readonly GnssMonitor m_gnssMonitor;
|
||||
private readonly bool m_startStopDevice;
|
||||
private double lastCourse = 0; // Course can fallback to NaN, but ArcGIS Datasource don't allow NaN course, so we cache last known as a fallback
|
||||
|
||||
public NmeaLocationDataSource(NmeaParser.NmeaDevice device, bool startStopDevice = true)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NmeaLocationDataSource"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The NMEA device to monitor</param>
|
||||
/// <param name="startStopDevice">Whether starting this datasource also controls the underlying NMEA device</param>
|
||||
public NmeaLocationDataSource(NmeaParser.NmeaDevice device, bool startStopDevice = true) : this(new GnssMonitor(device), startStopDevice)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException(nameof(device));
|
||||
this.m_device = device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NmeaLocationDataSource"/> class.
|
||||
/// </summary>
|
||||
/// <param name="monitor">The NMEA device to monitor</param>
|
||||
/// <param name="startStopDevice">Whether starting this datasource also controls the underlying NMEA device</param>
|
||||
public NmeaLocationDataSource(NmeaParser.Gnss.GnssMonitor monitor, bool startStopDevice = true)
|
||||
{
|
||||
if (monitor == null)
|
||||
throw new ArgumentNullException(nameof(monitor));
|
||||
this.m_gnssMonitor = monitor;
|
||||
m_startStopDevice = startStopDevice;
|
||||
}
|
||||
|
||||
void device_MessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs e)
|
||||
protected async override Task OnStartAsync()
|
||||
{
|
||||
var message = e.Message;
|
||||
ParseMessage(message);
|
||||
}
|
||||
public void ParseMessage(NmeaParser.Messages.NmeaMessage message)
|
||||
{
|
||||
bool isNewFix = false;
|
||||
bool lostFix = false;
|
||||
double lat = 0;
|
||||
double lon = 0;
|
||||
if (message.TalkerId == NmeaParser.Talker.GlobalNavigationSatelliteSystem)
|
||||
m_supportGNMessages = true;
|
||||
else if(m_supportGNMessages && message.TalkerId != NmeaParser.Talker.GlobalNavigationSatelliteSystem)
|
||||
return; // If device supports combined GN* messages, ignore non-GN messages
|
||||
m_gnssMonitor.LocationChanged += OnLocationChanged;
|
||||
m_gnssMonitor.LocationLost += OnLocationChanged;
|
||||
if (m_startStopDevice && !this.m_gnssMonitor.Device.IsOpen)
|
||||
await this.m_gnssMonitor.Device.OpenAsync();
|
||||
|
||||
if (message is NmeaParser.Messages.Garmin.Pgrme rme)
|
||||
{
|
||||
m_Accuracy = rme.HorizontalError;
|
||||
}
|
||||
else if(message is NmeaParser.Messages.Gst gst)
|
||||
{
|
||||
Gst = gst;
|
||||
m_Accuracy = Math.Round(Math.Sqrt(Gst.SigmaLatitudeError * Gst.SigmaLatitudeError + Gst.SigmaLongitudeError * Gst.SigmaLongitudeError), 3);
|
||||
}
|
||||
else if (message is NmeaParser.Messages.Rmc rmc)
|
||||
{
|
||||
Rmc = rmc;
|
||||
if (Rmc.Active)
|
||||
{
|
||||
m_speed = double.IsNaN(Rmc.Speed) ? 0 : Rmc.Speed;
|
||||
if (!double.IsNaN(Rmc.Course))
|
||||
m_course = Rmc.Course;
|
||||
lat = Rmc.Latitude;
|
||||
lon = Rmc.Longitude;
|
||||
}
|
||||
else
|
||||
{
|
||||
lostFix = true;
|
||||
}
|
||||
isNewFix = !m_supportGGaMessages;
|
||||
}
|
||||
else if (message is NmeaParser.Messages.Gga gga)
|
||||
{
|
||||
m_supportGGaMessages = true;
|
||||
if (gga.Quality != NmeaParser.Messages.Gga.FixQuality.Invalid)
|
||||
{
|
||||
lat = gga.Latitude;
|
||||
lon = gga.Longitude;
|
||||
m_altitude = gga.Altitude + gga.GeoidalSeparation; //Convert to ellipsoidal height
|
||||
}
|
||||
if (gga.Quality == NmeaParser.Messages.Gga.FixQuality.Invalid || gga.Quality == NmeaParser.Messages.Gga.FixQuality.Estimated)
|
||||
{
|
||||
lostFix = true;
|
||||
}
|
||||
isNewFix = true;
|
||||
}
|
||||
else if (message is NmeaParser.Messages.Gsa gsa)
|
||||
{
|
||||
Gsa = gsa;
|
||||
}
|
||||
if (isNewFix)
|
||||
{
|
||||
base.UpdateLocation(new Esri.ArcGISRuntime.Location.Location(
|
||||
!double.IsNaN(m_altitude) ? new MapPoint(lon, lat, m_altitude, wgs84_ellipsoidHeight) : new MapPoint(lon, lat, SpatialReferences.Wgs84),
|
||||
m_Accuracy, m_speed, m_course, lostFix));
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task OnStartAsync()
|
||||
{
|
||||
m_device.MessageReceived += device_MessageReceived;
|
||||
if (m_startStopDevice)
|
||||
return this.m_device.OpenAsync();
|
||||
else
|
||||
return System.Threading.Tasks.Task<bool>.FromResult(true);
|
||||
if (m_gnssMonitor.IsFixValid)
|
||||
OnLocationChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected override Task OnStopAsync()
|
||||
{
|
||||
m_device.MessageReceived -= device_MessageReceived;
|
||||
m_Accuracy = double.NaN;
|
||||
m_gnssMonitor.LocationChanged -= OnLocationChanged;
|
||||
m_gnssMonitor.LocationLost -= OnLocationChanged;
|
||||
if(m_startStopDevice)
|
||||
return this.m_device.CloseAsync();
|
||||
return m_gnssMonitor.Device.CloseAsync();
|
||||
else
|
||||
return System.Threading.Tasks.Task<bool>.FromResult(true);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public NmeaParser.Messages.Gsa Gsa { get; private set; }
|
||||
public NmeaParser.Messages.Gga Gga { get; private set; }
|
||||
public NmeaParser.Messages.Rmc Rmc { get; private set; }
|
||||
public NmeaParser.Messages.Gst Gst { get; private set; }
|
||||
private void OnLocationChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (double.IsNaN(m_gnssMonitor.Longitude) || double.IsNaN(m_gnssMonitor.Latitude)) return;
|
||||
if (!double.IsNaN(m_gnssMonitor.Course))
|
||||
lastCourse = m_gnssMonitor.Course;
|
||||
UpdateLocation(new Esri.ArcGISRuntime.Location.Location(
|
||||
timestamp: null,
|
||||
position: !double.IsNaN(m_gnssMonitor.Altitude) ? new MapPoint(m_gnssMonitor.Longitude, m_gnssMonitor.Latitude, m_gnssMonitor.Altitude, wgs84_ellipsoidHeight) : new MapPoint(m_gnssMonitor.Longitude, m_gnssMonitor.Latitude, SpatialReferences.Wgs84),
|
||||
horizontalAccuracy: m_gnssMonitor.HorizontalError,
|
||||
verticalAccuracy: m_gnssMonitor.VerticalError,
|
||||
velocity: double.IsNaN(m_gnssMonitor.Speed) ? 0 : m_gnssMonitor.Speed * 0.51444444,
|
||||
course: lastCourse,
|
||||
!m_gnssMonitor.IsFixValid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
namespace NmeaParser.Gnss.Ntrip
|
||||
{
|
||||
public enum Carrier : int
|
||||
{
|
||||
No = 0,
|
||||
L1 = 1,
|
||||
L1L2 = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
|
||||
namespace NmeaParser.Gnss.Ntrip
|
||||
{
|
||||
public class Caster : NtripSource
|
||||
{
|
||||
internal Caster (string[] d)
|
||||
{
|
||||
var a = d[1].Split(':');
|
||||
Address = IPAddress.Parse(a[0]);
|
||||
Port = int.Parse(a[1]);
|
||||
Identifier = d[3];
|
||||
Operator = d[4];
|
||||
SupportsNmea = d[5] == "1";
|
||||
CountryCode = d[6];
|
||||
Latitude = double.Parse(d[7], CultureInfo.InvariantCulture);
|
||||
Longitude = double.Parse(d[8], CultureInfo.InvariantCulture);
|
||||
FallbackAddress = IPAddress.Parse(d[9]);
|
||||
}
|
||||
|
||||
public Caster(IPAddress address, int port, string identifier, string _operator, bool supportsNmea, string countryCode, double latitude, double longitude, IPAddress fallbackkAddress)
|
||||
{
|
||||
Address = address;
|
||||
Port = port;
|
||||
Identifier = identifier;
|
||||
Operator = _operator;
|
||||
SupportsNmea = supportsNmea;
|
||||
CountryCode = countryCode;
|
||||
Latitude = latitude;
|
||||
Longitude = longitude;
|
||||
FallbackAddress = fallbackkAddress;
|
||||
}
|
||||
|
||||
public IPAddress Address { get; }
|
||||
public int Port { get; }
|
||||
public string Identifier { get; }
|
||||
public string Operator { get; }
|
||||
public bool SupportsNmea { get; }
|
||||
public string CountryCode { get; }
|
||||
public double Latitude { get; }
|
||||
public double Longitude { get; }
|
||||
public IPAddress FallbackAddress { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NmeaParser.Gnss.Ntrip
|
||||
{
|
||||
public class Client : IDisposable
|
||||
{
|
||||
private readonly string _host;
|
||||
private readonly int _port;
|
||||
private string? _auth;
|
||||
private Socket? sckt;
|
||||
private bool connected;
|
||||
private Task? runningTask;
|
||||
|
||||
public Client(string host, int port)
|
||||
{
|
||||
_host = host;
|
||||
_port = port;
|
||||
}
|
||||
|
||||
public Client(string host, int port, string username, string password) : this(host, port)
|
||||
{
|
||||
_auth = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
|
||||
}
|
||||
|
||||
public IEnumerable<NtripSource> GetSourceTable()
|
||||
{
|
||||
string data = "";
|
||||
byte[] buffer = new byte[1024];
|
||||
using (var sck = Request(""))
|
||||
{
|
||||
int count;
|
||||
while ((count = sck.Receive(buffer)) > 0)
|
||||
{
|
||||
data += System.Text.Encoding.UTF8.GetString(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
var lines = data.Split('\n');
|
||||
List<NtripSource> sources = new List<NtripSource>();
|
||||
foreach (var item in lines)
|
||||
{
|
||||
var d = item.Split(';');
|
||||
if (d.Length == 0) continue;
|
||||
if (d[0] == "ENDSOURCETABLE")
|
||||
break;
|
||||
if (d[0] == "CAS")
|
||||
{
|
||||
sources.Add(new Caster(d));
|
||||
}
|
||||
else if (d[0] == "STR")
|
||||
{
|
||||
sources.Add(new NtripStream(d));
|
||||
}
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
private Socket Request(string path)
|
||||
{
|
||||
var sckt = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sckt.Blocking = true;
|
||||
sckt.Connect(_host, _port);
|
||||
|
||||
string msg = $"GET /{path} HTTP/1.1\r\n";
|
||||
msg += "User-Agent: NTRIP ntripclient\r\n";
|
||||
if (_auth != null)
|
||||
{
|
||||
msg += "Authorization: Basic " + _auth + "\r\n";
|
||||
}
|
||||
msg += "Accept: */*\r\nConnection: close\r\n";
|
||||
msg += "\r\n";
|
||||
|
||||
byte[] data = System.Text.Encoding.ASCII.GetBytes(msg);
|
||||
sckt.Send(data);
|
||||
return sckt;
|
||||
}
|
||||
|
||||
public void Connect(string strName)
|
||||
{
|
||||
if (sckt != null) throw new Exception("Connection already open");
|
||||
sckt = Request(strName);
|
||||
connected = true;
|
||||
runningTask = Task.Run(ReceiveThread);
|
||||
}
|
||||
|
||||
private async Task ReceiveThread()
|
||||
{
|
||||
byte[] buffer = new byte[65536];
|
||||
sckt.ReceiveTimeout = 1000;
|
||||
while (connected && sckt != null)
|
||||
{
|
||||
int count = sckt.Receive(buffer, SocketFlags.None, out SocketError errorCode);
|
||||
if (count > 0)
|
||||
{
|
||||
DataReceived?.Invoke(this, buffer.Take(count).ToArray());
|
||||
}
|
||||
await Task.Yield();
|
||||
if (!sckt.Connected)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
connected = false;
|
||||
Disconnected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
sckt?.Shutdown(SocketShutdown.Both);
|
||||
sckt?.Dispose();
|
||||
sckt = null;
|
||||
}
|
||||
|
||||
public Task CloseAsync()
|
||||
{
|
||||
if (runningTask != null)
|
||||
{
|
||||
connected = false;
|
||||
var t = runningTask;
|
||||
runningTask = null;
|
||||
return t;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_ = CloseAsync();
|
||||
}
|
||||
|
||||
public event EventHandler<byte[]>? DataReceived;
|
||||
public event EventHandler Disconnected;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
namespace NmeaParser.Gnss.Ntrip
|
||||
{
|
||||
public class NtripSource
|
||||
{
|
||||
protected NtripSource()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NmeaParser.Gnss.Ntrip
|
||||
{
|
||||
public class NtripStream : NtripSource
|
||||
{
|
||||
internal NtripStream(string[] d)
|
||||
{
|
||||
Mountpoint = d[1];
|
||||
Identifier = d[2];
|
||||
Format = d[3];
|
||||
FormatDetails = d[4];
|
||||
if (int.TryParse(d[5], out int carrier))
|
||||
Carrier = (Carrier)carrier;
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
Network = d[7];
|
||||
CountryCode = d[8];
|
||||
Latitude = double.Parse(d[9], CultureInfo.InvariantCulture);
|
||||
Longitude = double.Parse(d[10], CultureInfo.InvariantCulture);
|
||||
SupportsNmea = d[11] == "1";
|
||||
}
|
||||
|
||||
public string Mountpoint { get; }
|
||||
public string Identifier { get; }
|
||||
public string Format { get; }
|
||||
public string FormatDetails { get; }
|
||||
public Carrier Carrier { get; }
|
||||
public string Network { get; }
|
||||
public string CountryCode { get; }
|
||||
public double Latitude { get; }
|
||||
public double Longitude { get; }
|
||||
public bool SupportsNmea { get; }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue