From 5bfc14544d83b889587d30356ba67569d7f4e0e7 Mon Sep 17 00:00:00 2001
From: Brendan Bartels <bbartels@iastate.edu>
Date: Fri, 31 Mar 2017 21:03:38 -0500
Subject: [PATCH] quad: create tests script for virtual quad

And add I2C implementation for virtual quad
---
 quad/scripts/tests/run_virtual_test_flight.rb | 106 ++++++++++++++++++
 quad/scripts/tests/test_safety_checks.rb      |  73 +++++++-----
 quad/src/virt_quad/hw_impl_unix_i2c.c         | 106 ++++++++++++++++++
 3 files changed, 257 insertions(+), 28 deletions(-)
 create mode 100644 quad/scripts/tests/run_virtual_test_flight.rb

diff --git a/quad/scripts/tests/run_virtual_test_flight.rb b/quad/scripts/tests/run_virtual_test_flight.rb
new file mode 100644
index 000000000..9bc3afa36
--- /dev/null
+++ b/quad/scripts/tests/run_virtual_test_flight.rb
@@ -0,0 +1,106 @@
+#!/usr/bin/env ruby
+
+# Test Flight
+#
+# A simple virtual test flight (take off, hover, and set back down)
+#
+
+THROTTLE_MIN = 110200
+THROTTLE_MAX = 191900
+THROTTLE_MID = (THROTTLE_MAX + THROTTLE_MIN)/2
+THROTTLE_3_4 = (THROTTLE_MAX + THROTTLE_MID)/2
+THROTTLE_QUAR = (THROTTLE_MID + THROTTLE_MIN)/2
+THROTTLE_EIGHTH = (THROTTLE_QUAR + THROTTLE_MIN)/2
+THROTTLE_16 = (THROTTLE_EIGHTH + THROTTLE_MIN)/2
+MOTOR_MIN = 100000
+MOTOR_MAX = 200000
+GEAR_ON = 170800
+GEAR_OFF = 118300
+GRAVITY = 4096
+
+MOTOR1 = "virt-quad-fifos/pwm-output-motor1"
+MOTOR2 = "virt-quad-fifos/pwm-output-motor2"
+MOTOR3 = "virt-quad-fifos/pwm-output-motor3"
+MOTOR4 = "virt-quad-fifos/pwm-output-motor4"
+
+UART_TX = "virt-quad-fifos/uart-tx"
+
+GEAR = "virt-quad-fifos/pwm-input-gear"
+THROTTLE = "virt-quad-fifos/pwm-input-throttle"
+
+LED = "virt-quad-fifos/mio7-led"
+
+I2C_MPU_ACCEL_X = "virt-quad-fifos/i2c-mpu-accel-x"
+I2C_MPU_ACCEL_Y = "virt-quad-fifos/i2c-mpu-accel-y"
+I2C_MPU_ACCEL_Z = "virt-quad-fifos/i2c-mpu-accel-z"
+
+require 'test/unit/assertions'
+require 'thread'
+include Test::Unit::Assertions
+
+script_dir = File.expand_path(File.dirname(__FILE__))
+bin_dir = script_dir + "/../../bin/"
+Dir.chdir(bin_dir)
+
+# Start virtual quad
+quad = Process.spawn("./virt-quad")
+
+sleep 1
+
+##################
+#  Begin Flight!
+##################
+
+begin
+  puts("Starting flight")
+
+  # Set gravity
+  File.write(I2C_MPU_ACCEL_Z, -1 * GRAVITY)
+
+  puts("Turning on GEAR...")
+  File.write(GEAR, GEAR_ON)
+  sleep 0.015
+
+  puts("Increasing Thrust to half maximum...")
+  for i in (THROTTLE_MIN..THROTTLE_MID).step(1000)
+    File.write(THROTTLE, i)
+    sleep 0.005
+  end
+
+  puts("Hovering for 10 seconds")
+  sleep 10
+
+  puts("Relaxing thrust to zero")
+  i = THROTTLE_MID
+  while i > THROTTLE_MIN
+    i -= 1000
+    File.write(THROTTLE, i)
+    sleep 0.005
+  end
+
+  puts("Swiching off GEAR...")
+  File.write(GEAR, GEAR_OFF)
+
+  # puts("Collecting logs...")
+  # msg = ""
+  # misses = 0
+  # fifo = File::open(UART_TX)
+  # while misses < 10
+  #   puts "trying..."
+  #   if fifo.eof?
+  #     misses += 1
+  #     next
+  #   end
+  #   msg += fifo.read()
+  # end
+  
+  # fifo.close()
+
+  # puts msg
+
+ensure
+
+  Process.kill(9, quad)
+
+end
+
diff --git a/quad/scripts/tests/test_safety_checks.rb b/quad/scripts/tests/test_safety_checks.rb
index b5075df08..f4e690495 100644
--- a/quad/scripts/tests/test_safety_checks.rb
+++ b/quad/scripts/tests/test_safety_checks.rb
@@ -15,6 +15,7 @@ MOTOR_MIN = 100000
 MOTOR_MAX = 200000
 GEAR_ON = 170800
 GEAR_OFF = 118300
