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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f68f04c6955bd3be0fac1c0e6c9f6744efb2463a
4
- data.tar.gz: 44d139c72d2ec7fec9eba8a1b9f12c72659bdc5c
3
+ metadata.gz: d54d7a04b331b5339f0e68b21e903101424a9f9e
4
+ data.tar.gz: b07d30015c9e27d411154880d00b23f1afd6cb00
5
5
  SHA512:
6
- metadata.gz: d8c3dc05456bd59a303cae8b5d4cd082e0b51a82c3a8533d0308c4ba0202a7cb67c254fb63cce29f1397e9a1f89961616c20f6f7eb674db5e1b6fdaa1c1311b3
7
- data.tar.gz: 51b785d6a816b966cb183f5235346ced4496dc510a020e2472994c7181ed7c6580db6ef6067d4ac1c8fdfa79b6986660dc29dae3fabbf13c5a0db0d0c9ebe07d
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
- puts "Syncing..."
36
- begin
37
- beacon.sync # discards any byte until the starting sequence "\xFFG" arrives
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
- puts "Reading..."
47
- while not beacon.closed? do
48
- begin
49
- p beacon.get_packet
50
- rescue MmGPSError => e
51
- puts "Packet Error: #{e.inspect}"
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
@@ -1,6 +1,7 @@
1
1
  require 'serialport'
2
2
  require 'mm_gps/version'
3
3
  require 'mm_gps/mm_gps_module'
4
+ require 'mm_gps/mm_gps_buffer'
4
5
  require 'mm_gps/mm_gps_beacon'
5
6
  require 'mm_gps/mm_gps'
6
7
 
@@ -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
- START_TOKEN = "\xFFG".force_encoding(Encoding::BINARY)
7
- EMPTY = ''.force_encoding(Encoding::BINARY)
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
- @sp = SerialPort.new(port, "baud" => baud)
18
- @sp.read_timeout = 1000 #1 sec
19
- @sp.binmode
20
- @last_pkt = ''.force_encoding(Encoding::BINARY)
33
+ @port, @baud = port, baud
34
+ self.open
21
35
  end
22
36
 
23
- # Istalls a signal handler for the given signal, default to SIGINT,
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 marrker is
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
- buf = EMPTY
52
- begin
53
- buf << (@sp.read(1) || EMPTY)
54
- buf = buf[-2..-1] if buf.size > 2
55
- end while buf != START_TOKEN
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
- # @return [String] a byte stream encoded with Encoding::BINARY
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
- buf = START_TOKEN.dup
63
- while true do
64
- char = @sp.read(1)
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 = buf[0...-2]
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 `%I(ts x y z f).zip(payload).to_h`
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module MmGPS
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
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
- puts "Syncing..."
11
- begin
12
- beacon.sync # discards any byte until the starting sequence "\xFFG" arrives
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
- puts "Reading..."
22
- while not beacon.closed? do
23
- begin
24
- p beacon.get_packet
25
- rescue MmGPSError => e
26
- puts "Packet Error: #{e.inspect}, reason: #{e.data[:reason]}"
27
- puts "Packet: #{MmGPS.hexify(e.data[:packet])}"
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.1.4
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-19 00:00:00.000000000 Z
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