mm_gps 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -21
- data/lib/mm_gps.rb +1 -0
- data/lib/mm_gps/mm_gps_beacon.rb +80 -34
- data/lib/mm_gps/mm_gps_buffer.rb +127 -0
- data/lib/mm_gps/mm_gps_module.rb +2 -3
- data/lib/mm_gps/version.rb +1 -1
- data/test.rb +10 -21
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d54d7a04b331b5339f0e68b21e903101424a9f9e
|
4
|
+
data.tar.gz: b07d30015c9e27d411154880d00b23f1afd6cb00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8f837ef92db27cd2a89db5a03c368f301cdcd5003bc7d43d54279407d0a60c433847ad7283826560e66e11ac3535a22d7ec2397ec62914e8b4eb88d563b4d6b
|
7
|
+
data.tar.gz: 86cd9f6dadc96679aaa1712b4620569262954635f29014b3672fdac13b5c115e53db40284854433c2d4b12bc62799b9e5e1273e223fac93f0536fccce2d23546
|
data/README.md
CHANGED
@@ -32,29 +32,17 @@ BAUD = 115200 # SerialPort class does not support non-standard 500 kbps
|
|
32
32
|
beacon = MmGPS::Beacon.new(PORT, BAUD)
|
33
33
|
beacon.trap # installs signal handler for CTRL-C
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
rescue MmGPSError => e
|
39
|
-
puts "Packet Error: #{e.inspect}"
|
40
|
-
p e.data[:packet]
|
41
|
-
rescue IOError => e
|
42
|
-
puts "Port closed #{e.inspect}"
|
43
|
-
exit
|
35
|
+
# Standard each loop. Type CTRL-C for interrupting it
|
36
|
+
beacon.each do |packet|
|
37
|
+
p packet
|
44
38
|
end
|
45
39
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
p e.data
|
53
|
-
rescue IOError => e
|
54
|
-
puts "Port closed #{e.inspect}"
|
55
|
-
exit
|
56
|
-
end
|
57
|
-
end
|
40
|
+
# Use the enumerator:
|
41
|
+
beacon.reopen # Needed, since CTRL-C in previous example also closes the Serialport connection
|
42
|
+
enum = beacon.each # gets the Enumerator
|
43
|
+
p enum.take 10 # Next 10 packets from enum
|
44
|
+
|
45
|
+
puts "Exiting"
|
58
46
|
```
|
59
47
|
|
60
48
|
## Contributing
|
data/lib/mm_gps.rb
CHANGED
data/lib/mm_gps/mm_gps_beacon.rb
CHANGED
@@ -2,10 +2,26 @@ module MmGPS
|
|
2
2
|
|
3
3
|
# Main interface. Represents a connection to a MarvelMind beacon/hedgehog.
|
4
4
|
# You may want (and can) to have more than one instance.
|
5
|
+
#
|
6
|
+
# @example Typical usage:
|
7
|
+
# beacon = MmGPS::Beacon.new(PORT, BAUD)
|
8
|
+
# beacon.trap # installs signal handler for CTRL-C
|
9
|
+
#
|
10
|
+
# # Standard each loop. Type CTRL-C for interrupting it
|
11
|
+
# beacon.each do |packet|
|
12
|
+
# p packet
|
13
|
+
# end
|
14
|
+
# @example Using the enumerator:
|
15
|
+
# # Use the enumerator:
|
16
|
+
# beacon.reopen # Needed, since CTRL-C in previous example also closes the Serialport connection
|
17
|
+
# enum = beacon.each # gets the Enumerator
|
18
|
+
# p enum.take 10 # next 10 packets from enum
|
19
|
+
#
|
5
20
|
class Beacon
|
6
|
-
|
7
|
-
|
21
|
+
include Enumerable
|
22
|
+
START_TOKEN = "\xFFG"
|
8
23
|
attr_reader :last_pkt
|
24
|
+
attr_reader :port, :baud
|
9
25
|
|
10
26
|
# Open a new connection on the given serial port. It also encapsulates
|
11
27
|
# the underlying SerialPort object instance, setting a read timeout of 1 s
|
@@ -14,17 +30,15 @@ module MmGPS
|
|
14
30
|
# @param port [String] 'COM1' on Windows, '/dev/ttyNNN' on *nix
|
15
31
|
# @param baud [Fixnum] baudrate, value must be supported by the platform
|
16
32
|
def initialize(port, baud = 115200)
|
17
|
-
@
|
18
|
-
|
19
|
-
@sp.binmode
|
20
|
-
@last_pkt = ''.force_encoding(Encoding::BINARY)
|
33
|
+
@port, @baud = port, baud
|
34
|
+
self.open
|
21
35
|
end
|
22
36
|
|
23
|
-
#
|
37
|
+
# Installs a signal handler for the given signal, default to +'SIGINT'+,
|
24
38
|
# which closes the serialport connection. Further readings are likely to
|
25
39
|
# trigger an IOError.
|
26
40
|
#
|
27
|
-
# @param signal [String] the signal to be trapped, default to 'SIGINT'
|
41
|
+
# @param signal [String] the signal to be trapped, default to +'SIGINT'+
|
28
42
|
def trap(signal="INT")
|
29
43
|
Signal.trap(signal) do
|
30
44
|
@sp.close
|
@@ -33,10 +47,31 @@ module MmGPS
|
|
33
47
|
end
|
34
48
|
|
35
49
|
# Close the serialport
|
50
|
+
#
|
51
|
+
# @return [Beacon] self
|
36
52
|
def close
|
37
|
-
@sp.close
|
53
|
+
@sp.close unless (!@sp || @sp.closed?)
|
54
|
+
return self
|
38
55
|
end
|
39
56
|
|
57
|
+
# Open the serialport connection with {#port} at {#baud} rate. It also
|
58
|
+
# resets internals to a clean state.
|
59
|
+
#
|
60
|
+
# @return [Beacon] self
|
61
|
+
def open
|
62
|
+
self.close
|
63
|
+
@sp = SerialPort.new(@port, "baud" => @baud)
|
64
|
+
@sp.read_timeout = 1000 #1 sec
|
65
|
+
@sp.binmode
|
66
|
+
@last_pkt = ''.force_encoding(Encoding::BINARY)
|
67
|
+
@buffer = Buffer.new(START_TOKEN)
|
68
|
+
@buffer.when_updating do
|
69
|
+
@sp.read(1)
|
70
|
+
end
|
71
|
+
return self
|
72
|
+
end
|
73
|
+
alias :reopen :open
|
74
|
+
|
40
75
|
# Check wether the serialport is closed
|
41
76
|
#
|
42
77
|
# @return [Bool] true if closed, false if open
|
@@ -44,47 +79,58 @@ module MmGPS
|
|
44
79
|
return @sp.closed?
|
45
80
|
end
|
46
81
|
|
47
|
-
# Reads and discards incoming bytes until the START_TOKEN
|
82
|
+
# Reads and discards incoming bytes until the START_TOKEN marker is
|
48
83
|
# received. Call this metod immediately after opening the connection
|
49
84
|
# and before start reading the data.
|
85
|
+
#
|
86
|
+
# @return [Beacon] self
|
87
|
+
# @deprecated No more needed, since this version automatically takes care of the incomplete packages.
|
50
88
|
def sync
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
89
|
+
warn "Beacon#sync is deprecated and no more necessary!"
|
90
|
+
return self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Iterates +block+ to each packet
|
94
|
+
#
|
95
|
+
# @yield [pkt] the decoded packet
|
96
|
+
# @yieldparam pkt [Hash|Array] the decoded packet as returned by +MmGPS#parse_packet+
|
97
|
+
def each
|
98
|
+
return enum_for(:each) unless block_given?
|
99
|
+
loop do
|
100
|
+
begin
|
101
|
+
yield self.get_packet
|
102
|
+
rescue MmGPSError => e
|
103
|
+
warn "Packet Error: #{e.inspect}, reason: #{e.data[:reason]}"
|
104
|
+
warn "Packet: #{MmGPS.hexify(e.data[:packet])}"
|
105
|
+
if e.data[:reason] == :nocrc then
|
106
|
+
warn "CRC16: #{e.data[:crc]}"
|
107
|
+
end
|
108
|
+
rescue IOError => e
|
109
|
+
warn "Port closed #{e.inspect}"
|
110
|
+
return
|
111
|
+
end
|
112
|
+
end
|
56
113
|
end
|
57
114
|
|
58
115
|
# Reads a raw packet.
|
59
116
|
#
|
60
|
-
#
|
117
|
+
# @return [String] a byte stream encoded with Encoding::BINARY
|
118
|
+
# @raise [MmGPSError] when data are not available, setting +e.data [:reason]+ to +:noavail+
|
61
119
|
def get_raw_packet
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
unless char
|
66
|
-
raise MmGPSError.new("Data unavailable",
|
67
|
-
{reason: :noavail, packet:nil})
|
68
|
-
else
|
69
|
-
buf << char
|
70
|
-
end
|
71
|
-
break if buf[-2..-1] == START_TOKEN
|
120
|
+
pkt = @buffer.next
|
121
|
+
if pkt.empty? then
|
122
|
+
raise MmGPSError.new("Data unavailable", {reason: :noavail})
|
72
123
|
end
|
73
|
-
@last_pkt =
|
124
|
+
@last_pkt = pkt
|
74
125
|
return @last_pkt
|
75
126
|
end
|
76
127
|
|
77
128
|
# Reads a raw packet, checks its CRC, and returns its contents as a Hash.
|
78
129
|
#
|
79
|
-
# @return [Hash] typically in the form
|
130
|
+
# @return [Hash|Array] typically in the form +%I(ts x y f).zip(payload).to_h+
|
131
|
+
# @raise [MmGPSError] when CRC16 check does not pass, setting +e.data [:reason]+ to +:nocrc+
|
80
132
|
def get_packet
|
81
133
|
return MmGPS::parse_packet(self.get_raw_packet)
|
82
|
-
rescue MmGPSError => e
|
83
|
-
if e.data[:reason] == :noavail then
|
84
|
-
return nil
|
85
|
-
else
|
86
|
-
raise e
|
87
|
-
end
|
88
134
|
end
|
89
135
|
|
90
136
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
class BufferError < RuntimeError; end
|
3
|
+
|
4
|
+
module MmGPS
|
5
|
+
# A general abstraction for dealing with character streams.
|
6
|
+
#
|
7
|
+
# @example Typical usage:
|
8
|
+
# buffer = Buffer.new(START_TOKEN)
|
9
|
+
# buffer.when_updating do
|
10
|
+
# @sp.read(1) # @sp is an IO instance
|
11
|
+
# end
|
12
|
+
# # more code...
|
13
|
+
# pkt = buffer.next
|
14
|
+
#
|
15
|
+
class Buffer
|
16
|
+
include Enumerable
|
17
|
+
attr_reader :separator, :stop_packet
|
18
|
+
attr_reader :buffer, :fiber
|
19
|
+
|
20
|
+
def initialize(sep="\xFF")
|
21
|
+
@update = lambda { empty }
|
22
|
+
@encoding = Encoding::BINARY
|
23
|
+
fiber_init
|
24
|
+
self.separator = sep
|
25
|
+
self.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set the {#separator} bytes sequence, as a String in {#encoding} format.
|
29
|
+
#
|
30
|
+
# @param s [String] the separator string, automatically converted into {#encoding}
|
31
|
+
def separator=(s)
|
32
|
+
@separator = s.force_encoding(@encoding)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Clear the {#buffer} and return its last content.
|
36
|
+
#
|
37
|
+
# @return [String] the last buffer content
|
38
|
+
def clear
|
39
|
+
tmp = @buffer
|
40
|
+
@buffer = empty
|
41
|
+
return tmp
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets the block to be executed for updating the {#buffer}. Typically,
|
45
|
+
# this block must read one single byte and return it. Queuing into the
|
46
|
+
# {#buffer} is automatically performed.
|
47
|
+
# @example To read from a serialport instance +@sp+:
|
48
|
+
# buffer.when_updating do
|
49
|
+
# @sp.read(1)
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# @param block [Proc] the block (with no parameters) that must return a new character every time it is called
|
53
|
+
def when_updating(&block)
|
54
|
+
@update = block
|
55
|
+
end
|
56
|
+
|
57
|
+
# Call the block set with {#when_updating} and queues its returned byte into {#buffer}
|
58
|
+
def update
|
59
|
+
@buffer << (@update.call || empty)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return the next available packet
|
63
|
+
#
|
64
|
+
# @return [String] the next available packet
|
65
|
+
def next
|
66
|
+
@fiber.resume
|
67
|
+
end
|
68
|
+
|
69
|
+
# Iterates a block over incoming packets
|
70
|
+
#
|
71
|
+
# @yield [pkt]
|
72
|
+
# @yieldparam pkt [String] the next packet
|
73
|
+
# @return [Enumerator] an {Enumerator} when called without block
|
74
|
+
def each
|
75
|
+
return enum_for(:each) unless block_given?
|
76
|
+
loop do
|
77
|
+
yield self.next
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def fiber_init
|
83
|
+
@fiber = Fiber.new do
|
84
|
+
loop do # over packets
|
85
|
+
pkt = empty
|
86
|
+
loop do # over chars
|
87
|
+
begin
|
88
|
+
self.update
|
89
|
+
rescue BufferError
|
90
|
+
Fiber.yield self.clear #yelds @buffer AND clears it!
|
91
|
+
end
|
92
|
+
if @buffer.start_with?(@separator) &&
|
93
|
+
@buffer.end_with?(@separator) &&
|
94
|
+
@buffer != @separator then # end of packet
|
95
|
+
pkt = @buffer.slice!(0...-@separator.length)
|
96
|
+
break
|
97
|
+
elsif @buffer.end_with?(@separator) then # Incomplete packet
|
98
|
+
@buffer.slice!(0...-@separator.length)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
Fiber.yield pkt
|
102
|
+
end
|
103
|
+
warn "Exiting fiber!"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def empty
|
108
|
+
String.new("", encoding:@encoding)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if $0 == __FILE__ then
|
115
|
+
require 'pry'
|
116
|
+
|
117
|
+
str = "OKjhagshj asjhgOKjhjhg jhgasjdhg aOKjhahg ashg jhgjhaOKstopppp"
|
118
|
+
cb = MmGPS::Buffer.new
|
119
|
+
cb.separator = "OK"
|
120
|
+
cb.when_updating do
|
121
|
+
raise BufferError if str.empty?
|
122
|
+
str.slice!(0...1)
|
123
|
+
end
|
124
|
+
p cb
|
125
|
+
|
126
|
+
binding.pry
|
127
|
+
end
|
data/lib/mm_gps/mm_gps_module.rb
CHANGED
@@ -51,9 +51,8 @@ module MmGPS
|
|
51
51
|
# timestamp, coordinates, and error code (data code 0x0001). Otherwise returns an Array of Hashes for beacons status (data code 0x0002).
|
52
52
|
def self.parse_packet(buf)
|
53
53
|
unless valid_crc16?(buf) then
|
54
|
-
raise MmGPSError.new("Invalid CRC", {reason: :nocrc, packet:buf, crc:crc16(buf)})
|
54
|
+
raise MmGPSError.new("Invalid CRC", {reason: :nocrc, packet:buf, crc:("%04X" % crc16(buf[0...-2]))})
|
55
55
|
end
|
56
|
-
# warn "Invalid CRC" unless valid_crc16?(buf)
|
57
56
|
header = buf[0..5].unpack('CCS<C')
|
58
57
|
if header[2] == 1 then # Regular GPS Data
|
59
58
|
result = {}
|
@@ -73,7 +72,7 @@ module MmGPS
|
|
73
72
|
else
|
74
73
|
unless valid_crc16?(buf) then
|
75
74
|
raise MmGPSError.new("Unexpected packet type #{header[2]}",
|
76
|
-
{reason: :notype, packet:buf, crc:crc16(buf), type:header[2]})
|
75
|
+
{reason: :notype, packet:buf, crc:("%04X" % crc16(buf[0...-2])), type:header[2]})
|
77
76
|
end
|
78
77
|
end
|
79
78
|
return result
|
data/lib/mm_gps/version.rb
CHANGED
data/test.rb
CHANGED
@@ -7,26 +7,15 @@ BAUD = 115200 # SerialPort class does not support non-standard 500 kbps
|
|
7
7
|
beacon = MmGPS::Beacon.new(PORT, BAUD)
|
8
8
|
beacon.trap # installs signal handler for CTRL-C
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
rescue MmGPSError => e
|
14
|
-
puts "Packet Error: #{e.inspect}, reason: #{e.data[:reason]}"
|
15
|
-
puts "Packet: #{MmGPS.hexify(e.data[:packet])}"
|
16
|
-
rescue IOError => e
|
17
|
-
puts "Port closed #{e.inspect}"
|
18
|
-
exit
|
10
|
+
# Standard each loop. Type CTRL-C for interrupting it
|
11
|
+
beacon.each do |packet|
|
12
|
+
p packet
|
19
13
|
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
rescue IOError => e
|
29
|
-
puts "Port closed #{e.inspect}"
|
30
|
-
exit
|
31
|
-
end
|
32
|
-
end
|
15
|
+
|
16
|
+
# Use the enumerator:
|
17
|
+
beacon.reopen # Needed, since CTRL-C in previous example also closes the Serialport connection
|
18
|
+
enum = beacon.each # gets the Enumerator
|
19
|
+
p enum.take 10 # Next 10 packets from enum
|
20
|
+
|
21
|
+
puts "Exiting"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mm_gps
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paolo Bosetti
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: serialport
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- formulas.nb
|
88
88
|
- lib/mm_gps.rb
|
89
89
|
- lib/mm_gps/mm_gps_beacon.rb
|
90
|
+
- lib/mm_gps/mm_gps_buffer.rb
|
90
91
|
- lib/mm_gps/mm_gps_module.rb
|
91
92
|
- lib/mm_gps/version.rb
|
92
93
|
- mm_gps.gemspec
|