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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +674 -0
  5. data/README.md +99 -0
  6. data/Rakefile +15 -0
  7. data/bin/crubyflie +85 -0
  8. data/configs/joystick_default.yaml +48 -0
  9. data/crubyflie.gemspec +50 -0
  10. data/examples/params_and_logging.rb +87 -0
  11. data/lib/crubyflie/crazyflie/commander.rb +54 -0
  12. data/lib/crubyflie/crazyflie/console.rb +67 -0
  13. data/lib/crubyflie/crazyflie/log.rb +383 -0
  14. data/lib/crubyflie/crazyflie/log_conf.rb +57 -0
  15. data/lib/crubyflie/crazyflie/param.rb +220 -0
  16. data/lib/crubyflie/crazyflie/toc.rb +239 -0
  17. data/lib/crubyflie/crazyflie/toc_cache.rb +87 -0
  18. data/lib/crubyflie/crazyflie.rb +282 -0
  19. data/lib/crubyflie/crazyradio/crazyradio.rb +301 -0
  20. data/lib/crubyflie/crazyradio/radio_ack.rb +48 -0
  21. data/lib/crubyflie/crubyflie_logger.rb +74 -0
  22. data/lib/crubyflie/driver/crtp_packet.rb +146 -0
  23. data/lib/crubyflie/driver/radio_driver.rb +333 -0
  24. data/lib/crubyflie/exceptions.rb +36 -0
  25. data/lib/crubyflie/input/input_reader.rb +168 -0
  26. data/lib/crubyflie/input/joystick_input_reader.rb +280 -0
  27. data/lib/crubyflie/version.rb +22 -0
  28. data/lib/crubyflie.rb +31 -0
  29. data/spec/commander_spec.rb +67 -0
  30. data/spec/console_spec.rb +76 -0
  31. data/spec/crazyflie_spec.rb +176 -0
  32. data/spec/crazyradio_spec.rb +226 -0
  33. data/spec/crtp_packet_spec.rb +79 -0
  34. data/spec/crubyflie_logger_spec.rb +39 -0
  35. data/spec/crubyflie_spec.rb +20 -0
  36. data/spec/input_reader_spec.rb +136 -0
  37. data/spec/joystick_cfg.yaml +48 -0
  38. data/spec/joystick_input_reader_spec.rb +238 -0
  39. data/spec/log_spec.rb +266 -0
  40. data/spec/param_spec.rb +166 -0
  41. data/spec/radio_ack_spec.rb +43 -0
  42. data/spec/radio_driver_spec.rb +227 -0
  43. data/spec/spec_helper.rb +51 -0
  44. data/spec/toc_cache_spec.rb +87 -0
  45. data/spec/toc_spec.rb +187 -0
  46. data/tools/sdl-joystick-axis.rb +69 -0
  47. metadata +222 -0
