hybridgroup-crubyflie 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.md +127 -0
  7. data/Rakefile +15 -0
  8. data/bin/crubyflie +94 -0
  9. data/configs/joystick_default.yaml +106 -0
  10. data/crubyflie.gemspec +50 -0
  11. data/examples/params_and_logging.rb +87 -0
  12. data/lib/crubyflie/crazyflie/commander.rb +54 -0
  13. data/lib/crubyflie/crazyflie/console.rb +67 -0
  14. data/lib/crubyflie/crazyflie/log.rb +383 -0
  15. data/lib/crubyflie/crazyflie/log_conf.rb +57 -0
  16. data/lib/crubyflie/crazyflie/param.rb +220 -0
  17. data/lib/crubyflie/crazyflie/toc.rb +239 -0
  18. data/lib/crubyflie/crazyflie/toc_cache.rb +87 -0
  19. data/lib/crubyflie/crazyflie.rb +282 -0
  20. data/lib/crubyflie/crazyradio/crazyradio.rb +301 -0
  21. data/lib/crubyflie/crazyradio/radio_ack.rb +48 -0
  22. data/lib/crubyflie/crubyflie_logger.rb +74 -0
  23. data/lib/crubyflie/driver/crtp_packet.rb +146 -0
  24. data/lib/crubyflie/driver/radio_driver.rb +363 -0
  25. data/lib/crubyflie/exceptions.rb +36 -0
  26. data/lib/crubyflie/input/input_reader.rb +190 -0
  27. data/lib/crubyflie/input/joystick_input_reader.rb +328 -0
  28. data/lib/crubyflie/version.rb +22 -0
  29. data/lib/crubyflie.rb +36 -0
  30. data/spec/commander_spec.rb +67 -0
  31. data/spec/console_spec.rb +76 -0
  32. data/spec/crazyflie_spec.rb +176 -0
  33. data/spec/crazyradio_spec.rb +228 -0
  34. data/spec/crtp_packet_spec.rb +79 -0
  35. data/spec/crubyflie_logger_spec.rb +39 -0
  36. data/spec/crubyflie_spec.rb +21 -0
  37. data/spec/input_reader_spec.rb +136 -0
  38. data/spec/joystick_cfg.yaml +44 -0
  39. data/spec/joystick_input_reader_spec.rb +323 -0
  40. data/spec/log_spec.rb +266 -0
  41. data/spec/param_spec.rb +166 -0
  42. data/spec/radio_ack_spec.rb +43 -0
  43. data/spec/radio_driver_spec.rb +227 -0
  44. data/spec/spec_helper.rb +53 -0
  45. data/spec/toc_cache_spec.rb +87 -0
  46. data/spec/toc_spec.rb +187 -0
  47. data/tools/sdl-joystick-axis.rb +69 -0
  48. metadata +225 -0
