argus 0.0.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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,12 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+
5
+ describe FloatEncoding do
6
+ Given(:f) { 5.0 }
7
+ Given(:encoded_f) { 1084227584 }
8
+ Then { FloatEncoding.encode_float(f) == encoded_f }
9
+ Then { FloatEncoding.decode_float(encoded_f) == f }
10
+ end
11
+
12
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+
5
+ describe LedAnimation do
6
+ describe ".lookup_name" do
7
+ Then { LedAnimation.lookup_name(3) == :blink_orange }
8
+ end
9
+
10
+ describe ".lookup_value" do
11
+ Then { LedAnimation.lookup_value(:blink_orange) == 3 }
12
+ Then { LedAnimation.lookup_value("blink_orange") == 3 }
13
+ Then { LedAnimation.lookup_value(3) == 3 }
14
+ Then { LedAnimation.lookup_value("3") == 3 }
15
+
16
+ Then { LedAnimation.lookup_value(:unknown) == nil }
17
+ Then { LedAnimation.lookup_value("UNKNOWN") == nil }
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+ describe NavData do
5
+ Given(:state_bits) { 0xcf8a0c94 }
6
+ Given(:seq_num) { 173064 }
7
+ Given(:vision_flag) { 0 }
8
+ Given(:raw_header) { Bytes.make_header(state_bits, seq_num, vision_flag) }
9
+ Given(:raw_nav_bytes) { Bytes.make_nav_data(raw_header) }
10
+
11
+ When(:nav_data) { NavData.new(raw_nav_bytes) }
12
+
13
+ Then { nav_data.sequence_number == 173064 }
14
+ Then { nav_data.vision_flag == 0 }
15
+
16
+ describe "sequence number" do
17
+ Given(:seq_num) { 1234 }
18
+ Then { nav_data.sequence_number == 1234 }
19
+ end
20
+
21
+ describe "vision flag" do
22
+ Given(:vision_flag) { 1 }
23
+ Then { nav_data.vision_flag == 1 }
24
+ end
25
+
26
+ describe "state queries" do
27
+
28
+ def bit(n)
29
+ 0x00000001 << n
30
+ end
31
+
32
+ # Matcher for handling the mask testing for the state mask.
33
+ #
34
+ # Usage:
35
+ # bit(5).should be_the_mask_for(:method [, off_value, on_value])
36
+ #
37
+ # If off_value and on_value are not given, then false/true will
38
+ # be assumed.
39
+ #
40
+ matcher :be_the_mask_for do |method, off_result=false, on_result=true|
41
+ match do |mask|
42
+ @msg = "OK"
43
+ try_alternative(method, 0, off_result) &&
44
+ try_alternative(method, mask, on_result)
45
+ end
46
+
47
+ failure_message_for_should do |mask|
48
+ @msg
49
+ end
50
+
51
+ # Try one of the alternatives. Sending method to the navdata
52
+ # constructed with mask should return teh expected value.
53
+ def try_alternative(method, mask, expected)
54
+ raw = Bytes.make_nav_data(Bytes.make_header(mask, 1, 0))
55
+ nav_data = NavData.new(raw)
56
+ result = nav_data.send(method)
57
+ return true if result == expected
58
+ @msg = "expected mask of 0x#{'%08x' % mask} with #{method}\n" +
59
+ "to return: #{expected.inspect}\n" +
60
+ "got: #{result.inspect}"
61
+ false
62
+ end
63
+ end
64
+
65
+ Then { bit(0).should be_the_mask_for(:flying?) }
66
+ Then { bit(1).should be_the_mask_for(:video_enabled?) }
67
+ Then { bit(2).should be_the_mask_for(:vision_enabled?) }
68
+ Then { bit(3).should be_the_mask_for(:control_algorithm, :euler, :angular_speed) }
69
+
70
+ Then { bit(4).should be_the_mask_for(:altitude_control_algorithm_active?) }
71
+ Then { bit(5).should be_the_mask_for(:start_button_pressed?) }
72
+ Then { bit(6).should be_the_mask_for(:control_command_ack?) }
73
+ Then { bit(7).should be_the_mask_for(:camera_ready?) }
74
+
75
+ Then { bit(8).should be_the_mask_for(:travelling_enabled?) }
76
+ Then { bit(9).should be_the_mask_for(:usb_ready?) }
77
+ Then { bit(10).should be_the_mask_for(:mode, :all, :demo) }
78
+ Then { bit(11).should be_the_mask_for(:bootstrap?) }
79
+
80
+ Then { bit(12).should be_the_mask_for(:moter_problem?) }
81
+ Then { bit(13).should be_the_mask_for(:communication_lost?) }
82
+ Then { bit(14).should be_the_mask_for(:software_fault?) }
83
+ Then { bit(15).should be_the_mask_for(:low_battery?) }
84
+
85
+ Then { bit(16).should be_the_mask_for(:emergency_landing_requested?) }
86
+ Then { bit(17).should be_the_mask_for(:timer_elapsed?) }
87
+ Then { bit(18).should be_the_mask_for(:magnometer_needs_calibration?) }
88
+ Then { bit(19).should be_the_mask_for(:angles_out_of_range?) }
89
+
90
+ Then { bit(20).should be_the_mask_for(:too_much_wind?) }
91
+ Then { bit(21).should be_the_mask_for(:ultrasonic_sensor_deaf?) }
92
+ Then { bit(22).should be_the_mask_for(:cutout_detected?) }
93
+ Then { bit(23).should be_the_mask_for(:pic_version_number_ok?) }
94
+
95
+ Then { bit(24).should be_the_mask_for(:at_codec_thread_on?) }
96
+ Then { bit(25).should be_the_mask_for(:navdata_thread_on?) }
97
+ Then { bit(26).should be_the_mask_for(:video_thread_on?) }
98
+ Then { bit(27).should be_the_mask_for(:acquisition_thread_on?) }
99
+
100
+ Then { bit(28).should be_the_mask_for(:control_watchdog_delayed?) }
101
+ Then { bit(29).should be_the_mask_for(:adc_watchdog_delayed?) }
102
+ Then { bit(30).should be_the_mask_for(:com_watchdog_problem?) }
103
+ Then { bit(31).should be_the_mask_for(:emergency_landing?) }
104
+ end
105
+
106
+ describe "state mask" do
107
+ context "all zeros" do
108
+ Given(:state_bits) { 0 }
109
+ Then { nav_data.state_mask == state_bits }
110
+ end
111
+
112
+ context "non-zero" do
113
+ Given(:state_bits) { 12345 }
114
+ Then { nav_data.state_mask == state_bits }
115
+ end
116
+ end
117
+
118
+ describe "#options" do
119
+ Then { nav_data.options.size == 1 }
120
+ Then { nav_data.options.first.is_a?(NavOptionChecksum) }
121
+ end
122
+ end
123
+
124
+ describe "multiple options" do
125
+ context "with good data" do
126
+ Given(:raw_header) { Bytes.make_header(0, 0, 0) }
127
+ Given(:raw_nav_bytes) {
128
+ Bytes.make_nav_data(raw_header, Bytes.make_demo_data)
129
+ }
130
+ When(:nav_data) { NavData.new(raw_nav_bytes) }
131
+ Then { nav_data.options.size == 2 }
132
+ Then { nav_data.options[0].is_a?(NavOptionDemo) }
133
+ Then { nav_data.options[1].is_a?(NavOptionChecksum) }
134
+ end
135
+
136
+ context "with short data" do
137
+ Given(:raw_nav_bytes) {
138
+ Bytes.make_header(0, 0, 0).pack("C*") +
139
+ [4321, 100, 0].pack("vvV")
140
+ }
141
+ When(:nav_data) { NavData.new(raw_nav_bytes) }
142
+ Then { nav_data.options.size == 1 }
143
+ Then { nav_data.options[0].is_a?(NavOptionUnknown) }
144
+ end
145
+ end
146
+
147
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+
5
+ describe NavOptionChecksum do
6
+ Given(:raw_data) { [NavTag::CHECKSUM, 8, 0x12345678].pack("vvV") }
7
+ Given(:csum) { NavOptionChecksum.new(raw_data) }
8
+
9
+ describe ".tag" do
10
+ Then { NavOptionChecksum.tag == NavTag::CHECKSUM }
11
+ end
12
+
13
+ describe "data fields" do
14
+ Then { csum.tag == NavOptionChecksum.tag }
15
+ Then { csum.size == raw_data.size }
16
+ Then { csum.chks == 0x12345678 }
17
+ Then { csum.checksum == 0x12345678 }
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'base64'
3
+
4
+ module Argus
5
+ describe NavOptionDemo do
6
+ def f(float)
7
+ FloatEncoding.encode_float(float)
8
+ end
9
+
10
+ # NOTE: This is a Base 64 encoded NavData packet recorded directly from the drone.
11
+ Given(:base64_data) {
12
+ "iHdmVdAEgA/5iwAAAAAAAAAAlAAAAAIAPAAAAADwREUAgCNFQFEiyAAAAABk" +
13
+ "AAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
14
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AAADWP3i/6kNxPniCg72IpXO+64R4" +
15
+ "v+kAAD2hLGG9u7U6PatYfz8AAAAAAAAAAAAAdcMQAEgBAAAAAAAAAAAAAAAA" +
16
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
17
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
18
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
19
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
20
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
21
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
22
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8I" +
23
+ "ANQdAAA="
24
+ }
25
+ Given(:raw_data) { Base64.decode64(base64_data) }
26
+ Given(:nav_data) { NavData.new(raw_data) }
27
+
28
+ When(:demo) { nav_data.options.first }
29
+
30
+ Then { ! demo.nil? }
31
+ Then { demo.tag == Argus::NavTag::DEMO }
32
+ Then { demo.size == 148 }
33
+
34
+ Then { demo.ctrl_state == 0x20000 }
35
+ Then { demo.control_state_name == :default }
36
+ Then { demo.vbat_flying_percentage == 60 }
37
+ Then { demo.battery_level == 60 }
38
+ Then { demo.theta == 3151.0 }
39
+ Then { demo.phi == 2616.0 }
40
+ Then { demo.psi == -166213.0 }
41
+ Then { demo.pitch == 3.151 }
42
+ Then { demo.roll == 2.616 }
43
+ Then { demo.yaw == -166.213 }
44
+ Then { demo.altitude == 0 }
45
+ Then { demo.vx == about(0).delta(0.00000001) }
46
+ Then { demo.vy == about(0).delta(0.00000001) }
47
+ Then { demo.vz == about(0).delta(0.00000001) }
48
+ Then { demo.detection_camera_rot }
49
+ Then { demo.detection_camera_trans }
50
+ Then { demo.detection_tag_index == 0 }
51
+ Then { demo.detection_camera_type == 13 }
52
+ Then { demo.drone_camera_rot }
53
+ Then { demo.drone_camera_trans }
54
+ end
55
+
56
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+ describe NavOption do
5
+ let(:raw_checksum) { [ NavTag::CHECKSUM, 8, 0x5555aaaa].pack("vvV") }
6
+ let(:raw_demo) { [ NavTag::DEMO, 148 ].pack("vv") + "\0" * 148 }
7
+ let(:raw_vision_detect) { [ NavTag::VISION_DETECT, 328 ].pack("vv") + "\0" * 328 }
8
+ let(:raw_unknown_tag) { [ 12345, 8 ].pack("vv") + " " }
9
+
10
+ When(:result) { NavOption.parse(raw_data) }
11
+
12
+ describe "parsing checksum options" do
13
+ Given(:raw_data) { raw_checksum }
14
+ Then { result.is_a?(NavOptionChecksum) }
15
+ end
16
+
17
+ describe "parsing demo option" do
18
+ Given(:raw_data) { raw_demo }
19
+ Then { result.is_a?(NavOptionDemo) }
20
+ end
21
+
22
+ describe "parsing vision detect option" do
23
+ Given(:raw_data) { raw_vision_detect }
24
+ Then { result.is_a?(NavOptionVisionDetect) }
25
+ end
26
+
27
+ describe "parsing bad tag" do
28
+ Given(:raw_data) { raw_unknown_tag }
29
+ Then { result.is_a?(NavOptionUnknown) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+
5
+ describe NavOptionUnknown do
6
+ Given(:raw_data) { [0x5231, 8, 0x12345678].pack("vvV") }
7
+ Given(:opt) { NavOptionUnknown.new(raw_data) }
8
+
9
+ describe ".tag" do
10
+ Then { NavOptionUnknown.tag == 0xfffe }
11
+ end
12
+
13
+ describe "data fields" do
14
+ Then { opt.tag == 0x5231 }
15
+ Then { opt.size == raw_data.size }
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+
5
+ describe NavOptionVisionDetect do
6
+ def f(float)
7
+ FloatEncoding.encode_float(float)
8
+ end
9
+
10
+ Given(:raw_data) {
11
+ [
12
+ NavTag::VISION_DETECT, 328,
13
+ 2,
14
+ 1, 2, 3, 4,
15
+ 10, 11, 12, 13,
16
+ 20, 21, 22, 23,
17
+ 30, 31, 32, 33,
18
+ 40, 41, 42, 43,
19
+ 50, 51, 52, 53,
20
+ f(60.0), f(61.0), f(62.0), f(63.0),
21
+ 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,
22
+ 0,0,0, 0,0,0, 0,0,0, 0,0,0,
23
+ 70, 71, 72, 73
24
+ ].flatten.pack("vv V V4 V4 V4 V4 V4 V4 V4 V36 V12 V4")
25
+ }
26
+
27
+ When(:detect) { NavOptionVisionDetect.new(raw_data) }
28
+
29
+ Then { NavOptionVisionDetect.tag == NavTag::VISION_DETECT }
30
+ Then { detect.tag == Argus::NavTag::VISION_DETECT }
31
+ Then { detect.size == 328 }
32
+
33
+ describe "C fields" do
34
+ Then { detect.nb_detected == 2 }
35
+ Then { detect.type == [1, 2, 3, 4] }
36
+ Then { detect.type_name == [:vertical, :vision, :none, :cocarde] }
37
+ Then { detect.xc == [10, 11, 12, 13] }
38
+ Then { detect.yc == [20, 21, 22, 23] }
39
+ Then { detect.width == [30, 31, 32, 33] }
40
+ Then { detect.height == [40, 41, 42, 43] }
41
+ Then { detect.dist == [50, 51, 52, 53] }
42
+ Then { detect.distance == [50, 51, 52, 53] }
43
+ Then { detect.orientation_angle == [60.0, 61.0, 62.0, 63.0] }
44
+ Then { detect.rotation.size == 9 * 4 }
45
+ Then { detect.translation.size == 3 * 4 }
46
+ Then { detect.camera_source == [70, 71, 72, 73] }
47
+ end
48
+
49
+ describe "detection info" do
50
+ Then { detect.detections.size == 2 }
51
+
52
+ let(:d0) { detect.detections[0] }
53
+ Then { d0.type == 1 }
54
+ Then { d0.type_name == :vertical }
55
+ Then { d0.x == 10 }
56
+ Then { d0.y == 20 }
57
+ Then { d0.width == 30 }
58
+ Then { d0.height == 40 }
59
+ Then { d0.orientation_angle == 60.0 }
60
+ Then { d0.camera_source == 70 }
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ module Argus
4
+ describe NavStreamer do
5
+ Given(:remote_host) { '192.168.1.1' }
6
+ Given(:port) { 5554 }
7
+ Given(:socket) { flexmock("Socket", send: nil).should_ignore_missing }
8
+ Given(:streamer) { NavStreamer.new(remote_host: remote_host, UDPSocket: flexmock(new: socket)) }
9
+
10
+ context "after starting" do
11
+ Given { streamer.start }
12
+
13
+ context "when receiving good data" do
14
+ Given(:bytes) { Bytes.make_nav_data(Bytes.make_header(0x1234, 0, 0)) }
15
+ Given { socket.should_receive(recvfrom: bytes) }
16
+ When(:nav_data) { streamer.receive_data }
17
+ Then { nav_data.state_mask == 0x1234}
18
+ end
19
+
20
+ context "when receiving bad data" do
21
+ Given(:bytes) { [0x89, 0x77, 0x66, 0x55, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].pack("C*") }
22
+ Given { socket.should_receive(:recvfrom => bytes) }
23
+ When(:nav_data) { streamer.receive_data }
24
+ Then { nav_data.nil? }
25
+ end
26
+ end
27
+
28
+ describe "State Transitions" do
29
+ Given {
30
+ flexmock(streamer, reconnect: nil, request_nav_data: nil)
31
+ }
32
+
33
+ def goto_wait
34
+ streamer.start
35
+ end
36
+
37
+ def goto_run
38
+ streamer.start
39
+ streamer.received_data
40
+ end
41
+
42
+ context "when init & start" do
43
+ Given { streamer.start }
44
+ Then { streamer.should have_received(:reconnect).once }
45
+ Then { streamer.state == :wait }
46
+ end
47
+
48
+ context "when init & short_timeout" do
49
+ Given { streamer.short_timeout }
50
+ Then { streamer.should have_received(:reconnect).never }
51
+ Then { streamer.should have_received(:request_nav_data).never }
52
+ Then { streamer.state == :init }
53
+ end
54
+
55
+ context "when init & long_timeout" do
56
+ Given { streamer.long_timeout }
57
+ Then { streamer.should have_received(:reconnect).never }
58
+ Then { streamer.should have_received(:request_nav_data).never }
59
+ Then { streamer.state == :init }
60
+ end
61
+
62
+ context "when wait & received_data" do
63
+ Given { goto_wait }
64
+ Given { streamer.received_data }
65
+ Then { streamer.should have_received(:reconnect).once }
66
+ Then { streamer.should have_received(:request_nav_data).never }
67
+ Then { streamer.state == :run }
68
+ end
69
+
70
+ context "when wait & short_timeout" do
71
+ Given { goto_wait }
72
+ Given { streamer.short_timeout }
73
+ Then { streamer.should have_received(:reconnect).once }
74
+ Then { streamer.should have_received(:request_nav_data).once }
75
+ Then { streamer.state == :wait }
76
+ end
77
+
78
+ context "when wait & long_timeout" do
79
+ Given { goto_wait }
80
+ Given { streamer.long_timeout }
81
+ Then { streamer.should have_received(:reconnect).twice }
82
+ Then { streamer.should have_received(:request_nav_data).never }
83
+ Then { streamer.state == :wait }
84
+ end
85
+
86
+ context "when run & received_data" do
87
+ Given { goto_run }
88
+ Given { streamer.received_data }
89
+ Then { streamer.should have_received(:reconnect).once }
90
+ Then { streamer.should have_received(:request_nav_data).never }
91
+ Then { streamer.state == :run }
92
+ end
93
+
94
+ context "when run & short_timeout" do
95
+ Given { goto_run }
96
+ Given { streamer.short_timeout }
97
+ Then { streamer.should have_received(:reconnect).once }
98
+ Then { streamer.should have_received(:request_nav_data).never }
99
+ Then { streamer.state == :run }
100
+ end
101
+
102
+ context "when run & long_timeout" do
103
+ Given { goto_run }
104
+ Given { streamer.long_timeout }
105
+ Then { streamer.should have_received(:reconnect).twice }
106
+ Then { streamer.should have_received(:request_nav_data).never }
107
+ Then { streamer.state == :wait }
108
+ end
109
+ end
110
+ end
111
+ end