+GRAVITY = 4096
 
 MOTOR1 = "virt-quad-fifos/pwm-output-motor1"
 MOTOR2 = "virt-quad-fifos/pwm-output-motor2"
@@ -26,12 +27,21 @@ THROTTLE = "virt-quad-fifos/pwm-input-throttle"
 
 LED = "virt-quad-fifos/mio7-led"
 
+I2C_MPU_ACCEL_X = "virt-quad-fifos/i2c-mpu-accel-x"
+I2C_MPU_ACCEL_Y = "virt-quad-fifos/i2c-mpu-accel-y"
+I2C_MPU_ACCEL_Z = "virt-quad-fifos/i2c-mpu-accel-z"
+
 require 'test/unit/assertions'
 require 'thread'
 include Test::Unit::Assertions
 
-def read_fifo_num(fifo)
-  File.read(fifo).chomp.split("\n").last.to_i
+$fifos = Hash.new
+
+def read_fifo_num(f)
+  if not $fifos.key?(f)
+    $fifos[f] = File.open(f)
+  end
+  $fifos[f].read().chomp.split("\n").last.to_i
 end
 
 # Utility functions
@@ -46,6 +56,23 @@ def check_motors_are_off
   assert_operator motor4, :<=, THROTTLE_MIN
 end
 
+def get_motor_averages
+  motors = [[], [], [], []]
+  for i in 0..100
+    motors[0].push(read_fifo_num(MOTOR1))
+    motors[1].push(read_fifo_num(MOTOR2))
+    motors[2].push(read_fifo_num(MOTOR3))
+    motors[3].push(read_fifo_num(MOTOR4))
+    sleep 0.010
+  end
+  average = []
+  average[0] = motors[0].inject(:+).to_f / motors[0].size
+  average[1] = motors[1].inject(:+).to_f / motors[1].size
+  average[2] = motors[2].inject(:+).to_f / motors[2].size
+  average[3] = motors[3].inject(:+).to_f / motors[3].size
+  average
+end
+
 def check_led(on)
   led = read_fifo_num(LED)
   assert_equal(led, on)
@@ -65,14 +92,18 @@ sleep 1
 #################
 
 begin
+  puts("beginning tests")
+  
+  # Set gravity
+  File.write(I2C_MPU_ACCEL_Z, -1 * GRAVITY)
 
-  # Check that motors are off at startup
+  puts("Check that motors are off at startup")
   check_motors_are_off
 
-  # Check that LED is off at startup
+  puts("Check that LED is off at startup")
   check_led(0)
 
-  # Check that increasing the throttle does nothing to motors
+  puts("Check that increasing the throttle does nothing to motors")
   # (because gear is still off)
   for i in (THROTTLE_MIN..THROTTLE_MAX).step(1000)
     File.write(THROTTLE, i)
@@ -80,7 +111,7 @@ begin
     sleep 0.005
   end
 
-  # Check that flipping gear to 1 while throttle is high does nothing
+  puts("Check that flipping gear to 1 while throttle is high does nothing")
   # (motors should still be off, LED should still be off)
   File.write(GEAR, GEAR_ON)
   sleep 0.015
@@ -98,38 +129,24 @@ begin
   File.write(GEAR, GEAR_OFF)
   File.write(THROTTLE, THROTTLE_MIN)
 
-  # Check that the LED turns on when gear is flipped on
+  puts("Check that the LED turns on when gear is flipped on")
   # (motors should still be off because our throttle is low)
   File.write(GEAR, GEAR_ON)
-  sleep 0.010
+  sleep 0.020
   check_led 1
   check_motors_are_off
 
-  # Check that motors turn on
+  puts("Check that motors turn on")
   File.write(THROTTLE, THROTTLE_MID)
-  motor1 = []
-  motor2 = []
-  motor3 = []
-  motor4 = []
-  for i in 0..100
-    motor1.push(read_fifo_num(MOTOR1))
-    motor2.push(read_fifo_num(MOTOR2))
-    motor3.push(read_fifo_num(MOTOR3))
-    motor4.push(read_fifo_num(MOTOR4))
-    sleep 0.005
-  end
-  average1 = motor1.inject(:+).to_f / motor1.size
-  average2 = motor2.inject(:+).to_f / motor2.size
-  average3 = motor3.inject(:+).to_f / motor3.size
-  average4 = motor4.inject(:+).to_f / motor4.size
-  average = (average1 + average2 + average3 + average4)/4
-  puts average1, average2, average3, average4, "(#{average})"
+  averages = get_motor_averages
+  average = (averages[0] + averages[1] + averages[2] + averages[3])/4
+  puts averages, "(#{average})"
   assert average.between?(THROTTLE_EIGHTH, MOTOR_MAX)
 
   # Check that gear switch kills the motors
   # (and that light goes off)
   File.write(GEAR, GEAR_OFF)
