1000円ぐらいで買いました。このモジュールはアンテナが分離型で、1PPS 出力がとれるという特徴があります。
ublox というメーカーのGPSチップが載っています。u-center というソフトウェアから詳細な設定ができることになっています。ただし u-center は Windows のみです。
GPS Fix に時間がかかる
窓際で受信させてみましたが、いつまで経っても GPS Fix しません。u-center でしばらく眺めていると1時間〜2時間ぐらいでようやく GPS Fix しました。
スマフォ内蔵のGPSだと窓際でもすぐGPS Fixできるので、ちょっと残念なところです。
1度3D Fixまでいけば、割と安定して受信が継続されました。
NEO-6M は GPS(アメリカ)のみに対応していて、GLONASS (ロシア)やQZSS(日本)は対応していないので、そのせいかもしれません。
1PPS
自宅でGPS使って位置を知れてもあんまり意味がありません。このGPS モジュールの最大の目的は1PPSです。
1PPS (Pulse Per Second) 出力はLED が接続されており、GPS Fix 時には1秒ごとにLEDがフラッシュします。
この1PPSはGPS衛星に搭載されている原子時計と同期しているので、とても正確です。ただし、GPSモジュール内蔵クロックが48MHzなので最大42nsぐらいのジッタはありそうです。そこそこに長い目で見ると原子時計並に正確な1Hzが得られるというものです。
スペック的には以下のようになっています(いまいち各項目の意味がわかりませんでしたが…)
モジュールのピンヘッダに出ていないので、必要なら直接引き出す必要があります。
• For best timepulse performance it is recommended to disable the SBAS subsystem.
日本にはSBASシステムがそもそもないので完全に無効にしときましょう。
NMEA を Ruby で雑に読んでみる
特にライブラリとかを使わずに、ちゃんと動いているかだけのテストをできるようにスクリプトを書きました。動かすと以下のような出力になります。
2016-02-16 15:28:5.000Z VALID:YES / 3D Fix [1: 31dB*] [3: 21dB*] [7: 26dB*] [8: 0dB] [10: 0dB] [11: 15dB*] [16: 15dB*] [17: 0dB] [22: 0dB] [27: 0dB] [28: 19dB*] [30: 0dB*]
#!ruby -v
require 'serialport'
@port = SerialPort.new(
"/dev/tty.usbserial-A50285BI",
9600,
8,
1,
0
)
cols = `tput cols`.to_i
status = {
using: [],
sates: {}
}
loop do
while line = @port.gets
type, *rest = line.chomp.split(/,/)
next unless type[0] == '$'
_ = rest.pop
case type
when "$GPRMC"
h, m, s = *rest[0].match(/(..)(..)(.+)/).captures.map {|i| i.to_f }
dd, mm, yy = *rest[8].match(/(..)(..)(..)/).captures.map {|i| i.to_i }
datetime = "%04d-%02d-%02d %02d:%02d:%02.3fZ" % [yy + 2000, mm, dd, h, m, s]
state = rest[1]
status[:UTC] = datetime
status[:is_valid] = state == "A"
when "$GPGGA"
sate_count = rest[6]
status[:sate_count] = sate_count
when "$GPGSA"
mode = rest[0]
type = rest[1] # 1 = invalid, 2 = 2d, 3 = 3d
sate_nums = rest[2, 12]
status[:mode] = mode
status[:type] = type
status[:using] = sate_nums.map {|i| i.to_i }
when "$GPGSV"
total = rest.shift
current = rest.shift
if current == 1
status[:sates] = {}
end
_ = rest.shift # count
until rest.empty?
num = rest.shift
e = rest.shift
d = rest.shift
sn = rest.shift
status[:sates][num] = {
num: num.to_i,
e: e,
d: d,
sn: sn.to_i,
}
end
if total == current
# done
end
else
# ignore
end
out = "%s VALID:%s / %s Fix %s" % [
status[:UTC],
status[:is_valid] ? "YES" : "NO",
status[:type] == "1" ? "NO" : "#{status[:type]}D",
status[:sates].values.sort_by {|i| i[:num] }.map {|i|
"[%s: %ddB%s]" % [
i[:num],
i[:sn],
status[:using].find_index(i[:num]) ? "*" : ""
]
}.join(" ")
]
print "%- #{cols-1}s\r" % out
$stdout.flush
end
end