argus 0.0.0 → 0.3.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +25 -1
  4. data/Rakefile +4 -0
  5. data/lib/argus.rb +5 -0
  6. data/lib/argus/ardrone_control_modes.rb +14 -0
  7. data/lib/argus/at_commander.rb +104 -0
  8. data/lib/argus/cad_type.rb +22 -0
  9. data/lib/argus/cfields.rb +80 -0
  10. data/lib/argus/controller.rb +165 -0
  11. data/lib/argus/drone.rb +62 -0
  12. data/lib/argus/float_encoding.rb +13 -0
  13. data/lib/argus/led_animation.rb +48 -0
  14. data/lib/argus/nav_data.rb +78 -0
  15. data/lib/argus/nav_monitor.rb +83 -0
  16. data/lib/argus/nav_option.rb +35 -0
  17. data/lib/argus/nav_option_checksum.rb +20 -0
  18. data/lib/argus/nav_option_demo.rb +79 -0
  19. data/lib/argus/nav_option_unknown.rb +9 -0
  20. data/lib/argus/nav_option_vision_detect.rb +83 -0
  21. data/lib/argus/nav_streamer.rb +111 -0
  22. data/lib/argus/nav_tag.rb +35 -0
  23. data/lib/argus/null_nav_monitor.rb +13 -0
  24. data/lib/argus/pave_parser.rb +39 -0
  25. data/lib/argus/tcp_video_streamer.rb +20 -0
  26. data/lib/argus/time_queue.rb +62 -0
  27. data/lib/argus/udp_sender.rb +13 -0
  28. data/lib/argus/version.rb +10 -0
  29. data/spec/argus/at_commander_spec.rb +82 -0
  30. data/spec/argus/controller_spec.rb +149 -0
  31. data/spec/argus/float_encoding_spec.rb +12 -0
  32. data/spec/argus/led_animation_spec.rb +21 -0
  33. data/spec/argus/nav_data_spec.rb +147 -0
  34. data/spec/argus/nav_option_checksum_spec.rb +21 -0
  35. data/spec/argus/nav_option_demo_spec.rb +56 -0
  36. data/spec/argus/nav_option_spec.rb +32 -0
  37. data/spec/argus/nav_option_unknown_spec.rb +19 -0
  38. data/spec/argus/nav_option_vision_detect_spec.rb +65 -0
  39. data/spec/argus/nav_streamer_spec.rb +111 -0
  40. data/spec/argus/null_nav_monitor_spec.rb +17 -0
  41. data/spec/argus/time_queue_spec.rb +61 -0
  42. data/spec/argus/udp_sender_spec.rb +33 -0
  43. data/spec/spec_helper.rb +13 -0
  44. metadata +51 -12
