rplidar 0.1.2 → 0.1.3

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
  SHA256:
3
- metadata.gz: 9c00e8000624de8044d80c1ecdcb65f99d9f806d60d684267b6a067176cf8bf3
4
- data.tar.gz: c267e4e70b865ce84b0df3767e5c8ec43166a56e8bc678568e4e69c6eb455778
3
+ metadata.gz: 9e897b58884e73242ae2efa07042f6c5c2d2ff59bf493903690974053d102cfc
4
+ data.tar.gz: ade82a5c1ffefa00b96eadafa5fbf8ca2c9ab708db71619823d8f7479b93fd07
5
5
  SHA512:
6
- metadata.gz: 2b58e8eab346bee4bbea60436e363dd5f534e6ea473564904d7cc130bff9f8582ed4ab2c5eee59ab9450972d5db2bf857237941cbe800232440731bb47bd9313
7
- data.tar.gz: db1f7b6f72e4baf940ac2b2fe0827d86b749164c59d4e6d4d6d3db89af465b2fdb1d5d5a1ebc8d03802d85a82a4eacc8dc46b896d0f0648d596fcd9074339ca1
6
+ metadata.gz: f77cb72a0a2d985fcf649d2ec2547ffe8b7220a0b3638a55531629a9f9f3ef0ddb30fbf4cfade728a168056ce878837731d4e982481dd7df2537a7767900f0fa
7
+ data.tar.gz: 9495f13a140c70827670fe1b2dce6e15eee523e8f887c76382800fd1df75b278b7e0d34c45a921a6e3bc9e93f4468e2ebb7bba980b21e4a165e2617df3d407c4
data/.gitignore CHANGED
@@ -1,2 +1,12 @@
1
- /coverage
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
2
7
  /spec/examples.txt
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
@@ -1,8 +1,10 @@
1
- #Metrics/BlockLength:
2
- # Exclude:
3
- # - 'Rakefile'
4
- # - '**/*.rake'
5
- # - 'spec/**/*.rb'
1
+ Metrics/BlockLength:
2
+ Exclude:
3
+ - 'Rakefile'
4
+ - '**/*.rake'
5
+ - 'spec/**/*.rb'
6
+ - '**/*.gemspec'
7
+
6
8
  require: rubocop-rspec
7
9
 
8
10
  Layout/IndentArray:
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rplidar (0.1.0)
4
+ rplidar (0.1.3)
5
5
  rubyserial (~> 0.6)
6
6
 
7
7
  GEM
@@ -13,7 +13,7 @@ GEM
13
13
  simplecov
14
14
  url
15
15
  diff-lcs (1.3)
16
- docile (1.3.0)
16
+ docile (1.3.1)
17
17
  ffi (1.9.25)
18
18
  jaro_winkler (1.5.1)
19
19
  json (2.1.0)
@@ -23,20 +23,20 @@ GEM
23
23
  powerpack (0.1.2)
24
24
  rainbow (3.0.0)
25
25
  rake (10.5.0)
26
- rspec (3.7.0)
27
- rspec-core (~> 3.7.0)
28
- rspec-expectations (~> 3.7.0)
29
- rspec-mocks (~> 3.7.0)
30
- rspec-core (3.7.1)
31
- rspec-support (~> 3.7.0)
32
- rspec-expectations (3.7.0)
26
+ rspec (3.8.0)
27
+ rspec-core (~> 3.8.0)
28
+ rspec-expectations (~> 3.8.0)
29
+ rspec-mocks (~> 3.8.0)
30
+ rspec-core (3.8.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-expectations (3.8.1)
33
33
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.7.0)
35
- rspec-mocks (3.7.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-mocks (3.8.0)
36
36
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.7.0)
38
- rspec-support (3.7.1)
39
- rubocop (0.58.2)
37
+ rspec-support (~> 3.8.0)
38
+ rspec-support (3.8.0)
39
+ rubocop (0.59.1)
40
40
  jaro_winkler (~> 1.5.1)
41
41
  parallel (~> 1.10)
42
42
  parser (>= 2.5, != 2.5.1.1)
@@ -44,7 +44,7 @@ GEM
44
44
  rainbow (>= 2.2.2, < 4.0)
45
45
  ruby-progressbar (~> 1.7)
46
46
  unicode-display_width (~> 1.0, >= 1.0.1)
47
- rubocop-rspec (1.29.0)
47
+ rubocop-rspec (1.29.1)
48
48
  rubocop (>= 0.58.0)
49
49
  ruby-progressbar (1.10.0)
50
50
  rubyserial (0.6.0)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rplidar
2
2
 
