hybridgroup-argus 0.2.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/MIT-LICENSE +20 -0
- data/README.md +27 -0
- data/Rakefile +9 -0
- data/lib/argus/at_commander.rb +76 -0
- data/lib/argus/controller.rb +130 -0
- data/lib/argus/drone.rb +36 -0
- data/lib/argus/nav_data.rb +61 -0
- data/lib/argus/nav_streamer.rb +18 -0
- data/lib/argus/pave_parser.rb +39 -0
- data/lib/argus/tcp_video_streamer.rb +20 -0
- data/lib/argus/udp_nav_streamer.rb +24 -0
- data/lib/argus/udp_sender.rb +13 -0
- data/lib/argus/version.rb +10 -0
- data/lib/argus.rb +9 -0
- data/spec/argus/at_commander_spec.rb +54 -0
- data/spec/argus/controller_spec.rb +95 -0
- data/spec/argus/udp_sender_spec.rb +20 -0
- data/spec/spec_helper.rb +10 -0
- metadata +91 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Jim Weirich
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Argus -- Parrot AR Drone Ruby API
|
2
|
+
|
3
|
+
## Current Status
|
4
|
+
|
5
|
+
* Experimental
|
6
|
+
* Subject to change
|
7
|
+
* Use at your own risk
|
8
|
+
* May cause cancer
|
9
|
+
|
10
|
+
## Example
|
11
|
+
|
12
|
+
<pre>
|
13
|
+
require 'argus'
|
14
|
+
|
15
|
+
drone = Argus::Drone.new
|
16
|
+
drone.start
|
17
|
+
|
18
|
+
drone.take_off
|
19
|
+
sleep 5
|
20
|
+
drone.turn_right(1.0)
|
21
|
+
sleep 5
|
22
|
+
drone.turn_left(1.0)
|
23
|
+
sleep 5
|
24
|
+
drone.hover.land
|
25
|
+
sleep 5
|
26
|
+
drone.stop
|
27
|
+
</pre>
|
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Argus
|
4
|
+
class ATCommander
|
5
|
+
def initialize(sender)
|
6
|
+
@sender = sender
|
7
|
+
@seq = 0
|
8
|
+
@ref_data = "0"
|
9
|
+
@pcmd_data = "0,0,0,0,0"
|
10
|
+
@buffer = ""
|
11
|
+
@thread = nil
|
12
|
+
@interval = 0.020
|
13
|
+
@mutex = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
@running = true
|
18
|
+
@thread = Thread.new do
|
19
|
+
while @running
|
20
|
+
tick
|
21
|
+
sleep @interval
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def tick
|
27
|
+
@mutex.synchronize do
|
28
|
+
packet do
|
29
|
+
command("REF", @ref_data)
|
30
|
+
command("PCMD", @pcmd_data)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop
|
36
|
+
@running = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def join
|
40
|
+
@thread.join if @thread
|
41
|
+
end
|
42
|
+
|
43
|
+
def interval=(new_interval)
|
44
|
+
@mutex.synchronize do @interval = new_interval end
|
45
|
+
end
|
46
|
+
|
47
|
+
def ref(data)
|
48
|
+
@mutex.synchronize do @ref_data = data end
|
49
|
+
end
|
50
|
+
|
51
|
+
def pcmd(data)
|
52
|
+
@mutex.synchronize do @pcmd_data = data end
|
53
|
+
end
|
54
|
+
|
55
|
+
def config(key, value)
|
56
|
+
@buffer << "AT*CONFIG=605,\"#{key}\",\"#{value}\"\r"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def packet
|
62
|
+
yield self
|
63
|
+
flush
|
64
|
+
end
|
65
|
+
|
66
|
+
def flush
|
67
|
+
@sender.send_packet(@buffer)
|
68
|
+
@buffer = ""
|
69
|
+
end
|
70
|
+
|
71
|
+
def command(name, args)
|
72
|
+
@seq += 1
|
73
|
+
@buffer << "AT*#{name}=#{@seq},#{args}\r"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Argus
|
2
|
+
class Controller
|
3
|
+
|
4
|
+
def initialize(at_commander)
|
5
|
+
@at_commander = at_commander
|
6
|
+
|
7
|
+
@emergency = false
|
8
|
+
land
|
9
|
+
hover
|
10
|
+
end
|
11
|
+
|
12
|
+
def take_off
|
13
|
+
@flying = true
|
14
|
+
@emergency = false
|
15
|
+
update_ref
|
16
|
+
end
|
17
|
+
|
18
|
+
def land
|
19
|
+
@flying = false
|
20
|
+
update_ref
|
21
|
+
end
|
22
|
+
|
23
|
+
def emergency
|
24
|
+
@flying = false
|
25
|
+
@emergency = true
|
26
|
+
update_ref
|
27
|
+
end
|
28
|
+
|
29
|
+
def hover
|
30
|
+
@moving = false
|
31
|
+
@roll = 0.0
|
32
|
+
@pitch = 0.0
|
33
|
+
@gaz = 0.0
|
34
|
+
@yaw = 0.0
|
35
|
+
update_pcmd
|
36
|
+
end
|
37
|
+
|
38
|
+
def forward(amount)
|
39
|
+
@moving = true
|
40
|
+
@pitch = -amount
|
41
|
+
update_pcmd
|
42
|
+
end
|
43
|
+
|
44
|
+
def backward(amount)
|
45
|
+
@moving = true
|
46
|
+
@pitch = amount
|
47
|
+
update_pcmd
|
48
|
+
end
|
49
|
+
|
50
|
+
def left(amount)
|
51
|
+
@moving = true
|
52
|
+
@roll = -amount
|
53
|
+
update_pcmd
|
54
|
+
end
|
55
|
+
|
56
|
+
def right(amount)
|
57
|
+
@moving = true
|
58
|
+
@roll = amount
|
59
|
+
update_pcmd
|
60
|
+
end
|
61
|
+
|
62
|
+
def up(amount)
|
63
|
+
@moving = true
|
64
|
+
@gaz = amount
|
65
|
+
update_pcmd
|
66
|
+
end
|
67
|
+
|
68
|
+
def down(amount)
|
69
|
+
@moving = true
|
70
|
+
@gaz = -amount
|
71
|
+
update_pcmd
|
72
|
+
end
|
73
|
+
|
74
|
+
def turn_left(amount)
|
75
|
+
@moving = true
|
76
|
+
@yaw = -amount
|
77
|
+
update_pcmd
|
78
|
+
end
|
79
|
+
|
80
|
+
def turn_right(amount)
|
81
|
+
@moving = true
|
82
|
+
@yaw = amount
|
83
|
+
update_pcmd
|
84
|
+
end
|
85
|
+
|
86
|
+
def front_camera
|
87
|
+
@at_commander.config("video:video_channel", "2")
|
88
|
+
end
|
89
|
+
|
90
|
+
def bottom_camera
|
91
|
+
@at_commander.config("video:video_channel", "1")
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
REF_BASE = [18, 20, 22, 24, 28].
|
97
|
+
inject(0) { |flag, bitnum| flag | (1 << bitnum) }
|
98
|
+
REF_FLY_BIT = (1 << 9)
|
99
|
+
REF_EMERGENCY_BIT = (1 << 8)
|
100
|
+
|
101
|
+
def update_ref
|
102
|
+
n = REF_BASE
|
103
|
+
n |= REF_FLY_BIT if @flying
|
104
|
+
n |= REF_EMERGENCY_BIT if @emergency
|
105
|
+
@at_commander.ref(n.to_s)
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def update_pcmd
|
110
|
+
flags = 0
|
111
|
+
if @moving
|
112
|
+
flags = 1
|
113
|
+
iroll = encode_float(@roll)
|
114
|
+
ipitch = encode_float(@pitch)
|
115
|
+
igaz = encode_float(@gaz)
|
116
|
+
iyaw = encode_float(@yaw)
|
117
|
+
data = "#{flags},#{iroll},#{ipitch},#{igaz},#{iyaw}"
|
118
|
+
else
|
119
|
+
data = "0,0,0,0,0"
|
120
|
+
end
|
121
|
+
@at_commander.pcmd(data)
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def encode_float(float)
|
126
|
+
[float].pack('g').unpack("l>").first
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
data/lib/argus/drone.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Argus
|
4
|
+
class Drone
|
5
|
+
def initialize(socket=nil, host='192.168.1.1', port='5556')
|
6
|
+
@socket = socket || UDPSocket.new
|
7
|
+
@sender = Argus::UdpSender.new(@socket, host, port)
|
8
|
+
@at = Argus::ATCommander.new(@sender)
|
9
|
+
@controller = Argus::Controller.new(@at)
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
@at.start
|
14
|
+
end
|
15
|
+
|
16
|
+
def stop
|
17
|
+
@controller.land
|
18
|
+
@at.stop
|
19
|
+
@at.join
|
20
|
+
end
|
21
|
+
|
22
|
+
%w(
|
23
|
+
take_off land hover emergency
|
24
|
+
forward backward
|
25
|
+
left right
|
26
|
+
up down
|
27
|
+
turn_left turn_right
|
28
|
+
front_camera bottom_camera
|
29
|
+
).each do |meth|
|
30
|
+
define_method(meth) { |*args|
|
31
|
+
@controller.send(meth, *args)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Argus
|
2
|
+
class NavData
|
3
|
+
attr_accessor :drone_state, :options, :vision_flag, :sequence_number
|
4
|
+
def initialize(data)
|
5
|
+
@data = data
|
6
|
+
parse_nav_data(@data)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def parse_nav_data(data)
|
11
|
+
state = data.unpack("V").first
|
12
|
+
@drone_state = {
|
13
|
+
:flying=>state[0], #/*!< FLY MASK : (0) ardrone is landed, (1) ardrone is flying */
|
14
|
+
:videoEnabled=>state[1], #/*!< VIDEO MASK : (0) video disable, (1) video enable */
|
15
|
+
:visionEnabled=>state[2], #/*!< VISION MASK : (0) vision disable, (1) vision enable */
|
16
|
+
:controlAlgorithm=>state[3], #/*!< CONTROL ALGO : (0) euler angles control, (1) angular speed control */
|
17
|
+
:altitudeControlAlgorithm=>state[4], #/*!< ALTITUDE CONTROL ALGO : (0) altitude control inactive (1) altitude control active */
|
18
|
+
:startButtonState=>state[5], #/*!< USER feedback : Start button state */
|
19
|
+
:controlCommandAck=>state[6], #/*!< Control command ACK : (0) None, (1) one received */
|
20
|
+
:cameraReady=>state[7], #/*!< CAMERA MASK : (0) camera not ready, (1) Camera ready */
|
21
|
+
:travellingEnabled=>state[8], #/*!< Travelling mask : (0) disable, (1) enable */
|
22
|
+
:usbReady=>state[9], #/*!< USB key : (0) usb key not ready, (1) usb key ready */
|
23
|
+
:navdataDemo=>state[10], #/*!< Navdata demo : (0) All navdata, (1) only navdata demo */
|
24
|
+
:navdataBootstrap=>state[11], #/*!< Navdata bootstrap : (0) options sent in all or demo mode, (1) no navdata options sent */
|
25
|
+
:motorProblem=>state[12], #/*!< Motors status : (0) Ok, (1) Motors problem */
|
26
|
+
:communicationLost=>state[13], #/*!< Communication Lost : (1) com problem, (0) Com is ok */
|
27
|
+
:softwareFault=>state[14], #/*!< Software fault detected - user should land as quick as possible (1) */
|
28
|
+
:lowBattery=>state[15], #/*!< VBat low : (1) too low, (0) Ok */
|
29
|
+
:userEmergencyLanding=>state[16], #/*!< User Emergency Landing : (1) User EL is ON, (0) User EL is OFF*/
|
30
|
+
:timerElapsed=>state[17], #/*!< Timer elapsed : (1) elapsed, (0) not elapsed */
|
31
|
+
:MagnometerNeedsCalibration=>state[18], #/*!< Magnetometer calibration state : (0) Ok, no calibration needed, (1) not ok, calibration needed */
|
32
|
+
:anglesOutOfRange=>state[19], #/*!< Angles : (0) Ok, (1) out of range */
|
33
|
+
:tooMuchWind=>state[20], #/*!< WIND MASK: (0) ok, (1) Too much wind */
|
34
|
+
:ultrasonicSensorDeaf=>state[21], #/*!< Ultrasonic sensor : (0) Ok, (1) deaf */
|
35
|
+
:cutoutDetected=>state[22], #/*!< Cutout system detection : (0) Not detected, (1) detected */
|
36
|
+
:picVersionNumberOk=>state[23], #/*!< PIC Version number OK : (0) a bad version number, (1) version number is OK */
|
37
|
+
:atCodecThreadOn=>state[24], #/*!< ATCodec thread ON : (0) thread OFF (1) thread ON */
|
38
|
+
:navdataThreadOn=>state[25], #/*!< Navdata thread ON : (0) thread OFF (1) thread ON */
|
39
|
+
:videoThreadOn=>state[26], #/*!< Video thread ON : (0) thread OFF (1) thread ON */
|
40
|
+
:acquisitionThreadOn=>state[27], #/*!< Acquisition thread ON : (0) thread OFF (1) thread ON */
|
41
|
+
:controlWatchdogDelay=>state[28], #/*!< CTRL watchdog : (1) delay in control execution (> 5ms), (0) control is well scheduled */
|
42
|
+
:adcWatchdogDelay=>state[29], #/*!< ADC Watchdog : (1) delay in uart2 dsr (> 5ms), (0) uart2 is good */
|
43
|
+
:comWatchdogProblem=>state[30], #/*!< Communication Watchdog : (1) com problem, (0) Com is ok */
|
44
|
+
:emergencyLanding=>state[31] #/*!< Emergency landing : (0) no emergency, (1) emergency */
|
45
|
+
}
|
46
|
+
data.slice!(0..3)
|
47
|
+
@sequence_number = data.unpack("V").first
|
48
|
+
data.slice!(0..3)
|
49
|
+
@vision_flag = data.unpack("V").first
|
50
|
+
@options = []
|
51
|
+
option1 = {}
|
52
|
+
data.slice!(0..3)
|
53
|
+
option1[:id] = data.unpack("v").first
|
54
|
+
data.slice!(0..1)
|
55
|
+
option1[:size] = data.unpack("v").first
|
56
|
+
data.slice!(0..1)
|
57
|
+
option1[:data] = data.unpack("V").first
|
58
|
+
@options.push(option1)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Argus
|
4
|
+
class NavStreamer
|
5
|
+
def initialize(socket=nil, host='192.168.1.1', port='5554')
|
6
|
+
@socket = socket || UDPSocket.new
|
7
|
+
@streamer = Argus::UdpNavStreamer.new(@socket, host, port)
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
@streamer.start_stream
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive_data
|
15
|
+
@streamer.receive_packet
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Argus
|
2
|
+
class PaVEParser
|
3
|
+
def initialize(tcp_video_streamer)
|
4
|
+
@tcp_video_streamer = tcp_video_streamer
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_frame
|
8
|
+
frame = {
|
9
|
+
:signature=>(@tcp_video_streamer.read(4)).unpack("A*").first,
|
10
|
+
:version=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
11
|
+
:video_codec=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
12
|
+
:header_size=>(@tcp_video_streamer.read(2)).unpack("v").first,
|
13
|
+
:payload_size=>(@tcp_video_streamer.read(4)).unpack("V").first,
|
14
|
+
:encoded_stream_width=>(@tcp_video_streamer.read(2)).unpack("v").first,
|
15
|
+
:encoded_stream_height=>(@tcp_video_streamer.read(2)).unpack("v").first,
|
16
|
+
:display_width=>(@tcp_video_streamer.read(2)).unpack("v").first,
|
17
|
+
:display_height=>(@tcp_video_streamer.read(2)).unpack("v").first,
|
18
|
+
:frame_number=>(@tcp_video_streamer.read(4)).unpack("V").first,
|
19
|
+
:timestamp=>(@tcp_video_streamer.read(4)).unpack("V").first,
|
20
|
+
:total_chunks=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
21
|
+
:chunk_index=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
22
|
+
:frame_type=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
23
|
+
:control=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
24
|
+
:stream_byte_position_lw=>(@tcp_video_streamer.read(4)).unpack("V").first,
|
25
|
+
:stream_byte_position_uw=>(@tcp_video_streamer.read(4)).unpack("V").first,
|
26
|
+
:stream_id=>(@tcp_video_streamer.read(2)).unpack("v").first,
|
27
|
+
:total_slices=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
28
|
+
:slice_index=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
29
|
+
:header1_size=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
30
|
+
:header2_size=>(@tcp_video_streamer.read(1)).unpack("C").first,
|
31
|
+
:reserved[2]=>(@tcp_video_streamer.read(2)).unpack("H*").first,
|
32
|
+
:advertised_size=>(@tcp_video_streamer.read(4)).unpack("V").first,
|
33
|
+
:reserved[12]=>(@tcp_video_streamer.read(12)).unpack("H*").first
|
34
|
+
}
|
35
|
+
@tcp_video_streamer.read(4)
|
36
|
+
return @tcp_video_streamer.read(frame[:payload_size])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'socket'
|
2
|
+
module Argus
|
3
|
+
class TcpVideoStreamer
|
4
|
+
def initialize(tcp_socket=nil, host='192.168.1.1', port=5555)
|
5
|
+
@tcp_socket = tcp_socket || TCPSocket.new(host, port)
|
6
|
+
@host = host
|
7
|
+
@port = port
|
8
|
+
end
|
9
|
+
|
10
|
+
def start_stream(udp_socket=nil)
|
11
|
+
sock = udp_socket || UDPSocket.new
|
12
|
+
sock.send("\x01\x00\x00\x00", 0, @host, @port)
|
13
|
+
sock.close
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(n)
|
17
|
+
@tcp_socket.read(n)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Argus
|
2
|
+
class UdpNavStreamer
|
3
|
+
def initialize(udp_socket, host='192.168.1.1', port=5554)
|
4
|
+
@udp_socket = udp_socket
|
5
|
+
@host = host
|
6
|
+
@port = port
|
7
|
+
@udp_socket.bind("0.0.0.0", port)
|
8
|
+
end
|
9
|
+
|
10
|
+
def start_stream
|
11
|
+
@udp_socket.send("\x01\x00\x00\x00", 0, @host, @port)
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive_packet
|
15
|
+
data, from = @udp_socket.recvfrom(1024)
|
16
|
+
if data.unpack("V").first == "0x55667788".hex
|
17
|
+
data.slice!(0..3)
|
18
|
+
return NavData.new(data)
|
19
|
+
else
|
20
|
+
return "Not nav data!"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Argus
|
2
|
+
class UdpSender
|
3
|
+
def initialize(udp_socket, host=nil, port=nil)
|
4
|
+
@udp_socket = udp_socket
|
5
|
+
@host = host || '192.168.1.1'
|
6
|
+
@port = port || 5556
|
7
|
+
end
|
8
|
+
|
9
|
+
def send_packet(data)
|
10
|
+
@udp_socket.send(data, 0, @host, @port)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/argus.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'argus/at_commander'
|
2
|
+
require 'argus/controller'
|
3
|
+
require 'argus/udp_sender'
|
4
|
+
require 'argus/udp_nav_streamer'
|
5
|
+
require 'argus/nav_data'
|
6
|
+
require 'argus/nav_streamer'
|
7
|
+
require 'argus/drone'
|
8
|
+
require 'argus/tcp_video_streamer'
|
9
|
+
require 'argus/pave_parser'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Argus::ATCommander do
|
4
|
+
Given(:sender) { flexmock(:on, Argus::UdpSender) }
|
5
|
+
Given(:cmdr) { Argus::ATCommander.new(sender) }
|
6
|
+
|
7
|
+
describe "#tick" do
|
8
|
+
When { cmdr.send(:tick) }
|
9
|
+
|
10
|
+
context "with no commands" do
|
11
|
+
Then { sender.should have_received(:send_packet).with(/AT\*REF=\d+,0.*AT\*PCMD=\d+,0,0,0,0,0/) }
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with a ref command" do
|
15
|
+
Given { cmdr.ref("512") }
|
16
|
+
Then { sender.should have_received(:send_packet).with(/AT\*REF=\d+,512.*AT\*PCMD=\d+,0,0,0,0,0/) }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with a pcmd command" do
|
20
|
+
Given { cmdr.pcmd("1,2,3,4,5") }
|
21
|
+
Then { sender.should have_received(:send_packet).with(/AT\*REF=\d+,0.*AT\*PCMD=\d+,1,2,3,4,5/) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "increasing sequence numbers" do
|
26
|
+
Given(:seq_numbers) { [ ] }
|
27
|
+
Given {
|
28
|
+
sender.should_receive(:send_packet).and_return { |data|
|
29
|
+
data.scan(%r{=(\d+),}) { |seq|
|
30
|
+
seq_numbers << seq.first.to_i }
|
31
|
+
}
|
32
|
+
}
|
33
|
+
When { 2.times { cmdr.tick } }
|
34
|
+
Then { seq_numbers.should == [1, 2, 3, 4] }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "sending thread" do
|
38
|
+
Given {
|
39
|
+
@count = 0
|
40
|
+
sender.should_receive(:send_packet).and_return {
|
41
|
+
@count += 1
|
42
|
+
cmdr.stop if @count > 5
|
43
|
+
}
|
44
|
+
}
|
45
|
+
When {
|
46
|
+
cmdr.start
|
47
|
+
cmdr.join
|
48
|
+
}
|
49
|
+
Then { sender.should have_received(:send_packet) }
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Argus::Controller do
|
4
|
+
|
5
|
+
REF_BASE = 0x11540000
|
6
|
+
|
7
|
+
def ref_bits(n=nil)
|
8
|
+
if n.nil?
|
9
|
+
REF_BASE.to_s
|
10
|
+
else
|
11
|
+
(REF_BASE | (1 << n)).to_s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Given(:at) { flexmock(:on, Argus::ATCommander) }
|
16
|
+
Given(:controller) { Argus::Controller.new(at) }
|
17
|
+
|
18
|
+
context "navigating commands" do
|
19
|
+
Invariant { result.should == controller }
|
20
|
+
|
21
|
+
context "when taking off" do
|
22
|
+
When(:result) { controller.take_off }
|
23
|
+
Then { at.should have_received(:ref).with(ref_bits(9)) }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when landing" do
|
27
|
+
When(:result) { controller.land }
|
28
|
+
Then { at.should have_received(:ref).with(ref_bits).twice }
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when emergency" do
|
32
|
+
Given { controller.take_off }
|
33
|
+
When(:result) { controller.emergency }
|
34
|
+
Then { at.should have_received(:ref).with(ref_bits(8)) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when taking off after an emergency" do
|
38
|
+
Given { controller.emergency }
|
39
|
+
When(:result) { controller.take_off }
|
40
|
+
Then { at.should have_received(:ref).with(ref_bits(9)) }
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when hovering" do
|
44
|
+
When(:result) { controller.hover }
|
45
|
+
Then { at.should have_received(:pcmd).with("0,0,0,0,0") }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when moving forward" do
|
49
|
+
When(:result) { controller.forward(0.80) }
|
50
|
+
Then { at.should have_received(:pcmd).with("1,0,-1085485875,0,0") }
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when moving backward" do
|
54
|
+
When(:result) { controller.backward(0.80) }
|
55
|
+
Then { at.should have_received(:pcmd).with("1,0,1061997773,0,0") }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when moving to the left" do
|
59
|
+
When(:result) { controller.left(0.80) }
|
60
|
+
Then { at.should have_received(:pcmd).with("1,-1085485875,0,0,0") }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when moving to the right" do
|
64
|
+
When(:result) { controller.right(0.80) }
|
65
|
+
Then { at.should have_received(:pcmd).with("1,1061997773,0,0,0") }
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when moving up" do
|
69
|
+
When(:result) { controller.up(0.80) }
|
70
|
+
Then { at.should have_received(:pcmd).with("1,0,0,1061997773,0") }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when moving down" do
|
74
|
+
When(:result) { controller.down(0.80) }
|
75
|
+
Then { at.should have_received(:pcmd).with("1,0,0,-1085485875,0") }
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when turning left" do
|
79
|
+
When(:result) { controller.turn_left(0.80) }
|
80
|
+
Then { at.should have_received(:pcmd).with("1,0,0,0,-1085485875") }
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when turning right" do
|
84
|
+
When(:result) { controller.turn_right(0.80) }
|
85
|
+
Then { at.should have_received(:pcmd).with("1,0,0,0,1061997773") }
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when executing several directions" do
|
89
|
+
When(:result) {
|
90
|
+
controller.forward(1.0).left(0.5).up(0.2).turn_left(0.8)
|
91
|
+
}
|
92
|
+
Then { at.should have_received(:pcmd).with("1,-1090519040,-1082130432,1045220557,-1085485875") }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
describe Argus::UdpSender do
|
5
|
+
Given(:port) { 5500 }
|
6
|
+
Given(:socket) { UDPSocket.new }
|
7
|
+
Given(:sender) { Argus::UdpSender.new(socket, 'localhost', port) }
|
8
|
+
Given!(:server) {
|
9
|
+
server = UDPSocket.new
|
10
|
+
server.bind(nil, port)
|
11
|
+
Thread.new {
|
12
|
+
@data, _ = server.recvfrom(1000)
|
13
|
+
}
|
14
|
+
}
|
15
|
+
When {
|
16
|
+
sender.send_packet("HI")
|
17
|
+
server.join
|
18
|
+
}
|
19
|
+
Then { @data.should == "HI" }
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hybridgroup-argus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jim Weirich
|
9
|
+
- Ron Evans
|
10
|
+
- Adrian Zankich
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2013-02-23 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec-given
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '2.1'
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '2.1'
|
32
|
+
description: Argus is a Ruby interface to a Parrot AR Drone quadcopter.Argus is extremely
|
33
|
+
experimental at this point. Use at your own risk.
|
34
|
+
email:
|
35
|
+
- jim.weirich@gmail.com
|
36
|
+
- ron dot evans at gmail dot com
|
37
|
+
executables: []
|
38
|
+
extensions: []
|
39
|
+
extra_rdoc_files:
|
40
|
+
- README.md
|
41
|
+
- MIT-LICENSE
|
42
|
+
files:
|
43
|
+
- README.md
|
44
|
+
- Rakefile
|
45
|
+
- lib/argus.rb
|
46
|
+
- lib/argus/at_commander.rb
|
47
|
+
- lib/argus/controller.rb
|
48
|
+
- lib/argus/drone.rb
|
49
|
+
- lib/argus/nav_data.rb
|
50
|
+
- lib/argus/nav_streamer.rb
|
51
|
+
- lib/argus/pave_parser.rb
|
52
|
+
- lib/argus/tcp_video_streamer.rb
|
53
|
+
- lib/argus/udp_nav_streamer.rb
|
54
|
+
- lib/argus/udp_sender.rb
|
55
|
+
- lib/argus/version.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
- spec/argus/at_commander_spec.rb
|
58
|
+
- spec/argus/controller_spec.rb
|
59
|
+
- spec/argus/udp_sender_spec.rb
|
60
|
+
- MIT-LICENSE
|
61
|
+
homepage: http://github.com/hybridgroup/argus
|
62
|
+
licenses: []
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options:
|
65
|
+
- --line-numbers
|
66
|
+
- --show-hash
|
67
|
+
- --main
|
68
|
+
- README.md
|
69
|
+
- --title
|
70
|
+
- Argus -- Parrot AR Drone Ruby API
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '1.9'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '1.0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project: n/a
|
87
|
+
rubygems_version: 1.8.23
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Ruby API for a Parrot AD Drone Quadcopter
|
91
|
+
test_files: []
|