ruby-player 0.0.1 → 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.
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/Guardfile +5 -0
- data/README.md +10 -17
- data/TODO.md +1 -12
- data/examples/simple_example.rb +27 -0
- data/{spec → examples}/world/test.cfg +0 -0
- data/{spec → examples}/world/test.world +5 -5
- data/lib/ruby-player.rb +12 -5
- data/lib/ruby-player/client.rb +136 -42
- data/lib/ruby-player/common.rb +44 -7
- data/lib/ruby-player/constants.rb +584 -0
- data/lib/ruby-player/dev_addr.rb +59 -0
- data/lib/ruby-player/device.rb +60 -0
- data/lib/ruby-player/header.rb +81 -0
- data/lib/ruby-player/position2d.rb +161 -91
- data/lib/ruby-player/ranger.rb +118 -88
- data/lib/ruby-player/version.rb +1 -1
- data/ruby-player.gemspec +3 -1
- data/spec/client_spec.rb +145 -11
- data/spec/position2d_spec.rb +115 -46
- data/spec/ranger_spec.rb +132 -41
- metadata +36 -28
- data/lib/ruby-player/c_type.rb +0 -36
- data/lib/ruby-player/c_type/bbox3d_t.rb +0 -24
- data/lib/ruby-player/c_type/client_t.rb +0 -36
- data/lib/ruby-player/c_type/devaddr.rb +0 -24
- data/lib/ruby-player/c_type/device_t.rb +0 -35
- data/lib/ruby-player/c_type/diagnostic.rb +0 -22
- data/lib/ruby-player/c_type/pose3d_t.rb +0 -27
- data/lib/ruby-player/c_type/position2d_t.rb +0 -32
- data/lib/ruby-player/c_type/ranger_t.rb +0 -42
- data/lib/ruby-player/c_type/sockaddr_in_t.rb +0 -24
@@ -0,0 +1,59 @@
|
|
1
|
+
# Ruby Player - Ruby client library for Player (tools for robots)
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
module Player
|
16
|
+
class DevAddr
|
17
|
+
include Common
|
18
|
+
|
19
|
+
attr_reader :host, :robot, :interface, :index
|
20
|
+
|
21
|
+
def initialize(addr = {})
|
22
|
+
@host = addr[:host].to_i
|
23
|
+
@robot = addr[:robot].to_i
|
24
|
+
@interface = addr[:interface].to_i
|
25
|
+
@index = addr[:index].to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def DevAddr.decode(str)
|
29
|
+
ary = str.unpack("NNNN")
|
30
|
+
DevAddr.new(host: ary[0], robot: ary[1], interface: ary[2], index: ary[3])
|
31
|
+
end
|
32
|
+
|
33
|
+
def encode
|
34
|
+
[@host, @robot, @interface, @index].pack("NNNN")
|
35
|
+
end
|
36
|
+
|
37
|
+
def interface_name
|
38
|
+
@interface_name ||= search_interface_name(@interface)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
@host == other.host and @robot == other.robot and @interface == other.interface and @index == other.index
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"device addr [#@host:#@robot] #@interface_name:#@index"
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_a
|
50
|
+
[@host, @robot, @interface, @index]
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def search_interface_name(code)
|
55
|
+
name = search_const_name code, /PLAYER_[\w]*_CODE/
|
56
|
+
name.to_s.scan(/PLAYER_([\w]*)_CODE/).join.downcase
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Ruby Player - Ruby client library for Player (tools for robots)
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
require "socket"
|
15
|
+
|
16
|
+
module Player
|
17
|
+
class Device
|
18
|
+
include Common
|
19
|
+
|
20
|
+
# Device address
|
21
|
+
attr_reader :addr
|
22
|
+
|
23
|
+
# Device geometry
|
24
|
+
# @return [Hash] geometry { :px, :py. :pz, :proll, :ppitch, :pyaw, :sw, :sl, :sh }
|
25
|
+
attr_reader :geom
|
26
|
+
|
27
|
+
|
28
|
+
def initialize(addr, client, log_level)
|
29
|
+
@addr, @client = addr, client
|
30
|
+
@log_level = log_level
|
31
|
+
|
32
|
+
@geom = {px: 0.0, py: 0.0, pz: 0.0, proll: 0.0, ppitch: 0.0, pyaw: 0.0, sw: 0.0, sl: 0.0, sh: 0.0}
|
33
|
+
end
|
34
|
+
|
35
|
+
def fill(hdr,msg)
|
36
|
+
raise_error "Method `fill` isn't implemented for `#{self.class}`"
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_response(hdr, msg)
|
40
|
+
raise_error "Method `handle_response` isn't implemented for `#{self.class}`"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def send_message(type, subtype, msg="")
|
45
|
+
@client.write( Header.new(
|
46
|
+
dev_addr: @addr,
|
47
|
+
type: type,
|
48
|
+
subtype: subtype,
|
49
|
+
size: msg.bytesize), msg)
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_geom(msg)
|
53
|
+
data = msg.unpack("G*")
|
54
|
+
[:px,:py,:pz, :proll,:ppitch,:pyaw, :sw,:sl,:sh].each_with_index do |k,i|
|
55
|
+
@geom[k] = data[i]
|
56
|
+
end
|
57
|
+
debug("Get geom px=%.2f py=%.2f pz=%.2f; proll=%.2f, ppitch=%.2f, pyaw=%.2f, sw=%.2f, sl=%.2f, sh=%.2f" % @geom.values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# Ruby Player - Ruby client library for Player (tools for robots)
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
module Player
|
16
|
+
class Header
|
17
|
+
include Common
|
18
|
+
|
19
|
+
attr_reader :dev_addr, :type, :subtype, :time, :seq, :size
|
20
|
+
|
21
|
+
def initialize(param = {})
|
22
|
+
@dev_addr = param[:dev_addr]
|
23
|
+
@type = param[:type].to_i
|
24
|
+
@subtype = param[:subtype].to_i
|
25
|
+
@time = param[:time] || Time.now.to_f / 1000
|
26
|
+
@seq = param[:seq].to_i
|
27
|
+
@size = param[:size].to_i
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def Header.decode(str)
|
32
|
+
dev_addr = DevAddr.decode(str[0,16])
|
33
|
+
type, subtype, time, seq, size = str[16,24].unpack("NNGNN")
|
34
|
+
Header.new(dev_addr: dev_addr, type: type, subtype: subtype, time: time, seq: seq, size: size)
|
35
|
+
end
|
36
|
+
|
37
|
+
def Header.from_a(ary)
|
38
|
+
Header.decode ary.pack("NNNNNNGNN")
|
39
|
+
end
|
40
|
+
|
41
|
+
def encode
|
42
|
+
@dev_addr.encode + [@type, @subtype, @time, @seq, @size].pack("NNGNN")
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
@dev_addr == other.dev_addr and @type == other.type and @subtype == other.subtype and @time == other.time and @seq == other.seq and @size == other.size
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"header to #@dev_addr of message [type=#{type_name}, subtype=#{subtype_name}, size=#@size]"
|
51
|
+
end
|
52
|
+
|
53
|
+
def type_name
|
54
|
+
@type_name ||= search_msg_type_name(@type)
|
55
|
+
end
|
56
|
+
|
57
|
+
def subtype_name
|
58
|
+
@subtype_name ||= search_msg_subtype_name(dev_addr.interface_name, type_name, @subtype)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def search_msg_type_name(type)
|
63
|
+
name = search_const_name type, /PLAYER_MSGTYPE_[\w]*/
|
64
|
+
if name != ""
|
65
|
+
name[7..-1]
|
66
|
+
else
|
67
|
+
type.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def search_msg_subtype_name(interface_name, type_name, subtype)
|
72
|
+
type_prefix = type_name == "MSGTYPE_RESP_ACK" ? "" : type_name.split("_")[-1]
|
73
|
+
name = search_const_name subtype, "PLAYER_" + interface_name.upcase + "_" + type_prefix + ".*"
|
74
|
+
if name != ""
|
75
|
+
name[7..-1]
|
76
|
+
else
|
77
|
+
subtype.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -17,146 +17,192 @@ module Player
|
|
17
17
|
#
|
18
18
|
# @example
|
19
19
|
# # get proxy object
|
20
|
-
# pos2d = client
|
20
|
+
# pos2d = client.subcribe("position2d", index: 0)
|
21
21
|
# # setup speed of robot
|
22
22
|
# pos2d.set_speed(vx: 1.2, vy: 0.1, va: 0.3)
|
23
23
|
#
|
24
24
|
# #update data from server
|
25
|
-
# client.read
|
25
|
+
# client.read!
|
26
26
|
# #read velocityand position by X,Y and angle
|
27
|
-
# pos2d.
|
28
|
-
# pos2d.
|
29
|
-
|
30
|
-
class Position2d
|
31
|
-
include CType
|
32
|
-
include Common
|
27
|
+
# pos2d.position #=> { :px => 0.2321, :py => 0,01, :pa => 0.2, :vx => 1.2, :vy => 0.1, :va => 0.3, :stall => 1 }
|
28
|
+
# pos2d.stop!
|
29
|
+
class Position2d < Device
|
33
30
|
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
# Position of robot
|
32
|
+
# @return [Hash] hash position {:px, :py, :pa, :vx, :vy, :va, :stall }
|
33
|
+
attr_reader :position
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
def initialize(addr, client, log_level)
|
36
|
+
super
|
37
|
+
@position = {px: 0.0, py: 0.0, pa: 0.0, vx: 0.0, vy: 0.0, va: 0.0, stall: 0}
|
38
|
+
end
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
# Query robot geometry
|
41
|
+
# @return self
|
42
|
+
def query_geom
|
43
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_GET_GEOM)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Turn on motor
|
48
|
+
# @return self
|
49
|
+
def turn_on!
|
50
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_MOTOR_POWER, [1].pack("N"))
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Turn off motor
|
55
|
+
def turn_off!
|
56
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_MOTOR_POWER, [0].pack("N"))
|
57
|
+
self
|
48
58
|
end
|
49
59
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
60
|
+
def direct_speed_control!
|
61
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_VELOCITY_MODE, [0].pack("N"))
|
62
|
+
self
|
63
|
+
end
|
53
64
|
|
54
|
-
|
65
|
+
def separate_speed_control!
|
66
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_VELOCITY_MODE, [1].pack("N"))
|
67
|
+
self
|
55
68
|
end
|
56
69
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
{
|
61
|
-
px: @pos2d[:px],
|
62
|
-
py: @pos2d[:py],
|
63
|
-
pa: @pos2d[:pa]
|
64
|
-
}
|
70
|
+
def position_control!
|
71
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_POSITION_MODE, [0].pack("N"))
|
72
|
+
self
|
65
73
|
end
|
66
|
-
|
74
|
+
|
75
|
+
def speed_control!
|
76
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_POSITION_MODE, [1].pack("N"))
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
67
80
|
# Set odometry of robot.
|
68
|
-
# @
|
81
|
+
# @param [Hash] odometry
|
69
82
|
# @option odometry :px x position (m)
|
70
83
|
# @option odometry :py y position (m)
|
71
84
|
# @option odometry :pa angle (rad).
|
72
85
|
# @return self
|
73
|
-
def set_odometry(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
86
|
+
def set_odometry(odom)
|
87
|
+
data = [
|
88
|
+
odom[:px].to_f,
|
89
|
+
odom[:py].to_f,
|
90
|
+
odom[:pa].to_f
|
78
91
|
]
|
92
|
+
|
93
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SET_ODOM, data.pack("GGG"))
|
94
|
+
self
|
95
|
+
end
|
79
96
|
|
80
|
-
|
97
|
+
# Set speed PID paramters
|
98
|
+
# @param [Hash] params PID params
|
99
|
+
# @option params :kp P
|
100
|
+
# @option params :ki I
|
101
|
+
# @option params :kd D
|
102
|
+
# @return self
|
103
|
+
def set_speed_pid(params={})
|
104
|
+
data = [
|
105
|
+
params[:kp].to_f,
|
106
|
+
params[:ki].to_f,
|
107
|
+
params[:kd].to_f
|
108
|
+
]
|
109
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SPEED_PID, data.pack("GGG"))
|
81
110
|
self
|
82
111
|
end
|
83
112
|
|
84
|
-
#
|
113
|
+
# Set position PID paramters
|
114
|
+
# @param [Hash] params PID params
|
115
|
+
# @option params :kp P
|
116
|
+
# @option params :ki I
|
117
|
+
# @option params :kd D
|
85
118
|
# @return self
|
86
|
-
def
|
87
|
-
|
119
|
+
def set_position_pid(params={})
|
120
|
+
data = [
|
121
|
+
params[:kp].to_f,
|
122
|
+
params[:ki].to_f,
|
123
|
+
params[:kd].to_f
|
124
|
+
]
|
125
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_POSITION_PID, data.pack("GGG"))
|
126
|
+
self
|
88
127
|
end
|
89
128
|
|
90
|
-
#
|
91
|
-
# @
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
129
|
+
# Set speed profile
|
130
|
+
# @param [Hash] params profile prarams
|
131
|
+
# @option params :spped max speed (m/s)
|
132
|
+
# @option params :acc max acceleration (m/s^2)
|
133
|
+
# @return self
|
134
|
+
def set_speed_profile(params={})
|
135
|
+
data = [
|
136
|
+
params[:speed].to_f,
|
137
|
+
params[:acc].to_f
|
138
|
+
]
|
139
|
+
|
140
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SPEED_PROF, data.pack("GG"))
|
141
|
+
self
|
103
142
|
end
|
104
143
|
|
144
|
+
# Reset odometry to zero
|
145
|
+
# @return self
|
146
|
+
def reset_odometry
|
147
|
+
send_message(PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_RESET_ODOM)
|
148
|
+
self
|
149
|
+
end
|
105
150
|
|
106
151
|
# Set speed of robot. All speeds are defined in the robot coordinate system.
|
107
|
-
# @
|
152
|
+
# @param [Hash] speeds
|
108
153
|
# @option speeds :vx forward speed (m/s)
|
109
154
|
# @option speeds :vy sideways speed (m/s); this field is used by omni-drive robots only.
|
110
155
|
# @option speeds :va rotational speed (rad/s).
|
156
|
+
# @option speeds :stall state of motor
|
111
157
|
# @return self
|
112
158
|
def set_speed(speeds)
|
113
|
-
|
114
|
-
speeds[:vx]
|
115
|
-
|
116
|
-
|
117
|
-
|
159
|
+
data = [
|
160
|
+
speeds[:vx] || @position[:vx],
|
161
|
+
speeds[:vy] || @position[:vy],
|
162
|
+
speeds[:va] || @position[:va],
|
163
|
+
speeds[:stall] || @position[:stall]
|
118
164
|
]
|
119
|
-
|
165
|
+
send_message(PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_VEL, data.pack("GGGN"))
|
120
166
|
self
|
121
167
|
end
|
122
168
|
|
123
169
|
# Set speed of carlike robot
|
124
|
-
# @
|
170
|
+
# @param [Hash] speeds
|
125
171
|
# @option speeds :vx forward speed (m/s)
|
126
|
-
# @option speeds :a angle
|
172
|
+
# @option speeds :a turning angle (rad).
|
127
173
|
def set_car(speeds)
|
128
|
-
|
129
|
-
speeds[:vx]
|
130
|
-
speeds[:a]
|
174
|
+
data = [
|
175
|
+
speeds[:vx] || @position[:vx],
|
176
|
+
speeds[:a] || @position[:pa]
|
131
177
|
]
|
132
|
-
|
178
|
+
send_message(PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_CAR, data.pack("GG"))
|
133
179
|
self
|
134
180
|
end
|
135
181
|
|
136
|
-
#
|
137
|
-
#
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
182
|
+
# The position interface accepts translational velocity + absolute heading commands
|
183
|
+
# (speed and angular position) for the robot's motors (only supported by some drivers)
|
184
|
+
# @param [Hash] speeds
|
185
|
+
# @option speeds :vx forward speed (m/s)
|
186
|
+
# @option speeds :a absolutle angle (rad).
|
187
|
+
def set_speed_head(speeds)
|
188
|
+
data = [
|
189
|
+
speeds[:vx] || @position[:vx],
|
190
|
+
speeds[:a] || @position[:pa]
|
191
|
+
]
|
192
|
+
send_message(PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_VEL_HEAD, data.pack("GG"))
|
193
|
+
self
|
144
194
|
end
|
145
195
|
|
196
|
+
|
146
197
|
# State of motor
|
147
198
|
# @return [Boolean] true - on
|
148
|
-
def
|
149
|
-
@
|
150
|
-
end
|
151
|
-
|
152
|
-
# Turn on\off motor
|
153
|
-
# @param [Boolean] true - turn on
|
154
|
-
def enable=(val)
|
155
|
-
try_with_error C.playerc_position2d_enable(@pos2d, val ? 1 : 0)
|
199
|
+
def power
|
200
|
+
@position[:stall] == 1
|
156
201
|
end
|
157
202
|
|
158
203
|
# Stop robot set speed to 0
|
159
|
-
|
204
|
+
# @return self
|
205
|
+
def stop!
|
160
206
|
set_speed(vx: 0, vy: 0, va: 0)
|
161
207
|
end
|
162
208
|
|
@@ -165,11 +211,35 @@ module Player
|
|
165
211
|
speed[:vx] == 0 && speed[:vy] == 0 && speed[:va] == 0
|
166
212
|
end
|
167
213
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
214
|
+
def fill(hdr, msg)
|
215
|
+
case hdr.subtype
|
216
|
+
when PLAYER_POSITION2D_DATA_STATE
|
217
|
+
read_position(msg)
|
218
|
+
when PLAYER_POSITION2D_DATA_GEOM
|
219
|
+
read_geom(msg)
|
220
|
+
else
|
221
|
+
unexpected_message hdr
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def handle_response(hdr, msg)
|
226
|
+
case hdr.subtype
|
227
|
+
when PLAYER_POSITION2D_REQ_GET_GEOM
|
228
|
+
read_geom(msg)
|
229
|
+
when 2..9
|
230
|
+
nil #null response
|
231
|
+
else
|
232
|
+
unexpected_message hdr
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
def read_position(msg)
|
238
|
+
data = msg.unpack("GGGGGGN")
|
239
|
+
@position.keys.each_with_index do |k,i|
|
240
|
+
@position[k] = data[i]
|
241
|
+
end
|
242
|
+
debug("Get position px=%.2f py=%.2f pa=%.2f; vx=%.2f, vy=%.2f, va=%.2f, stall=%d" % position.values)
|
173
243
|
end
|
174
244
|
end
|
175
245
|
end
|