しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Raspberry Pi 2 に繋いだ USB-GPS レシーバーの情報を C# から利用してみた

Raspberry Pi 2 で USB-GPS レシーバーを使ってみた - しばやん雑記 から USB-GPS レシーバーを Raspberry Pi 2 に刺しっぱなしだったので、そろそろ C# から使ってみようかと思いました。

基本的に USB-GPS レシーバーからは NMEA フォーマットでデータが流れてくるので、それを C# からパースするだけで問題なさそうです。データ自体は cat などでレシーバーを開いてみると簡単に確認出来ます。

Windows だと USB デバイスを使うのはちょっとめんどくさい感じありますが、Linux だとファイルとして開いて読み込むだけなので簡単です。

まずは NMEA フォーマットのデータを読み込むだけのコードを書いてみました。

class Program
{
    static void Main(string[] args)
    {
        var file = File.OpenRead("/dev/ttyACM0");

        using (var reader = new StreamReader(file))
        {
            while (true)
            {
                var line = reader.ReadLine();

                Console.WriteLine(line);
            }
        }
    }
}

StreamReader で 1 行ずつ読み込むだけのシンプルなコードです。

デバイス名は自分の Raspberry Pi 2 では USB-GPS レシーバーが /dev/ttyACM0 として認識されているので、それを決め打ちで読み込むようにしています。

Mono を使って実行してみると、NMEA フォーマットのデータが読み込めていることが分かりますね。

さて、これから実際に位置情報を取得するわけですが、折角なので NMEA フォーマットのパーサーなどを使わずに自前で計算してみたいと思います。フォーマットに関しては簡単に調べました。

NMEA 0183 - Wikipedia
緯度、経度を測る(GPSモジュール使用)

$GPRMC から始まるデータだけ見れば、最低限の緯度経度は取得できるみたいですね。緯度経度の計算部分を参考にしつつコードを書きました。

class Program
{
    static void Main(string[] args)
    {
        var file = File.OpenRead("/dev/ttyACM0");

        using (var reader = new StreamReader(file))
        {
            while (true)
            {
                var line = reader.ReadLine();

                if (string.IsNullOrEmpty(line))
                {
                    continue;
                }

                var values = line.Split(',');

                if (values[0] != "$GPRMC")
                {
                    continue;
                }

                var latitude = ParsePosition(values[3]);
                var longitude = ParsePosition(values[5]);

                Console.WriteLine("{0:f8} {1},{2:f8} {3}", latitude, values[4], longitude, values[6]);
            }
        }
    }

    static double ParsePosition(string str)
    {
        var value = double.Parse(str);

        var degree = (int)(value / 100);
        var minute = value - (degree * 100);

        return degree + (minute / 60);
    }
}

ちょっと冗長な感じになってますが、緯度経度はちゃんと計算できていました。

3G 通信モジュールとモバイルバッテリーを組み合わせれば、位置情報を取得しつつリアルタイムで EventHub に投げて地図にマッピングとか出来そうです。