@@ -0,0 +1,111 @@
1
+ require 'socket'
2
+
3
+ module Argus
4
+
5
+ # NavStreamer State Transitions:
6
+ #
7
+ # State Trigger Next Action
8
+ # ------ -------------- ---- -----------------
9
+ # init start wait reconnect
10
+ #
11
+ # wait short_timeout wait request_nav_data
12
+ # wait long_timeout wait reconnect
13
+ # wait received_data run --
14
+ #
15
+ # run short_timeout run --
16
+ # run long_timeout wait reconnect
17
+ # run received_data run --
18
+
19
+ class NavStreamer
20
+ attr_reader :state
21
+
22
+ def initialize(opts={})
23
+ @state = :init
24
+ @socket = nil
25
+ @remote_host = opts.fetch(:remote_host)
26
+ @local_host = opts[:local_host] || '0.0.0.0'
27
+ @port = opts[:port] || 5554
28
+ @socket_class = opts[:UDPSocket] || UDPSocket
29
+ start_timer
30
+ end
31
+
32
+ def start
33
+ @state = :wait
34
+ reconnect
35
+ end
36
+
37
+ def short_timeout
38
+ if state == :wait
39
+ request_nav_data
40
+ end
41
+ end
42
+
43
+ def long_timeout
44
+ if @state == :wait || @state == :run
45
+ @state = :wait
46
+ reconnect
47
+ end
48
+ end
49
+
50
+ def receive_data
51
+ data, from = @socket.recvfrom(1024)
52
+ if data.unpack("V").first == 0x55667788
53
+ received_data
54
+ NavData.new(data)
55
+ else
56
+ nil
57
+ end
58
+ end
59
+
60
+ def received_data
61
+ if state == :wait || state == :run
62
+ @state = :run
63
+ @long_flag = false
64
+ @short_flag = false
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def request_nav_data
71
+ @socket.send("\x01\x00\x00\x00", 0, @remote_host, @port)
72
+ end
73
+
74
+ def reconnect
75
+ disconnect if @socket
76
+ @socket = new_socket
77
+ @socket.bind(@local_host, @port) rescue nil
78
+ request_nav_data
79
+ end
80
+
81
+ def disconnect
82
+ @socket.close
83
+ @socket = nil
84
+ end
85
+
86
+ def new_socket
87
+ @socket_class.new
88
+ end
89
+
90
+ def tick
91
+ short_timeout if @short_flag
92
+ @short_flag = true
93
+ if @n == 0
94
+ long_timeout if @long_flag
95
+ @long_flag = true
96
+ end
97
+ @n += 1
98
+ @n = 0 if @n == 10
99
+ end
100
+
101
+ def start_timer
102
+ @n = 0
103
+ @thread = Thread.new do
104
+ loop do
105
+ tick
106
+ sleep 1.0
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,35 @@
1
+ module Argus
2
+ module NavTag
3
+ DEMO,
4
+ TIME,
5
+ RAW_MEASURES,
6
+ PHYS_MEASURES,
7
+ GYROS_OFFSETS,
8
+ EULER_ANGLES,
9
+ REFERENCES,
10
+ TRIMS,
11
+ RC_REFERENCES,
12
+ PWM,
13
+ ALTITUDE,
14
+ VISION_RAW,
15
+ VISION_OF,
16
+ VISION,
17
+ VISION_PERF,
18
+ TRACKERS_SEND,
19
+ VISION_DETECT,
20
+ WATCHDOG,
21
+ ADC_DATA_FRAME,
22
+ VIDEO_STREAM,
23
+ GAMES,
24
+ PRESSURE_RAW,
25
+ MAGNETO,
26
+ WIND,
27
+ KALMAN_PRESSURE,
28
+ HDVIDEO_STREAM,
29
+ WIFI = (0..100).to_a
30
+
31
+ CHECKSUM = 0xffff
32
+ UNKNOWN = 0xfffe # Not part of the AR Drone DSK, Only used in Argus
33
+ end
34
+
35
+ end
@@ -0,0 +1,13 @@
1
+ module Argus
2
+
3
+ class NullNavMonitor
4
+ UsageError = Class.new(StandardError)
5
+ def callback(*)
6
+ fail UsageError, "Callbacks are not supported when the NavMonitor is disabled."
7
+ end
8
+ def method_missing(*)
9
+ # Noop
10
+ end
11
+ end
12
+
13
+ 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(opts={})
5
+ @host = opts.fetch(:remote_host)
6
+ @port = opts.fetch(:port, 5555)
7
+ @tcp_socket = TCPSocket.new(@host, @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,62 @@
1
+ class TimeQueue
2
+ Item = Struct.new(:time, :value) do
3
+ def <=>(other)
4
+ time <=> other.time
5
+ end
6
+
7
+ def key
8
+ object_id
9
+ end
10
+
11
+ def to_s(basetime=nil)
12
+ if basetime
13
+ "#{value.inspect}@+#{time-basetime}"
14
+ else
15
+ "#{value.inspect}@#{time.strftime('%H:%M:%S.%L')}"
16
+ end
17
+ end
18
+ end
19
+
20
+ def initialize
21
+ @items = []
22
+ end
23
+
24
+ def add(time, value)
25
+ item = Item.new(time, value)
26
+ @items << item
27
+ @items.sort!
28
+ item.key
29
+ end
30
+
31
+ def remove(key)
32
+ @items.delete_if { |item| item.key == key }
33
+ end
34
+
35
+ def any_ready?(time)
36
+ ! @items.empty? && @items.first.time <= time
37
+ end
38
+
39
+ def all_ready(time)
40
+ result = []
41
+ each_ready(time) do |value| result << value end
42
+ result
43
+ end
44
+
45
+ def each_ready(time)
46
+ while any_ready?(time)
47
+ item = @items.shift
48
+ yield item.value
49
+ end
50
+ end
51
+
52
+ def to_s
53
+ base_time = nil
54
+ strings = @items[0,3].map { |item|
55
+ string = item.to_s(base_time)
56
+ base_time ||= item.time
57
+ string
58
+ }
59
+ strings << "..." if @items.size > 3
60
+ "[#{strings.join(', ')}]"
61
+ end
62
+ end
@@ -0,0 +1,13 @@
1
+ module Argus
2
+ class UdpSender
3
+ def initialize(opts={})
4
+ @udp_socket = opts[:socket] || UDPSocket.new
5
+ @host = opts.fetch(:remote_host)
6
+ @port = opts.fetch(:port, 5556)
7
+ end
8
+
9
+ def send_packet(data)
10
+ @udp_socket.send(data, 0, @host, @port)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Argus
2
+ module Version # :nodoc: all
3
+ NUMBERS = [
4
+ MAJOR = 0,
5
+ MINOR = 3,
6
+ BUILD = 0,
7
+ ]
8
+ end
9
+ VERSION = Version::NUMBERS.join('.')
10
+ end
@@ -0,0 +1,82 @@
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
+ Given(:expected_commands) {
9
+ [
10
+ "AT\\*REF=\\d+,0",
11
+ "AT\\*PCMD=\\d+,0,0,0,0,0",
12
+ ]
13
+ }
14
+ Given(:command_pattern) { Regexp.new('\A' + expected_commands.map { |s| "#{s}\r" }.join + '\Z') }
15
+
16
+ When { cmdr.send(:tick) }
17
+
18
+ context "with no commands" do
19
+ Then { sender.should have_received(:send_packet).with(command_pattern) }
20
+ end
21
+
22
+ context "with a ref command" do
23
+ Given { expected_commands[0] = 'AT\*REF=\d+,512' }
24
+ Given { cmdr.ref("512") }
25
+ Then { sender.should have_received(:send_packet).with(command_pattern) }
26
+ end
27
+
28
+ context "with a pcmd command" do
29
+ Given { expected_commands[1] = 'AT\*PCMD=\d+,1,2,3,4,5' }
30
+ Given { cmdr.pcmd("1,2,3,4,5") }
31
+ Then { sender.should have_received(:send_packet).with(command_pattern) }
32
+ end
33
+
34
+ context "with a config command" do
35
+ Given { expected_commands.unshift('AT\*CONFIG=\d+,"general:navdata_demo","TRUE"') }
36
+ Given { cmdr.config("general:navdata_demo", "TRUE") }
37
+ Then { sender.should have_received(:send_packet).with(command_pattern) }
38
+ end
39
+
40
+ context "with a reset watchdog command" do
41
+ Given { expected_commands.unshift('AT\*COMWDG=\d+') }
42
+ Given { cmdr.comwdg }
43
+ Then { sender.should have_received(:send_packet).with(command_pattern) }
44
+ end
45
+
46
+ context "with a contrl command" do
47
+ Given { expected_commands.unshift('AT\*CTRL=\d+,4,0') }
48
+ Given { cmdr.ctrl(4) }
49
+ Then { sender.should have_received(:send_packet).with(command_pattern) }
50
+ end
51
+ end
52
+
53
+ describe "increasing sequence numbers" do
54
+ Given(:seq_numbers) { [ ] }
55
+ Given {
56
+ sender.should_receive(:send_packet).and_return { |data|
57
+ data.scan(%r{=(\d+),}) { |seq|
58
+ seq_numbers << seq.first.to_i }
59
+ }
60
+ }
61
+ When { 2.times { cmdr.tick } }
62
+ Then { seq_numbers.should == [1, 2, 3, 4] }
63
+ end
64
+
65
+ describe "sending thread" do
66
+ Given {
67
+ @count = 0
68
+ sender.should_receive(:send_packet).and_return {
69
+ @count += 1
70
+ cmdr.stop if @count > 5
71
+ }
72
+ }
73
+ When {
74
+ cmdr.start
75
+ cmdr.join
76
+ }
77
+ Then { sender.should have_received(:send_packet) }
78
+ end
79
+
80
+
81
+
82
+ end
@@ -0,0 +1,149 @@
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
+
96
+ describe "led command" do
97
+ Invariant { result.should == controller }
98
+
99
+ context "when setting with numeric sequence" do
100
+ When(:result) { controller.led(3, 2.0, 4) }
101
+ Then {
102
+ at.should have_received(:config)
103
+ .with("leds:leds_anim", "3,1073741824,4")
104
+ }
105
+ end
106
+
107
+ context "when setting with symbolic sequence" do
108
+ When(:result) { controller.led(:blink_orange, 2.0, 4) }
109
+ Then {
110
+ at.should have_received(:config)
111
+ .with("leds:leds_anim", "3,1073741824,4")
112
+ }
113
+ end
114
+ end
115
+
116
+ describe "enabled detection command" do
117
+ Invariant { result.should == controller }
118
+
119
+ context "when setting all parameters" do
120
+ Given(:enemy) { 2 }
121
+ Given(:type) { 11 }
122
+ Given(:select) { 33 }
123
+
124
+ When(:result) { controller.enable_detection(enemy, type, select) }
125
+
126
+ Then { at.should have_received(:config).with("detect:enemy_colors", enemy.to_s) }
127
+ Then { at.should have_received(:config).with("detect:detect_type", type.to_s) }
128
+ Then { at.should have_received(:config).with("detect:detections_select_h", select.to_s) }
129
+ end
130
+
131
+ context "when setting with only enemy" do
132
+ Given(:enemy) { 2 }
133
+ Given(:type) { 10 }
134
+ Given(:select) { 32 }
135
+
136
+ When(:result) { controller.enable_detection(enemy) }
137
+
138
+ Then { at.should have_received(:config).with("detect:enemy_colors", enemy.to_s) }
139
+ Then { at.should have_received(:config).with("detect:detect_type", type.to_s) }
140
+ Then { at.should have_received(:config).with("detect:detections_select_h", select.to_s) }
141
+ end
142
+ end
143
+
144
+ describe "ack controll command" do
145
+ Invariant { result.should == controller }
146
+ When(:result) { controller.ack_control_mode }
147
+ Then { at.should have_received(:ctrl).with(5) }
148
+ end
149
+ end