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 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