@@ -0,0 +1,301 @@
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 'rubygems'
20
+ require 'libusb'
21
+
22
+ require 'exceptions'
23
+ require 'crazyradio/radio_ack'
24
+
25
+ module Crubyflie
26
+
27
+ # This module defines some Crazyradio-related constants
28
+ module CrazyradioConstants
29
+ # USB dongle vendor ID
30
+ CRAZYRADIO_VENDOR_ID = 0x1915
31
+ # USB dongle product ID
32
+ CRAZYRADIO_PRODUCT_ID = 0x7777
33
+
34
+ # Set radio channel instruction code
35
+ SET_RADIO_CHANNEL = 0x01
36
+ # Set address instruction code
37
+ SET_RADIO_ADDRESS = 0x02
38
+ # Set data rate instruction code. For values see below
39
+ SET_DATA_RATE = 0x03
40
+ # Set radio power instruction code. For valid values see below
41
+ SET_RADIO_POWER = 0x04
42
+ # Set ARD (Auto Retry Delay) instruction code
43
+ SET_RADIO_ARD = 0x05
44
+ # Set ARC (Auto Retry Count) instruction code
45
+ SET_RADIO_ARC = 0x06
46
+ # Set ack instruction code
47
+ ACK_ENABLE = 0x10
48
+ # Set control carrier instruction code
49
+ SET_CONT_CARRIER = 0x20
50
+ # Scan N channels instruction code
51
+ SCANN_CHANNELS = 0x21
52
+ # Launch bootloader instruction code
53
+ LAUNCH_BOOTLOADER = 0xFF
54
+
55
+ # Default channel to talk to a Crazyflie
56
+ DEFAULT_CHANNEL = 2
57
+
58
+ # 250 Kb/s datarate
59
+ DR_250KPS = 0
60
+ # 1 Mb/s datarate
61
+ DR_1MPS = 1
62
+ # 2 Mb/s datarate
63
+ DR_2MPS = 2
64
+
65
+ # 18db power attenuation
66
+ P_M18DBM = 0
67
+ # 12db power attenuation
68
+ P_M12DBM = 1
69
+ # 6db power attenuation
70
+ P_M6DBM = 2
71
+ # 0db power attenuation
72
+ P_0DBM = 3
73
+ end
74
+
75
+ # Driver for the USB crazyradio dongle
76
+ class Crazyradio
77
+ include CrazyradioConstants
78
+ # Default settings for Crazyradio
79
+ DEFAULT_SETTINGS = {
80
+ :data_rate => DR_2MPS,
81
+ :channel => 2,
82
+ :cont_carrier => false,
83
+ :address => [0xE7] * 5, #5 times 0xE7
84
+ :power => P_0DBM,
85
+ :arc => 3,
86
+ :ard_bytes => 32 # 32
87
+ }
88
+
89
+ attr_reader :device, :handle, :dev_handle
90
+ # Initialize a crazyradio
91
+ # @param device [LIBUSB::Device] A crazyradio USB device
92
+ # @param settings [Hash] Crazyradio settings. @see #DEFAULT_SETTINGS
93
+ # @raise [USBDongleException] when something goes wrong
94
+ def initialize(device=nil, settings={})
95
+ if device.nil? || !device.is_a?(LIBUSB::Device)
96
+ raise USBDongleException.new("Wrong USB device")
97
+ end
98
+
99
+ @device = device
100
+ reopen()
101
+ @settings = DEFAULT_SETTINGS
102
+ @settings.update(settings)
103
+ apply_settings()
104
+ end
105
+
106
+ # Initializes the device and the USB handle
107
+ # If they are open, it releases the resources first
108
+ def reopen
109
+ close()
110
+ @handle = @device.open()
111
+ # USB configuration 0 means unconfigured state
112
+ @handle.configuration = 1 # hardcoded
113
+ @handle.claim_interface(0) # hardcoded
114
+ end
115
+
116
+ # Return some information as string
117
+ # @return [String] Dongle information
118
+ def self.status
119
+ cr = Crazyradio.factory()
120
+ serial = cr.device.serial_number
121
+ manufacturer = cr.device.manufacturer
122
+ cr.close()
123
+ return "Found #{serial} USB dongle from #{manufacturer}"
124
+ end
125
+
126
+ # Release interface, reset device and close the handle
127
+ def close
128
+ @handle.release_interface(0) if @handle
129
+ @handle.reset_device() if @handle
130
+ # WARNING: This hangs badly and randomly!!!
131
+ # @handle.close() if @handle
132
+ @handle = nil
133
+ end
134
+
135
+ # Determines if the dongle has hardware scanning.
136
+ # @return [nil] defaults to nil to mitigate a dongle bug
137
+ def has_fw_scan
138
+ # it seems there is a bug on fw scan
139
+ nil
140
+ end
141
+
142
+ # Scans channels for crazyflies
143
+ def scan_channels(start, stop, packet=[0xFF])
144
+ if has_fw_scan()
145
+ send_vendor_setup(SCANN_CHANNELS, start, stop, packet)
146
+ return get_vendor_setup(SCANN_CHANNELS, 0, 0, 64)
147
+ end
148
+
149
+ result = []
150
+ (start..stop).each do |ch|
151
+ self[:channel] = ch
152
+ status = send_packet(packet)
153
+ result << ch if status && status.ack
154
+ end
155
+ return result
156
+ end
157
+
158
+ # Creates a Crazyradio object with the first USB dongle found
159
+ # @param settings [Hash] Crazyradio settings. @see #DEFAULT_SETTINGS
160
+ # @return [Crazyradio] a Crazyradio
161
+ # @raise [USBDongleException] when no USB dongle is found
162
+ def self.factory(settings={})
163
+ devs = Crazyradio.find_devices()
164
+ raise USBDongleException.new("No dongles found") if devs.empty?()
165
+ return Crazyradio.new(devs.first, settings)
166
+ end
167
+
168
+ # List crazyradio dongles
169
+ def self.find_devices
170
+ usb = LIBUSB::Context.new
171
+ usb.devices(:idVendor => CRAZYRADIO_VENDOR_ID,
172
+ :idProduct => CRAZYRADIO_PRODUCT_ID)
173
+ end
174
+
175
+ # Send a data packet and reads the response into an Ack
176
+ # @param [Array] data to be sent
177
+ def send_packet(data)
178
+ out_args = {
179
+ :endpoint => 1,
180
+ :dataOut => data.pack('C*')
181
+ }
182
+ @handle.bulk_transfer(out_args)
183
+ in_args = {
184
+ :endpoint => 0x81,
185
+ :dataIn => 64
186
+ }
187
+ response = @handle.bulk_transfer(in_args)
188
+
189
+ return nil unless response
190
+ return RadioAck.from_raw(response, @settings[:arc])
191
+ end
192
+
193
+ # Set a crazyradio setting
194
+ # @param setting [Symbol] a valid Crazyradio setting name
195
+ # @param value [Object] the setting value
196
+ def []=(setting, value)
197
+ @settings[setting] = value
198
+ apply_settings(setting)
199
+ end
200
+
201
+ # Get a crazyradio setting
202
+ # @param setting [Symbol] a valid Crazyradio setting name
203
+ # @return [Integer] the value
204
+ def [](setting)
205
+ return @settings[setting]
206
+ end
207
+
208
+ # Applies the indicated setting or all settings if not specified
209
+ # @param setting [Symbol] a valid crazyradio setting name
210
+ def apply_settings(setting=nil)
211
+ to_apply = setting.nil? ? @settings.keys() : [setting]
212
+ to_apply.each do |setting|
213
+ value = @settings[setting]
214
+ next if value.nil?
215
+
216
+ case setting
217
+ when :data_rate
218
+ set_data_rate(value)
219
+ when :channel
220
+ set_channel(value)
221
+ when :arc
222
+ set_arc(value)
223
+ when :cont_carrier
224
+ set_cont_carrier(value)
225
+ when :address
226
+ set_address(value)
227
+ when :power
228
+ set_power(value)
229
+ when :ard_bytes
230
+ set_ard_bytes(value)
231
+ else
232
+ @settings.delete(setting)
233
+ end
234
+ end
235
+ end
236
+
237
+ def send_vendor_setup(request, value, index=0, dataOut=[])
238
+ args = {
239
+ :bmRequestType => LIBUSB::REQUEST_TYPE_VENDOR,
240
+ :bRequest => request,
241
+ :wValue => value,
242
+ :wIndex => index,
243
+ :dataOut => dataOut.pack('C*')
244
+ }
245
+ @handle.control_transfer(args)
246
+ end
247
+ private :send_vendor_setup
248
+
249
+ def get_vendor_setup(request, value, index, dataIn=0)
250
+ args = {
251
+ # Why this mask?
252
+ :bmRequestType => LIBUSB::REQUEST_TYPE_VENDOR | 0x80,
253
+ :bRequest => request,
254
+ :wValue => value,
255
+ :wIndex => index,
256
+ :dataIn => dataIn
257
+ }
258
+ return @handle.control_transfer(args).unpack('C*')
259
+ end
260
+ private :get_vendor_setup
261
+
262
+ def set_channel(channel)
263
+ send_vendor_setup(SET_RADIO_CHANNEL, channel)
264
+ end
265
+ private :set_channel
266
+
267
+ def set_address(addr)
268
+ if addr.size != 5
269
+ raise USBDongleException.new("Address needs 5 bytes")
270
+ end
271
+ send_vendor_setup(SET_RADIO_ADDRESS, 0, 0, addr)
272
+ end
273
+ private :set_address
274
+
275
+ def set_data_rate(datarate)
276
+ send_vendor_setup(SET_DATA_RATE, datarate)
277
+ end
278
+ private :set_data_rate
279
+
280
+ def set_power(power)
281
+ send_vendor_setup(SET_RADIO_POWER, power)
282
+ end
283
+ private :set_power
284
+
285
+ def set_arc(arc)
286
+ send_vendor_setup(SET_RADIO_ARC, arc)
287
+ end
288
+ private :set_arc
289
+
290
+ def set_ard_bytes(nbytes)
291
+ # masking this way converts 32 to 0xA0 for example
292
+ send_vendor_setup(SET_RADIO_ARD, 0x80 | nbytes)
293
+ end
294
+ private :set_ard_bytes
295
+
296
+ def set_cont_carrier(active)
297
+ send_vendor_setup(SET_CONT_CARRIER, active ? 1 : 0)
298
+ end
299
+ private :set_cont_carrier
300
+ end
301
+ end
@@ -0,0 +1,48 @@
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
+ module Crubyflie
20
+ # An acknowlegdement packet from the Crazyflie
21
+ class RadioAck
22
+ attr_accessor :ack, :powerDet, :retry_count, :data
23
+
24
+ # Initialize a Radio Ack
25
+ # @param ack [TrueClass,FalseClass] indicates if it is an ack
26
+ # @param powerDet [TrueClass,FalseClass] powerDet
27
+ # @param retry_count [Integer] the times we retried to send the packet
28
+ # @param data [Array] the payload of the ack packet
29
+ def initialize(ack=nil, powerDet=nil, retry_count=0, data=[])
30
+ @ack = ack
31
+ @powerDet = powerDet
32
+ @retry_count = retry_count
33
+ @data = data
34
+ end
35
+
36
+ # Create from raw usb response
37
+ # @param data [String] binary data
38
+ # @return [RadioAck] a properly initialized RadioAck
39
+ def self.from_raw(data, arc=0)
40
+ response = data.unpack('C*')
41
+ header = response.shift()
42
+ ack = (header & 0x01) != 0
43
+ powerDet = (header & 0x02) != 0
44
+ retry_count = header != 0 ? header >> 4 : arc
45
+ return RadioAck.new(ack, powerDet, retry_count, response)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,74 @@
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
+ # A simple script to list SDL axis/button numbering and read values
20
+
21
+ # This module is included where needed and offers
22
+ # easy access to the logger
23
+ module Logging
24
+ # Give me a logger
25
+ # @return [CrubyflieLogger]
26
+ def self.logger
27
+ Logging.logger
28
+ end
29
+
30
+ # Lazy initialization for a logger
31
+ # @return [CrubyflieLogger]
32
+ def logger
33
+ @logger ||= CrubyflieLogger.new()
34
+ end
35
+
36
+ # Set a logger
37
+ # @param logger [CrubyflieLogger] the new logger to use
38
+ def logger=(logger)
39
+ @logger = logger
40
+ end
41
+ end
42
+
43
+ # A simple logger to log debug messages, info, warnings and errors
44
+ class CrubyflieLogger
45
+ # Initialize a logger and enable debug logs
46
+ # @param debug [TrueClass,nil] enable output of debug messages
47
+ def initialize(debug=$debug)
48
+ @@debug = debug
49
+ end
50
+
51
+ # Logs a debug message
52
+ # @param msg [String] the message to be logged
53
+ def debug(msg)
54
+ $stderr.puts "DEBUG: #{msg}" if @@debug
55
+ end
56
+
57
+ # Logs an info message to $stdout
58
+ # @param msg [String] the message to be logged
59
+ def info(msg)
60
+ $stdout.puts "INFO: #{msg}"
61
+ end
62
+
63
+ # Logs a warning message to $stderr
64
+ # @param msg [String] the message to be logged
65
+ def warn(msg)
66
+ $stderr.puts "WARNING: #{msg}"
67
+ end
68
+
69
+ # Logs an error message to $stderr
70
+ # @param msg [String] the message to be logged
71
+ def error(msg)
72
+ $stderr.puts "ERROR: #{msg}"
73
+ end
74
+ end
@@ -0,0 +1,146 @@
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
+
20
+ module Crubyflie
21
+
22
+ # Constants related to CRTP Packets
23
+ module CRTPConstants
24
+ # The ports for the different facilities
25
+ CRTP_PORTS = {
26
+ :console => 0x00,
27
+ :param => 0x02,
28
+ :commander => 0x03,
29
+ :logging => 0x05,
30
+ :debugdriver => 0x0E,
31
+ :linkctrl => 0x0F,
32
+ :all => 0xFF
33
+ }
34
+
35
+ # How many seconds until we give up waiting for a packet to
36
+ # appear in a queue
37
+ WAIT_PACKET_TIMEOUT = 2
38
+
39
+ # TOC channel
40
+ TOC_CHANNEL = 0
41
+
42
+ # Channel to retrieve Log settings
43
+ LOG_SETTINGS_CHANNEL = 1
44
+ # Channel to retrieve Log data
45
+ LOG_DATA_CHANNEL = 2
46
+
47
+ # Channel to read parameters
48
+ PARAM_READ_CHANNEL = 1
49
+ # Channel to write parameters
50
+ PARAM_WRITE_CHANNEL = 2
51
+
52
+
53
+
54
+ # Command to request a TOC element
55
+ CMD_TOC_ELEMENT = 0
56
+ # Command to request TOC information
57
+ CMD_TOC_INFO = 1
58
+ # Create block command
59
+ CMD_CREATE_BLOCK = 0
60
+ # Append block command
61
+ CMD_APPEND_BLOCK = 1
62
+ # Delete block command
63
+ CMD_DELETE_BLOCK = 2
64
+ # Start logging command
65
+ CMD_START_LOGGING = 3
66
+ # Stop logging command
67
+ CMD_STOP_LOGGING = 4
68
+ # Reset logging command
69
+ CMD_RESET_LOGGING = 5
70
+
71
+
72
+ # These come from param.rb
73
+ # # TOC access command
74
+ # TOC_RESET = 0
75
+ # TOC_GETNEXT = 1
76
+ # TOC_GETCRC32 = 2
77
+
78
+
79
+ end
80
+
81
+
82
+ # A data packet. Raw packet data is sent to the USB driver
83
+ # Some related docs:
84
+ # http://wiki.bitcraze.se/
85
+ # projects:crazyflie:firmware:comm_protocol#serial_port
86
+ class CRTPPacket
87
+
88
+ attr_reader :size, :header, :channel, :port, :data
89
+ # Initialize a package with a header and data
90
+ # @param header [Integer] represents an 8 bit header
91
+ # @param payload [Array] @see #set_data
92
+ def initialize(header=0, payload=[])
93
+ modify_header(header)
94
+ @data = payload || []
95
+ @size = data.size #+ 1 # header. Bytes
96
+ end
97
+
98
+ # Set new data for this packet and update the size
99
+ # @param new_data [Array] the new data
100
+ def data=(new_data)
101
+ @data = new_data
102
+ @size = @data.size
103
+ end
104
+
105
+ # Modify the full header, or the channel or the port
106
+ # @param header [Integer] a new full header. Prevails over the rest
107
+ # @param port [Integer] a new port (4 bits)
108
+ # @param channel [Integer] a new channel (2 bits)
109
+ def modify_header(header=nil, port=nil, channel=nil)
110
+ if header
111
+ @header = header
112
+ @channel = header & 0b11 # lowest 2 bits of header
113
+ @port = (header >> 4) & 0b1111 # bits 4-7
114
+ return
115
+ end
116
+ if channel
117
+ @channel = channel & 0b11 # 2 bits
118
+ @header = (@header & 0b11111100) | @channel
119
+ end
120
+ if port
121
+ @port = (port & 0b1111) # 4 bits
122
+ @header = (@header & 0b00001111) | @port << 4
123
+ end
124
+ end
125
+
126
+ # Creates a packet from a raw data array
127
+ def self.unpack(data)
128
+ return CRTPPacket.new() if !data.is_a?(Array) || data.empty?()
129
+ header = data[0]
130
+ data = data[1..-1]
131
+ CRTPPacket.new(header, data)
132
+ end
133
+
134
+ # Concat the header and the data and return it
135
+ # @return [Array] header concatenated with data
136
+ def pack
137
+ [@header].concat(@data)
138
+ end
139
+
140
+ # Pack the data of the packet into unsigned chars when needed
141
+ # @return [String] binary data
142
+ def data_repack
143
+ return @data.pack('C*')
144
+ end
145
+ end
146
+ end