enginevib 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.
- checksums.yaml +7 -0
- data/.editorconfig +12 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/Rakefile +17 -0
- data/bin/bundler +16 -0
- data/bin/console +14 -0
- data/bin/enginevib +16 -0
- data/bin/rake +16 -0
- data/bin/rake-compiler +16 -0
- data/bin/rubocop +16 -0
- data/bin/ruby-parse +16 -0
- data/bin/ruby-rewrite +16 -0
- data/bin/setup +7 -0
- data/dtrace/dtrace_rtos_classes.png +0 -0
- data/dtrace/dtrace_rtos_classes_no_str.png +0 -0
- data/dtrace/dtrace_rtos_syscalls.png +0 -0
- data/dtrace/sample_output_rtos_classes.csv +85 -0
- data/dtrace/sample_output_rtos_syscall.csv +53 -0
- data/dtrace/sample_output_soft_classes.csv +86 -0
- data/dtrace/sample_output_soft_syscall.csv +142 -0
- data/dtrace/script.d +14 -0
- data/enginevib.gemspec +30 -0
- data/exe/enginevib +20 -0
- data/exe/openmic +9 -0
- data/ext/rtos/extconf.rb +3 -0
- data/ext/rtos/rtos.c +32 -0
- data/lib/enginevib/averager.rb +27 -0
- data/lib/enginevib/controller.rb +9 -0
- data/lib/enginevib/io.rb +13 -0
- data/lib/enginevib/launcher.rb +50 -0
- data/lib/enginevib/main.rb +40 -0
- data/lib/enginevib/output.rb +55 -0
- data/lib/enginevib/scheduler.rb +70 -0
- data/lib/enginevib/sensor.rb +26 -0
- data/lib/enginevib/soft_ticker.rb +12 -0
- data/lib/enginevib/stats.rb +13 -0
- data/lib/enginevib/system.rb +15 -0
- data/lib/enginevib/version.rb +4 -0
- data/lib/enginevib.rb +7 -0
- data/lib/rtos.bundle +0 -0
- data/test/averager_test.rb +16 -0
- data/test/controller_test.rb +8 -0
- data/test/enginevib_test.rb +7 -0
- data/test/fixtures/dummy +2 -0
- data/test/io_test.rb +10 -0
- data/test/main_test.rb +20 -0
- data/test/scheduler_test.rb +8 -0
- data/test/sensor_test.rb +13 -0
- data/test/soft_ticker_test.rb +8 -0
- data/test/stats_test.rb +16 -0
- data/test/system_test.rb +8 -0
- data/test/test_helper.rb +24 -0
- metadata +186 -0
data/dtrace/script.d
ADDED
data/enginevib.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'enginevib/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "enginevib"
|
8
|
+
spec.version = Enginevib::VERSION
|
9
|
+
spec.authors = ["Eduardo Mourão"]
|
10
|
+
spec.email = ["eduardo.a20@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Aircraft Avionics Built in Ruby}
|
13
|
+
spec.description = %q{Aircraft Avionics Built in Ruby}
|
14
|
+
spec.homepage = "https://github.com/eduardordm/enginevib"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split("\n")
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
spec.extensions = ["ext/rtos/extconf.rb"]
|
22
|
+
|
23
|
+
spec.add_dependency "curses"
|
24
|
+
spec.add_dependency "rake-compiler"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "minitest"
|
29
|
+
spec.add_development_dependency "rubocop"
|
30
|
+
end
|
data/exe/enginevib
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
require 'enginevib/launcher'
|
6
|
+
require 'enginevib/io'
|
7
|
+
require 'enginevib/main'
|
8
|
+
require 'enginevib/sensor'
|
9
|
+
require 'enginevib/output'
|
10
|
+
require 'enginevib/system'
|
11
|
+
require 'enginevib/soft_ticker'
|
12
|
+
require 'enginevib/scheduler'
|
13
|
+
require 'enginevib/controller'
|
14
|
+
require 'enginevib/stats'
|
15
|
+
require 'enginevib/averager'
|
16
|
+
|
17
|
+
|
18
|
+
require 'rtos'
|
19
|
+
|
20
|
+
Enginevib::Main.new.launch
|
data/exe/openmic
ADDED
data/ext/rtos/extconf.rb
ADDED
data/ext/rtos/rtos.c
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <unistd.h>
|
3
|
+
|
4
|
+
static
|
5
|
+
VALUE initialize(VALUE self)
|
6
|
+
{
|
7
|
+
return self;
|
8
|
+
}
|
9
|
+
|
10
|
+
static
|
11
|
+
VALUE delay_rt(VALUE c, VALUE t)
|
12
|
+
{
|
13
|
+
usleep(NUM2DBL(t)*1000000);
|
14
|
+
return Qnil;
|
15
|
+
}
|
16
|
+
|
17
|
+
static
|
18
|
+
VALUE preempt_rt(VALUE c)
|
19
|
+
{
|
20
|
+
rb_need_block();
|
21
|
+
rb_yield(Qnil);
|
22
|
+
return Qnil;
|
23
|
+
}
|
24
|
+
|
25
|
+
void Init_rtos(void)
|
26
|
+
{
|
27
|
+
VALUE cRtos = rb_define_module("Rtos");
|
28
|
+
|
29
|
+
rb_define_method(cRtos, "initialize", initialize, 0);
|
30
|
+
rb_define_method(cRtos, "preempt_rt", preempt_rt, 0);
|
31
|
+
rb_define_method(cRtos, "delay_rt", delay_rt, 1);
|
32
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Enginevib
|
2
|
+
# Defines the Averager class
|
3
|
+
class Averager
|
4
|
+
AVERAGE_SAMPLE_SIZE = 100
|
5
|
+
|
6
|
+
def initialize(size = AVERAGE_SAMPLE_SIZE)
|
7
|
+
@size = size
|
8
|
+
@nums = []
|
9
|
+
@sum = 0.0
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(hello)
|
13
|
+
@nums << hello
|
14
|
+
goodbye = @nums.length > @size ? @nums.shift : 0
|
15
|
+
@sum += hello - goodbye
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def average
|
20
|
+
@sum / @nums.length
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
average.to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/enginevib/io.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Enginevib
|
2
|
+
# Defines the Launcher module which must be defined in Main.rb
|
3
|
+
module Launcher
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def launch
|
7
|
+
start_scheduler
|
8
|
+
rescue SystemExit, Interrupt
|
9
|
+
puts 'Good bye!' # May not be printed since we are using curses.
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_arguments
|
13
|
+
@options = {
|
14
|
+
simulation_mode: true,
|
15
|
+
serial_port: nil
|
16
|
+
}
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.banner = 'Usage: enginevib [options]'
|
19
|
+
on_simulation_mode(opts)
|
20
|
+
on_serial_port(opts)
|
21
|
+
on_help(opts)
|
22
|
+
end.parse!
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def on_simulation_mode(opts)
|
28
|
+
opts.on('-s',
|
29
|
+
'--simulation-mode',
|
30
|
+
'Enables the simulation mode and ignores --serial-port') do |s|
|
31
|
+
@options[:simulation_mode] = s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_serial_port(opts)
|
36
|
+
opts.on('-pSERIAL_PORT',
|
37
|
+
'--serial-port=SERIAL_PORT',
|
38
|
+
'Serial device to read data from. Example: /dev/ttys005') do |p|
|
39
|
+
@options[:serial_port] = p
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_help(opts)
|
44
|
+
opts.on('-h', '--help', 'Prints this help') do
|
45
|
+
puts opts
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Enginevib
|
2
|
+
# The main class. run is called when run from command line.
|
3
|
+
class Main
|
4
|
+
include Launcher
|
5
|
+
attr_accessor :scheduler, :sensor, :output, :system, :controller
|
6
|
+
|
7
|
+
DEADLINE = 30.0 / 1000.0 # 30 milliseconds
|
8
|
+
TOLERANCE = 7.0 / 1000.0 # 7 miliseconds
|
9
|
+
AVERAGE_SAMPLE_SIZE = 100 # 100 ticks
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
parse_arguments
|
13
|
+
initialize_components
|
14
|
+
initialize_scheduler
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize_components
|
18
|
+
@sensor = Sensor.new(@options[:serial_port], @options[:simulation_mode])
|
19
|
+
@output = Output.new
|
20
|
+
@system = System.new
|
21
|
+
@controller = Controller.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize_scheduler
|
25
|
+
@scheduler = Scheduler.new(DEADLINE, TOLERANCE) do |stats|
|
26
|
+
if @controller.inop
|
27
|
+
sleep DEADLINE - TOLERANCE
|
28
|
+
else
|
29
|
+
data = @sensor.read
|
30
|
+
computed_data = @system.compute(data)
|
31
|
+
@output.refresh_display(computed_data, stats)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_scheduler
|
37
|
+
@scheduler.start
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
module Enginevib
|
4
|
+
# Defines an output class
|
5
|
+
class Output
|
6
|
+
attr_accessor :screen, :window
|
7
|
+
|
8
|
+
attr_writer :warning
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
return if $TEST
|
12
|
+
init_renderer
|
13
|
+
end
|
14
|
+
|
15
|
+
def refresh_display(data, stats)
|
16
|
+
return if $TEST
|
17
|
+
render_message data, stats
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def init_renderer
|
23
|
+
setup_curses
|
24
|
+
@window = Curses::Window.new(8, 23,
|
25
|
+
(Curses.lines - 8) / 2,
|
26
|
+
(Curses.cols - 23) / 2)
|
27
|
+
end
|
28
|
+
|
29
|
+
def setup_curses
|
30
|
+
Curses.tap do |c|
|
31
|
+
c.init_screen
|
32
|
+
c.nonl
|
33
|
+
c.cbreak
|
34
|
+
c.noecho
|
35
|
+
c.curs_set(0)
|
36
|
+
@screen = c.stdscr
|
37
|
+
end
|
38
|
+
@screen.scrollok(false)
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_message(data, stats)
|
42
|
+
@window.tap do |w|
|
43
|
+
w.setpos(2, 3)
|
44
|
+
w.addstr(data.to_s)
|
45
|
+
w.setpos(4, 3)
|
46
|
+
|
47
|
+
wnd = stats.window_avg
|
48
|
+
st = stats.warning ? 'WARN' : 'OP '
|
49
|
+
|
50
|
+
w.addstr(">>#{st}/#{stats.memory}/#{wnd} ms ")
|
51
|
+
w.refresh
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rtos'
|
2
|
+
|
3
|
+
module Enginevib
|
4
|
+
# Defines the scheduler class
|
5
|
+
class Scheduler
|
6
|
+
include $SOFT_TICKER ? SoftTicker : Rtos
|
7
|
+
|
8
|
+
attr_accessor :deadline, :block, :tolerance, :stats, :averager, :inop
|
9
|
+
|
10
|
+
def initialize(deadline, tolerance, &block)
|
11
|
+
@deadline = deadline
|
12
|
+
@block = block
|
13
|
+
@tolerance = tolerance
|
14
|
+
@stats = Stats.new
|
15
|
+
@averager = Averager.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
preempt_rt { loop { tick } }
|
20
|
+
end
|
21
|
+
|
22
|
+
def tick
|
23
|
+
execution_time = execute_block
|
24
|
+
|
25
|
+
actual_wait_time = wait_for_next_tick(execution_time)
|
26
|
+
|
27
|
+
measure_time = measure_execution(execution_time, actual_wait_time)
|
28
|
+
|
29
|
+
verify_measure_time(execution_time, actual_wait_time, measure_time)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def execute_block
|
35
|
+
Benchmark.realtime do
|
36
|
+
@block.call @stats
|
37
|
+
GC.start
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def wait_for_next_tick(execution_time)
|
42
|
+
Benchmark.realtime do
|
43
|
+
delay_rt([@deadline - execution_time - tolerance, 0].max)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def measure_execution(execution_time, actual_wait_time)
|
48
|
+
Benchmark.realtime do
|
49
|
+
@stats.tap do |s|
|
50
|
+
s.memory =
|
51
|
+
GC.stat(:total_allocated_objects) - GC.stat(:total_freed_objects)
|
52
|
+
s.window = execution_time + actual_wait_time
|
53
|
+
s.warning = @stats.window > @deadline
|
54
|
+
measure_execution_avg
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def measure_execution_avg
|
60
|
+
@averager << @stats.window * 1000
|
61
|
+
@stats.window_avg = @averager.average.to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
def verify_measure_time(execution_time, actual_wait_time, measure_time)
|
65
|
+
@stats.warning =
|
66
|
+
(execution_time + actual_wait_time + measure_time) > @deadline
|
67
|
+
!@stats.warning
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Enginevib
|
2
|
+
# Defines a sensor class which can read data from virtual ports, non-blocking.
|
3
|
+
class Sensor
|
4
|
+
attr_accessor :fd, :io, :buffered_data, :simulation_mode
|
5
|
+
|
6
|
+
def initialize(serial_port, simulation_mode)
|
7
|
+
@simulation_mode = simulation_mode
|
8
|
+
init_io(serial_port)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read
|
12
|
+
@buffered_data =
|
13
|
+
@simulation_mode ? rand(0.10..1.00) : @io.readline_nonblock.to_f
|
14
|
+
rescue
|
15
|
+
@buffered_data
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def init_io(serial_port)
|
21
|
+
return if @simulation_mode
|
22
|
+
@fd = IO.sysopen(serial_port, 'r')
|
23
|
+
@io = IO.new(fd, 'r')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/enginevib.rb
ADDED
data/lib/rtos.bundle
ADDED
Binary file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AveragerTest < Minitest::Test
|
4
|
+
def test_average_must_be_correct
|
5
|
+
avg = Enginevib::Averager.new(10)
|
6
|
+
|
7
|
+
avg << 15
|
8
|
+
avg << 5
|
9
|
+
|
10
|
+
assert_equal 10, avg.average
|
11
|
+
|
12
|
+
avg << 40
|
13
|
+
|
14
|
+
assert_equal 20, avg.average
|
15
|
+
end
|
16
|
+
end
|
data/test/fixtures/dummy
ADDED
data/test/io_test.rb
ADDED
data/test/main_test.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MainTest < Minitest::Test
|
4
|
+
def test_must_initialize_components
|
5
|
+
main = Enginevib::Main.new
|
6
|
+
refute_nil main.controller
|
7
|
+
refute_nil main.sensor
|
8
|
+
refute_nil main.output
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_must_initialize_scheduler
|
12
|
+
main = Enginevib::Main.new
|
13
|
+
refute_nil main.scheduler
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_deadline_must_be_met
|
17
|
+
main = Enginevib::Main.new
|
18
|
+
(1..100).each { assert main.scheduler.tick }
|
19
|
+
end
|
20
|
+
end
|
data/test/sensor_test.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SensorTest < Minitest::Test
|
4
|
+
def test_must_run_simulation
|
5
|
+
sensor = Enginevib::Sensor.new(nil, true)
|
6
|
+
refute_nil sensor.read
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_must_read_device
|
10
|
+
sensor = Enginevib::Sensor.new("test/fixtures/dummy", false)
|
11
|
+
assert_equal 1234.0, sensor.read
|
12
|
+
end
|
13
|
+
end
|
data/test/stats_test.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class StatsTest < Minitest::Test
|
4
|
+
def test_must_initialize
|
5
|
+
stats = Enginevib::Stats.new
|
6
|
+
refute_nil stats
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_must_initialize_with_default_values
|
10
|
+
stats = Enginevib::Stats.new
|
11
|
+
assert_equal false, stats.warning
|
12
|
+
assert_equal 0, stats.window
|
13
|
+
assert_equal 0, stats.memory
|
14
|
+
assert_equal 0, stats.window_avg
|
15
|
+
end
|
16
|
+
end
|
data/test/system_test.rb
ADDED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
require 'enginevib/launcher'
|
5
|
+
require 'enginevib/io'
|
6
|
+
require 'enginevib/main'
|
7
|
+
require 'enginevib/sensor'
|
8
|
+
require 'enginevib/output'
|
9
|
+
require 'enginevib/system'
|
10
|
+
require 'enginevib/soft_ticker'
|
11
|
+
require 'enginevib/scheduler'
|
12
|
+
require 'enginevib/controller'
|
13
|
+
require 'enginevib/stats'
|
14
|
+
require 'enginevib/averager'
|
15
|
+
require 'enginevib/version'
|
16
|
+
|
17
|
+
|
18
|
+
require 'rtos'
|
19
|
+
|
20
|
+
require 'minitest/autorun'
|
21
|
+
|
22
|
+
$VERBOSE = true
|
23
|
+
$SOFT_TICKER = false
|
24
|
+
$TEST = true
|