如何使用 ruby 读取源源不断的 NMEA http 数据

How to read a constant stream of NMEA http data using ruby

我有一个名为 'gps2ip' 的 iPhone 应用程序,它会启动一个网络服务器,您可以访问该服务器以获取流式 NMEA 数据。

您可以使用 qgis 直接连接到此流以获取地图上的更新位置。我想以编程方式访问此流。

如果我在我的浏览器中输入这个 url window: http://192.168.1.116:11123 其中 192.168.1.116 是我智能手机的 ip,如 gps2ip 应用程序所示

我的 safari/chrome/mozilla 浏览器屏幕上不断出现换行符分隔的 NMEA 字符串流,不断在底部更新新的数据行。

    GPS 2 IP Server started. "exit" to finish.
    $GPGGA,005730,3403.415,N,07914.488,W,1,8,0.9,13.6,M,46.9,M,0,2*56
    $GPRMC,005730,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*66
    $GPGGA,005730,3403.415,N,07914.488,W,1,8,0.9,13.6,M,46.9,M,0,2*56
    $GPRMC,005730,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*66
    $GPGGA,005731,3403.415,N,07914.488,W,1,8,0.9,13.7,M,46.9,M,0,2*56
    $GPRMC,005731,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*67
    $GPGGA,005731,3403.415,N,07914.488,W,1,8,0.9,13.7,M,46.9,M,0,2*56
    $GPRMC,005731,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*67
    $GPGGA,005732,3403.415,N,07914.488,W,1,8,0.9,13.6,M,46.9,M,0,2*54
    $GPRMC,005732,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*64
    $GPGGA,005732,3403.415,N,07914.488,W,1,8,0.9,13.6,M,46.9,M,0,2*54
    $GPRMC,005732,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*64
    $GPGGA,005733,3403.415,N,07914.488,W,1,8,0.9,13.5,M,46.9,M,0,2*56
    $GPRMC,005733,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*65
    $GPGGA,005733,3403.415,N,07914.488,W,1,8,0.9,13.5,M,46.9,M,0,2*56
    $GPRMC,005733,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*65
    $GPGGA,005734,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*50
    $GPRMC,005734,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*62
    $GPGGA,005734,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*50
    $GPRMC,005734,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*62
    $GPGGA,005735,3403.415,N,07914.488,W,1,8,0.9,13.3,M,46.9,M,0,2*56
    $GPRMC,005735,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*63
    $GPGGA,005735,3403.415,N,07914.488,W,1,8,0.9,13.3,M,46.9,M,0,2*56
    $GPRMC,005735,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*63
    $GPGGA,005736,3403.415,N,07914.488,W,1,8,0.9,13.2,M,46.9,M,0,2*54
    $GPRMC,005736,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*60
    $GPGGA,005736,3403.415,N,07914.488,W,1,8,0.9,13.2,M,46.9,M,0,2*54
    $GPRMC,005736,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*60
    $GPGGA,005737,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*53
    $GPRMC,005737,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*61
    $GPGGA,005737,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*53
    $GPRMC,005737,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*61
    $GPGGA,005738,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*5C
    $GPRMC,005738,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*6E
    $GPGGA,005738,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*5C
    $GPRMC,005738,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*6E
    $GPGGA,005739,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*5D
    $GPRMC,005739,A,3403.415,N,07914.488,W,0.00,,120115,003.1,W*6F
    $GPGGA,005739,3403.415,N,07914.488,W,1,8,0.9,13.4,M,46.9,M,0,2*5D

我知道如何将这些 NMEA 代码行解析为 latitude/longitude 对,我只需要能够在 ruby 环境中轻松访问 "the last one"。

我希望能够提取最后一两行并手动解析 NMEA 字符串,但我还没有找到一种方法来 "sip from the firehose" 数据而不生成错误消息。

当我尝试这个时:

require 'open-uri'
open("http://192.168.1.116:11123")

我收到这个错误:

Net::HTTPBadResponse: wrong status line: "GPS 2 IP Server started. \"exit\" to finish."

其中 "GPS 2 IP Server Started. 'exit' to finish." 当然是响应的第一行。

我应该使用什么 ruby gem 来从这个数据流中抽取?显然 open-uri 想要 html headers 而我的流中有 none。我只需要显然地传输纯文本。

由于这不是 HTTP 协议,您需要更通用的 Socket。你可以这样做:

require 'socket'

s = TCPSocket.new '192.168.1.116', 11123

while line = s.gets 
  puts line
end

s.close 

根据数据到达的速度和处理每一行所需的时间,您可能需要研究将每一行放入队列中,例如 Sidekiq 以便多个工作人员可以同时处理行。

由于您拥有的是永无止境的数据流,因此您不能只获取最后一条消息。您必须使用流,并决定何时收到足够的数据以开始处理。您或许可以利用 GPRMC 消息来做到这一点。

nmea_plus(完全公开,我写的)中,你可以这样做:

require 'socket'
require 'nmea_plus'

allowable_delay = 3  # threshold to consider the stream as fresh
caught_up = false    # flag to say whether we've crossed the threshold

io_source = TCPSocket.new('192.168.1.116', 11123)
source_decoder = NMEAPlus::SourceDecoder.new(io_source)

source_decoder.each_message do |msg|
  case msg.data_type
  when 'GPRMC'
    if Time.now - msg.utc_time < allowable_delay
      caught_up = true
    end
  when 'GPGLL'
    if caught_up
      puts "Fix: #{msg.latitude}, #{msg.longitude}"
    end
  end
end

默认情况下,解码器会忽略任何未解析为 NMEA 的行。