/**************************************************************************
* GPS.cs
* GPS関連演算のための構造体・クラス群 for C#
* 基本演算に必要なクラスや構造体を集めました。
* 軌道演算クラス等も今後開発していく予定です(ただし、C++での開発が先行)
* ただし、軌道は軌道関係で同じ名前空間の別ファイルにするつもりです。
* 暇ができればVBヴァージョンも整備します。できれば誰かもっと便利なクラスを整備して欲しいです。
*
* 開発者 :森下功啓(K.Morhista Kumamoto-University)
* 開発履歴:2011/7/3 整備開始
* 2011/7/6 2地点間の距離を求める、GetDistanceのデバッグ。単位長までしか計算していなかった…。
* 2011/7/17 XYZとBLHに二項演算子を追加
* 2011/9/19 Open(string fname)のバグを修正
* GetPositions(),GetPositioningResults()において、ubloxのNMEAフォーマットに対応(現時点での要求仕様を満たすだけなので万能ではないことに注意)
* PositioningInfosのToString()に高度も含まれるように変更
* ***********************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
/// GPS名前空間
///
/// 名前空間を共有すればどこからでも呼び出せます。
/// 共有していないスコープからは、GPS.hogeと呼び出せます。
/// usingを使えばスコープ外からでもいきなりメンバが呼び出せて便利ですが、推奨しません。
namespace GPS
{
#region /********************************** 構造体 ***********************************/
///
/// 南北、東西方向の距離・長さを収めるための構造体。
///
public struct length
{
public double E;
public double N;
///
/// 構造体初期化
///
/// 経度方向の長さ
/// 緯度方向の長さ
public length(double _E = 0.0d, double _N = 0.0d) { this.E = _E; this.N = _N; }
}
///
/// 2点で張られる長方形領域を緯度・経度で表す構造体
/// たとえば、GPSのログ領域の広さやマップの広さを表すことができる。
///
public class rectangleField
{
///
/// 右上の座標
///
public BLH upperRight;
///
/// 左下の座標
///
public BLH lowerLeft;
///
/// コンストラクタ
/// 2つの座標を指定すると、大小関係からマップの座標を自動的に決定する。
///
/// 座標1
/// 座標2
public rectangleField(BLH x1, BLH x2)
{
double N_bigger, E_bigger, N_smaller, E_smaller;
if (x1.B >= x2.B)
{
N_bigger = x1.B;
N_smaller = x2.B;
}
else
{
N_bigger = x2.B;
N_smaller = x1.B;
}
if (x1.L >= x2.L)
{
E_bigger = x1.L;
E_smaller = x2.L;
}
else
{
E_bigger = x2.L;
E_smaller = x1.L;
}
this.lowerLeft = new GPS.BLH(N_smaller, E_smaller, 0, GPS.BLH.units.Degree);
this.upperRight = new GPS.BLH(N_bigger, E_bigger, 0, GPS.BLH.units.Degree);
}
///
/// 引数省略時のコンストラクタ
///
public rectangleField()
{
BLH temp0 = new BLH(); BLH temp1 = new BLH();
this.lowerLeft = temp0;
this.upperRight = temp1;
}
}
/*2011/7/6 動作確認済み*/
///
/// x,y,zを格納する構造体
/// 半径の取得も簡単
/// BLH座標系(緯度・経度・楕円体高[m])への変換も簡単
///
public struct XYZ
{
/************************* メンバ変数 ***************************/
public double x;
public double y;
public double z;
/******************** 演算子 ********************************/
public static XYZ operator +(XYZ c1, XYZ c2) //二項+演算子をオーバーロードする(これで足し算が簡単にできる)
{
return new XYZ(c1.x + c2.x, c1.y + c2.y, c1.z + c1.z);
}
public static XYZ operator -(XYZ c1, XYZ c2) //二項-演算子をオーバーロードする(これで引き算が簡単にできる)
{
return new XYZ(c1.x - c2.x, c1.y - c2.y, c1.z - c1.z);
}
/******************** プロパティ ****************************/
///
/// 半径(地心からの距離)をプロパティとして提供(呼び出すたびに計算する)
///
public double r { get { return (Math.Sqrt(Math.Pow(this.x, 2) + Math.Pow(this.y, 2) + Math.Pow(this.z, 2))); } } // 構造体だけどプロパティを定義
/******************** メソッド ****************************/
///
/// XYZからBLH座標系(緯度・経度・楕円体高[m])へ変換
///
/// BLHに変換した結果
public BLH ToBLH()
{
BLH ans = new BLH();
double a, b, e, n, h, p, t, sint, cost;
ans.H = -WGS84.a;
if(this.x == 0 && this.y == 0 && this.z == 0)return ans;
a = WGS84.a; // 長半径
b = WGS84.b; // 短半径
e = Math.Sqrt(WGS84.e2); // 離心率
// 座標変換のためのパラメータ
h = Math.Pow(a, 2) - Math.Pow(b, 2);
p = Math.Sqrt(Math.Pow(this.x, 2) + Math.Pow(this.y, 2));
t = Math.Atan2(this.z * a, p * b);
sint = Math.Sin(t);
cost = Math.Cos(t);
ans.B = Math.Atan2(this.z + h / b * Math.Pow(sint, 3), p - h / a * Math.Pow(cost, 3)); // 緯度[rad]を計算する
ans.L = Math.Atan2(this.y, this.x); // 経度[rad]を求める
n = a / Math.Sqrt(1 - WGS84.e2 * Math.Pow(Math.Sin(ans.B), 2)); // 卯酉線曲率半径
ans.H = (p / Math.Cos(ans.B)) - n; // 楕円体高[m]
return ans;
}
///
/// コンストラクタ
///
public XYZ(double _x = 0.0d, double _y = 0.0d, double _z = 0.0d) { this.x = _x; this.y = _y; this.z = _z; } // 初めから値を指定していてもよい
}
/*2011/7/6 動作確認済み*/
///
/// 緯度・経度・楕円体高を表すクラス
/// 語源はドイツ語らしい。
///
public struct BLH
{
public enum units { Degree, Radian}; // 角度の単位
public double B; // latitude
public double L; // longitude
public double H; // height(楕円体高m)
public units unit; // unit
/******************** 演算子 ********************************/
public static BLH operator +(BLH c1, BLH c2) //二項+演算子をオーバーロードする(これで足し算が簡単にできる)
{
return new BLH(c1.ToDegree.B + c2.ToDegree.B, c1.ToDegree.L + c2.ToDegree.L, c1.ToDegree.H + c2.ToDegree.H, units.Degree);
}
public static BLH operator -(BLH c1, BLH c2) //二項-演算子をオーバーロードする(これで引き算が簡単にできる)
{
return new BLH(c1.ToDegree.B - c2.ToDegree.B, c1.ToDegree.L - c2.ToDegree.L, c1.ToDegree.H - c2.ToDegree.H, units.Degree);
}
/******************** プロパティ ****************************/
///
/// 単位を度へ変換した物を返す
///
public BLH ToDegree
{
get
{
BLH ans = this;
if (this.unit == units.Radian)
{
ans.B = this.B * 180.0d / Math.PI;
ans.L = this.L * 180.0d / Math.PI;
ans.unit = units.Degree;
}
return ans;
}
}
///
/// 単位をラジアン単位へ変換した物を返す
///
public BLH ToRadian
{
get
{
BLH ans = this;
if (this.unit == units.Degree)
{
ans.B = this.B * Math.PI / 180.0d;
ans.L = this.L * Math.PI / 180.0d;
ans.unit = units.Radian;
}
return ans;
}
}
///
/// 楕円体に沿った、東西方向の単位長[m/deg]を返す
/// 実際は高度の分だけ若干の誤差が発生するし、2地点の高度差は考慮しない。
/// 参考:理科年表,p.563,2003.
/// ちなみに、http://yamadarake.web.fc2.com/trdi/2009/report000001.html を見るとヒュベニの公式と言うものであることが分かる
///
///
public length GetUnitLengthForEN
{
get
{
length ans = new length(0, 0);
BLH temp = this.ToRadian;
ans.E = Math.PI / 180.0 * WGS84.a * Math.Cos(temp.B) / Math.Sqrt(1.0d - WGS84.e2 * Math.Pow(Math.Sin(temp.B), 2.0d)); // 高度については無視
ans.N = Math.PI / 180.0 * WGS84.a * (1.0d - WGS84.e2) / Math.Pow(1.0d - WGS84.e2 * Math.Pow(Math.Sin(temp.B), 2.0d), 1.5d); // 地表面に限れば誤差は無視可能。誤差は、地上では最大6408/6400程度
return ans;
}
}
/******************** メソッド ****************************/
///
/// 自身の単位を度へ変換する
///
///
public void ChangeToDegree()
{
if (this.unit == units.Radian)
{
this.B = this.B * 180.0 / Math.PI;
this.L = this.L * 180.0 / Math.PI;
this.unit = units.Degree;
}
return;
}
///
/// 自身の単位をラジアン単位へ変換する
///
public void ChangeToRadian()
{
if (this.unit == units.Degree)
{
this.B = this.B * Math.PI / 180.0;
this.L = this.L * Math.PI / 180.0;
this.unit = units.Radian;
}
return;
}
///
/// 引数で渡された座標までの距離を返す。
/// 簡易計算なので、距離が数十km以下で有効です。
///
/// 求めたい地点の座標
/// 南北・東西方向の距離[m]を構造体で返す
public length GetDistance(BLH secondPos)
{
length ans;
BLH me = this.ToDegree; // 自身のコピー
BLH se = secondPos.ToDegree; // 引数のコピー。単位はdegに統一する
BLH center = new BLH((me.B + se.B) / 2.0d, (me.L + se.L) / 2.0d, (me.H + se.H) / 2.0d, BLH.units.Degree); // 2地点の中間座標を求める
ans = new length((se.L - me.L) * center.GetUnitLengthForEN.E, (se.B - me.B) * center.GetUnitLengthForEN.N); // 緯度・経度差から距離を求める。
return ans;
}
///
/// 簡易な距離演算その2
/// 精度は、近距離では(少なくとも20km程度)その1の方法と大して変わらない。
/// 参考:http://homepage3.nifty.com/kubota01/distance.htm
/// 距離が50kmを超えるようなら、こちらのメソッドの方が実際の距離を表すと思う。
/// [2011/7/18 追記] 現時点ではXYZの直交座標系への変換精度が悪く、極近距離で精度が出ない。
///
/// 求めたい地点の座標
/// 距離
public double GetDistance2(BLH secondPos)
{
XYZ me, se;
double distance = 0.0d, N, Nme, Nse, d_straight, angle;
me = this.ToXYZ();
se = secondPos.ToXYZ();
d_straight = Math.Sqrt(Math.Pow(me.x - se.x, 2.0) + Math.Pow(me.y - se.y, 2.0) + Math.Pow(me.z - se.z, 2.0)); // XYZ直交座標系を用いた直線距離(線は地中に潜る)
Nme = WGS84.a / Math.Sqrt(1.0d - WGS84.e2 * Math.Sin(this.ToRadian.B));
Nse = WGS84.a / Math.Sqrt(1.0d - WGS84.e2 * Math.Sin(secondPos.ToRadian.B));
N = (Nse + Nme) / 2.0d; // 平均の半径のようなもの
angle = Math.Asin(d_straight / 2.0d / N); // 半射程角(絵を描けば分かる)
distance = 2.0d * angle * N;
return distance;
}
///
/// GetDistanceを利用して、直線距離を求めるメソッド
///
/// 求めたい地点の座標
/// 距離
public double GetDistance3(BLH secondPos)
{
length distance0 = this.GetDistance(secondPos);
double distance3 = Math.Sqrt(distance0.E * distance0.E + distance0.N * distance0.N);
return distance3;
}
///
/// XYZ座標系へ変換するメソッドです。
/// 参考:理科年表,p.563,2003.
/// [2011/7/18 追記]計算精度が悪い気がしてならん。
///
/// 変換したXYZ座標
public XYZ ToXYZ()
{
XYZ xyz = new XYZ();
double N;
N = WGS84.a / Math.Sqrt(1.0d - WGS84.e2 * Math.Pow(Math.Sin(this.ToRadian.B), 2.0d));
xyz.x = (N + this.H) * Math.Cos(this.ToRadian.B) * Math.Cos(this.ToRadian.L);
xyz.y = (N + this.H) * Math.Cos(this.ToRadian.B) * Math.Sin(this.ToRadian.L);
xyz.z = (N * (1.0d - WGS84.e2) + this.H) * Math.Sin(this.ToRadian.B);
return xyz;
}
///
/// コンストラクタ
///
public BLH(double _B = 0.0d, double _L = 0.0d, double _H = 0.0d, units _unit = units.Degree) { this.B = _B; this.L = _L; this.H = _H; this.unit = _unit; }
}
#endregion
#region /********************************** クラス ***********************************/
/*2011/7/6 主要パラメータチェック*/
///
/// WGS84座標系における各種パラメータ
/// 参考:西修二郎訳,GNSSのすべて,p.265,2010.2.
///
public static class WGS84
{
// 基本定数
public const double PI = 3.141592653589;
public const double c = 2.99792458E+08; // 光速[m/s]
// 地球に関するパラメータ
///
/// Equatorial Radius 赤道半径(楕円体長半径)[m]
///
public const double a = 6378137.0;
///
/// 扁平率の逆数
///
public const double invers_oblateness = 298.257223563;
///
/// 扁平率
///
public const double f = 1.0d / invers_oblateness;
///
/// Short Radius 短半径
///
public const double b = a * (1.0d - f);
///
/// Square Eccentricity 離心率^2
///
public const double e2 = f * (2.0d - f);
///
/// Angular Velocity of The Earth 地球の角速度[rad/sec]
///
public const double wE = 7292115E-11;
///
/// Gravitational Constant of The Earth 地球重力定数[m^3/s^-2]
///
public const double u = 3986004.418E+8;
///
/// Eccentricity 離心率
///
/// /******************** プロパティ ****************************/
public static double e { get { return Math.Sqrt(e2); } } // プロパティの形でしか提供できない。インスタンスは必要ない。
}
///
/// 2011/7/17作りかけ
/// 突貫で作ったので設計思想が固まっているわけでもない。
/// 現時点では、ログを一旦全て読み込んでいるのでメモリ量がかなり必要となっている。
/// 処理にも若干時間がかかる
/// 開発時間を優先した。
///
/// 今後は、NMEAパーサが必要とされるなら、“object型でGGAクラスを返す”などの動作を実装する予定。
/// 受け側では、以下の様にして呼び出す。
///
/// string nmea_str = "$GPGGA,*******";
/// GPS.NMEA nmea_parser = new GPS.NMEA();
/// object hoge;
///
/// hoge = nmea_parser.parse(nmea_str); // NMEAフォーマットの文字列を処理して、オブジェクトを返してもらう。
/// if(hoge.GetType() == tyepof(GPS.NMEA)) // オブジェクトの型をチェックし、任意の型と一致した場合に所望の処理を呼び出す。
/// {
/// // たとえば、こんな感じか?
/// int sat = hoge.sat;
/// }
///
/// NMEAパーサとして想定される使用環境
/// 1) ログを処理する(センテンス毎・1エポック毎)
/// 2) リアルタイムで処理する(センテンス毎に処理)
/// 3)
///
public class NMEA
{
/****************構造体**********************/
///
/// 観測データを取り扱うクラス
/// 構造体にしないのは、構造体だと初期化を必ず求められるため。
///
public class PositioningInfo
{
/*-------メンバ変数-----*/
public DateTime time;
public BLH position;
/*-------コンストラクタ系----------*/
public PositioningInfo() { }
~PositioningInfo() { }
}
///
/// 測位情報を配列をして格納
///
public class PositioningInfos
{
/*-------メンバ変数-----*/
public PositioningInfo[] data;
/*-------メソッド-----*/
///
/// 測位点情報をコピーする専用メソッド
///
///
public BLH[] GetPositions()
{
BLH[] ans = new BLH[data.Length];
int i;
for (i = 0; i < data.Length; i++) ans[i] = data[i].position;
return ans;
}
///
/// 時刻情報をコピーしてくれる専用メソッド
///
///
public DateTime[] GetTimes()
{
DateTime[] ans = new DateTime[data.Length];
int i;
for (i = 0; i < data.Length; i++) ans[i] = data[i].time;
return ans;
}
///
/// データ内容をstring型にして返す
/// データには、時刻・経度・緯度・高度が含まれます。
///
///
public override string ToString()
{
StringBuilder sb = new StringBuilder(3375000); // 予め大きなメモリ容量を確保しておく
int i;
for (i = 0; i < this.data.Length; i++) sb.Append(this.data[i].time.ToString()).Append(",").Append(this.data[i].position.L.ToString()).Append(",").Append(this.data[i].position.B.ToString()).Append(",").Append(this.data[i].position.H.ToString()).Append("\n");
return sb.ToString();
}
///
/// 指定ファイル名でデータを保存する
/// KMLに加工するメソッドもその内作りたいなぁ。
///
/// ファイル名
public void SaveFileAsNormal(string fname)
{
using (System.IO.StreamWriter nmea_writer = new System.IO.StreamWriter(fname))
{
nmea_writer.Write(this.ToString());
}
return;
}
/* [2011/7/19] 動作確認 */
///
/// 経路を全操作し、2つ以上のフィールドがあれば領域の右上・左下の座標のセットを返す
///
/// 領域またはnull
public rectangleField GetRectangleField()
{
int i;
rectangleField field = new rectangleField(); // lat:0 lon:0 に初期化される
if (data.Length >= 2) // 領域は2点以上なければ指定できない
{
field.upperRight = data[0].position;
field.lowerLeft = data[1].position;
for (i = 0; i < data.Length; i++) // 座標の大小関係を比較することで領域を決定する
{
if (field.upperRight.B < data[i].position.B) field.upperRight.B = data[i].position.B;
if (field.upperRight.L < data[i].position.L) field.upperRight.L = data[i].position.L;
if (field.lowerLeft.B > data[i].position.B) field.lowerLeft.B = data[i].position.B;
if (field.lowerLeft.L > data[i].position.L) field.lowerLeft.L = data[i].position.L;
}
field.upperRight.H = 0; // 楕円体高については本メソッドでは取り扱わない
field.lowerLeft.H = 0;
return field;
}
else
{
return null;
}
}
/*-------コンストラクタ系----------*/
public PositioningInfos() { }
~PositioningInfos() { }
}
public class GGA
{
public GGA() { }
~GGA() { }
}
/****************メンバ変数******************/
private string[] text;
private System.IO.StreamReader nmeaStream; // ログのサイズが大きすぎて危険な場合はこちらでちょっとずづ読み込むようにする(予定)
/****************プロパティ******************/
///
/// バッファ上に読み込み済みならtrueとなる。
/// 2011/7/20 時点では、streamに関しては関知しない。
///
public Boolean IsOpen
{
get {
if (this.text == null)
return false;
else
return true;
}
}
/****************メソッド********************/
///
/// NMEA形式のファイルが格納されたファイルを開いて、処理の準備を行う。
/// NMEAの処理の仕方はたくさんあるはずなので、オーバーロードを利用してこれから増やす予定。
/// バイナリ交じりのデータだとstreamがエラーを起こす可能性がある。
/// その場合はバイナリ読み取り指定が必要。
///
public void Open()
{
OpenFileDialog f = new OpenFileDialog();
System.IO.StreamReader sr;
int i = 0;
f.Title = "NMEAフォーマットのログファイルを指定して下さい";
f.Filter = "u-bloxログ|*.ubx|NMEAログ|*.nmea|NMEAログ|*.nme|tera termログ|*.log|テキストファイル|*.txt";
if (f.ShowDialog() == DialogResult.OK)
{
if(this.text == null)this.text = new string[0]; // まだnullなら空を確保する
using (sr = new System.IO.StreamReader(f.FileName))
{
while (sr.EndOfStream == false)
{
Array.Resize(ref this.text, text.Length + 1);
this.text[i++] = sr.ReadLine();
}
}
}
return;
}
///
/// ファイル名の指定で開く
/// 開いた後は、メモリ上に全展開
///
///
public void Open(string fname)
{
System.IO.StreamReader sr;
int i = 0;
if (this.text == null) this.text = new string[0]; // まだnullなら空を確保する
using (sr = new System.IO.StreamReader(fname))
{
while (sr.EndOfStream == false)
{
Array.Resize(ref this.text, text.Length + 1); // サイズ調整
this.text[i++] = sr.ReadLine();
}
}
return;
}
///
/// ストリームを使用したファイルのオープン。
/// 巨大なログファイルを処理する際に使用する。
/// ストリームを使用した処理に関しては未実装です。
///
///
public void OpenStream(string fname)
{
try
{
this.nmeaStream = new System.IO.StreamReader(fname); // ファイルを開く
}
catch
{ }
return;
}
///
/// GGAとZDAから緯度・経度を配列で取得する
///
///
public BLH[] GetPositions()
{
BLH[] positions = new BLH[0]; // 要素数0の配列ってなんだ?
int i, j = 0;
string line;
string[] field;
if (this.IsOpen)
{
for (i = 0; i < this.text.Length; i++)
{
line = this.text[i]; // 1行分データを取得
field = line.Split(','); // スプリットでカンマを使って区切る
if (field[0] == "$GPGGA")
{
Array.Resize(ref positions, positions.Length + 1);
if (field[2] != "") positions[j].B = double.Parse(field[2]); else positions[j].B = 0.0;
if (positions[j].B != 0.0) positions[j].B = (double)((int)(positions[j].B / 100.0d)) + (positions[j].B % 100.0) / 60.0d;
if (field[4] != "") positions[j].L = double.Parse(field[4]); else positions[j].L = 0.0;
if (positions[j].L != 0.0) positions[j].L = (double)((int)(positions[j].L / 100.0d)) + (positions[j].L % 100.0) / 60.0d;
if (field[9] != "") positions[j].H = double.Parse(field[9]); else positions[j].H = 0.0;
j++;
}
}
}
return positions;
}
///
/// GGAからUTCの秒単位の時刻を配列で得る
/// 日付の経過などは考慮していない。
///
///
public float[] GetTimes()
{
float[] times = new float[0]; // 要素数0の配列ってなんだ?
int i, j = 0, k;
string line;
string[] field;
if (this.IsOpen)
{
for (i = 0; i < this.text.Length; i++)
{
line = this.text[i]; // 1行分データを取得
field = line.Split(','); // スプリットでカンマを使って区切る
if (field[0] == "$GPGGA")
{
Array.Resize(ref times, times.Length + 1);
k = (int)(float.Parse(field[1]) * 100.0f); // hhmmss.ss形式
times[j] = (float)((k / 1000000) * 3600 + (k % 1000000) / 10000 * 60) + (float)(k % 10000) / 100.0f; // 時分秒を秒に直す
j++;
}
}
}
return times;
}
///
/// GPGGAをトリガにして、タイミングごとに測位情報をまとめて配列として返すメソッド
/// NMEAはセンテンスの出力順序を規定していないので、もしかすると時刻と測位位置がずれるかもしれない。
///
///
public PositioningInfos GetPositioningResults()
{
PositioningInfos ans = new PositioningInfos();
ans.data = new PositioningInfo[0]; // 要素数0の配列ってなんだ?
int i, j = 0, k, m;
string line;
string[] field;
int year, month, day, hh, mm, ss;
if (this.IsOpen)
{
for (i = 0; i < this.text.Length; i++)
{
line = this.text[i]; // 1行分データを取得
field = line.Split(','); // スプリットでカンマを使って区切る
if (field[0] == "$GPGGA")
{
Array.Resize(ref ans.data, ans.data.Length + 1);
if (ans.data[j] == null) ans.data[j] = new PositioningInfo(); // インスタンスを作成
if (field[2] != "") ans.data[j].position.B = double.Parse(field[2]); else ans.data[j].position.B = 0.0;
if (ans.data[j].position.B != 0.0) ans.data[j].position.B = (double)((int)(ans.data[j].position.B / 100.0d)) + (ans.data[j].position.B % 100.0) / 60.0d;
if (field[4] != "") ans.data[j].position.L = double.Parse(field[4]); else ans.data[j].position.L = 0.0;
if (ans.data[j].position.L != 0.0) ans.data[j].position.L = (double)((int)(ans.data[j].position.L / 100.0d)) + (ans.data[j].position.L % 100.0) / 60.0d;
if (field[9] != "") ans.data[j].position.H = double.Parse(field[9]); else ans.data[j].position.H = 0.0;
j++; // このカウントアップがどこにあるかは重要
}
else if (field[0] == "$GPZDA")
{
if (ans.data.Length > 0)
{
m = ans.data.Length - 1; // 既存の最後の要素へアクセスする
if (ans.data[m] == null) ans.data[m] = new PositioningInfo();// この時点ではないはずはないが、一応インスタンス生成ができるようにしておく
k = (int)(float.Parse(field[1]) * 100.0f); // hhmmss.ss形式
hh = k / 1000000;
mm = (k % 1000000) / 10000;
ss = (k % 10000) / 100;
day = int.Parse(field[2]);
month = int.Parse(field[3]);
year = int.Parse(field[4]);
ans.data[m].time = new DateTime(year, month, day, hh, mm, ss);
}
}
}
}
return ans;
}
///
/// ZDAから時刻データ列のみを取得する
/// GGAのトリガはないのでGetPositioningResults()とは時刻がずれる可能性がある。
/// 日付も考慮してみる。
///
///
public DateTime[] GetDateTimes()
{
DateTime[] times = new DateTime[0]; // 要素数0の配列ってなんだ?
int i, j = 0, k;
string line;
string[] field;
int year, month, day, hh, mm, ss;
if (this.IsOpen)
{
for (i = 0; i < this.text.Length; i++)
{
line = this.text[i]; // 1行分データを取得
field = line.Split(','); // スプリットでカンマを使って区切る
if (field[0] == "$GPZDA")
{
Array.Resize(ref times, times.Length + 1);
k = (int)(float.Parse(field[1]) * 100.0f); // hhmmss.ss形式
hh = k / 1000000;
mm = (k % 1000000) / 10000;
ss = (k % 10000) / 100;
day = int.Parse(field[2]);
month = int.Parse(field[3]);
year = int.Parse(field[4]);
times[j] = new DateTime(year, month, day, hh, mm, ss);
j++;
}
}
}
return times;
}
/****************コンストラクタ系************/
public NMEA()
{ }
~NMEA()
{
if (this.nmeaStream != null)
{
this.nmeaStream.Close(); // ファイル解放
}
}
}
#endregion
}