mm_gps 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|