-  sleep 0.015
+  sleep 0.040
   check_motors_are_off
   check_led 0
 
@@ -138,7 +155,7 @@ begin
 
   # Check that we can resume flight
   File.write(GEAR, GEAR_ON)
-  sleep 0.015
+  sleep 0.040
   check_led 1
 
   sleep 1
diff --git a/quad/src/virt_quad/hw_impl_unix_i2c.c b/quad/src/virt_quad/hw_impl_unix_i2c.c
index 09ab43c8b..9de9815c2 100644
--- a/quad/src/virt_quad/hw_impl_unix_i2c.c
+++ b/quad/src/virt_quad/hw_impl_unix_i2c.c
@@ -1,6 +1,59 @@
 #include "hw_impl_unix.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include "iic_utils.h"
+
+void * update_i2c_input_cache(void *);
+
+union val {
+  unsigned char b[2];
+  unsigned short s;
+};
+
+static char *input_names[6];
+static int fifos[6];
+static union val cache[6];
+
+static short last_dev;
+static short last_reg;
+static short last_val;
+
+static int zero = 0;
+static int one = 1;
+static int two = 2;
+static int three = 3;
+static int four = 4;
+static int five = 5;
+
+static pthread_t worker;
 
 int unix_i2c_reset(struct I2CDriver *self) {
+  input_names[0] = "i2c-mpu-accel-x";
+  input_names[1] = "i2c-mpu-accel-y";
+  input_names[2] = "i2c-mpu-accel-z";
+  input_names[3] = "i2c-mpu-gryo-x";
+  input_names[4] = "i2c-mpu-gryo-y";
+  input_names[5] = "i2c-mpu-gyro-z";
+
+  mkdir(VIRT_QUAD_FIFOS_DIR, 0777);
+
+  // Start up worker thread whose job is to update the caches
+  pthread_create(&worker, 0, update_i2c_input_cache, &zero);
+  pthread_create(&worker, 0, update_i2c_input_cache, &one);
+  pthread_create(&worker, 0, update_i2c_input_cache, &two);
+  pthread_create(&worker, 0, update_i2c_input_cache, &three);
+  pthread_create(&worker, 0, update_i2c_input_cache, &four);
+  pthread_create(&worker, 0, update_i2c_input_cache, &five);
+
+  cache[0].s = 0;
+  cache[1].s = 0;
+  cache[2].s = 0;
+  cache[3].s = 0;
+  cache[4].s = 0;
+  cache[5].s = 0;
+
   return 0;
 }
 
@@ -8,6 +61,11 @@ int unix_i2c_write(struct I2CDriver *self,
                    unsigned short device_addr,
                    unsigned char *data,
                    unsigned int length) {
+  if (length == 2) {
+    last_dev = device_addr;
+    last_reg = data[0];
+    last_val = data[1];
+  }
   return 0;
 }
 
@@ -15,5 +73,53 @@ int unix_i2c_read(struct I2CDriver *self,
                   unsigned short device_addr,
                   unsigned char *buff,
                   unsigned int length) {
+  if (last_dev != device_addr) {
+    return -1;
+  }
+
+  switch (device_addr) {
+  case MPU9150_DEVICE_ADDR:
+    if (last_reg == ACCEL_GYRO_BASE_ADDR) {
+      buff[0] = cache[0].b[0];
+      buff[1] = cache[0].b[1];
+      buff[2] = cache[1].b[0];
+      buff[3] = cache[1].b[1];
+      buff[4] = cache[2].b[0];
+      buff[5] = cache[2].b[1];
+      buff[6] = 0;
+      buff[7] = 0;
+      buff[8] = cache[3].b[0];
+      buff[9] = cache[3].b[1];
+      buff[10] = cache[4].b[0];
+      buff[11] = cache[4].b[1];
+      buff[12] = cache[5].b[0];
+      buff[13] = cache[5].b[1];
+    }
+  }
   return 0;
 }
+
+void * update_i2c_input_cache(void *arg) {
+  int *cache_index = arg;
+  int i = *cache_index;
+  char buff[16];
+
+  // Setup FIFO
+  unlink(input_names[i]);
+  char fifoname[64];
+  sprintf(fifoname, "%s/%s", VIRT_QUAD_FIFOS_DIR, input_names[i]);
+  mkfifo(fifoname, 0666);
+  fifos[i] = open(fifoname, O_RDONLY);
+
+  // Block while waiting for reads
+  while (1) {
+    int bytes_read = read(fifos[i], buff, 15);
+    if (bytes_read > 0) {
+      buff[bytes_read] = '\0';
+      unsigned long val = strtoll(buff, NULL, 10);
+      cache[i].s = val;
+      printf("%s: %ld\n", input_names[i], val);
+    }
+  }
+  return NULL;
+}
-- 
GitLab