Skip to content

Instantly share code, notes, and snippets.

@ChiiAyano
Last active August 15, 2017 11:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChiiAyano/63c9288a21641ef265d8016b27c916a4 to your computer and use it in GitHub Desktop.
Save ChiiAyano/63c9288a21641ef265d8016b27c916a4 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.Geolocation;
using Windows.Devices.SerialCommunication;
namespace Mikaboshi.Iot.LocationPost
{
public class GpsClient
{
DeviceInformation device;
SerialDevice serialPort;
Stream readStream;
public BasicGeoposition Position { get; private set; }
public DateTimeOffset PositionSourceTime { get; private set; }
public double Heading { get; private set; }
public bool Succeeded { get; private set; }
public int Count { get; private set; }
public event EventHandler LoggingStarted;
public event EventHandler<GpsPositionEventArgs> PositionChanged;
public async Task InitializeAsync()
{
// シリアルポートの初期化
var selector = SerialDevice.GetDeviceSelector();
var devices = await DeviceInformation.FindAllAsync(selector);
this.device = devices.FirstOrDefault();
}
public async Task OpenPort()
{
if (this.device == null) return;
this.serialPort = await SerialDevice.FromIdAsync(this.device.Id);
this.serialPort.BaudRate = 9600;
this.readStream = this.serialPort.InputStream.AsStreamForRead(0);
}
public async void Listen()
{
try
{
if (this.serialPort == null) return;
while (true)
{
await ReadAsync();
}
}
catch (Exception ex)
{
}
finally
{
if (this.readStream != null)
{
this.readStream.Dispose();
this.readStream = null;
}
}
}
private async Task ReadAsync()
{
var buf = new StringBuilder();
var readStart = false;
var carriageReturn = false;
while (true)
{
var c = (char)this.readStream.ReadByte();
if (c < 0) break;
if (c == '$')
{
if (readStart)
{
// リード中に $ がきたら異常と見なしてリセットする
buf.Clear();
readStart = false;
continue;
}
readStart = true;
}
if (!readStart)
{
continue;
}
buf.Append(c);
if (c == '\r')
{
carriageReturn = true;
}
else if (c == '\n' && carriageReturn)
{
break;
}
else
{
carriageReturn = false;
}
}
var str = buf.ToString();
Debug.WriteLine(str);
await ParseAsync(str);
}
private async Task ParseAsync(string message)
{
using (var sr = new StringReader(message))
{
while (sr.Peek() > -1)
{
var d = await sr.ReadLineAsync();
var s = d.Split(',');
if (s.Length < 0) return;
switch (s[0])
{
case "$GPRMC":
Gprmc(s);
break;
default:
break;
}
}
}
}
/// <summary>
/// 緯度・経度・進行方位・速度のデータ
/// </summary>
/// <param name="data"></param>
private void Gprmc(string[] data)
{
if (data.Length < 12) return;
// R は Raw
// UTC 時刻。 hhmmss.fff
var timeR = data[1];
// ステータス。 V = 警告 / A = 有効
var statusR = data[2];
// 緯度。 dddmm.mmmm
var latitudeR = data[3];
// N = 北緯 / S = 南緯
var latitudePollR = data[4];
// 経度。 dddmm.mmmm
var longitudeR = data[5];
// E = 東経 / W = 西経
var longitudePollR = data[6];
// 移動速度。 knot
var speedR = data[7];
// 移動方位。
var directionR = data[8];
// UTC 日付。 ddmmyy
var dateR = data[9];
// 磁北と真北の差。
var mDirectR = data[10];
// 磁北と真北の差の方向。 E = 東
var mDirectPollR = data[11];
// モード。
var modeR = data[12];
// 時間
var date = ParseDateTime(dateR, timeR).ToLocalTime();
if (statusR != "A")
{
this.Succeeded = false;
return;
}
// 緯度
var latitude1 = double.Parse(latitudeR) / 100;
var latitude2 = (int)Math.Floor(latitude1);
var latitude3 = (latitude1 % 1.0) * 100;
var latitude = latitude2 + latitude3 / 60;
if (latitudePollR == "S") latitude *= -1;
// 経度
var longitude1 = double.Parse(longitudeR) / 100;
var longitude2 = (int)Math.Floor(longitude1);
var longitude3 = (longitude1 % 1.0) * 100;
var longitude = longitude2 + longitude3 / 60;
if (longitudePollR == "W") longitude *= -1;
// 移動速度
const double knotToKph = 1.852;
var kph = double.Parse(speedR) * knotToKph;
this.Succeeded = true;
this.Position = new BasicGeoposition { Latitude = latitude, Longitude = longitude };
this.PositionSourceTime = date;
this.Count++;
if (this.Count == 1)
{
this.LoggingStarted?.Invoke(this, EventArgs.Empty);
}
var heading = 0d;
double.TryParse(directionR, out heading);
this.Heading = heading;
this.PositionChanged?.Invoke(this, new GpsPositionEventArgs(this.Position, this.PositionSourceTime, this.Heading, this.Succeeded, this.Count));
}
/// <summary>
/// 日付と時刻を <see cref="DateTimeOffset"/> として変換します。時刻は <see cref="DateTimeOffset.UtcNow"/> で示されるタイムゾーンで返します。
/// </summary>
/// <param name="date"></param>
/// <param name="time"></param>
/// <returns></returns>
private static DateTimeOffset ParseDateTime(string date, string time)
{
var year = int.Parse("20" + date.Substring(4, 2));
var month = int.Parse(date.Substring(2, 2));
var day = int.Parse(date.Substring(0, 2));
var hour = int.Parse(time.Substring(0, 2));
var minute = int.Parse(time.Substring(2, 2));
var second = int.Parse(time.Substring(4, 2));
var mSecond = double.Parse("0" + time.Substring(6));
var result = new DateTimeOffset(year, month, day, hour, minute, second, (int)(mSecond * 1000), DateTimeOffset.UtcNow.Offset);
result = result.ToLocalTime();
return result;
}
}
}
using System;
using Windows.Devices.Geolocation;
namespace Mikaboshi.Iot.LocationPost
{
public class GpsPositionEventArgs : EventArgs
{
public BasicGeoposition Position { get; }
public DateTimeOffset PositionSourceTime { get; }
public double Heading { get; }
public bool Succeeded { get; }
public int Count { get; }
public GpsPositionEventArgs(BasicGeoposition position, DateTimeOffset sourceTime, double heading, bool succeeded, int count)
{
this.Position = position;
this.PositionSourceTime = sourceTime;
this.Heading = heading;
this.Succeeded = succeeded;
this.Count = count;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment