crubyflie 0.1.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 +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +674 -0
- data/README.md +99 -0
- data/Rakefile +15 -0
- data/bin/crubyflie +85 -0
- data/configs/joystick_default.yaml +48 -0
- data/crubyflie.gemspec +50 -0
- data/examples/params_and_logging.rb +87 -0
- data/lib/crubyflie/crazyflie/commander.rb +54 -0
- data/lib/crubyflie/crazyflie/console.rb +67 -0
- data/lib/crubyflie/crazyflie/log.rb +383 -0
- data/lib/crubyflie/crazyflie/log_conf.rb +57 -0
- data/lib/crubyflie/crazyflie/param.rb +220 -0
- data/lib/crubyflie/crazyflie/toc.rb +239 -0
- data/lib/crubyflie/crazyflie/toc_cache.rb +87 -0
- data/lib/crubyflie/crazyflie.rb +282 -0
- data/lib/crubyflie/crazyradio/crazyradio.rb +301 -0
- data/lib/crubyflie/crazyradio/radio_ack.rb +48 -0
- data/lib/crubyflie/crubyflie_logger.rb +74 -0
- data/lib/crubyflie/driver/crtp_packet.rb +146 -0
- data/lib/crubyflie/driver/radio_driver.rb +333 -0
- data/lib/crubyflie/exceptions.rb +36 -0
- data/lib/crubyflie/input/input_reader.rb +168 -0
- data/lib/crubyflie/input/joystick_input_reader.rb +280 -0
- data/lib/crubyflie/version.rb +22 -0
- data/lib/crubyflie.rb +31 -0
- data/spec/commander_spec.rb +67 -0
- data/spec/console_spec.rb +76 -0
- data/spec/crazyflie_spec.rb +176 -0
- data/spec/crazyradio_spec.rb +226 -0
- data/spec/crtp_packet_spec.rb +79 -0
- data/spec/crubyflie_logger_spec.rb +39 -0
- data/spec/crubyflie_spec.rb +20 -0
- data/spec/input_reader_spec.rb +136 -0
- data/spec/joystick_cfg.yaml +48 -0
- data/spec/joystick_input_reader_spec.rb +238 -0
- data/spec/log_spec.rb +266 -0
- data/spec/param_spec.rb +166 -0
- data/spec/radio_ack_spec.rb +43 -0
- data/spec/radio_driver_spec.rb +227 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/toc_cache_spec.rb +87 -0
- data/spec/toc_spec.rb +187 -0
- data/tools/sdl-joystick-axis.rb +69 -0
- metadata +222 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2013 Hector Sanjuan
|
3
|
+
|
4
|
+
# This file is part of Crubyflie.
|
5
|
+
|
6
|
+
# Crubyflie is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
|
11
|
+
# Crubyflie is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
|
18
|
+
|
19
|
+
require 'crazyradio/radio_ack'
|
20
|
+
|
21
|
+
describe RadioAck do
|
22
|
+
describe "#initialize" do
|
23
|
+
it "should initialize a RadioAck" do
|
24
|
+
rack = RadioAck.new(true, 1, 5, [1,2,3])
|
25
|
+
rack.ack.should == true
|
26
|
+
rack.powerDet.should == 1
|
27
|
+
rack.retry_count.should == 5
|
28
|
+
rack.data.should == [1,2,3]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
describe "#from_raw" do
|
32
|
+
it "should create a radio ack from raw USB data" do
|
33
|
+
header = 0b11110101 # ack = 1, powerDet = 0, retry_c = 1111
|
34
|
+
data = [0xFF] * 31
|
35
|
+
packet = [header].concat(data)
|
36
|
+
rack = RadioAck.from_raw(packet.pack('C*'))
|
37
|
+
rack.ack.should == true
|
38
|
+
rack.powerDet.should == false
|
39
|
+
rack.retry_count.should == 15
|
40
|
+
rack.data == data
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2013 Hector Sanjuan
|
3
|
+
|
4
|
+
# This file is part of Crubyflie.
|
5
|
+
|
6
|
+
# Crubyflie is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
|
11
|
+
# Crubyflie is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
|
18
|
+
|
19
|
+
require 'driver/radio_driver'
|
20
|
+
|
21
|
+
describe RadioDriver do
|
22
|
+
before :each do
|
23
|
+
@radiodriver = RadioDriver.new()
|
24
|
+
@link_error_cb = Proc.new {|m| m}
|
25
|
+
@link_quality_cb = Proc.new {|m| m}
|
26
|
+
@connect_params = ['radio://0/2/1M',{
|
27
|
+
:link_quality_cb => @link_quality_cb,
|
28
|
+
:link_error_cb => @link_error_cb
|
29
|
+
}]
|
30
|
+
@crazyradio = double("Crazyradio")
|
31
|
+
allow(@crazyradio).to receive(:close)
|
32
|
+
@ack = RadioAck.new(true, true, 3, [1,2,3])
|
33
|
+
allow(@crazyradio).to receive(:send_packet).and_return(@ack)
|
34
|
+
allow(Crazyradio).to receive(:new).and_return(@crazyradio)
|
35
|
+
allow(Crazyradio).to receive(:factory).and_return(@crazyradio)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#initialize" do
|
40
|
+
it "should intialize the radio driver" do
|
41
|
+
rd = RadioDriver.new()
|
42
|
+
rd.should be_an_instance_of RadioDriver
|
43
|
+
rd.uri.should be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#connect" do
|
48
|
+
it "should connect" do
|
49
|
+
expect(@radiodriver).to receive(:start_radio_thread)
|
50
|
+
|
51
|
+
@radiodriver.connect(*@connect_params)
|
52
|
+
|
53
|
+
expected_retries = RadioDriver::RETRIES_BEFORE_DISCONNECT
|
54
|
+
expected_queue_size = RadioDriver::OUT_QUEUE_MAX_SIZE
|
55
|
+
@radiodriver.retries_before_disconnect.should == expected_retries
|
56
|
+
@radiodriver.out_queue_max_size.should == expected_queue_size
|
57
|
+
@radiodriver.disconnect()
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it "should raise an exception if the link is active" do
|
62
|
+
@radiodriver.connect(*@connect_params)
|
63
|
+
mesg = "Active link to radio://0/2/1M. Disconnect first"
|
64
|
+
expect {
|
65
|
+
@radiodriver.connect('radio://1/2/2M')
|
66
|
+
}.to raise_exception(OpenLink, mesg)
|
67
|
+
@radiodriver.disconnect()
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should raise an exception a callback is missing" do
|
71
|
+
expect {
|
72
|
+
@radiodriver.connect('radio://1/2/2M', {
|
73
|
+
:link_quality_cb => Proc.new {}
|
74
|
+
})
|
75
|
+
}.to raise_exception(CallbackMissing,
|
76
|
+
"Callback link_error_cb mandatory")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#disconnect" do
|
81
|
+
it "should disconnect if it is not connected" do
|
82
|
+
expect(@crazyradio).not_to receive(:close)
|
83
|
+
@radiodriver.disconnect()
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should disconnect if it is connected" do
|
87
|
+
expect(@crazyradio).to receive(:close)
|
88
|
+
@radiodriver.connect(*@connect_params)
|
89
|
+
@radiodriver.disconnect()
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should kill the thread if disconnect is called with true" do
|
93
|
+
expect_any_instance_of(Thread).to receive(:kill)
|
94
|
+
@radiodriver.connect(*@connect_params)
|
95
|
+
@radiodriver.disconnect(true)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#send_packet" do
|
100
|
+
it "should do nothing when not connected" do
|
101
|
+
#expect_any_instance_of(Queue).not_to receive(:push)
|
102
|
+
@radiodriver.send_packet([1,2,3])
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should push a packet to the queue and send it" do
|
106
|
+
#expect_any_instance_of(Queue).to receive(:push)
|
107
|
+
expect(@crazyradio).to receive(:send_packet)
|
108
|
+
@radiodriver.connect(*@connect_params)
|
109
|
+
@radiodriver.send_packet(CRTPPacket.unpack([1,2,3]))
|
110
|
+
@radiodriver.disconnect()
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should call the link error callback if max size is reached" do
|
114
|
+
# prevent consuming packages
|
115
|
+
expect(@radiodriver).to receive(:start_radio_thread)
|
116
|
+
@radiodriver.connect(*@connect_params)
|
117
|
+
max = @radiodriver.out_queue_max_size
|
118
|
+
m = "Reached #{max} elements in outgoing queue"
|
119
|
+
# When it reaches 50 it disconnects and not send anymore
|
120
|
+
expect(@link_error_cb).to receive(:call).with(m).once
|
121
|
+
(1..52).each do
|
122
|
+
@radiodriver.send_packet([4,5,6])
|
123
|
+
end
|
124
|
+
@radiodriver.disconnect()
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#receive_packet" do
|
129
|
+
# note that thread sends control packages all the time
|
130
|
+
it "should receive nil if queue is empty" do
|
131
|
+
@radiodriver.connect(*@connect_params)
|
132
|
+
@radiodriver.disconnect() #empty queues
|
133
|
+
@radiodriver.receive_packet(true).should be_nil
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should raise error if Crazyradio complains" do
|
137
|
+
e = USBDongleException
|
138
|
+
expect(@crazyradio).to receive(:send_packet).and_raise(e, "aa")
|
139
|
+
m = "Error talking to Crazyradio: aa"
|
140
|
+
expect(@link_error_cb).to receive(:call).with(m)
|
141
|
+
@radiodriver.connect(*@connect_params)
|
142
|
+
sleep 0.1
|
143
|
+
@radiodriver.disconnect()
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should raise error if nil is returned as response" do
|
147
|
+
allow(@crazyradio).to receive(:send_packet).and_return(nil)
|
148
|
+
m = "Dongle communication error (ack is nil)"
|
149
|
+
expect(@link_error_cb).to receive(:call).with(m)
|
150
|
+
@radiodriver.connect(*@connect_params)
|
151
|
+
sleep 0.1
|
152
|
+
@radiodriver.disconnect()
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should suicide if too many packets are lost" do
|
156
|
+
allow(@ack).to receive(:ack).and_return(false)
|
157
|
+
m = "Too many packets lost"
|
158
|
+
expect(@link_error_cb).to receive(:call).with(m).once
|
159
|
+
@radiodriver.connect(*@connect_params)
|
160
|
+
sleep 0.1
|
161
|
+
@radiodriver.disconnect()
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should receive a packet" do
|
165
|
+
expect(@link_quality_cb).to receive(:call).with(70).at_least(:once)
|
166
|
+
@radiodriver.connect(*@connect_params)
|
167
|
+
@radiodriver.send_packet(CRTPPacket.unpack([1,2,3]))
|
168
|
+
packet = @radiodriver.receive_packet(false)
|
169
|
+
# send package returns an ack with [1,2,3] as data
|
170
|
+
expect(packet).to be_an_instance_of CRTPPacket
|
171
|
+
packet.size.should == 2
|
172
|
+
packet.header.should == 1
|
173
|
+
packet.data.should == [2,3]
|
174
|
+
@radiodriver.disconnect()
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#scan_interface" do
|
179
|
+
it "should complain if link is open" do
|
180
|
+
@radiodriver.connect(*@connect_params)
|
181
|
+
expect {
|
182
|
+
@radiodriver.scan_interface()
|
183
|
+
}.to raise_exception(OpenLink, "Cannot scan when link is open")
|
184
|
+
@radiodriver.disconnect()
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should return the found some uris" do
|
188
|
+
allow(@crazyradio).to receive(:scan_channels).and_return([1,2,3],
|
189
|
+
[7,8,9],
|
190
|
+
[21,22,4])
|
191
|
+
expect(@crazyradio).to receive(:[]=).with(:arc, 1)
|
192
|
+
expect(@crazyradio).to receive(:[]=).with(:data_rate,
|
193
|
+
Crazyradio::DR_250KPS)
|
194
|
+
expect(@crazyradio).to receive(:[]=).with(:data_rate,
|
195
|
+
Crazyradio::DR_1MPS)
|
196
|
+
expect(@crazyradio).to receive(:[]=).with(:data_rate,
|
197
|
+
Crazyradio::DR_2MPS)
|
198
|
+
expect(@crazyradio).to receive(:close)
|
199
|
+
@radiodriver.scan_interface().should == [
|
200
|
+
'radio://0/1/250K',
|
201
|
+
'radio://0/2/250K',
|
202
|
+
'radio://0/3/250K',
|
203
|
+
'radio://0/7/1M',
|
204
|
+
'radio://0/8/1M',
|
205
|
+
'radio://0/9/1M',
|
206
|
+
'radio://0/21/2M',
|
207
|
+
'radio://0/22/2M',
|
208
|
+
'radio://0/4/2M']
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should raise a usb dongle exception it it happens" do
|
212
|
+
e = USBDongleException
|
213
|
+
allow(@crazyradio).to receive(:scan_channels).and_raise(e)
|
214
|
+
allow(@crazyradio).to receive(:[]=)
|
215
|
+
expect {
|
216
|
+
@radiodriver.scan_interface()
|
217
|
+
}.to raise_exception(e)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe "#get_status" do
|
222
|
+
it "should get status" do
|
223
|
+
allow(Crazyradio).to receive(:status).and_return("hola")
|
224
|
+
@radiodriver.get_status.should == "hola"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2013 Hector Sanjuan
|
3
|
+
|
4
|
+
# This file is part of Crubyflie.
|
5
|
+
|
6
|
+
# Crubyflie is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
|
11
|
+
# Crubyflie is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
|
18
|
+
|
19
|
+
require 'simplecov'
|
20
|
+
SimpleCov.at_exit do
|
21
|
+
SimpleCov.minimum_coverage 95
|
22
|
+
SimpleCov.result.format!
|
23
|
+
end
|
24
|
+
SimpleCov.start do
|
25
|
+
add_group "Libraries", "lib"
|
26
|
+
add_group "Specs", "spec"
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
$: << File.join(File.dirname(__FILE__), "lib")
|
31
|
+
$: << File.join(File.dirname(__FILE__))
|
32
|
+
|
33
|
+
require 'crubyflie'
|
34
|
+
include Crubyflie
|
35
|
+
include CRTPConstants
|
36
|
+
|
37
|
+
require 'radio_ack_spec'
|
38
|
+
require 'crtp_packet_spec'
|
39
|
+
require 'crazyradio_spec'
|
40
|
+
require 'radio_driver_spec'
|
41
|
+
require 'radio_ack_spec'
|
42
|
+
require 'toc_cache_spec'
|
43
|
+
require 'toc_spec'
|
44
|
+
require 'crazyflie_spec'
|
45
|
+
require 'log_spec'
|
46
|
+
require 'param_spec'
|
47
|
+
require 'console_spec'
|
48
|
+
require 'commander_spec'
|
49
|
+
require 'joystick_input_reader_spec'
|
50
|
+
require 'input_reader_spec'
|
51
|
+
require 'crubyflie_logger_spec'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2013 Hector Sanjuan
|
3
|
+
|
4
|
+
# This file is part of Crubyflie.
|
5
|
+
|
6
|
+
# Crubyflie is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
|
11
|
+
# Crubyflie is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
|
18
|
+
|
19
|
+
require 'crazyflie/toc_cache'
|
20
|
+
|
21
|
+
describe TOCCache do
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
allow_any_instance_of(TOCCache).to receive(:warn)
|
25
|
+
|
26
|
+
|
27
|
+
allow(File).to receive(:exist?).with('baa').and_return(true)
|
28
|
+
allow(File).to receive(:directory?).with('baa').and_return(true)
|
29
|
+
expect(FileUtils).to receive(:touch)
|
30
|
+
expect(FileUtils).to receive(:rm)
|
31
|
+
@cache = TOCCache.new('baa')
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#initialize" do
|
35
|
+
it "shoud not create a folder where it does not have permissions" do
|
36
|
+
allow(File).to receive(:exist?).with('baa').and_return(false)
|
37
|
+
do_this = receive(:mkdir_p).with('baa').and_raise(Errno::EACCES)
|
38
|
+
expect(FileUtils).to do_this
|
39
|
+
m = "Deactivating cache. Cannot create folder"
|
40
|
+
expect_any_instance_of(TOCCache).to receive(:warn).with(m)
|
41
|
+
cache = TOCCache.new('baa')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should veryfy the cache folder is a directory" do
|
45
|
+
allow(File).to receive(:exist?).with('baa').and_return(true)
|
46
|
+
allow(File).to receive(:directory?).with('baa').and_return(false)
|
47
|
+
m = "Deactivating cache. Folder is not a directory"
|
48
|
+
expect_any_instance_of(TOCCache).to receive(:warn).with(m)
|
49
|
+
cache = TOCCache.new('baa')
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should test if the cache folder is writable" do
|
53
|
+
allow(File).to receive(:exist?).with('baa').and_return(true)
|
54
|
+
allow(File).to receive(:directory?).with('baa').and_return(true)
|
55
|
+
allow(FileUtils).to receive(:touch).and_raise(Errno::EACCES)
|
56
|
+
expect(FileUtils).not_to receive(:rm)
|
57
|
+
m = "Deactivating cache. Cannot write to folder"
|
58
|
+
expect_any_instance_of(TOCCache).to receive(:warn).with(m)
|
59
|
+
cache = TOCCache.new('baa')
|
60
|
+
end
|
61
|
+
|
62
|
+
it "initialize correctly otherwise" do
|
63
|
+
allow(File).to receive(:exist?).with('baa').and_return(true)
|
64
|
+
allow(File).to receive(:directory?).with('baa').and_return(true)
|
65
|
+
expect(FileUtils).not_to receive(:mkdir_p)
|
66
|
+
expect(FileUtils).to receive(:touch)
|
67
|
+
expect(FileUtils).to receive(:rm)
|
68
|
+
cache = TOCCache.new('baa')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#fetch" do
|
73
|
+
it "should return nil if file does not exist" do
|
74
|
+
allow(File).to receive(:open).and_raise(Errno::ENOENT)
|
75
|
+
expect(Marshal).not_to receive(:load)
|
76
|
+
@cache.fetch("123").should be_nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#insert" do
|
81
|
+
it "should do nothing if an error happens" do
|
82
|
+
allow(File).to receive(:open).and_raise(Errno::EACCES)
|
83
|
+
expect(Marshal).not_to receive(:dump)
|
84
|
+
@cache.insert("123",{})
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/toc_spec.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2013 Hector Sanjuan
|
3
|
+
|
4
|
+
# This file is part of Crubyflie.
|
5
|
+
|
6
|
+
# Crubyflie is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
|
11
|
+
# Crubyflie is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
|
18
|
+
|
19
|
+
require 'crazyflie/toc'
|
20
|
+
|
21
|
+
describe TOCElement do
|
22
|
+
describe "#initialize" do
|
23
|
+
it "should assign the variables correctly" do
|
24
|
+
te = TOCElement.new({
|
25
|
+
:ident => 1,
|
26
|
+
:group => 2,
|
27
|
+
:name => 3,
|
28
|
+
:ctype => 4,
|
29
|
+
:directive => 'C*',
|
30
|
+
:access => 6
|
31
|
+
})
|
32
|
+
te.ident.should == 1
|
33
|
+
te.group.should == 2
|
34
|
+
te.name.should == 3
|
35
|
+
te.ctype.should == 4
|
36
|
+
te.directive.should == 'C*'
|
37
|
+
te.access.should == 6
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe TOC do
|
43
|
+
before :each do
|
44
|
+
@cache = double("TOCCache")
|
45
|
+
allow(TOCCache).to receive(:new).and_return(@cache)
|
46
|
+
@toc = TOC.new()
|
47
|
+
@element = TOCElement.new({
|
48
|
+
:ident => 1,
|
49
|
+
:group => "mygroup",
|
50
|
+
:name => "myname",
|
51
|
+
:ctype => 4,
|
52
|
+
:rtype => 5,
|
53
|
+
:access => 6
|
54
|
+
})
|
55
|
+
@logger = @toc.logger
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#initialize" do
|
59
|
+
it "should setup the cache" do
|
60
|
+
expect(TOCCache).to receive(:new).with('abc')
|
61
|
+
toc = TOC.new('abc')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#[]" do
|
66
|
+
it "should find an element by name only" do
|
67
|
+
@toc.insert(@element)
|
68
|
+
@toc["myname",:by_name].should == @element
|
69
|
+
@toc["mygroup.myname",:by_name].should == @element
|
70
|
+
@toc[1,:by_name].should be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should find an element by id only" do
|
74
|
+
@toc.insert(@element)
|
75
|
+
@toc["myname",:by_id].should be_nil
|
76
|
+
@toc[1,:by_id].should == @element
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should find an element by both name and id" do
|
80
|
+
@toc.insert(@element)
|
81
|
+
@toc["myname"].should == @element
|
82
|
+
@toc[1].should == @element
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should return nil when the element is not found" do
|
86
|
+
@toc.insert(@element)
|
87
|
+
@toc["baa"].should be_nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#insert" do
|
92
|
+
it "should insert correctly an element" do
|
93
|
+
@toc[1].should be_nil
|
94
|
+
@toc.insert(@element)
|
95
|
+
@toc[1].should == @element
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#import/#export" do
|
100
|
+
it "should import and export" do
|
101
|
+
expect(@cache).to receive(:insert).with('abc', @toc.toc)
|
102
|
+
expect(@cache).to receive(:fetch).with('abc').and_return({:a => 3})
|
103
|
+
|
104
|
+
@toc.insert(@element)
|
105
|
+
@toc.export_to_cache('abc')
|
106
|
+
@toc.import_from_cache('abc')
|
107
|
+
@toc.toc.should == {:a => 3}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#fetch_from_crazyflie" do
|
112
|
+
it "should import from cache when the TOC is in it" do
|
113
|
+
cf = double("Crazyflie")
|
114
|
+
queue = Queue.new
|
115
|
+
resp = CRTPPacket.new()
|
116
|
+
resp.modify_header(nil, 0, TOC_CHANNEL)
|
117
|
+
resp.data = [0, 5, 0x01, 0x00, 0x00, 0x00, 0x00]
|
118
|
+
bad_resp = CRTPPacket.new()
|
119
|
+
bad_resp.modify_header(nil, 0, 33)
|
120
|
+
|
121
|
+
allow(@cache).to receive(:fetch).and_return({:abc => "def"}).once
|
122
|
+
|
123
|
+
expect(queue).to receive(:pop).and_return(bad_resp, resp).twice
|
124
|
+
expect(queue).to receive(:<<).with(bad_resp)
|
125
|
+
expect(cf).to receive(:send_packet).with(anything, true).once
|
126
|
+
m = "Got a non-TOC packet. Requeueing..."
|
127
|
+
m2 = "TOC crc #{1}, 5 items"
|
128
|
+
m3 = "TOC found in cache"
|
129
|
+
expect(@logger).to receive(:debug).with(m)
|
130
|
+
expect(@logger).to receive(:debug).with(m2)
|
131
|
+
expect(@logger).to receive(:debug).with(m3)
|
132
|
+
|
133
|
+
@toc.fetch_from_crazyflie(cf, 0, queue)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should request the elements in the TOC when it is not in cache" do
|
137
|
+
cf = double("Crazyflie")
|
138
|
+
queue = Queue.new
|
139
|
+
resp = CRTPPacket.new()
|
140
|
+
resp.modify_header(nil, 0, TOC_CHANNEL)
|
141
|
+
resp.data = [0, 5, 0x01, 0x00, 0x00, 0x00, 0x00]
|
142
|
+
|
143
|
+
resp_elem = CRTPPacket.new()
|
144
|
+
resp_elem.modify_header(nil, 0, TOC_CHANNEL)
|
145
|
+
resp_elem.data = [0, 0, 0xFF, 0xFF]
|
146
|
+
|
147
|
+
allow(@cache).to receive(:fetch).and_return(nil).once
|
148
|
+
|
149
|
+
do_this = receive(:pop).and_return(resp,
|
150
|
+
resp_elem).exactly(6).times
|
151
|
+
expect(queue).to do_this
|
152
|
+
expect(cf).to receive(:send_packet).with(anything,
|
153
|
+
true).exactly(6).times
|
154
|
+
|
155
|
+
mw = "Not in cache"
|
156
|
+
m = "TOC crc 1, 5 items"
|
157
|
+
m0 = "Added 0 to TOC"
|
158
|
+
m1 = "Added 1 to TOC"
|
159
|
+
m2 = "Added 2 to TOC"
|
160
|
+
m3 = "Added 3 to TOC"
|
161
|
+
m4 = "Added 4 to TOC"
|
162
|
+
|
163
|
+
expect(@logger).to receive(:debug).with(mw)
|
164
|
+
expect(@logger).to receive(:debug).with(m)
|
165
|
+
expect(@logger).to receive(:debug).with(m0)
|
166
|
+
expect(@logger).to receive(:debug).with(m1)
|
167
|
+
expect(@logger).to receive(:debug).with(m2)
|
168
|
+
expect(@logger).to receive(:debug).with(m3)
|
169
|
+
expect(@logger).to receive(:debug).with(m4)
|
170
|
+
|
171
|
+
one = { :ident => 0 }
|
172
|
+
two = { :ident => 1 }
|
173
|
+
three = { :ident => 2 }
|
174
|
+
four = { :ident => 3 }
|
175
|
+
five = { :ident => 4 }
|
176
|
+
allow(TOCElement).to receive(:new).and_return(
|
177
|
+
TOCElement.new(one),
|
178
|
+
TOCElement.new(two),
|
179
|
+
TOCElement.new(three),
|
180
|
+
TOCElement.new(four),
|
181
|
+
TOCElement.new(five))
|
182
|
+
|
183
|
+
expect(@cache).to receive(:insert).with("1", anything)
|
184
|
+
@toc.fetch_from_crazyflie(cf, 0, queue)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (C) 2013 Hector Sanjuan
|
3
|
+
|
4
|
+
# This file is part of Crubyflie.
|
5
|
+
|
6
|
+
# Crubyflie is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
|
11
|
+
# Crubyflie is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
|
18
|
+
|
19
|
+
# A simple script to list SDL axis/button numbering and read values
|
20
|
+
|
21
|
+
require 'sdl'
|
22
|
+
require 'pp'
|
23
|
+
|
24
|
+
warn "Welcome to Crubyflie joystick utility. Ctrl-C to exit."
|
25
|
+
warn "--"
|
26
|
+
Signal.trap("SIGINT") do
|
27
|
+
warn "\n\n"
|
28
|
+
warn "Bye bye!"
|
29
|
+
SDL.quit
|
30
|
+
exit 0
|
31
|
+
end
|
32
|
+
|
33
|
+
SDL.init(SDL::INIT_JOYSTICK)
|
34
|
+
n_joy = SDL::Joystick.num
|
35
|
+
if n_joy == 0
|
36
|
+
warn "No joysticks found"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
warn "Total number of Joysticks: #{n_joy}"
|
42
|
+
n_joy.times do |i|
|
43
|
+
warn "#{i}: #{SDL::Joystick.index_name(i)}"
|
44
|
+
end
|
45
|
+
warn "--"
|
46
|
+
print "Which one should we use (0-#{n_joy-1}): "
|
47
|
+
joy_id = gets.to_i
|
48
|
+
joy = SDL::Joystick.open(joy_id)
|
49
|
+
warn "Opened Joystick #{joy_id}"
|
50
|
+
warn "Name: SDL::Joystick.index_name(#{joy_id})"
|
51
|
+
warn "Number of Axes: #{joy.num_axes}"
|
52
|
+
warn "Number of buttons: #{joy.num_buttons}"
|
53
|
+
warn "Here is the reading for axis and buttons:"
|
54
|
+
loop {
|
55
|
+
SDL::Joystick.update_all
|
56
|
+
joy.num_axes.times do |i|
|
57
|
+
print "A##{i}: #{joy.axis(i)} | "
|
58
|
+
end
|
59
|
+
|
60
|
+
print " || "
|
61
|
+
|
62
|
+
button_read = []
|
63
|
+
joy.num_buttons.times do |i|
|
64
|
+
print "B##{i}: #{joy.button(i) ? 1 : 0} | "
|
65
|
+
end
|
66
|
+
print "\r"
|
67
|
+
$stdout.flush
|
68
|
+
sleep 0.05
|
69
|
+
}
|