@@ -0,0 +1,383 @@
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
+ require 'crazyflie/log_conf.rb'
21
+
22
+ module Crubyflie
23
+
24
+ # An element in the Logging Table of Contents
25
+ # A LogTOCElement knows what the type of data that comes in a #LogBlock
26
+ # and is able to initialize the #TOCElement from a TOC Logging packet
27
+ class LogTOCElement < TOCElement
28
+
29
+ # A map between crazyflie C types and ruby directives to
30
+ # interpret them. This will help parsing the logging data
31
+ C_RUBY_TYPE_MAP = {
32
+ 1 => {
33
+ :ctype => "uint8_t",
34
+ :directive => 'C',
35
+ :size => 1
36
+ },
37
+ 2 => {
38
+ :ctype => "uint16_t",
39
+ :directive => 'S<',
40
+ :size => 2
41
+ },
42
+ 3 => {
43
+ :ctype => "uint32_t",
44
+ :directive => 'L<',
45
+ :size => 4
46
+ },
47
+ 4 => {
48
+ :ctype => "int8_t",
49
+ :directive => 'c',
50
+ :size => '1'
51
+ },
52
+ 5 => {
53
+ :ctype => "int16_t",
54
+ :directive => 's<',
55
+ :size => 2
56
+ },
57
+ 6 => {
58
+ :ctype => "int32_t",
59
+ :directive => 'l<',
60
+ :size => 4
61
+ },
62
+ 7 => {
63
+ :ctype => "float",
64
+ :directive => 'e',
65
+ :size => 4
66
+ },
67
+ # Unsupported
68
+ # 8 => {
69
+ # :ctype => "FP16",
70
+ # :directive => '',
71
+ # :size => 2
72
+ # }
73
+ }
74
+
75
+ # Initializes a Log TOC element, which means interpreting the
76
+ # data in the packet and calling the parent class
77
+ # @param data [String] a binary payload
78
+ def initialize(data)
79
+ # unpack two null padded strings
80
+ group, name = data[2..-1].unpack('Z*Z*')
81
+ ident = data[0].ord()
82
+ ctype_id = data[1].ord() & 0b1111 # go from 0 to 15
83
+ ctype = C_RUBY_TYPE_MAP[ctype_id][:ctype]
84
+ directive = C_RUBY_TYPE_MAP[ctype_id][:directive]
85
+ access = data[1].ord & 0b00010000 # 0x10, the 5th bit
86
+
87
+ super({
88
+ :ident => ident,
89
+ :group => group,
90
+ :name => name,
91
+ :ctype => ctype,
92
+ :type_id => ctype_id,
93
+ :directive => directive,
94
+ :access => access
95
+ })
96
+ end
97
+
98
+
99
+ end
100
+
101
+ # A LogBlock represents a piece of logging information that is
102
+ # received periodically from the Crazyflie after having set the
103
+ # START_LOGGING command. Each LogBlock will trigger a callback when
104
+ # a piece of data is received for it.
105
+ #
106
+ # Note log blocks are added/removed by the Logging class through the
107
+ # interface provided. So you should not need to use them directly
108
+ class LogBlock
109
+
110
+ @@block_id_counter = 0
111
+ attr_reader :ident, :period
112
+ attr_writer :data_callback
113
+
114
+ # Initialize a LogBlock
115
+ # @param variables [Array] a set of LogConfVariables
116
+ # @param opts [Hash] current options:
117
+ # :period, in centiseconds (100 = 1s)
118
+ def initialize(variables, opts={})
119
+ @variables = variables || []
120
+ @ident = @@block_id_counter
121
+ @@block_id_counter += 1
122
+
123
+ @period = opts.delete(:period) || 10
124
+
125
+ @data_callback = nil
126
+ end
127
+
128
+ # Finds out the binary data by unpacking each of the variables
129
+ # depending on the number of bites for the declared size
130
+ # @param data [String] Binary data string
131
+ def unpack_log_data(data)
132
+ unpacked_data = {}
133
+ position = 0
134
+ @variables.each do |var|
135
+ fetch_as = var.fetch_as
136
+ map = LogTOCElement::C_RUBY_TYPE_MAP
137
+ size = map[fetch_as][:size]
138
+ directive = map[fetch_as][:directive]
139
+ name = var.name
140
+ data_to_unpack = data[position..position + size - 1]
141
+ value = data_to_unpack.unpack(directive).first
142
+ unpacked_data[name] = value
143
+ position += size
144
+ end
145
+ @data_callback.call(unpacked_data) if @data_callback
146
+ end
147
+ end
148
+
149
+ # The logging facility class
150
+ #
151
+ # This class is used to read packages received in the Logging port.
152
+ # It maintains a list of log blocks, which are conveniently added,
153
+ # or removed and for which logging is started or stopped.
154
+ # When a packet with new information for log block comes in,
155
+ # the block in question unpacks the data and triggers a callback.
156
+ #
157
+ # In Crubyflie, the Log class includes all the functionality
158
+ # which is to be found in the Python library LogEntry class (start logging,
159
+ # add block etc) and the Crazyflie class (callbacks for intialization), so
160
+ # interfacing with Log should be done through this class primarily.
161
+ #
162
+ # Unlike the original Pyhton library, there are no callbacks registered
163
+ # somewhere else or anything and functions being called from them. In turn,
164
+ # the Crazyflie class will queue all the logging requests in the @in_queue
165
+ # while a thread in the Logging class takes care of processing them
166
+ # and doing the appropiate. This saves us from registering callbacks
167
+ # in other places and from selecting which data we are to use here.
168
+ class Log
169
+ include Logging
170
+ include CRTPConstants
171
+
172
+ attr_reader :log_blocks, :toc
173
+ # Store the crazyflie, find the incoming packet queue for this
174
+ # facility and initialize a new TOC
175
+ # @param crazyflie [Crazyflie]
176
+ def initialize(crazyflie)
177
+ @log_blocks = {}
178
+ @crazyflie = crazyflie
179
+ @in_queue = crazyflie.crtp_queues[:logging]
180
+ @toc = TOC.new(@crazyflie.cache_folder, LogTOCElement)
181
+ @packet_reader_thread = nil
182
+ end
183
+
184
+ # Refreshes the TOC. TOC class implement this step synchronously
185
+ # so there is no need to provide callbacks or anything
186
+ def refresh_toc
187
+ reset_packet = packet_factory()
188
+ reset_packet.data = [CMD_RESET_LOGGING]
189
+ port = Crazyflie::CRTP_PORTS[:logging]
190
+ channel = TOC_CHANNEL
191
+ @crazyflie.send_packet(reset_packet)
192
+ @toc.fetch_from_crazyflie(@crazyflie, port, @in_queue)
193
+ end
194
+
195
+ # Creates a log block with the information from a configuration
196
+ # object.
197
+ # @param log_conf [LogConf] Configuration for this block
198
+ # @return [Integer] block ID if things went well,
199
+ def create_log_block(log_conf)
200
+ start_packet_reader_thread() if !@packet_reader_thread
201
+ block = LogBlock.new(log_conf.variables,
202
+ {:period => log_conf.period})
203
+ block_id = block.ident
204
+ @log_blocks[block_id] = block
205
+ packet = packet_factory()
206
+ packet.data = [CMD_CREATE_BLOCK, block_id]
207
+ log_conf.variables.each do |var|
208
+ if var.is_toc_variable?
209
+ packet.data << var.stored_fetch_as
210
+ packet.data << @toc[var.name].ident
211
+ else
212
+ bin_stored_fetch_as = [var.stored_fetch_as].pack('C')
213
+ bin_address = [var.address].pack('L<')
214
+ packet.data += bin_stored_fetch_as.unpack('C*')
215
+ packet.data += bin_address.unpack('C*')
216
+ end
217
+ end
218
+ logger.debug "Adding block #{block_id}"
219
+ @crazyflie.send_packet(packet)
220
+ return block_id
221
+ end
222
+
223
+ # Sends the START_LOGGING command for a given block.
224
+ # It should be called after #create_toc_log_block. This call
225
+ # will return immediately, but the provided block will be called
226
+ # regularly as logging data is received, until #stop_logging is
227
+ # issued for the same log
228
+ # Crazyflie. It fails silently if the block does not exist.
229
+ #
230
+ # @param block_id [Integer]
231
+ # @param data_callback [Proc] a block to be called everytime
232
+ # the log data is received.
233
+ def start_logging(block_id, &data_callback)
234
+ block = @log_blocks[block_id]
235
+ block.data_callback = data_callback
236
+ return if !block
237
+ start_packet_reader_thread() if !@packet_reader_thread
238
+ packet = packet_factory()
239
+ period = block.period
240
+ packet.data = [CMD_START_LOGGING, block_id, period]
241
+ logger.debug("Start logging on #{block_id} every #{period*10} ms")
242
+ @crazyflie.send_packet(packet)
243
+ end
244
+
245
+ # Sends the STOP_LOGGING command to the crazyflie for a given block.
246
+ # It fails silently if the block does not exist.
247
+ # @param block_id [Integer]
248
+ def stop_logging(block_id)
249
+ block = @log_blocks[block_id]
250
+ return if !block
251
+ packet = packet_factory()
252
+ packet.data = [CMD_STOP_LOGGING, block_id]
253
+ logger.debug("Stop logging on #{block_id}")
254
+ @crazyflie.send_packet(packet)
255
+ end
256
+
257
+ # Sends the DELETE_BLOCK command to the Crazyflie for a given block.
258
+ # It fails silently if the block does not exist.
259
+ # To be called after #stop_logging.
260
+ def delete_block(block_id)
261
+ block = @log_blocks.delete(block_id)
262
+ return if !block
263
+ packet = packet_factory()
264
+ packet.data = [CMD_DELETE_BLOCK, block_id]
265
+ @crazyflie.send_packet(packet)
266
+ end
267
+
268
+ # A thread that processes the queue of packets intended for this
269
+ # facility. Recommended to start it after TOC has been refreshed.
270
+ def start_packet_reader_thread
271
+ stop_packet_reader_thread()
272
+ @packet_reader_thread = Thread.new do
273
+ Thread.current.priority = -4
274
+ loop do
275
+ packet = @in_queue.pop() # block here if nothing is up
276
+ # @todo align these two
277
+ case packet.channel()
278
+ when LOG_SETTINGS_CHANNEL
279
+ handle_settings_packet(packet)
280
+ when LOG_DATA_CHANNEL
281
+ handle_logdata_packet(packet)
282
+ when TOC_CHANNEL
283
+ # We are refreshing TOC probably
284
+ @in_queue << packet
285
+ sleep 0.2
286
+ else
287
+ logger.debug("Log on #{packet.channel}. Cannot handle")
288
+ ## @in_queue << packet
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ # Stop the facility's packet processing
295
+ def stop_packet_reader_thread
296
+ @packet_reader_thread.kill() if @packet_reader_thread
297
+ @packet_reader_thread = nil
298
+ end
299
+
300
+ # Finds a log block by id
301
+ # @param block_id [Integer] the block ID
302
+ # @return p
303
+ def [](block_id)
304
+ @log_blocks[block_id]
305
+ end
306
+
307
+ # Processes an incoming settings packet. Sort of a callback
308
+ # @param packet [CRTPPacket] the packet on the settings channel
309
+ def handle_settings_packet(packet)
310
+ cmd = packet.data[0] # byte 0 of data
311
+ payload = packet.data_repack()[1..-1] # the rest of data
312
+
313
+ block_id = payload[0].ord()
314
+ #See projects:crazyflie:firmware:comm_protocol#list_of_return_codes
315
+ # @todo write down error codes in some constant
316
+ error_st = payload[1].ord() # 0 is no error
317
+
318
+ case cmd
319
+ when CMD_CREATE_BLOCK
320
+ if !@log_blocks[block_id]
321
+ logger.error "No log entry for #{block_id}"
322
+ return
323
+ end
324
+ if error_st != 0
325
+ hex_error = error_st.to_s(16)
326
+ mesg = "Error creating block #{block_id}: #{hex_error}"
327
+ logger.error(mesg)
328
+ return
329
+ end
330
+ # Block was created, let's start logging
331
+ logger.debug "Log block #{block_id} created"
332
+ # We do not start logging right away do we?
333
+ when CMD_APPEND_BLOCK
334
+ logger.debug "Received log settings with APPEND_LOG"
335
+ when CMD_DELETE_BLOCK
336
+ logger.debug "Received log settings with DELETE_LOG"
337
+ when CMD_START_LOGGING
338
+ if error_st != 0
339
+ hex_error = error_st.to_s(16)
340
+ mesg = "Error starting to log #{block_id}: #{hex_error}"
341
+ logger.error(mesg)
342
+ else
343
+ logger.debug "Logging started for #{block_id}"
344
+ end
345
+ when CMD_STOP_LOGGING
346
+ # @todo
347
+ logger.debug "Received log settings with STOP_LOGGING"
348
+ when CMD_RESET_LOGGING
349
+ # @todo
350
+ logger.debug "Received log settings with RESET_LOGGING"
351
+ else
352
+ mesg = "Received log settings with #{cmd}. Dont now what to do"
353
+ logger.warn(mesg)
354
+ end
355
+
356
+ end
357
+ private :handle_settings_packet
358
+
359
+ def handle_logdata_packet(packet)
360
+ block_id = packet.data[0]
361
+ #logger.debug("Handling log data for block #{block_id}")
362
+ #timestamp = packet.data[1..3] . pack and re unpack as 4 bytes
363
+ logdata = packet.data_repack()[4..-1]
364
+ block = @log_blocks[block_id]
365
+ if block
366
+ block.unpack_log_data(logdata)
367
+ else
368
+ logger.error "No entry for logdata for block #{block_id}"
369
+ end
370
+ end
371
+ private :handle_logdata_packet
372
+
373
+ def packet_factory
374
+ packet = CRTPPacket.new()
375
+ packet.modify_header(nil,
376
+ CRTP_PORTS[:logging],
377
+ LOG_SETTINGS_CHANNEL)
378
+ return packet
379
+ end
380
+ private :packet_factory
381
+
382
+ end
383
+ end
@@ -0,0 +1,57 @@
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
+
21
+ # Interface for Logging configuration objects
22
+ class LogConf
23
+ attr_reader :variables, :data_callback, :period
24
+ def initialize(variables, data_callback, opts={})
25
+ @variables = variables
26
+ @data_callback = data_callback
27
+ @period = opts[:period] || 20
28
+ end
29
+ end
30
+
31
+ # Interface for Logging variable configuration objects
32
+ # this class lists methods to be implemented
33
+ # Python implementation is in cfclient/utils/logconfigreader.py
34
+ class LogConfVariable
35
+
36
+ attr_reader :name, :stored_as, :fetch_as, :address
37
+
38
+ def initialize(name, is_toc, stored_as, fetch_as, address=0)
39
+ @name = name
40
+ @is_toc = is_toc
41
+ @stored_as = stored_as
42
+ @fetch_as = fetch_as
43
+ @address = address
44
+ end
45
+ # @return [Integer] a byte where the upper 4 bits are the
46
+ # type indentifier of how the variable is stored and the lower
47
+ # 4 bits are the type the variable should be fetched as
48
+ def stored_fetch_as
49
+ return @stored_as << 4 | (0x0F & @fetch_as)
50
+ end
51
+
52
+ # @return [TrueClass,FalseClass] true if it is stored in the TOC
53
+ def is_toc_variable?
54
+ return @is_toc == true
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,220 @@
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 'timeout'
20
+
21
+ module Crubyflie
22
+ # An element in the Parameters Table of Contents
23
+ class ParamTOCElement < TOCElement
24
+ # A map between crazyflie C types and ruby directives to
25
+ # interpret them. This will help parsing the parameter data
26
+ C_RUBY_TYPE_MAP = {
27
+ 0 => {
28
+ :ctype => "int8_t",
29
+ :directive => 'c',
30
+ :size => 1
31
+ },
32
+ 1 => {
33
+ :ctype => "int16_t",
34
+ :directive => 's<',
35
+ :size => 2
36
+ },
37
+ 2 => {
38
+ :ctype => "int32_t",
39
+ :directive => 'l<',
40
+ :size => 4
41
+ },
42
+ 3 => {
43
+ :ctype => "int64_t",
44
+ :directive => 'q<',
45
+ :size => 8
46
+ },
47
+ 5 => {
48
+ :ctype => "FP16",
49
+ :directive => 'e',
50
+ :size => 2
51
+ },
52
+ 6 => {
53
+ :ctype => "float",
54
+ :directive => 'e',
55
+ :size => 4
56
+ },
57
+ 7 => {
58
+ :ctype => "double",
59
+ :directive => 'E',
60
+ :size => 8
61
+ },
62
+ 8 => {
63
+ :ctype => "uint8_t",
64
+ :directive => 'C',
65
+ :size => 1
66
+ },
67
+ 9 => {
68
+ :ctype => "uint16_t",
69
+ :directive => 'S<',
70
+ :size => 2
71
+ },
72
+ 10 => {
73
+ :ctype => "uint32_t",
74
+ :directive => 'L<',
75
+ :size => 4
76
+ },
77
+ 11 => {
78
+ :ctype => "int64_t",
79
+ :directive => 'Q<',
80
+ :size => '8'
81
+ }
82
+ }
83
+
84
+ # Initializes a Param TOC element, which means interpreting the
85
+ # data in the packet and calling the parent class
86
+ # @param data [String] a binary payload
87
+ # @todo It turns out this is the same as Log. Only the type conversion
88
+ # changes
89
+ def initialize(data)
90
+ # The group and name are zero-terminated strings from the 3rd byte
91
+ group, name = data[2..-1].unpack('Z*Z*')
92
+ ident = data[0].ord()
93
+ ctype_id = data[1].ord() & 0b1111 #from 0 to 15
94
+ ctype = C_RUBY_TYPE_MAP[ctype_id][:ctype]
95
+ directive = C_RUBY_TYPE_MAP[ctype_id][:directive]
96
+ access = data[1].ord() & 0b00010000 # 5th bit
97
+
98
+ super({
99
+ :ident => ident,
100
+ :group => group,
101
+ :name => name,
102
+ :ctype => ctype,
103
+ :type_id => ctype_id,
104
+ :directive => directive,
105
+ :access => access
106
+ })
107
+ end
108
+ end
109
+
110
+ # The parameter facility. Used to retrieve the table of contents,
111
+ # set the value of a parameter and read the value of a parameter
112
+ class Param
113
+ include Logging
114
+ include CRTPConstants
115
+
116
+ attr_reader :toc
117
+ # Initialize the parameter facility
118
+ # @param crazyflie [Crazyflie]
119
+ def initialize(crazyflie)
120
+ @crazyflie = crazyflie
121
+ @in_queue = crazyflie.crtp_queues[:param]
122
+ @toc = TOC.new(@crazyflie.cache_folder, ParamTOCElement)
123
+ end
124
+
125
+ # Refreshes the TOC. It only returns when it is finished
126
+ def refresh_toc
127
+ channel = TOC_CHANNEL
128
+ port = Crazyflie::CRTP_PORTS[:param]
129
+ @toc.fetch_from_crazyflie(@crazyflie, port, @in_queue)
130
+ end
131
+
132
+ # Set the value of a paremeter. This call will only return after
133
+ # a ack response has been received, or will timeout if
134
+ # #CRTPConstants::WAIT_PACKET_TIMEOUT is reached.
135
+ # @param name [String] parameter group.name
136
+ # @param value [Numeric] a value. It must be packable as binary data,
137
+ # the type being set in the params TOC
138
+ # @param block [Proc] an optional block that will be called with the
139
+ # response CRTPPacket received. Otherwise will
140
+ # log to debug
141
+ def set_value(name, value, &block)
142
+ element = @toc[name]
143
+ if element.nil?
144
+ logger.error "Param #{name} not in TOC!"
145
+ return
146
+ end
147
+
148
+ ident = element.ident
149
+ packet = CRTPPacket.new()
150
+ packet.modify_header(nil, CRTP_PORTS[:param],
151
+ PARAM_WRITE_CHANNEL)
152
+ packet.data = [ident]
153
+ packet.data += [value].pack(element.directive()).unpack('C*')
154
+ @in_queue.clear()
155
+ @crazyflie.send_packet(packet, true) # expect answer
156
+
157
+ response = wait_for_response()
158
+ return if response.nil?
159
+
160
+ if block_given?
161
+ yield response
162
+ else
163
+ mesg = "Got answer to setting param '#{name}' with '#{value}'"
164
+ logger.debug(mesg)
165
+ end
166
+ end
167
+
168
+ # Request an update for a parameter and call the provided block with
169
+ # the value of the parameter. This call will block until the response
170
+ # has been received or the #CRTPConstants::WAIT_PACKET_TIMEOUT is
171
+ # reached.
172
+ # @param name [String] a name in the form group.name
173
+ # @param block [Proc] a block to be called with the value as argument
174
+ def get_value(name, &block)
175
+ element = @toc[name]
176
+ if element.nil?
177
+ logger.error "Cannot update #{name}, not in TOC"
178
+ return
179
+ end
180
+ packet = CRTPPacket.new()
181
+ packet.modify_header(nil, CRTP_PORTS[:param],
182
+ PARAM_READ_CHANNEL)
183
+ packet.data = [element.ident]
184
+ @in_queue.clear()
185
+ @crazyflie.send_packet(packet, true)
186
+
187
+ # Pop packages until mine comes up
188
+ begin
189
+ response = wait_for_response()
190
+ return if response.nil?
191
+
192
+ ident = response.data()[0]
193
+ if ident != element.ident()
194
+ mesg = "Value expected for element with ID #{element.ident}"
195
+ mesg << " but got for element with ID #{ident}. Requeing"
196
+ logger.debug(mesg)
197
+ end
198
+ end until ident == element.ident()
199
+
200
+ value = response.data_repack()[1..-1]
201
+ value = value.unpack(element.directive).first
202
+ yield(value)
203
+ end
204
+ alias_method :request_param_update, :get_value
205
+
206
+ def wait_for_response
207
+ begin
208
+ wait = WAIT_PACKET_TIMEOUT
209
+ response = timeout(wait, WaitTimeoutException) do
210
+ @in_queue.pop()
211
+ end
212
+ return response
213
+ rescue WaitTimeoutException
214
+ logger.error("Param: waited too long to get response")
215
+ return nil
216
+ end
217
+ end
218
+ private :wait_for_response
219
+ end
220
+ end