3
- [![Build Status](https://semaphoreci.com/api/v1/yurykotlyarov/rplidar/branches/master/shields_badge.svg)](https://semaphoreci.com/yurykotlyarov/rplidar) [![codecov](https://codecov.io/gh/yura/rplidar/branch/master/graph/badge.svg)](https://codecov.io/gh/yura/rplidar) [![Maintainability](https://api.codeclimate.com/v1/badges/3e73393095982858c97b/maintainability)](https://codeclimate.com/github/yura/rplidar/maintainability) [![security](https://hakiri.io/github/yura/rplidar/master.svg)](https://hakiri.io/github/yura/rplidar/master)
3
+ [![Build Status](https://semaphoreci.com/api/v1/yurykotlyarov/rplidar/branches/master/shields_badge.svg)](https://semaphoreci.com/yurykotlyarov/rplidar) [![codecov](https://codecov.io/gh/yura/rplidar/branch/master/graph/badge.svg)](https://codecov.io/gh/yura/rplidar) [![Maintainability](https://api.codeclimate.com/v1/badges/3e73393095982858c97b/maintainability)](https://codeclimate.com/github/yura/rplidar/maintainability) [![security](https://hakiri.io/github/yura/rplidar/master.svg)](https://hakiri.io/github/yura/rplidar/master) [![Gem Version](https://badge.fury.io/rb/rplidar.svg)](https://badge.fury.io/rb/rplidar)
4
4
 
5
5
  Ruby implementation of SLAMTEK RPLIDAR A2M8 lidar.
6
6
 
@@ -25,7 +25,7 @@ Or install it yourself as:
25
25
  Run `bundle exec irb`
26
26
 
27
27
  ```ruby
28
- require './lib/rplidar'
28
+ require 'rplidar'
29
29
 
30
30
  # for Mac OS
31
31
  lidar = Rplidar::Driver.new('/dev/tty.SLAB_USBtoUART')
@@ -1,2 +1,15 @@
1
1
  require 'rplidar/driver'
2
+ require 'rplidar/response'
3
+ require 'rplidar/response_descriptor'
4
+ require 'rplidar/scan_data_response'
5
+ require 'rplidar/current_state_data_response'
6
+ require 'rplidar/device_info_data_response'
2
7
  require 'rplidar/version'
8
+
9
+ module Rplidar
10
+ # Lidar states
11
+ STATE_GOOD = 0
12
+ STATE_WARNING = 1
13
+ STATE_ERROR = 2
14
+
15
+ end
@@ -0,0 +1,16 @@
1
+ module Rplidar
2
+ # Implementation of response to the GET_HEALTH request.
3
+ class CurrentStateDataResponse < Response
4
+ def response
5
+ case raw_response[0]
6
+ when STATE_GOOD then { state: :good, error_code: error_code }
7
+ when STATE_WARNING then { state: :warning, error_code: error_code }
8
+ when STATE_ERROR then { state: :error, error_code: error_code }
9
+ end
10
+ end
11
+
12
+ def error_code
13
+ (raw_response[2] << 8) + raw_response[1]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ module Rplidar
2
+ class DeviceInfoDataResponse < Response
3
+ # RPLIDAR model ID.
4
+ def model
5
+ raw_response[0]
6
+ end
7
+
8
+ # Firmware version number, the minor value part.
9
+ def firmware_minor
10
+ raw_response[1]
11
+ end
12
+
13
+ # Firmware version number, the major value part.
14
+ def firmware_major
15
+ raw_response[2]
16
+ end
17
+
18
+ def firmware
19
+ "#{firmware_major}.#{firmware_minor}"
20
+ end
21
+
22
+ # Hardware version number.
23
+ def hardware
24
+ raw_response[3]
25
+ end
26
+
27
+ # 128bit unique serial number. When converting to text in hex,
28
+ # the Least Significant Byte prints first.
29
+ def serial_number
30
+ raw_response[4..-1].pack('c*').unpack('H*').first.upcase
31
+ end
32
+
33
+ def response
34
+ {
35
+ model: model, firmware: firmware,
36
+ hardware: hardware, serial_number: serial_number
37
+ }
38
+ end
39
+ end
40
+ end
@@ -3,13 +3,9 @@ require 'rubyserial'
3
3
  module Rplidar
4
4
  # Ruby implementation of driver of the SLAMTEC RPLIDAR A2.
5
5
  class Driver
6
- # Lidar states
7
- STATE_GOOD = 0
8
- STATE_WARNING = 1
9
- STATE_ERROR = 2
10
-
11
6
  # Commands
12
7
  COMMAND_GET_HEALTH = 0x52
8
+ COMMAND_GET_INFO = 0x50
13
9
  COMMAND_MOTOR_PWM = 0xF0
14
10
  COMMAND_SCAN = 0x20
15
11
  COMMAND_STOP = 0x25
@@ -17,11 +13,13 @@ module Rplidar
17
13
 
18
14
  COMMANDS_WITH_RESPONSE = [
19
15
  COMMAND_GET_HEALTH,
16
+ COMMAND_GET_INFO,
20
17
  COMMAND_SCAN
21
18
  ].freeze
22
19
 
23
20
  # Default length of responses
24
21
  RESPONSE_DESCRIPTOR_LENGTH = 7
22
+ GET_INFO_RESPONSE_LENGTH = 20
25
23
  SCAN_DATA_RESPONSE_LENGTH = 5
26
24
 
27
25
  UART_BAUD_RATE = 115_200
@@ -32,12 +30,14 @@ module Rplidar
32
30
 
33
31
  def current_state
34
32
  descriptor = command(COMMAND_GET_HEALTH)
35
- response = data_response(descriptor[:data_response_length])
36
- case response[0]
37
- when STATE_GOOD then [:good, []]
38
- when STATE_WARNING then [:warning, []]
39
- when STATE_ERROR then [:error, response[1..-1]]
40
- end
33
+ raw_response = read_response(descriptor[:data_response_length])
34
+ Rplidar::CurrentStateDataResponse.new(raw_response).response
35
+ end
36
+
37
+ def device_info
38
+ descriptor = command(COMMAND_GET_INFO)
39
+ raw_response = read_response(descriptor[:data_response_length])
40
+ Rplidar::DeviceInfoDataResponse.new(raw_response).response
41
41
  end
42
42
 
43
43
  def start_motor(pwm = 660)
@@ -121,84 +121,27 @@ module Rplidar
121
121
  binary_to_ints(string).reduce(:^)
122
122
  end
123
123
 
124
- # Format of Response Descriptor:
125
- #
126
- # Start Flag 1 Start Flag 2 Data Response Length Send Mode Data Type
127
- #
128
- # 1 byte (0xA5) 1 bytes (0x5A) 30 bits 2 bits 1 byte
129
124
  def response_descriptor
130
- response = data_response(RESPONSE_DESCRIPTOR_LENGTH)
131
-
132
- # TODO: check response headers
125
+ raw_response = read_response(RESPONSE_DESCRIPTOR_LENGTH)
126
+ Rplidar::ResponseDescriptor.new(raw_response).response
127
+ end
133
128
 
134
- {
135
- data_response_length: (response[4] << 16) +
136
- (response[3] << 8) + response[2],
137
- send_mode: response[5] >> 6,
138
- data_type: response[6]
139
- }
129
+ def scan_data_response
130
+ raw_response = read_response(SCAN_DATA_RESPONSE_LENGTH)
131
+ Rplidar::ScanDataResponse.new(raw_response).response
140
132
  end
141
133
 
142
- def data_response(length)
143
- start = Time.now
134
+ def read_response(length)
135
+ t = Time.now
144
136
  response = []
145
137
  while response.size < length
146
138
  byte = port.getbyte
147
139
  response << byte if byte
148
- raise 'Timeout while getting byte from the port' if Time.now - start > 2
140
+ raise 'Timeout while reading a byte from the port' if Time.now - t > 2
149
141
  end
150
142
  response
151
143
  end
152
144
 
153
- def scan_data_response
154
- response = data_response(SCAN_DATA_RESPONSE_LENGTH)
155
- check_data_response_header(response)
156
-
157
- {
158
- start: response[0][0] == 1,
159
- quality: quality(response),
160
- angle: angle(response),
161
- distance: distance(response)
162
- }
163
- end
164
-
165
- def check_data_response_header(response)
166
- unless correct_start_bit?(response)
167
- raise 'Inversed start bit of the data response ' \
168
- 'is not inverse of the start bit'
169
- end
170
-
171
- unless correct_check_bit?(response)
172
- raise 'Check bit of the data response is not equal ' \
173
- 'to 1'
174
- end
175
- end
176
-
177
- def correct_start_bit?(response)
178
- # start bit
179
- start = response[0][0]
180
- # inversed start bit
181
- inversed = response[0][1]
182
-
183
- (start == 1 && inversed.zero?) || (start.zero? && inversed == 1)
184
- end
185
-
186
- def correct_check_bit?(response)
187
- response[1][0] == 1
188
- end
189
-
190
- def quality(response)
191
- response[0] >> 2
192
- end
193
-
194
- def angle(response)
195
- ((response[2] << 7) + (response[1] >> 1)) / 64.0
196
- end
197
-
198
- def distance(response)
199
- ((response[4] << 8) + response[3]) / 4.0
200
- end
201
-
202
145
  def clear_port
203
146
  while port.getbyte
204
147
  end
@@ -0,0 +1,22 @@
1
+ module Rplidar
2
+ # Generic lidar response class
3
+ class Response
4
+ attr_reader :raw_response
5
+
6
+ def initialize(raw_response)
7
+ @raw_response = raw_response
8
+ check_response
9
+ end
10
+
11
+ def check_response
12
+ check_header
13
+ check_payload
14
+ end
15
+
16
+ def check_header; end
17
+
18
+ def check_payload; end
19
+
20
+ def response; end
21
+ end
22
+ end
@@ -0,0 +1,94 @@
1
+ module Rplidar
2
+ DATA_TYPE_DEVICE_INFO = 0x4
3
+
4
+ # Incapsulates Response Descriptor processing. Format of Response Descriptor:
5
+ #
6
+ # Start Flag 1 Start Flag 2 Data Response Length Send Mode Data Type
7
+ # 1 byte (0xA5) 1 bytes (0x5A) 30 bits 2 bits 1 byte
8
+ class ResponseDescriptor < Response
9
+ def check_response
10
+ check_header
11
+ check_payload
12
+ end
13
+
14
+ def check_header
15
+ unless correct_first_byte?
16
+ raise 'Wrong first byte of the response descriptor: ' \
17
+ "'#{int_to_hex(raw_response[0])}'"
18
+ end
19
+
20
+ unless correct_second_byte?
21
+ raise 'Wrong second byte of the response descriptor: ' \
22
+ "'#{int_to_hex(raw_response[1])}'"
23
+ end
24
+ end
25
+
26
+ def check_payload
27
+ unless correct_send_mode?
28
+ raise 'Wrong send mode value of the response descriptor: ' \
29
+ "'#{int_to_hex(send_mode)}'"
30
+ end
31
+
32
+ unless correct_data_type?
33
+ raise 'Wrong data type value of the response descriptor: ' \
34
+ "'#{int_to_hex(data_type)}'"
35
+ end
36
+ end
37
+
38
+ def correct_first_byte?
39
+ raw_response[0] == 0xA5
40
+ end
41
+
42
+ def correct_second_byte?
43
+ raw_response[1] == 0x5A
44
+ end
45
+
46
+ def correct_send_mode?
47
+ [0x0, 0x1].include?(send_mode)
48
+ end
49
+
50
+ def correct_data_type?
51
+ [0x6, 0x81, DATA_TYPE_DEVICE_INFO].include?(data_type)
52
+ end
53
+
54
+ def data_response_length
55
+ (raw_response[4] << 16) + (raw_response[3] << 8) + raw_response[2]
56
+ end
57
+
58
+ # The 2 bits Send Mode field describes the request/response mode
59
+ # of the current session. Values:
60
+ # * 0x0 - Single Request - Single Response mode, RPLIDAR will send
61
+ # only one data response packet in the current session.
62
+ # * 0x1 - Single Request - Multiple Response mode, RPLIDAR will
63
+ # continuously send out data response packets with the same format
64
+ # in the current session.
65
+ # * 0x2 and 0x3 are reserved for future use
66
+ def send_mode
67
+ raw_response[5] >> 6
68
+ end
69
+
70
+ # The 1byte Data Type describes the type of the incoming
71
+ # data response packets.
72
+ def data_type
73
+ raw_response[6]
74
+ end
75
+
76
+ def response
77
+ {
78
+ data_response_length: data_response_length,
79
+ send_mode: send_mode,
80
+ data_type: data_type
81
+ }
82
+ end
83
+
84
+ private
85
+
86
+ def int_to_hex(value)
87
+ if value
88
+ "0x#{value.to_s(16).upcase}"
89
+ else
90
+ value.inspect
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,52 @@
1
+ module Rplidar
2
+ # Data response for one scan measurement
3
+ class ScanDataResponse < Response
4
+ def check_header
5
+ unless correct_start_bit?
6
+ raise 'Inversed start bit of the data response ' \
7
+ 'is not inverse of the start bit'
8
+ end
9
+
10
+ raise 'Check bit of the data response is not equal to 1' \
11
+ unless correct_check_bit?
12
+ end
13
+
14
+ def correct_start_bit?
15
+ # start bit
16
+ start = raw_response[0][0]
17
+ # inversed start bit
18
+ inversed = raw_response[0][1]
19
+
20
+ (start == 1 && inversed.zero?) || (start.zero? && inversed == 1)
21
+ end
22
+
23
+ def correct_check_bit?
24
+ raw_response[1][0] == 1
25
+ end
26
+
27
+ def start?
28
+ raw_response[0][0] == 1
29
+ end
30
+
31
+ def quality
32
+ raw_response[0] >> 2
33
+ end
34
+
35
+ def angle
36
+ ((raw_response[2] << 7) + (raw_response[1] >> 1)) / 64.0
37
+ end
38
+
39
+ def distance
40
+ ((raw_response[4] << 8) + raw_response[3]) / 4.0
41
+ end
42
+
43
+ def response
44
+ {
45
+ start: start?,
46
+ quality: quality,
47
+ angle: angle,
48
+ distance: distance
49
+ }
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
1
  module Rplidar
2
- VERSION = '0.1.2'.freeze
2
+ VERSION = '0.1.3'.freeze
3
3
  end
@@ -0,0 +1,27 @@
1
+ # Data Responses
2
+ DR_HEALTH_GOOD = [0, 0, 0].freeze
3
+ DR_HEALTH_WARN = [1, 0, 0].freeze
4
+ DR_HEALTH_ERR = [2, 3, 5].freeze
5
+
6
+ RSpec.describe Rplidar::CurrentStateDataResponse do
7
+ let(:data_response) { described_class.new([0, 0, 0]) }
8
+
9
+ describe '#response' do
10
+ subject(:response) { data_response.response }
11
+
12
+ it 'returns :good if lidar is in Good (0) state' do
13
+ allow(data_response).to receive(:raw_response).and_return(DR_HEALTH_GOOD)
14
+ expect(response).to eq(state: :good, error_code: 0)
15
+ end
16
+
17
+ it 'returns :warning if lidar is in Warning (1) state' do
18
+ allow(data_response).to receive(:raw_response).and_return(DR_HEALTH_WARN)
19
+ expect(response).to eq(state: :warning, error_code: 0)
20
+ end
21
+
22
+ it 'returns :error if lidar is in Error (2) state' do
23
+ allow(data_response).to receive(:raw_response).and_return(DR_HEALTH_ERR)
24
+ expect(response).to eq(state: :error, error_code: 1283)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,65 @@
1
+ DR_GET_INFO = [
2
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
3
+ ].freeze
4
+
5
+ DR_GET_INFO_REAL = [
6
+ 40, 24, 1, 4, 168, 226, 154, 240, 197, 226,
7
+ 157, 210, 182, 227, 157, 245, 43, 49, 49, 22
8
+ ].freeze
9
+
10
+ RSpec.describe Rplidar::DeviceInfoDataResponse do
11
+ let(:response) { described_class.new(DR_GET_INFO) }
12
+
13
+ describe '#model' do
14
+ it 'returns model' do
15
+ expect(response.model).to eq(1)
16
+ end
17
+ end
18
+
19
+ describe '#firmware_minor' do
20
+ it 'returns firmware minor' do
21
+ expect(response.firmware_minor).to eq(2)
22
+ end
23
+ end
24
+
25
+ describe '#firmware_major' do
26
+ it 'returns firmware major' do
27
+ expect(response.firmware_major).to eq(3)
28
+ end
29
+ end
30
+
31
+ describe '#firmware' do
32
+ it 'returns firmware version' do
33
+ expect(response.firmware).to eq('3.2')
34
+ end
35
+ end
36
+
37
+ describe '#hardware' do
38
+ it 'returns hardware' do
39
+ expect(response.hardware).to eq(4)
40
+ end
41
+ end
42
+
43
+ describe '#serial_number' do
44
+ it 'returns serial_number' do
45
+ expect(response.serial_number).to eq('05060708090A0B0C0D0E0F1011121314')
46
+ end
47
+ end
48
+
49
+ describe '#response' do
50
+ it 'returns device info' do
51
+ expect(response.response).to eq(
52
+ model: 1, firmware: '3.2',
53
+ hardware: 4, serial_number: '05060708090A0B0C0D0E0F1011121314'
54
+ )
55
+ end
56
+
57
+ it 'returns real device info' do
58
+ allow(response).to receive(:raw_response).and_return(DR_GET_INFO_REAL)
59
+ expect(response.response).to eq(
60
+ model: 40, firmware: '1.24',
61
+ hardware: 4, serial_number: 'A8E29AF0C5E29DD2B6E39DF52B313116'
62
+ )
63
+ end
64
+ end
65
+ end
@@ -10,11 +10,6 @@ RAW_RD_SCAN = ascii("\xA5Z\x05\x00\x00@\x81")
10
10
  RD_GET_HEALTH = [165, 90, 3, 0, 0, 0, 6].freeze
11
11
  RD_SCAN = [165, 90, 5, 0, 0, 64, 129].freeze
12
12
 
13
- # Data Responses
14
- DR_HEALTH_GOOD = [0, 0, 0].freeze
15
- DR_HEALTH_WARNING = [1, 0, 0].freeze
16
- DR_HEALTH_ERROR = [2, 3, 5].freeze
17
-
18
13
  DR_SCAN = [62, 63, 3, 117, 4].freeze
19
14
 
20
15
  RSpec.describe Rplidar::Driver do
@@ -34,7 +29,7 @@ RSpec.describe Rplidar::Driver do
34
29
  allow(lidar).to receive(:command)
35
30
  .with(0x52)
36
31
  .and_return(data_response_length: 3)
37
- allow(lidar).to receive(:data_response)
32
+ allow(lidar).to receive(:read_response)
38
33
  .with(3)
39
34
  .and_return([0, 0, 0])
40
35
  end
@@ -46,31 +41,29 @@ RSpec.describe Rplidar::Driver do
46
41
 
47
42
  it 'reads data_response' do
48
43
  current_state
49
- expect(lidar).to have_received(:data_response).with(3)
44
+ expect(lidar).to have_received(:read_response).with(3)
50
45
  end
51
46
 
52
47
  it 'returns :good if lidar is in Good (0) state' do
53
- allow(lidar).to receive(:data_response)
48
+ allow(lidar).to receive(:read_response)
54
49
  .with(3)
55
- .and_return(DR_HEALTH_GOOD)
56
- expect(current_state).to eq([:good, []])
50
+ .and_return([0, 0, 0])
51
+ expect(current_state).to eq(state: :good, error_code: 0)
57
52
  end
58
53
 
59
54
  it 'returns :warning if lidar is in Warning (1) state' do
60
- allow(lidar).to receive(:data_response)
55
+ allow(lidar).to receive(:read_response)
61
56
  .with(3)
62
- .and_return(DR_HEALTH_WARNING)
63
- expect(current_state).to eq([:warning, []])
57
+ .and_return([1, 0, 0])
58
+ expect(current_state).to eq(state: :warning, error_code: 0)
64
59
  end
65
60
 
66
61
  it 'returns :error if lidar is in Error (2) state' do
67
- allow(lidar).to receive(:data_response)
62
+ allow(lidar).to receive(:read_response)
68
63
  .with(3)
69
- .and_return(DR_HEALTH_ERROR)
70
- expect(current_state).to eq([:error, [3, 5]])
64
+ .and_return([2, 3, 5])
65
+ expect(current_state).to eq(state: :error, error_code: 1283)
71
66
  end
72
-
73
- it 'concatenates error code bytes'
74
67
  end
75
68
 
76
69
  describe '#start_motor' do
@@ -209,25 +202,25 @@ RSpec.describe Rplidar::Driver do
209
202
 
210
203
  describe '#response_descriptor' do
211
204
  before do
212
- allow(lidar).to receive(:data_response)
205
+ allow(lidar).to receive(:read_response)
213
206
  .with(7)
214
207
  .and_return(RD_GET_HEALTH)
215
208
  end
216
209
 
217
210
  it 'reads 7 bytes from the port' do
218
211
  lidar.response_descriptor
219
- expect(lidar).to have_received(:data_response).with(7)
212
+ expect(lidar).to have_received(:read_response).with(7)
220
213
  end
221
214
 
222
215
  it 'processes GET_HEALTH response descriptor correctly' do
223
- allow(lidar).to receive(:data_response)
216
+ allow(lidar).to receive(:read_response)
224
217
  .with(7).and_return(RD_GET_HEALTH)
225
218
  expect(lidar.response_descriptor).to \
226
219
  eq(data_response_length: 3, send_mode: 0, data_type: 6)
227
220
  end
228
221
 
229
222
  it 'processes scan response descriptor correctly' do
230
- allow(lidar).to receive(:data_response)
223
+ allow(lidar).to receive(:read_response)
231
224
  .with(7).and_return(RD_SCAN)
232
225
  expect(lidar.response_descriptor).to \
233
226
  eq(data_response_length: 5, send_mode: 1, data_type: 129)
@@ -258,27 +251,27 @@ RSpec.describe Rplidar::Driver do
258
251
  end
259
252
  end
260
253
 
261
- describe '#data_response' do
262
- subject(:data_response) { lidar.data_response(5) }
254
+ describe '#read_response' do
255
+ subject(:read_response) { lidar.read_response(5) }
263
256
 
264
257
  before do
265
258
  allow(port).to receive(:getbyte).and_return(1, 55, 88, 111, 222, 111)
266
259
  end
267
260
 
268
261
  it 'reads bytes from the port' do
269
- data_response
262
+ read_response
270
263
  expect(port).to have_received(:getbyte).exactly(5).times
271
264
  end
272
265
 
273
266
  it 'returns all read bytes' do
274
- expect(data_response).to eq([1, 55, 88, 111, 222])
267
+ expect(read_response).to eq([1, 55, 88, 111, 222])
275
268
  end
276
269
 
277
270
  it 'raises timeout exception if read takes more than 2 seconds' do
278
271
  allow(port).to receive(:getbyte).and_return(1, 2, 3, nil)
279
272
  expect do
280
- data_response
281
- end.to raise_error('Timeout while getting byte from the port')
273
+ read_response
274
+ end.to raise_error('Timeout while reading a byte from the port')
282
275
  end
283
276
  end
284
277
 
@@ -286,47 +279,21 @@ RSpec.describe Rplidar::Driver do
286
279
  subject(:scan_data_response) { lidar.scan_data_response }
287
280
 
288
281
  before do
289
- allow(lidar).to receive(:data_response).with(5).and_return(DR_SCAN)
290
- allow(lidar).to receive(:check_data_response_header).with(DR_SCAN)
291
- allow(lidar).to receive(:angle).with(DR_SCAN).and_return(111)
292
- allow(lidar).to receive(:distance).with(DR_SCAN).and_return(222)
293
- allow(lidar).to receive(:quality).with(DR_SCAN).and_return(333)
294
- end
295
-
296
- it 'reads data_response' do
297
- scan_data_response
298
- expect(lidar).to have_received(:data_response).with(5)
282
+ allow(lidar).to receive(:read_response).with(5).and_return(DR_SCAN)
299
283
  end
300
284
 
301
- it 'checks headers' do
285
+ it 'reads response' do
302
286
  scan_data_response
303
- expect(lidar).to have_received(:check_data_response_header).with(DR_SCAN)
287
+ expect(lidar).to have_received(:read_response).with(5)
304
288
  end
305
289
 
306
290
  it 'returns hash with processed values' do
307
291
  expect(scan_data_response).to eq(
308
- start: false, angle: 111, distance: 222, quality: 333
292
+ start: false, angle: 6.484375, distance: 285.25, quality: 15
309
293
  )
310
294
  end
311
295
  end
312
296
 
313
- describe '#check_data_response_header' do
314
- it 'raises inversed start flag bit is not inverse of the start flag bit' do
315
- [[[1, 1]], [[0, 0]], [[1, -2]], [[0, -1]]].each do |wrong_response|
316
- expect { lidar.check_data_response_header(wrong_response) }.to \
317
- raise_error('Inversed start bit of the data response ' \
318
- 'is not inverse of the start bit')
319
- end
320
- end
321
-
322
- it 'raises an exception if 3rd bit is not equal to 1' do
323
- [[[1, 0], [0]], [[0, 1], [2]]].each do |wrong_response|
324
- expect { lidar.check_data_response_header(wrong_response) }.to \
325
- raise_error('Check bit of the data response is not equal to 1')
326
- end
327
- end
328
- end
329
-
330
297
  describe '#close' do
331
298
  subject(:close) { lidar.close }
332
299
 
@@ -365,46 +332,6 @@ RSpec.describe Rplidar::Driver do
365
332
  end
366
333
  end
367
334
 
368
- describe '#correct_start_bit?' do
369
- it 'raises inversed start flag bit is not inverse of the start flag bit' do
370
- [[[1, 1]], [[0, 0]], [[1, -2]], [[0, -1]]].each do |response|
371
- expect(lidar.correct_start_bit?(response)).to be_falsy
372
- end
373
- end
374
- end
375
-
376
- describe '#correct_check_bit?' do
377
- it 'returns true if first bit of the second byte is equal to 1' do
378
- [[0, 1], [0, 3], [0, 5], [0, 255]].each do |response|
379
- expect(lidar.correct_check_bit?(response)).to be_truthy
380
- end
381
- end
382
-
383
- it 'returns false if 1st bit of the 2nd byte is not equal to 1' do
384
- [[0, 0], [0, 2], [0, 254]].each do |response|
385
- expect(lidar.correct_check_bit?(response)).to be_falsy
386
- end
387
- end
388
- end
389
-
390
- describe '#angle' do
391
- it 'processes angle from the 2nd and 3rd bytes' do
392
- expect(lidar.angle([62, 155, 2, 112, 4])).to eq(5.203125)
393
- end
394
- end
395
-
396
- describe '#distance' do
397
- it 'processes angle from the 4th and 5th bytes' do
398
- expect(lidar.distance([62, 155, 2, 112, 4])).to eq(284)
399
- end
400
- end
401
-
402
- describe '#quality' do
403
- it 'processes quantity from the 1st bit' do
404
- expect(lidar.quality([62, 155, 2, 112, 4])).to eq(15)
405
- end
406
- end
407
-
408
335
  describe '#clear_port' do
409
336
  subject(:clear_port) { lidar.clear_port }
410
337
 
@@ -0,0 +1,88 @@
1
+ RD_GET_HEALTH = [165, 90, 3, 0, 0, 0, 6].freeze
2
+ RD_SCAN = [165, 90, 5, 0, 0, 64, 129].freeze
3
+
4
+ RSpec.describe Rplidar::ResponseDescriptor do
5
+ let(:descriptor) { described_class.new(RD_SCAN) }
6
+
7
+ describe '#check_header' do
8
+ subject(:check_header) { descriptor.check_header }
9
+
10
+ it 'raises "Wrong first byte of the response descriptor" exception' do
11
+ allow(descriptor).to receive(:raw_response).and_return([90, 0, 0, 0, 0])
12
+ expect { check_header }.to \
13
+ raise_error("Wrong first byte of the response descriptor: '0x5A'")
14
+ end
15
+
16
+ it 'raises exception for nils' do
17
+ allow(descriptor).to receive(:raw_response).and_return([nil, nil, nil])
18
+ expect { check_header }.to \
19
+ raise_error("Wrong first byte of the response descriptor: 'nil'")
20
+ end
21
+
22
+ it 'raises "Wrong second byte of the response descriptor" exception' do
23
+ allow(descriptor).to receive(:raw_response).and_return([165, 165, 0, 0])
24
+ expect { check_header }.to \
25
+ raise_error("Wrong second byte of the response descriptor: '0xA5'")
26
+ end
27
+ end
28
+
29
+ describe '#check_payload' do
30
+ subject(:check_payload) { descriptor.check_payload }
31
+
32
+ it 'raises "Wrong send mode value of the response descriptor" exception' do
33
+ allow(descriptor).to \
34
+ receive(:raw_response).and_return([165, 90, 3, 0, 0, 128])
35
+ expect { check_payload }.to \
36
+ raise_error("Wrong send mode value of the response descriptor: '0x2'")
37
+ end
38
+
39
+ it 'raises "Wrong data type value of the response descriptor" exception' do
40
+ allow(descriptor).to \
41
+ receive(:raw_response).and_return([165, 90, 3, 0, 0, 64, 1])
42
+ expect { check_payload }.to \
43
+ raise_error("Wrong data type value of the response descriptor: '0x1'")
44
+ end
45
+ end
46
+
47
+ describe '#data_response_length' do
48
+ subject(:data_response_length) { descriptor.data_response_length }
49
+
50
+ it 'returns data response length for GET_HEALTH request' do
51
+ allow(descriptor).to \
52
+ receive(:raw_response).and_return(RD_GET_HEALTH)
53
+ expect(data_response_length).to eq(3)
54
+ end
55
+
56
+ it 'returns data response length for SCAN request' do
57
+ allow(descriptor).to \
58
+ receive(:raw_response).and_return(RD_SCAN)
59
+ expect(data_response_length).to eq(5)
60
+ end
61
+ end
62
+
63
+ describe '#send_mode' do
64
+ subject(:send_mode) { descriptor.send_mode }
65
+
66
+ it 'returns Single Request - Single Response for GET_HEALTH request' do
67
+ allow(descriptor).to receive(:raw_response).and_return(RD_GET_HEALTH)
68
+ expect(send_mode).to eq(0x0)
69
+ end
70
+
71
+ it 'returns Single Request - Multiple Response for SCAN request' do
72
+ allow(descriptor).to receive(:raw_response).and_return(RD_SCAN)
73
+ expect(send_mode).to eq(0x1)
74
+ end
75
+ end
76
+
77
+ describe '#response' do
78
+ subject(:response) { descriptor.response }
79
+
80
+ it 'returns processed values for SCAN request' do
81
+ expect(response).to eq(
82
+ data_response_length: 5,
83
+ send_mode: 1,
84
+ data_type: 0x81
85
+ )
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Rplidar::Response do
4
+ let(:response) { described_class.new([1, 2, 3, 4, 5]) }
5
+
6
+ describe '.new' do
7
+ before do
8
+ allow_any_instance_of(described_class).to receive(:check_response)
9
+ end
10
+
11
+ it 'creates new instance of Rplidar::Response' do
12
+ expect(response.raw_response).to eq([1, 2, 3, 4, 5])
13
+ end
14
+
15
+ it 'checks response' do
16
+ expect(response).to have_received(:check_response)
17
+ end
18
+ end
19
+
20
+ describe '#check_response' do
21
+ subject(:check_response) { response.check_response }
22
+
23
+ before do
24
+ allow(response).to receive(:check_header)
25
+ allow(response).to receive(:check_payload)
26
+ end
27
+
28
+ it 'calls #check_header' do
29
+ check_response
30
+ expect(response).to have_received(:check_header)
31
+ end
32
+
33
+ it 'calls #check_payload' do
34
+ check_response
35
+ expect(response).to have_received(:check_payload)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,111 @@
1
+ RSpec.describe Rplidar::ScanDataResponse do
2
+ let(:raw_response) { [62, 155, 2, 112, 4] }
3
+ let(:response) { described_class.new(raw_response) }
4
+
5
+ describe '.new' do
6
+ it 'checks header' do
7
+ allow_any_instance_of(described_class).to receive(:check_header)
8
+ response
9
+ expect(response).to have_received(:check_header)
10
+ end
11
+ end
12
+
13
+ describe '#check_header' do
14
+ subject(:check_header) { response.check_header }
15
+
16
+ before do
17
+ allow(response).to receive(:correct_start_bit?).and_return(true)
18
+ allow(response).to receive(:correct_check_bit?).and_return(true)
19
+ end
20
+
21
+ it 'does not raise any exception if header bits are correct' do
22
+ expect { check_header }.not_to raise_error
23
+ end
24
+
25
+ it 'raises inversed start flag bit is not inverse of the start flag bit' do
26
+ allow(response).to receive(:correct_start_bit?).and_return(false)
27
+
28
+ expect { check_header }.to \
29
+ raise_error('Inversed start bit of the data response ' \
30
+ 'is not inverse of the start bit')
31
+ end
32
+
33
+ it 'raises an exception if 3rd bit is not equal to 1' do
34
+ allow(response).to receive(:correct_check_bit?).and_return(false)
35
+
36
+ expect { response.check_header }.to \
37
+ raise_error('Check bit of the data response is not equal to 1')
38
+ end
39
+ end
40
+
41
+ describe '#correct_start_bit?' do
42
+ it 'returns true if 1st bit of 1st byte is 1 and 2nd bit is 0' do
43
+ allow(response).to receive(:raw_response).and_return([0b01])
44
+ expect(response).to be_correct_start_bit
45
+ end
46
+
47
+ it 'returns true if 1st bit of 1st byte is 0 and 2nd bit is 1' do
48
+ allow(response).to receive(:raw_response).and_return([0b110])
49
+ expect(response).to be_correct_start_bit
50
+ end
51
+
52
+ it 'returns false if both 1st bit and 2nd one of the 1st byte are 1s' do
53
+ allow(response).to receive(:raw_response).and_return([0b1011])
54
+ expect(response).not_to be_correct_start_bit
55
+ end
56
+
57
+ it 'returns false if both 1st bit and 2nd one of the 1st byte are 0s' do
58
+ allow(response).to receive(:raw_response).and_return([0b10100])
59
+ expect(response).not_to be_correct_start_bit
60
+ end
61
+ end
62
+
63
+ describe '#correct_check_bit?' do
64
+ it 'returns true if 1st bit of the 2nd byte is equal to 1' do
65
+ allow(response).to receive(:raw_response).and_return([0b10101, 0b101])
66
+ expect(response).to be_correct_check_bit
67
+ end
68
+
69
+ it 'returns false if 1st bit of the 2nd byte is not equal to 1' do
70
+ allow(response).to receive(:raw_response).and_return([0b10101, 0b10])
71
+ expect(response).not_to be_correct_check_bit
72
+ end
73
+ end
74
+
75
+ describe '#start?' do
76
+ it 'returns false if 1st bit of the 1st byte is equal to 0' do
77
+ expect(response).not_to be_start
78
+ end
79
+
80
+ it 'returns true if 1st bit of the 1st byte is equal to 1' do
81
+ allow(response).to receive(:raw_response).and_return([0b1011])
82
+ expect(response).to be_start
83
+ end
84
+ end
85
+
86
+ describe '#angle' do
87
+ it 'processes angle from the 2nd and 3rd bytes' do
88
+ expect(response.angle).to eq(5.203125)
89
+ end
90
+ end
91
+
92
+ describe '#distance' do
93
+ it 'processes angle from the 4th and 5th bytes' do
94
+ expect(response.distance).to eq(284)
95
+ end
96
+ end
97
+
98
+ describe '#quality' do
99
+ it 'processes quantity from the 1st bit' do
100
+ expect(response.quality).to eq(15)
101
+ end
102
+ end
103
+
104
+ describe '#response' do
105
+ it 'returns a hash with response' do
106
+ expect(response.response).to eq(
107
+ start: false, angle: 5.203125, distance: 284, quality: 15
108
+ )
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.describe Rplidar do
2
+ it 'has a version number' do
3
+ expect(Rplidar::VERSION).not_to be nil
4
+ end
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rplidar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yury Kotlyarov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-13 00:00:00.000000000 Z
11
+ date: 2018-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyserial
@@ -112,10 +112,21 @@ files:
112
112
  - bin/console
113
113
  - bin/setup
114
114
  - lib/rplidar.rb
115
+ - lib/rplidar/current_state_data_response.rb
116
+ - lib/rplidar/device_info_data_response.rb
115
117
  - lib/rplidar/driver.rb
118
+ - lib/rplidar/response.rb
119
+ - lib/rplidar/response_descriptor.rb
120
+ - lib/rplidar/scan_data_response.rb
116
121
  - lib/rplidar/version.rb
117
122
  - rplidar.gemspec
123
+ - spec/rplidar/current_state_data_response_spec.rb
124
+ - spec/rplidar/device_info_data_response_spec.rb
118
125
  - spec/rplidar/driver_spec.rb
126
+ - spec/rplidar/response_descriptor_spec.rb
127
+ - spec/rplidar/response_spec.rb
128
+ - spec/rplidar/scan_data_response_spec.rb
129
+ - spec/rplidar_spec.rb
119
130
  - spec/spec_helper.rb
120
131
  homepage: http://github.com/yura/rplidar
121
132
  licenses: