diff --git a/src/SampleApp.WinDesktop/MainWindow.xaml b/src/SampleApp.WinDesktop/MainWindow.xaml
index d693e8f..ef5541f 100644
--- a/src/SampleApp.WinDesktop/MainWindow.xaml
+++ b/src/SampleApp.WinDesktop/MainWindow.xaml
@@ -75,7 +75,7 @@
-
+
1200
2400
4800
@@ -91,6 +91,9 @@
+
+
+
diff --git a/src/SampleApp.WinDesktop/MainWindow.xaml.cs b/src/SampleApp.WinDesktop/MainWindow.xaml.cs
index fa5c453..0e71fbd 100644
--- a/src/SampleApp.WinDesktop/MainWindow.xaml.cs
+++ b/src/SampleApp.WinDesktop/MainWindow.xaml.cs
@@ -14,7 +14,7 @@ namespace SampleApp.WinDesktop
public partial class MainWindow : Window
{
private Queue messages = new Queue(101);
- private NmeaParser.NmeaDevice currentDevice;
+ public static NmeaParser.NmeaDevice currentDevice;
//Dialog for browsing to nmea log files
private Microsoft.Win32.OpenFileDialog nmeaOpenFileDialog = new Microsoft.Win32.OpenFileDialog()
{
diff --git a/src/SampleApp.WinDesktop/Ntrip/Carrier.cs b/src/SampleApp.WinDesktop/Ntrip/Carrier.cs
new file mode 100644
index 0000000..aed0979
--- /dev/null
+++ b/src/SampleApp.WinDesktop/Ntrip/Carrier.cs
@@ -0,0 +1,23 @@
+// *******************************************************************************
+// * 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
+ }
+}
diff --git a/src/SampleApp.WinDesktop/Ntrip/Caster.cs b/src/SampleApp.WinDesktop/Ntrip/Caster.cs
new file mode 100644
index 0000000..639f1f5
--- /dev/null
+++ b/src/SampleApp.WinDesktop/Ntrip/Caster.cs
@@ -0,0 +1,59 @@
+// *******************************************************************************
+// * 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; }
+ }
+}
diff --git a/src/SampleApp.WinDesktop/Ntrip/Client.cs b/src/SampleApp.WinDesktop/Ntrip/Client.cs
new file mode 100644
index 0000000..9c1b540
--- /dev/null
+++ b/src/SampleApp.WinDesktop/Ntrip/Client.cs
@@ -0,0 +1,140 @@
+// *******************************************************************************
+// * 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.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 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 sources = new List();
+ 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];
+
+ while (connected && sckt != null)
+ {
+ int count = sckt.Receive(buffer);
+ if (count > 0)
+ {
+ DataReceived?.Invoke(this, buffer.Take(count).ToArray());
+ }
+ await Task.Delay(10);
+ }
+ 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? DataReceived;
+ }
+}
diff --git a/src/SampleApp.WinDesktop/Ntrip/NtripSource.cs b/src/SampleApp.WinDesktop/Ntrip/NtripSource.cs
new file mode 100644
index 0000000..8cc71da
--- /dev/null
+++ b/src/SampleApp.WinDesktop/Ntrip/NtripSource.cs
@@ -0,0 +1,23 @@
+// *******************************************************************************
+// * 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()
+ {
+ }
+ }
+}
diff --git a/src/SampleApp.WinDesktop/Ntrip/NtripStream.cs b/src/SampleApp.WinDesktop/Ntrip/NtripStream.cs
new file mode 100644
index 0000000..9893109
--- /dev/null
+++ b/src/SampleApp.WinDesktop/Ntrip/NtripStream.cs
@@ -0,0 +1,52 @@
+// *******************************************************************************
+// * 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; }
+ }
+}
diff --git a/src/SampleApp.WinDesktop/NtripView.xaml b/src/SampleApp.WinDesktop/NtripView.xaml
new file mode 100644
index 0000000..97d31ff
--- /dev/null
+++ b/src/SampleApp.WinDesktop/NtripView.xaml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SampleApp.WinDesktop/NtripView.xaml.cs b/src/SampleApp.WinDesktop/NtripView.xaml.cs
new file mode 100644
index 0000000..1222419
--- /dev/null
+++ b/src/SampleApp.WinDesktop/NtripView.xaml.cs
@@ -0,0 +1,102 @@
+using NmeaParser.Gnss.Ntrip;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace SampleApp.WinDesktop
+{
+ ///
+ /// Interaction logic for NtripView.xaml
+ ///
+ public partial class NtripView : UserControl
+ {
+ public NtripView()
+ {
+ InitializeComponent();
+ }
+ NmeaParser.Gnss.Ntrip.Client client;
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ sourceList.ItemsSource = null;
+ if (!int.TryParse(port.Text, out int portNumber))
+ {
+ MessageBox.Show("Invalid port number");
+ return;
+ }
+ client = new NmeaParser.Gnss.Ntrip.Client(host.Text, portNumber, username.Text, password.Password);
+ client.DataReceived += Client_DataReceived;
+ List sources;
+ try
+ {
+ sources = client.GetSourceTable().OfType().ToList();
+ }
+ catch(System.Exception ex)
+ {
+ MessageBox.Show("Failed to connect: " + ex.Message);
+ return;
+ }
+ sourceList.ItemsSource = sources.OrderBy(s=>s.CountryCode);
+ }
+ Func stop;
+ private async void Connect_Click(object sender, RoutedEventArgs e)
+ {
+ var stream = ((Button)sender).DataContext as NtripStream;
+ if (stream == null)
+ return;
+ if (stop != null)
+ {
+ await stop();
+ }
+ counter = 0;
+ client.Connect(stream.Mountpoint);
+ stop = client.CloseAsync;
+ ntripstatus.Text = $"Connected";
+ }
+
+ System.Threading.Tasks.Task writingTask;
+ object writeLock = new object();
+ long counter = 0;
+ private async void Client_DataReceived(object sender, byte[] rtcm)
+ {
+ var device = MainWindow.currentDevice;
+ if (device != null && device.CanWrite)
+ {
+ try
+ {
+ //lock (writeLock)
+ {
+ await device.WriteAsync(rtcm, 0, rtcm.Length);
+ counter += rtcm.Length;
+ Dispatcher.Invoke(() =>
+ {
+ ntripstatus.Text = $"Transmitted {counter} bytes";
+ });
+ }
+ }
+ catch
+ {
+
+ }
+ }
+ //ParseData(rtcm);
+ }
+ Queue rtcmData = new Queue();
+ private void ParseData(byte[] rtcm)
+ {
+ foreach (var b in rtcm)
+ rtcmData.Enqueue(b);
+
+ }
+ }
+}