summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt65
-rw-r--r--Dockerfile15
-rw-r--r--README.md53
-rw-r--r--Vagrantfile9
-rwxr-xr-xbbb_setup.sh33
-rwxr-xr-xbuild.sh3
-rw-r--r--compose-dev.yaml14
-rw-r--r--include/SimpleKalmanFilter.h32
-rw-r--r--include/bno055.hpp292
m---------include/eigen0
-rw-r--r--include/kalmanfilter.hpp2
m---------include/pico-sdk0
-rw-r--r--include/pwm.hpp36
-rw-r--r--include/spi_flash.h55
-rw-r--r--include/state_machine.hpp26
-rw-r--r--include/unused/actuationPlan.hpp36
-rw-r--r--include/unused/actuator.hpp30
-rw-r--r--include/unused/ads.hpp66
-rw-r--r--include/unused/logger.hpp83
-rw-r--r--include/unused/motor.hpp36
-rw-r--r--include/unused/rocketUtils.hpp121
-rw-r--r--include/unused/sensorAltimeter.hpp165
-rw-r--r--include/unused/sensorI2C.hpp105
-rw-r--r--include/unused/sensorIMU.hpp312
-rw-r--r--include/unused/surfaceFitModel.hpp32
-rw-r--r--src/AltEst/algebra.cpp292
-rw-r--r--src/AltEst/algebra.h96
-rw-r--r--src/AltEst/altitude.cpp58
-rw-r--r--src/AltEst/altitude.h55
-rw-r--r--src/AltEst/filters.cpp202
-rw-r--r--src/AltEst/filters.h65
-rw-r--r--src/CMakeLists.txt101
-rw-r--r--src/SimpleKalmanFilter.cpp48
-rw-r--r--src/active_drag_system.cpp655
-rw-r--r--src/altimeter.cpp50
-rw-r--r--src/bno055.cpp201
-rw-r--r--src/kalmanfilter.cpp3
-rw-r--r--src/pwm.cpp35
-rw-r--r--src/read_flash.c71
-rw-r--r--src/servo_test.cpp27
-rw-r--r--src/spi_flash.c267
-rw-r--r--src/unused/actuationPlan.cpp60
-rw-r--r--src/unused/ads.cpp286
-rw-r--r--src/unused/logger.cpp132
-rw-r--r--src/unused/motor.cpp46
-rw-r--r--src/unused/rocketUtils.cpp35
-rw-r--r--src/unused/sensorAltimeter.cpp115
-rw-r--r--src/unused/sensorIMU.cpp385
-rw-r--r--src/unused/surfaceFitModel.cpp40
-rw-r--r--test/kalmanFilterTest.cpp2
52 files changed, 4776 insertions, 179 deletions
diff --git a/.gitignore b/.gitignore
index dd2214a..affb9f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-<<<<<<< HEAD
Testing/*
.DS_Store
.vagrant/*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..4d3602d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "include/pico-sdk"]
+ path = include/pico-sdk
+ url = https://github.com/raspberrypi/pico-sdk
+[submodule "include/eigen"]
+ path = include/eigen
+ url = https://gitlab.com/libeigen/eigen.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 28e3426..4973b9d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,51 +1,32 @@
-cmake_minimum_required(VERSION 3.14)
-set(CMAKE_SYSTEM_NAME Linux)
-set(CMAKE_SYSTEM_PROCESSOR ARM)
-set(CMAKE_BUILD_TYPE Debug)
-set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
-
-# Set the cross-compiler for ARM32
-if(CMAKE_HOST_APPLE)
- message(STATUS "Running on an Apple system")
- set(CMAKE_CXX_STANDARD 14)
- set(CMAKE_CXX_STANDARD_REQUIRED ON)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
- include(CTest)
- # Apple Settings
- # Musl has many targets, must select one
- set(CMAKE_C_COMPILER /opt/homebrew/bin/arm-linux-musleabihf-gcc)
- set(CMAKE_CXX_COMPILER /opt/homebrew/bin/arm-linux-musleabihf-g++)
-elseif(UNIX)
- message(STATUS "Running on an Linux system")
-
- # Linux Settings
- # (arm-linux-musleabihf- is 32 bit, aarch64-linux... is the 64 bit version)
- set(CMAKE_C_COMPILER arm-linux-musleabihf-gcc)
- set(CMAKE_CXX_COMPILER arm-linux-musleabihf-g++)
-endif()
+cmake_minimum_required(VERSION 3.12)
-# Set the architecture and flags
-# set(CMAKE_C_FLAGS "-march=armv7-a+fp")
-# set(CMAKE_CXX_FLAGS "-march=armv7-a+fp")
+set(PICO_BOARD pico_w)
+# Pull in SDK (must be before project)
+include(include/pico-sdk/pico_sdk_init.cmake)
+include_directories(include/eigen)
-# project(Active-Drag-System CXX)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+set(CMAKE_C_COMPILER arm-none-eabi-gcc)
+set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
-# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out")
+project(active-drag-system C CXX ASM)
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
-add_subdirectory(src)
+if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
+ message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
+endif()
-if(BUILD_TESTING)
- include(CTest)
+# Initialize the SDK
+pico_sdk_init()
- add_subdirectory(test)
+add_subdirectory(src)
- include(FetchContent)
- FetchContent_Declare(
- googletest
- URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
- )
- # For Windows: Prevent overriding the parent project's compiler/linker settings
- set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
- FetchContent_MakeAvailable(googletest)
+add_compile_options(-Wall
+ -Wno-format # int != int32_t as far as the compiler is concerned because gcc has int32_t as long int
+ -Wno-unused-function # we have some for the docs that aren't called
+ )
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ add_compile_options(-Wno-maybe-uninitialized)
endif()
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..62b9af6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,15 @@
+FROM alpine:3.17.0
+
+# Install toolchain
+RUN apk update && \
+ apk upgrade && \
+ apk add git \
+ python3 \
+ py3-pip \
+ cmake \
+ build-base \
+ libusb-dev \
+ bsd-compat-headers \
+ newlib-arm-none-eabi \
+ gcc-arm-none-eabi \
+ valgrind \ No newline at end of file
diff --git a/README.md b/README.md
index cb569aa..ae9dcec 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,13 @@
-# THIS IS THE BRANCH USED FOR SOUTH CAROLINA TEST LAUNCH 02/24/2024
-
# Active Drag System (ADS)
-This is the main codebase for Rocketry at Virginia Tech's Active Drag System, also known colloquially as the ADS, for the 2023-2024 competition year. It runs primarily on a BeagleBone Black, and its goal is to autonomously control the ADS' deployment during flight.
+This is the main codebase for Rocketry at Virginia Tech's Active Drag System, also known colloquially as the ADS, for the 2023-2024 competition year. It runs primarily on a Raspberry Pi Pico, and its goal is to autonomously control the ADS' deployment during flight.
-`Eigen` Library, `cmake` and `Google Test` required for successful build.
+`Eigen` Library, `cmake`, and `arm-none-eabi-gcc` tooling required for successful build.
+## Before Build
+```shell
+git clone https://github.com/RocketryVT/active-drag-system.git
+cd active-drag-system/
+git submodule update --init --recursive
+```
## BUILD
```shell
@@ -18,50 +22,31 @@ Enable WSL2 in windows
Install Ubuntu 22 LTS from Windows Store
```shell
sudo apt update && upgrade
-sudo apt install build-essential cmake valgrind crossbuild-essential-armhf
-sudo apt install musl musl-tools
+sudo apt install build-essential cmake valgrind gcc-arm-none-eabi
```
Then to actually build:
```shell
cmake -B build
-cmake --build build/
+cmake --build build
```
## BUILD Alternative (Mac)
```shell
-brew install arm-linux-gnueabihf-binutils
-# This installs just the arm library, you can remove x86_64 if you want those too
-brew install FiloSottile/musl-cross/musl-cross --without-x86_64 --with-arm-hf
+brew install arm-none-eabi-gcc
```
To check if installed correctly run:
```shell
-ls /opt/homebrew/bin | grep "musl"
+ls /opt/homebrew/bin | grep "none"
```
-you should see:
+you should see a list including but not limited to:
```shell
-arm-linux-musleabihf-gcc
-arm-linux-musleabihf-g++
+arm-none-eabi-gcc
+arm-none-eabi-g++
```
-
-## RUN
+Next:
```shell
-scp -r src/ads [email protected]:~/
-./ads
-```
-
-## TEST
-```shell
-scp -r test/test_ads [email protected]:~/
-./test_ads
+cmake -B build
+cmake --build build
```
-## GPIO Pins
-The GPIO number is calculated by taking the GPIO chip
-number, multiplying it by 32, and then adding the offset. For example,
-GPIO1_12 = (1 × 32) + 12 = GPIO 44.
-
-```shell
-/sys/class/gpio/gpio44 = GPIO1_12
-```
+Binary files should be located in build/src/*.uf2 after a successful build.
diff --git a/Vagrantfile b/Vagrantfile
index 7f542e1..ff65b09 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -5,14 +5,7 @@ apt-get update && apt-get upgrade
# install base development tools
apt-get -y install build-essential
-apt-get -y install cmake valgrind
-apt-get -y install crossbuild-essential-armhf
-apt-get -y install libboost-all-dev
-apt-get -y install libasio-dev
-wget https://musl.cc/arm-linux-musleabihf-cross.tgz
-tar -xzvf arm-linux-musleabihf-cross.tgz
-echo 'export PATH="\$PATH:/home/vagrant/arm-linux-musleabihf-cross/bin"' >> .bashrc
-rm -r arm-linux-musleabihf-cross.tgz
+apt-get -y install cmake valgrind gcc-arm-none-eabi
BOOTSTRAP
#-----------------------------------------------------------------------
diff --git a/bbb_setup.sh b/bbb_setup.sh
deleted file mode 100755
index 42e008d..0000000
--- a/bbb_setup.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-
-# Change based upon device specifics
-IFACE_BB=enp0s20f0u5
-IFACE_SELF=wlan0
-EXE=ads
-
-echo "Compiling Executeables"
-vagrant up
-vagrant ssh << EOF
- cmake /vagrant;
- cmake --build .;
- mkdir -p build
- cp -r out/* /vagrant/build/
-EOF
-
-echo "Forwarding Traffic to Beaglebone"
-# Forwards all internet traffic requested by beaglebone to proper interface
-sudo sh -c "ip link set $IFACE_BB"
-sudo sh -c "dhclient $IFACE_BB"
-
-sudo sh -c "iptables --table nat --append POSTROUTING --out-interface $IFACE_SELF -j MASQUERADE"
-sudo sh -c "iptables --append FORWARD --in-interface $IFACE_BB -j ACCEPT"
-sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
-
-echo "Copying Executeables to Beaglebone"
-scp -r build/* [email protected]:~/;
-rm -r build
-
-echo "Running Main Program"
-ssh [email protected] /bin/bash << EOF
- ./$EXE;
-EOF
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..6c2ece8
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+cmake -B build
+cmake --build build \ No newline at end of file
diff --git a/compose-dev.yaml b/compose-dev.yaml
new file mode 100644
index 0000000..81a2ce4
--- /dev/null
+++ b/compose-dev.yaml
@@ -0,0 +1,14 @@
+services:
+ app:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ entrypoint:
+ - sleep
+ - infinity
+ init: true
+ volumes:
+ - type: bind
+ source: /var/run/docker.sock
+ target: /var/run/docker.sock
+
diff --git a/include/SimpleKalmanFilter.h b/include/SimpleKalmanFilter.h
new file mode 100644
index 0000000..61b682e
--- /dev/null
+++ b/include/SimpleKalmanFilter.h
@@ -0,0 +1,32 @@
+/*
+ * SimpleKalmanFilter - a Kalman Filter implementation for single variable models.
+ * Created by Denys Sene, January, 1, 2017.
+ * Released under MIT License - see LICENSE file for details.
+ */
+
+#ifndef SimpleKalmanFilter_h
+#define SimpleKalmanFilter_h
+
+class SimpleKalmanFilter
+{
+
+public:
+ SimpleKalmanFilter(float mea_e, float est_e, float q);
+ float updateEstimate(float mea);
+ void setMeasurementError(float mea_e);
+ void setEstimateError(float est_e);
+ void setProcessNoise(float q);
+ float getKalmanGain();
+ float getEstimateError();
+
+private:
+ float _err_measure;
+ float _err_estimate;
+ float _q;
+ float _current_estimate = 0;
+ float _last_estimate = 0;
+ float _kalman_gain = 0;
+
+};
+
+#endif
diff --git a/include/bno055.hpp b/include/bno055.hpp
new file mode 100644
index 0000000..7fbac4d
--- /dev/null
+++ b/include/bno055.hpp
@@ -0,0 +1,292 @@
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include "boards/pico_w.h"
+#include "hardware/gpio.h"
+#include "hardware/i2c.h"
+#include "pico/stdlib.h"
+#include "pico/time.h"
+#include "pico/types.h"
+
+#include <Eigen/Geometry>
+
+//Register addresses and data structs copied from Adafruit implementation
+/** BNO055 Address A **/
+#define BNO055_ADDRESS_A (0x28)
+/** BNO055 Address B **/
+#define BNO055_ADDRESS_B (0x29)
+/** BNO055 ID **/
+#define BNO055_ID (0xA0)
+
+/** Offsets registers **/
+#define NUM_BNO055_OFFSET_REGISTERS (22)
+
+
+/** A structure to represent offsets **/
+typedef struct {
+ int16_t accel_offset_x; /**< x acceleration offset */
+ int16_t accel_offset_y; /**< y acceleration offset */
+ int16_t accel_offset_z; /**< z acceleration offset */
+
+ int16_t mag_offset_x; /**< x magnetometer offset */
+ int16_t mag_offset_y; /**< y magnetometer offset */
+ int16_t mag_offset_z; /**< z magnetometer offset */
+
+ int16_t gyro_offset_x; /**< x gyroscrope offset */
+ int16_t gyro_offset_y; /**< y gyroscrope offset */
+ int16_t gyro_offset_z; /**< z gyroscrope offset */
+
+ int16_t accel_radius; /**< acceleration radius */
+
+ int16_t mag_radius; /**< magnetometer radius */
+} bno055_offsets_t;
+
+/** Operation mode settings **/
+typedef enum {
+ OPERATION_MODE_CONFIG = 0X00,
+ OPERATION_MODE_ACCONLY = 0X01,
+ OPERATION_MODE_MAGONLY = 0X02,
+ OPERATION_MODE_GYRONLY = 0X03,
+ OPERATION_MODE_ACCMAG = 0X04,
+ OPERATION_MODE_ACCGYRO = 0X05,
+ OPERATION_MODE_MAGGYRO = 0X06,
+ OPERATION_MODE_AMG = 0X07,
+ OPERATION_MODE_IMUPLUS = 0X08,
+ OPERATION_MODE_COMPASS = 0X09,
+ OPERATION_MODE_M4G = 0X0A,
+ OPERATION_MODE_NDOF_FMC_OFF = 0X0B,
+ OPERATION_MODE_NDOF = 0X0C
+} bno055_opmode_t;
+
+/** BNO055 Registers **/
+typedef enum {
+ /* Page id register definition */
+ BNO055_PAGE_ID_ADDR = 0X07,
+
+ /* PAGE0 REGISTER DEFINITION START*/
+ BNO055_CHIP_ID_ADDR = 0x00,
+ BNO055_ACCEL_REV_ID_ADDR = 0x01,
+ BNO055_MAG_REV_ID_ADDR = 0x02,
+ BNO055_GYRO_REV_ID_ADDR = 0x03,
+ BNO055_SW_REV_ID_LSB_ADDR = 0x04,
+ BNO055_SW_REV_ID_MSB_ADDR = 0x05,
+ BNO055_BL_REV_ID_ADDR = 0X06,
+
+ /* Accel data register */
+ BNO055_ACCEL_DATA_X_LSB_ADDR = 0X08,
+ BNO055_ACCEL_DATA_X_MSB_ADDR = 0X09,
+ BNO055_ACCEL_DATA_Y_LSB_ADDR = 0X0A,
+ BNO055_ACCEL_DATA_Y_MSB_ADDR = 0X0B,
+ BNO055_ACCEL_DATA_Z_LSB_ADDR = 0X0C,
+ BNO055_ACCEL_DATA_Z_MSB_ADDR = 0X0D,
+
+ /* Mag data register */
+ BNO055_MAG_DATA_X_LSB_ADDR = 0X0E,
+ BNO055_MAG_DATA_X_MSB_ADDR = 0X0F,
+ BNO055_MAG_DATA_Y_LSB_ADDR = 0X10,
+ BNO055_MAG_DATA_Y_MSB_ADDR = 0X11,
+ BNO055_MAG_DATA_Z_LSB_ADDR = 0X12,
+ BNO055_MAG_DATA_Z_MSB_ADDR = 0X13,
+
+ /* Gyro data registers */
+ BNO055_GYRO_DATA_X_LSB_ADDR = 0X14,
+ BNO055_GYRO_DATA_X_MSB_ADDR = 0X15,
+ BNO055_GYRO_DATA_Y_LSB_ADDR = 0X16,
+ BNO055_GYRO_DATA_Y_MSB_ADDR = 0X17,
+ BNO055_GYRO_DATA_Z_LSB_ADDR = 0X18,
+ BNO055_GYRO_DATA_Z_MSB_ADDR = 0X19,
+
+ /* Euler data registers */
+ BNO055_EULER_H_LSB_ADDR = 0X1A,
+ BNO055_EULER_H_MSB_ADDR = 0X1B,
+ BNO055_EULER_R_LSB_ADDR = 0X1C,
+ BNO055_EULER_R_MSB_ADDR = 0X1D,
+ BNO055_EULER_P_LSB_ADDR = 0X1E,
+ BNO055_EULER_P_MSB_ADDR = 0X1F,
+
+ /* Quaternion data registers */
+ BNO055_QUATERNION_DATA_W_LSB_ADDR = 0X20,
+ BNO055_QUATERNION_DATA_W_MSB_ADDR = 0X21,
+ BNO055_QUATERNION_DATA_X_LSB_ADDR = 0X22,
+ BNO055_QUATERNION_DATA_X_MSB_ADDR = 0X23,
+ BNO055_QUATERNION_DATA_Y_LSB_ADDR = 0X24,
+ BNO055_QUATERNION_DATA_Y_MSB_ADDR = 0X25,
+ BNO055_QUATERNION_DATA_Z_LSB_ADDR = 0X26,
+ BNO055_QUATERNION_DATA_Z_MSB_ADDR = 0X27,
+
+ /* Linear acceleration data registers */
+ BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR = 0X28,
+ BNO055_LINEAR_ACCEL_DATA_X_MSB_ADDR = 0X29,
+ BNO055_LINEAR_ACCEL_DATA_Y_LSB_ADDR = 0X2A,
+ BNO055_LINEAR_ACCEL_DATA_Y_MSB_ADDR = 0X2B,
+ BNO055_LINEAR_ACCEL_DATA_Z_LSB_ADDR = 0X2C,
+ BNO055_LINEAR_ACCEL_DATA_Z_MSB_ADDR = 0X2D,
+
+ /* Gravity data registers */
+ BNO055_GRAVITY_DATA_X_LSB_ADDR = 0X2E,
+ BNO055_GRAVITY_DATA_X_MSB_ADDR = 0X2F,
+ BNO055_GRAVITY_DATA_Y_LSB_ADDR = 0X30,
+ BNO055_GRAVITY_DATA_Y_MSB_ADDR = 0X31,
+ BNO055_GRAVITY_DATA_Z_LSB_ADDR = 0X32,
+ BNO055_GRAVITY_DATA_Z_MSB_ADDR = 0X33,
+
+ /* Temperature data register */
+ BNO055_TEMP_ADDR = 0X34,
+
+ /* Status registers */
+ BNO055_CALIB_STAT_ADDR = 0X35,
+ BNO055_SELFTEST_RESULT_ADDR = 0X36,
+ BNO055_INTR_STAT_ADDR = 0X37,
+
+ BNO055_SYS_CLK_STAT_ADDR = 0X38,
+ BNO055_SYS_STAT_ADDR = 0X39,
+ BNO055_SYS_ERR_ADDR = 0X3A,
+
+ /* Unit selection register */
+ BNO055_UNIT_SEL_ADDR = 0X3B,
+
+ /* Mode registers */
+ BNO055_OPR_MODE_ADDR = 0X3D,
+ BNO055_PWR_MODE_ADDR = 0X3E,
+
+ BNO055_SYS_TRIGGER_ADDR = 0X3F,
+ BNO055_TEMP_SOURCE_ADDR = 0X40,
+
+ /* Axis remap registers */
+ BNO055_AXIS_MAP_CONFIG_ADDR = 0X41,
+ BNO055_AXIS_MAP_SIGN_ADDR = 0X42,
+
+ /* SIC registers */
+ BNO055_SIC_MATRIX_0_LSB_ADDR = 0X43,
+ BNO055_SIC_MATRIX_0_MSB_ADDR = 0X44,
+ BNO055_SIC_MATRIX_1_LSB_ADDR = 0X45,
+ BNO055_SIC_MATRIX_1_MSB_ADDR = 0X46,
+ BNO055_SIC_MATRIX_2_LSB_ADDR = 0X47,
+ BNO055_SIC_MATRIX_2_MSB_ADDR = 0X48,
+ BNO055_SIC_MATRIX_3_LSB_ADDR = 0X49,
+ BNO055_SIC_MATRIX_3_MSB_ADDR = 0X4A,
+ BNO055_SIC_MATRIX_4_LSB_ADDR = 0X4B,
+ BNO055_SIC_MATRIX_4_MSB_ADDR = 0X4C,
+ BNO055_SIC_MATRIX_5_LSB_ADDR = 0X4D,
+ BNO055_SIC_MATRIX_5_MSB_ADDR = 0X4E,
+ BNO055_SIC_MATRIX_6_LSB_ADDR = 0X4F,
+ BNO055_SIC_MATRIX_6_MSB_ADDR = 0X50,
+ BNO055_SIC_MATRIX_7_LSB_ADDR = 0X51,
+ BNO055_SIC_MATRIX_7_MSB_ADDR = 0X52,
+ BNO055_SIC_MATRIX_8_LSB_ADDR = 0X53,
+ BNO055_SIC_MATRIX_8_MSB_ADDR = 0X54,
+
+ /* Accelerometer Offset registers */
+ ACCEL_OFFSET_X_LSB_ADDR = 0X55,
+ ACCEL_OFFSET_X_MSB_ADDR = 0X56,
+ ACCEL_OFFSET_Y_LSB_ADDR = 0X57,
+ ACCEL_OFFSET_Y_MSB_ADDR = 0X58,
+ ACCEL_OFFSET_Z_LSB_ADDR = 0X59,
+ ACCEL_OFFSET_Z_MSB_ADDR = 0X5A,
+
+ /* Magnetometer Offset registers */
+ MAG_OFFSET_X_LSB_ADDR = 0X5B,
+ MAG_OFFSET_X_MSB_ADDR = 0X5C,
+ MAG_OFFSET_Y_LSB_ADDR = 0X5D,
+ MAG_OFFSET_Y_MSB_ADDR = 0X5E,
+ MAG_OFFSET_Z_LSB_ADDR = 0X5F,
+ MAG_OFFSET_Z_MSB_ADDR = 0X60,
+
+ /* Gyroscope Offset register s*/
+ GYRO_OFFSET_X_LSB_ADDR = 0X61,
+ GYRO_OFFSET_X_MSB_ADDR = 0X62,
+ GYRO_OFFSET_Y_LSB_ADDR = 0X63,
+ GYRO_OFFSET_Y_MSB_ADDR = 0X64,
+ GYRO_OFFSET_Z_LSB_ADDR = 0X65,
+ GYRO_OFFSET_Z_MSB_ADDR = 0X66,
+
+ /* Radius registers */
+ ACCEL_RADIUS_LSB_ADDR = 0X67,
+ ACCEL_RADIUS_MSB_ADDR = 0X68,
+ MAG_RADIUS_LSB_ADDR = 0X69,
+ MAG_RADIUS_MSB_ADDR = 0X6A,
+
+ /* Interrupt registers*/
+ RST_INT = 0x01,
+
+ NO_MOTION_INT_ADDR = 0x00,
+ SLOW_NO_MOTION_INT_ADDR = 0x01,
+ THRESHOLD_INT_ADDR = 0x02,
+} bno055_reg_t;
+
+/** BNO055 power settings */
+typedef enum {
+ POWER_MODE_NORMAL = 0X00,
+ POWER_MODE_LOWPOWER = 0X01,
+ POWER_MODE_SUSPEND = 0X02
+} bno055_powermode_t;
+
+/** Vector Mappings **/
+typedef enum {
+ VECTOR_ACCELEROMETER = BNO055_ACCEL_DATA_X_LSB_ADDR,
+ VECTOR_MAGNETOMETER = BNO055_MAG_DATA_X_LSB_ADDR,
+ VECTOR_GYROSCOPE = BNO055_GYRO_DATA_X_LSB_ADDR,
+ VECTOR_EULER = BNO055_EULER_H_LSB_ADDR,
+ VECTOR_LINEARACCEL = BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR,
+ VECTOR_GRAVITY = BNO055_GRAVITY_DATA_X_LSB_ADDR
+} vector_type_t;
+
+struct quarternion {
+ float w;
+ float x;
+ float y;
+ float z;
+};
+
+struct vector3f {
+ float x;
+ float y;
+ float z;
+};
+
+struct CALIB_STATUS {
+ uint8_t sys;
+ uint8_t gyro;
+ uint8_t accel;
+ uint8_t mag;
+};
+
+extern volatile vector3f linear_acceleration;
+extern volatile vector3f acceleration;
+extern volatile quarternion abs_quaternion;
+extern volatile CALIB_STATUS calib_status;
+
+extern volatile vector3f euler_angles;
+extern volatile vector3f abs_lin_accel;
+extern volatile vector3f rot_y_vec;
+extern volatile vector3f vel_at_angle;
+
+extern volatile vector3f accel_gravity;
+
+extern uint8_t accel[6];
+extern uint8_t quat[8];
+
+class BNO055 {
+ public:
+ BNO055();
+ //Sanity check for factory device ID
+ void reset_bno055();
+ void init();
+ void read_lin_accel();
+ void read_abs_quaternion();
+ void read_euler_angles();
+ void read_accel();
+ void read_calib_status();
+ void calculate_abs_linear_acceleration();
+ void accel_to_gravity();
+ void quaternion_to_euler();
+ void get_rotation_vector();
+ void clamp_close_zero(volatile float &val);
+
+ private:
+ unsigned char bno055_address;
+ int32_t _sensorID;
+ bno055_opmode_t default_mode;
+};
diff --git a/include/eigen b/include/eigen
new file mode 160000
+Subproject 122befe54cc0c31273d9e1caef80b49ad834bf4
diff --git a/include/kalmanfilter.hpp b/include/kalmanfilter.hpp
index 2c8398c..8b9e942 100644
--- a/include/kalmanfilter.hpp
+++ b/include/kalmanfilter.hpp
@@ -1,5 +1,5 @@
#pragma once
-#include "eigen3/Eigen/Dense"
+#include <Eigen/Dense>
#include <iostream>
#include <cmath>
diff --git a/include/pico-sdk b/include/pico-sdk
new file mode 160000
+Subproject 6a7db34ff63345a7badec79ebea3aaef1712f37
diff --git a/include/pwm.hpp b/include/pwm.hpp
new file mode 100644
index 0000000..90e31cc
--- /dev/null
+++ b/include/pwm.hpp
@@ -0,0 +1,36 @@
+#include "boards/pico_w.h"
+#include "hardware/gpio.h"
+#include "hardware/i2c.h"
+#include "hardware/pwm.h"
+#include "pico/stdlib.h"
+#include "pico/time.h"
+#include "pico/types.h"
+#include "pico/divider.h"
+
+#define SERVO_PIN 27 // Servo motor connected to GPIO PIN 32 (GP27)
+#define WRAP_VALUE 65535
+#define CLOCK_DIV_RATE 38.15
+#define SERVO_MIN 13
+#define SERVO_MAX 3
+#define SERVO_RANGE -10
+
+class PWM {
+ public:
+ /**
+ * @brief Initialize the PWM
+ *
+ */
+ static void init();
+ /**
+ * @brief Set the duty cycle of our servo
+ *
+ * @param duty_cycle_percent from 0 to 100
+ */
+ static void set_duty_cycle(int duty_cycle_percent);
+ /**
+ * @brief Set the servo percent objec to the given percent
+ *
+ * @param percent from 0 to 100
+ */
+ static void set_servo_percent(int percent);
+};
diff --git a/include/spi_flash.h b/include/spi_flash.h
new file mode 100644
index 0000000..d3c0247
--- /dev/null
+++ b/include/spi_flash.h
@@ -0,0 +1,55 @@
+#ifndef SPI_FLASH
+#define SPI_FLASH
+
+#include <stdint.h>
+#include "hardware/spi.h"
+#include "boards/pico_w.h"
+#include "pico/stdlib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FLASH_PAGE_SIZE 256
+#define FLASH_NUM_PAGES 32768
+#define FLASH_SECTOR_SIZE 4096
+#define FLASH_BLOCK_SIZE 65536
+#define FLASH_PHYS_SIZE (FLASH_PAGE_SIZE * FLASH_NUM_PAGES)
+
+#define FLASH_CMD_PAGE_PROGRAM 0x02
+#define FLASH_CMD_READ 0x03
+#define FLASH_CMD_STATUS 0x05
+#define FLASH_CMD_WRITE_EN 0x06
+#define FLASH_CMD_SECTOR_ERASE 0x20
+#define FLASH_CMD_BLOCK_ERASE 0xD8
+#define FLASH_CMD_CHIP_ERASE 0xC7
+
+#define FLASH_STATUS_BUSY_MASK 0x01
+
+// #define FLASH_TEST
+
+#define PACKET_SIZE 32
+
+
+static uint8_t page_buffer[FLASH_PAGE_SIZE];
+static uint32_t base_addr = 0;
+
+void write_entry(uint8_t* data_entry);
+
+void __not_in_flash_func(flash_read)(spi_inst_t *spi, uint cs_pin, uint32_t addr, uint8_t *dest, size_t len);
+
+
+void __not_in_flash_func(flash_write_enable)(spi_inst_t *spi, uint cs_pin);
+void __not_in_flash_func(flash_page_program)(spi_inst_t *spi, uint cs_pin, uint32_t addr, uint8_t* src);
+void __not_in_flash_func(flash_write)(spi_inst_t *spi, uint cs_pin, uint32_t addr, uint8_t* src, size_t size);
+
+void __not_in_flash_func(flash_wait_done)(spi_inst_t *spi, uint cs_pin);
+
+void __not_in_flash_func(flash_sector_erase)(spi_inst_t *spi, uint cs_pin, uint32_t addr);
+void __not_in_flash_func(flash_block_erase)(spi_inst_t *spi, uint cs_pin, uint32_t addr);
+void __not_in_flash_func(flash_erase)(spi_inst_t *spi, uint cs_pin);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/state_machine.hpp b/include/state_machine.hpp
new file mode 100644
index 0000000..874fc08
--- /dev/null
+++ b/include/state_machine.hpp
@@ -0,0 +1,26 @@
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+#include <stdint.h>
+
+extern volatile float altitude = 0.0f;
+extern volatile float prev_altitude = 0.0f;
+extern volatile float velocity;
+extern volatile state_t state;
+extern volatile float threshold_altitude;
+extern volatile float threshold_velocity;
+extern volatile uint8_t deployment_percent;
+extern volatile vector3f linear_acceleration;
+extern volatile vector3f acceleration;
+extern volatile quarternion abs_quaternion;
+extern volatile vector3f velocity_vector;
+extern volatile vector3f euler_angles;
+extern volatile vector3f abs_lin_accel;
+extern volatile vector3f prev_abs_lin_accel;
+extern volatile vector3f rot_y_vec;
+extern volatile vector3f vel_at_angle;
+extern volatile vector3f accel_gravity;
+extern volatile CALIB_STATUS calib_status;
+
+#endif
+
diff --git a/include/unused/actuationPlan.hpp b/include/unused/actuationPlan.hpp
new file mode 100644
index 0000000..8d1ef26
--- /dev/null
+++ b/include/unused/actuationPlan.hpp
@@ -0,0 +1,36 @@
+#pragma once
+#include <algorithm>
+#include <ctime>
+#include "surfaceFitModel.hpp"
+#include "rocketUtils.hpp"
+#include "sensorIMU.hpp"
+#include "sensorAltimeter.hpp"
+
+class ActuationPlan {
+
+ private:
+ SurfaceFitModel sFitModel;
+
+ public:
+
+ /**
+ * @brief Construct a new Actuation Plan object
+ *
+ */
+ ActuationPlan();
+
+ /**
+ * @brief Construct a new Actuation Plan object
+ *
+ * @param sFitModel
+ */
+ ActuationPlan(SurfaceFitModel sFitModel);
+
+ /**
+ * @brief Run the Fin Actuation Plan.
+ * Adjusts the fin angle values depending on the current vehicle state during the launch
+ *
+ * @param rocket Provides current rocket status and hold updated fin angle value.
+ */
+ void runPlan(Vehicle& rocket);
+}; \ No newline at end of file
diff --git a/include/unused/actuator.hpp b/include/unused/actuator.hpp
new file mode 100644
index 0000000..e7c4120
--- /dev/null
+++ b/include/unused/actuator.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+
+class Actuator {
+
+ private:
+
+
+ public:
+
+ /**
+ * @brief Initialize the actuator.
+ *
+ * @param data Data for initializing the actuator
+ *
+ * @return true Initialization Success
+ * @return false Initialization Failure
+ */
+ virtual bool init(void* data) = 0;
+
+ /**
+ * @brief Pass data to the actuator.
+ *
+ * @param data Data to sent to the actuator
+ *
+ * @return true Actuator write Success
+ * @return false Actuator write Failure
+ */
+ virtual bool writeData(void* data) = 0;
+}; \ No newline at end of file
diff --git a/include/unused/ads.hpp b/include/unused/ads.hpp
new file mode 100644
index 0000000..b16c4fb
--- /dev/null
+++ b/include/unused/ads.hpp
@@ -0,0 +1,66 @@
+#pragma once
+#include <iostream>
+#include <exception>
+#include <stdexcept>
+#include <chrono>
+#include <thread>
+#include "kalmanfilter.hpp"
+#include "logger.hpp"
+#include "actuationPlan.hpp"
+#include "rocketUtils.hpp"
+#include "sensorIMU.hpp"
+#include "sensorAltimeter.hpp"
+#include "motor.hpp"
+#include "eigen3/Eigen/Dense"
+
+using namespace Eigen;
+
+class ADS {
+
+ private:
+
+ KalmanFilter kf;
+ ActuationPlan plan;
+
+ IMUSensor imu;
+ AltimeterSensor altimeter;
+ Motor motor;
+ Vehicle rocket;
+
+ /**
+ * @brief Logs a summary of all pertinent current rocket data
+ * (e.g. Altitude, Velocity, Acceleration)
+ */
+ virtual void logSummary();
+
+ /**
+ * @brief Performs a routine to calculate the average altitude
+ * while the vehicle is waiting on the pad.
+ */
+ virtual void updateOnPadAltitude();
+
+ /**
+ * @brief Update the vehicle with the current sensor (IMU & Altimeter) readings
+ */
+ virtual void updateSensorData();
+
+ /**
+ * @brief Update the rocket state based on its current telemetry readings.
+ * Also Log pertinent telemetry and rocket state data
+ */
+ virtual void updateRocketState();
+
+ public:
+
+ /**
+ * @brief Construct a new ADS object
+ *
+ * @param plan The Actuation Plan for the Rocket
+ */
+ ADS(ActuationPlan plan);
+
+ /**
+ * @brief Run the full active drag system from launch to landing.
+ */
+ virtual void run();
+}; \ No newline at end of file
diff --git a/include/unused/logger.hpp b/include/unused/logger.hpp
new file mode 100644
index 0000000..90fa05a
--- /dev/null
+++ b/include/unused/logger.hpp
@@ -0,0 +1,83 @@
+#pragma once
+#include <iostream>
+#include <fstream>
+#include <ctime>
+
+class Logger {
+
+ private:
+ std::fstream file;
+ std::string filename;
+ time_t t;
+ tm* now;
+ bool file_open;
+
+ std::string infoTag = "-> [INFO]: ";
+ std::string errorTag = "-> [ERROR]: ";
+ std::string months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
+ std::string days[7] = {"Sun", "Mon", "Tues", "Wed", "Thur", "Frid", "Sat"};
+
+ /**
+ * @brief Create formatted current-date tag
+ *
+ * @return string
+ */
+ std::string getDate();
+
+ /**
+ * @brief Create formatted current-time tag
+ *
+ * @return string
+ */
+ std::string getTime();
+
+
+ public:
+
+ /**
+ * @brief
+ *
+ */
+ static Logger& Get();
+
+ /**
+ * @brief Open a Log File for writing
+ *
+ * @param _filename Name of file to open
+ * @return true Successful Open
+ * @return false Unsuccessful Open
+ */
+ bool openLog(std::string _filename);
+
+ /**
+ * @brief Close the Log File
+ *
+ */
+ void closeLog();
+
+ /**
+ * @brief Write data to Log file
+ *
+ * @param data Data to log
+ * @return true Data Successfully Logged
+ * @return false Data Logging Unsuccessful
+ */
+ bool log(std::string data);
+
+ /**
+ * @brief Write error data to Log file
+ *
+ * @param data Error Data to log
+ * @return true Data Successfully Logged
+ * @return false Data Logging Unsuccessful
+ */
+ bool logErr(std::string data);
+
+ /**
+ * @brief Print Log Data to Terminal
+ *
+ * @return true Successful Print
+ * @return false Unsuccessful Print
+ */
+ bool printLog();
+}; \ No newline at end of file
diff --git a/include/unused/motor.hpp b/include/unused/motor.hpp
new file mode 100644
index 0000000..cb95b6a
--- /dev/null
+++ b/include/unused/motor.hpp
@@ -0,0 +1,36 @@
+#pragma once
+#include "actuator.hpp"
+#include "logger.hpp"
+#include "rocketUtils.hpp"
+
+class Motor : public Actuator {
+
+ private:
+
+
+ public:
+
+ /**
+ * @brief Construct a new Motor object
+ *
+ */
+ Motor();
+
+ /**
+ * @brief Initialize the motor.
+ *
+ * @param data Data for initializing the motor
+ * @return true Initialization Success
+ * @return false Initialization Failure
+ */
+ virtual bool init(void* data) override;
+
+ /**
+ * @brief Write data to the motor.
+ *
+ * @param data Data to be writen to the motor
+ * @return true Motor write Success
+ * @return false Motor write Failure
+ */
+ virtual bool writeData(void* data) override;
+}; \ No newline at end of file
diff --git a/include/unused/rocketUtils.hpp b/include/unused/rocketUtils.hpp
new file mode 100644
index 0000000..65b9cea
--- /dev/null
+++ b/include/unused/rocketUtils.hpp
@@ -0,0 +1,121 @@
+#pragma once
+#include <iostream>
+#include <vector>
+#include <ctime>
+#include <vector>
+#include <iomanip>
+#include <sstream>
+#include <fstream>
+
+// Deployment angle limits
+#define MAX_ANGLE 100
+#define MIN_ANGLE 120
+
+// Altimeter initialization count limit
+#define COUNT_LIMIT 50
+
+// Constants
+#define G_0 9.8066
+
+// Threshold limits
+#define BOOST_ACCEL_THRESH 3
+#define BOOST_HEIGHT_THRESH 20
+#define GLIDE_ACCEL_THRESH 0.5
+
+#define ALTI_DEPL_THRESHOLD 0.5
+#define RATE_ALTI_DEPL 1/50
+#define FSM_DONE_SURFACE_ALTITUDE 200
+#define APOGEE_FSM_CHANGE 3
+
+#define INIT_DEPLOYMENT 0
+
+#define TIME_BO 8
+#define TIME_APO 25
+#define TIME_END 120
+
+#define PAD_PRESSURE 102250
+
+#define DUTY_MAX 14.5
+#define DUTY_MIN 3
+
+#define LAUNCH_DATE "4-15-2023"
+#define LOG_FILENAME "DataLog_" LAUNCH_DATE ".txt"
+
+#define LED_GAP_TIME 0.5
+#define LED_ONE_PATH "/sys/class/leds/beaglebone:green:usr1"
+#define LED_BRIGHTNESS_FILE "brightness"
+#define LED_FILENAME LED_ONE_PATH LED_BRIGHTNESS_FILE
+
+#define TEST_MODE false
+
+
+enum VehicleState {ON_PAD, BOOST, GLIDE, APOGEE, DONE};
+extern std::string state_for_log[5];
+
+struct Vehicle {
+
+ int status;
+
+ std::vector<double> acceleration;
+ std::vector<double> linear_acceleration;
+
+ double apogee_altitude;
+ double previous_altitude;
+ double current_altitude;
+ double filtered_altitude;
+
+ double filtered_velocity;
+
+ double deployment_angle;
+
+ bool imuInitFail;
+ bool imuReadFail;
+ bool altiInitFail;
+ bool altiReadFail;
+
+ double ON_PAD_altitude;
+ bool ON_PAD_fail;
+
+ double duty_span;
+
+ double dt;
+
+ int led_brightness;
+
+ time_t start_time;
+ time_t fail_time; // For failure termination
+ time_t liftoff_time;
+ time_t relog_time;
+ time_t deploy_time; // NOT INITIALIZED YET
+ time_t loop_time;
+ time_t led_time;
+};
+
+/**
+ * @brief Convert fin deployment percentage to fin rotation angle
+ *
+ * @param percentage Fin deployment percentage
+ * @return double
+ */
+double deploy_percentage_to_angle(double percentage);
+
+/**
+ * @brief Set the decimal precision of the given data, and return it
+ * as a formatted string with a prefix containing a relevant description of the data.
+ *
+ * @param prefix Identifying or clarifying information about the loggef data
+ * @param data Data to Log
+ * @param precision The decimal precision value for the data
+ *
+ * @return A string with the formatted data.
+ */
+std::string format_data(std::string prefix, double data, int precision);
+
+/**
+ * @brief Blink Beaglebone LED 1
+ *
+ * @param vehicle Holds settings pertinent to the Beaglebone LED
+ * @return true Successful Blink
+ * @return false Unsuccessful Blink
+ */
+bool led_out(Vehicle *vehicle);
diff --git a/include/unused/sensorAltimeter.hpp b/include/unused/sensorAltimeter.hpp
new file mode 100644
index 0000000..aa3a3cd
--- /dev/null
+++ b/include/unused/sensorAltimeter.hpp
@@ -0,0 +1,165 @@
+#pragma once
+#include "sensorI2C.hpp"
+#include <string>
+
+/*
+Header file for driver for MPL3115a2 breakout, based heavily on https://github.com/adafruit/Adafruit_MPL3115A2_Library/tree/master
+Based on register declarations found https://cdn-shop.adafruit.com/datasheets/1893_datasheet.pdf
+Designed using subclass for I2C handler for Beaglebone Black
+*/
+
+class AltimeterSensor : public sensorI2C {
+
+
+ private:
+ double internalTemperature = 0;
+ double internalAltitude = 0;
+
+ //ENUMS COPIED DIRECTLY FROM ADAFRUIT IMPLEMENTATION
+ /** MPL3115A2 registers **/
+ enum {
+ MPL3115A2_REGISTER_STATUS = (0x00),
+
+ MPL3115A2_REGISTER_PRESSURE_MSB = (0x01),
+ MPL3115A2_REGISTER_PRESSURE_CSB = (0x02),
+ MPL3115A2_REGISTER_PRESSURE_LSB = (0x03),
+
+ MPL3115A2_REGISTER_TEMP_MSB = (0x04),
+ MPL3115A2_REGISTER_TEMP_LSB = (0x05),
+
+ MPL3115A2_REGISTER_DR_STATUS = (0x06),
+
+ MPL3115A2_OUT_P_DELTA_MSB = (0x07),
+ MPL3115A2_OUT_P_DELTA_CSB = (0x08),
+ MPL3115A2_OUT_P_DELTA_LSB = (0x09),
+
+ MPL3115A2_OUT_T_DELTA_MSB = (0x0A),
+ MPL3115A2_OUT_T_DELTA_LSB = (0x0B),
+
+ MPL3115A2_WHOAMI = (0x0C),
+ //This is hard-coded in the device from the factory
+ MPL3115A2_WHOAMI_EXPECTED = (0xC4),
+
+ MPL3115A2_BAR_IN_MSB = (0x14),
+ MPL3115A2_BAR_IN_LSB = (0x15),
+
+ MPL3115A2_OFF_H = (0x2D),
+ };
+
+ /** MPL3115A2 status register BITS **/
+ enum {
+ MPL3115A2_REGISTER_STATUS_TDR = 0x02,
+ MPL3115A2_REGISTER_STATUS_PDR = 0x04,
+ MPL3115A2_REGISTER_STATUS_PTDR = 0x08,
+ };
+
+ /** MPL3115A2 PT DATA register BITS **/
+ enum {
+ MPL3115A2_PT_DATA_CFG = 0x13,
+ MPL3115A2_PT_DATA_CFG_TDEFE = 0x01,
+ MPL3115A2_PT_DATA_CFG_PDEFE = 0x02,
+ MPL3115A2_PT_DATA_CFG_DREM = 0x04,
+ };
+
+ /** MPL3115A2 control registers **/
+ enum {
+ MPL3115A2_CTRL_REG1 = (0x26),
+ MPL3115A2_CTRL_REG2 = (0x27),
+ MPL3115A2_CTRL_REG3 = (0x28),
+ MPL3115A2_CTRL_REG4 = (0x29),
+ MPL3115A2_CTRL_REG5 = (0x2A),
+ };
+
+ /** MPL3115A2 control register BITS **/
+ enum {
+ MPL3115A2_CTRL_REG1_SBYB = 0x01,
+ MPL3115A2_CTRL_REG1_OST = 0x02,
+ MPL3115A2_CTRL_REG1_RST = 0x04,
+ MPL3115A2_CTRL_REG1_RAW = 0x40,
+ MPL3115A2_CTRL_REG1_ALT = 0x80,
+ MPL3115A2_CTRL_REG1_BAR = 0x00,
+ };
+
+ /** MPL3115A2 oversample values **/
+ enum {
+ MPL3115A2_CTRL_REG1_OS1 = 0x00,
+ MPL3115A2_CTRL_REG1_OS2 = 0x08,
+ MPL3115A2_CTRL_REG1_OS4 = 0x10,
+ MPL3115A2_CTRL_REG1_OS8 = 0x18,
+ MPL3115A2_CTRL_REG1_OS16 = 0x20,
+ MPL3115A2_CTRL_REG1_OS32 = 0x28,
+ MPL3115A2_CTRL_REG1_OS64 = 0x30,
+ MPL3115A2_CTRL_REG1_OS128 = 0x38,
+ };
+
+ /** MPL3115A2 measurement modes **/
+ typedef enum {
+ MPL3115A2_BAROMETER = 0,
+ MPL3115A2_ALTIMETER,
+ } mpl3115a2_mode_t;
+
+ /** MPL3115A2 measurement types **/
+ typedef enum {
+ MPL3115A2_PRESSURE,
+ MPL3115A2_ALTITUDE,
+ MPL3115A2_TEMPERATURE,
+ } mpl3115a2_meas_t;
+
+ //This never actually gets used, and I can't find anything in the datasheet about it??
+ #define MPL3115A2_REGISTER_STARTCONVERSION (0x12) ///< start conversion
+
+ //Store current operating mode, sent to device during startup procedure
+ //This is why an enum is used rather than raw #define statements
+ mpl3115a2_mode_t currentMode;
+
+ //Struct for storing ctrl register contents, copied from adafruit implementation
+ typedef union {
+ struct {
+ uint8_t SBYB : 1;
+ uint8_t OST : 1;
+ uint8_t RST : 1;
+ uint8_t OS : 3;
+ uint8_t RAW : 1;
+ uint8_t ALT : 1;
+ } bit;
+ uint8_t reg;
+ } CTRL_REG_1_STRUCT;
+ //Create instance of this register config to use during device startup and operation
+ CTRL_REG_1_STRUCT ctrl_reg1;
+
+ public:
+
+ /**
+ * @brief Construct a new Altimeter Sensor object
+ *
+ */
+ AltimeterSensor(std::string I2C_FILE);
+
+ /**
+ * @brief Initialize the Altimeter
+ *
+ * @param data Data for initializing the sensor
+ * @return true Initialization Success
+ * @return false Initialization Failure
+ */
+ bool init() override;
+
+ //Data getters and setters
+ // double getPressure();
+ double getAltitude();
+ double getTemperature();
+ double setSeaLevelPressure(double pressure);
+
+ //Data and mode handlers
+ //Use altimeter mode by default as this is what rocket logger wants
+ void setMode(mpl3115a2_mode_t mode = MPL3115A2_ALTIMETER);
+ void requestOneShotReading();
+ bool isNewDataAvailable();
+ void updateCurrentDataBuffer();
+
+
+};
+
+
+
+
diff --git a/include/unused/sensorI2C.hpp b/include/unused/sensorI2C.hpp
new file mode 100644
index 0000000..bd497a5
--- /dev/null
+++ b/include/unused/sensorI2C.hpp
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <cstdint>
+#include <string>
+
+class sensorI2C {
+
+
+ //Predominantly used for I2C handler functions; implement high-level functions in sensor classes
+ //Implemented as a combination of Jazz' implementation and Derek Malloy's code
+ public:
+
+ //Initial single byte write, used at beginning of read operation
+ //Returns 0 if successful, -1 if not
+ int initialWrite(unsigned char registerAddress) {
+ unsigned char *convertedAddressBuffer = new unsigned char[1];
+ convertedAddressBuffer[0] = registerAddress;
+ //Expect 1 byte response from 1 byte write
+ if (write(i2c_bus, convertedAddressBuffer, 1) != 1) {
+ fprintf(stderr, "ERROR DOING INITIAL READ TRANSACTION WRITE TO REGISTER %x FOR DEVICE %x\n", registerAddress, deviceAddress);
+ return 0;
+ }
+ return 1;
+ }
+
+ int writeRegister(unsigned char registerAddress, unsigned char value) {
+ //initialWrite() not used here because it's easier to just pack it into one buffer for file writing
+ unsigned char writeBuffer[2];
+ writeBuffer[0] = registerAddress;
+ writeBuffer[1] = value;
+
+ //Expect 2 byte output
+ if (write(i2c_bus, writeBuffer, 2) != 2) {
+ //These error messages are kind of obtuse but I'd rather have too much information than not enough
+ fprintf(stderr, "ERROR WRITING %x TO REGISTER %x ON DEVICE %x\n", value, registerAddress, deviceAddress);
+ return -1;
+ }
+ return 0;
+ }
+
+ //Could probably be uint8_t but Derek Malloy does it with unsigned chars and that's what worked during testing so I don't want to touch it
+ unsigned char readSingleRegister(unsigned char registerAddress) {
+ printf("reg addr: %X\n", registerAddress);
+
+ initialWrite(registerAddress);
+ unsigned char* readBuffer = new unsigned char[1];
+ if (read(i2c_bus, readBuffer, 1) != 1){
+ fprintf(stderr, "FAILED TO READ VALUE FROM REGISTER %x ON DEVICE %x\n", registerAddress, deviceAddress);
+ return -1;
+ }
+ printf("readBuffer: %X\n", readBuffer[0]);
+ return readBuffer[0];
+ }
+
+ unsigned char* readMultipleRegisters(unsigned char startingRegisterAddress, int numberOfRegisters) {
+ initialWrite(startingRegisterAddress);
+ unsigned char* readBuffer = new unsigned char[numberOfRegisters];
+ if (read(i2c_bus, readBuffer, numberOfRegisters) != numberOfRegisters) {
+ fprintf(stderr, "ERROR TRYING TO READ %d REGISTERS STARTING AT ADDRESS %x ON DEVICE %x\n",
+ numberOfRegisters, startingRegisterAddress, deviceAddress);
+ }
+ return readBuffer;
+ }
+
+
+ //Intakes device address and file
+ //Private because IT'S ASSUMED PROGRAMMER WILL CALL THIS METHOD DURING INIT() ROUTINE
+ int setupI2C(std::string I2C_FILE) {
+ // Open i2c driver file
+ i2c_bus = open("/dev/i2c-2", O_RDWR);
+ if(i2c_bus < 0){
+ fprintf(stderr, "FAILED TO OPEN I2C BUS USING FILE %s\n", "/dev/i2c-2");
+ close(i2c_bus);
+ return -1;
+ }
+
+ // Identify slave device address (MODIFIED FROM INITIAL IMPLEMENTATION, USES INTERNAL deviceAddress INSTEAD OF PARAMETER)
+ if(ioctl(i2c_bus, I2C_SLAVE, deviceAddress) < 0){
+ fprintf(stderr, "FAILED TO CONNECT TO DEVICE AT ADDRESS %x VIA I2C\n", deviceAddress);
+ close(i2c_bus);
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * @brief Initialize the sensor.
+ *
+ * @param data Data for initializing the sensor
+ *
+ * @return true Initialization Success
+ * @return false Initialization Failure
+ */
+ virtual bool init() = 0;
+
+ unsigned char deviceAddress;
+ int i2c_bus;
+ std::string I2C_FILE;
+};
diff --git a/include/unused/sensorIMU.hpp b/include/unused/sensorIMU.hpp
new file mode 100644
index 0000000..caddbe4
--- /dev/null
+++ b/include/unused/sensorIMU.hpp
@@ -0,0 +1,312 @@
+#pragma once
+#include <vector>
+#include "sensorI2C.hpp"
+#include "logger.hpp"
+#include "rocketUtils.hpp"
+#include "imumath.h"
+#include <string>
+
+struct IMUData {
+ std::vector<double> acceleration[3];
+ std::vector<double> linear_acceleration[3];
+};
+
+//Register addresses and data structs copied from Adafruit implementation
+/** BNO055 Address A **/
+#define BNO055_ADDRESS_A (0x28)
+/** BNO055 Address B **/
+#define BNO055_ADDRESS_B (0x29)
+/** BNO055 ID **/
+#define BNO055_ID (0xA0)
+
+/** Offsets registers **/
+#define NUM_BNO055_OFFSET_REGISTERS (22)
+
+/** A structure to represent offsets **/
+typedef struct {
+ int16_t accel_offset_x; /**< x acceleration offset */
+ int16_t accel_offset_y; /**< y acceleration offset */
+ int16_t accel_offset_z; /**< z acceleration offset */
+
+ int16_t mag_offset_x; /**< x magnetometer offset */
+ int16_t mag_offset_y; /**< y magnetometer offset */
+ int16_t mag_offset_z; /**< z magnetometer offset */
+
+ int16_t gyro_offset_x; /**< x gyroscrope offset */
+ int16_t gyro_offset_y; /**< y gyroscrope offset */
+ int16_t gyro_offset_z; /**< z gyroscrope offset */
+
+ int16_t accel_radius; /**< acceleration radius */
+
+ int16_t mag_radius; /**< magnetometer radius */
+} adafruit_bno055_offsets_t;
+
+/** Operation mode settings **/
+typedef enum {
+ OPERATION_MODE_CONFIG = 0X00,
+ OPERATION_MODE_ACCONLY = 0X01,
+ OPERATION_MODE_MAGONLY = 0X02,
+ OPERATION_MODE_GYRONLY = 0X03,
+ OPERATION_MODE_ACCMAG = 0X04,
+ OPERATION_MODE_ACCGYRO = 0X05,
+ OPERATION_MODE_MAGGYRO = 0X06,
+ OPERATION_MODE_AMG = 0X07,
+ OPERATION_MODE_IMUPLUS = 0X08,
+ OPERATION_MODE_COMPASS = 0X09,
+ OPERATION_MODE_M4G = 0X0A,
+ OPERATION_MODE_NDOF_FMC_OFF = 0X0B,
+ OPERATION_MODE_NDOF = 0X0C
+} adafruit_bno055_opmode_t;
+class IMUSensor : public sensorI2C {
+
+ private:
+ deviceAddress = BNO055_ADDRESS_A;
+
+ adafruit_bno055_opmode_t default_mode = OPERATION_MODE_NDOF;
+ /** BNO055 Registers **/
+ typedef enum {
+ /* Page id register definition */
+ BNO055_PAGE_ID_ADDR = 0X07,
+
+ /* PAGE0 REGISTER DEFINITION START*/
+ BNO055_CHIP_ID_ADDR = 0x00,
+ BNO055_ACCEL_REV_ID_ADDR = 0x01,
+ BNO055_MAG_REV_ID_ADDR = 0x02,
+ BNO055_GYRO_REV_ID_ADDR = 0x03,
+ BNO055_SW_REV_ID_LSB_ADDR = 0x04,
+ BNO055_SW_REV_ID_MSB_ADDR = 0x05,
+ BNO055_BL_REV_ID_ADDR = 0X06,
+
+ /* Accel data register */
+ BNO055_ACCEL_DATA_X_LSB_ADDR = 0X08,
+ BNO055_ACCEL_DATA_X_MSB_ADDR = 0X09,
+ BNO055_ACCEL_DATA_Y_LSB_ADDR = 0X0A,
+ BNO055_ACCEL_DATA_Y_MSB_ADDR = 0X0B,
+ BNO055_ACCEL_DATA_Z_LSB_ADDR = 0X0C,
+ BNO055_ACCEL_DATA_Z_MSB_ADDR = 0X0D,
+
+ /* Mag data register */
+ BNO055_MAG_DATA_X_LSB_ADDR = 0X0E,
+ BNO055_MAG_DATA_X_MSB_ADDR = 0X0F,
+ BNO055_MAG_DATA_Y_LSB_ADDR = 0X10,
+ BNO055_MAG_DATA_Y_MSB_ADDR = 0X11,
+ BNO055_MAG_DATA_Z_LSB_ADDR = 0X12,
+ BNO055_MAG_DATA_Z_MSB_ADDR = 0X13,
+
+ /* Gyro data registers */
+ BNO055_GYRO_DATA_X_LSB_ADDR = 0X14,
+ BNO055_GYRO_DATA_X_MSB_ADDR = 0X15,
+ BNO055_GYRO_DATA_Y_LSB_ADDR = 0X16,
+ BNO055_GYRO_DATA_Y_MSB_ADDR = 0X17,
+ BNO055_GYRO_DATA_Z_LSB_ADDR = 0X18,
+ BNO055_GYRO_DATA_Z_MSB_ADDR = 0X19,
+
+ /* Euler data registers */
+ BNO055_EULER_H_LSB_ADDR = 0X1A,
+ BNO055_EULER_H_MSB_ADDR = 0X1B,
+ BNO055_EULER_R_LSB_ADDR = 0X1C,
+ BNO055_EULER_R_MSB_ADDR = 0X1D,
+ BNO055_EULER_P_LSB_ADDR = 0X1E,
+ BNO055_EULER_P_MSB_ADDR = 0X1F,
+
+ /* Quaternion data registers */
+ BNO055_QUATERNION_DATA_W_LSB_ADDR = 0X20,
+ BNO055_QUATERNION_DATA_W_MSB_ADDR = 0X21,
+ BNO055_QUATERNION_DATA_X_LSB_ADDR = 0X22,
+ BNO055_QUATERNION_DATA_X_MSB_ADDR = 0X23,
+ BNO055_QUATERNION_DATA_Y_LSB_ADDR = 0X24,
+ BNO055_QUATERNION_DATA_Y_MSB_ADDR = 0X25,
+ BNO055_QUATERNION_DATA_Z_LSB_ADDR = 0X26,
+ BNO055_QUATERNION_DATA_Z_MSB_ADDR = 0X27,
+
+ /* Linear acceleration data registers */
+ BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR = 0X28,
+ BNO055_LINEAR_ACCEL_DATA_X_MSB_ADDR = 0X29,
+ BNO055_LINEAR_ACCEL_DATA_Y_LSB_ADDR = 0X2A,
+ BNO055_LINEAR_ACCEL_DATA_Y_MSB_ADDR = 0X2B,
+ BNO055_LINEAR_ACCEL_DATA_Z_LSB_ADDR = 0X2C,
+ BNO055_LINEAR_ACCEL_DATA_Z_MSB_ADDR = 0X2D,
+
+ /* Gravity data registers */
+ BNO055_GRAVITY_DATA_X_LSB_ADDR = 0X2E,
+ BNO055_GRAVITY_DATA_X_MSB_ADDR = 0X2F,
+ BNO055_GRAVITY_DATA_Y_LSB_ADDR = 0X30,
+ BNO055_GRAVITY_DATA_Y_MSB_ADDR = 0X31,
+ BNO055_GRAVITY_DATA_Z_LSB_ADDR = 0X32,
+ BNO055_GRAVITY_DATA_Z_MSB_ADDR = 0X33,
+
+ /* Temperature data register */
+ BNO055_TEMP_ADDR = 0X34,
+
+ /* Status registers */
+ BNO055_CALIB_STAT_ADDR = 0X35,
+ BNO055_SELFTEST_RESULT_ADDR = 0X36,
+ BNO055_INTR_STAT_ADDR = 0X37,
+
+ BNO055_SYS_CLK_STAT_ADDR = 0X38,
+ BNO055_SYS_STAT_ADDR = 0X39,
+ BNO055_SYS_ERR_ADDR = 0X3A,
+
+ /* Unit selection register */
+ BNO055_UNIT_SEL_ADDR = 0X3B,
+
+ /* Mode registers */
+ BNO055_OPR_MODE_ADDR = 0X3D,
+ BNO055_PWR_MODE_ADDR = 0X3E,
+
+ BNO055_SYS_TRIGGER_ADDR = 0X3F,
+ BNO055_TEMP_SOURCE_ADDR = 0X40,
+
+ /* Axis remap registers */
+ BNO055_AXIS_MAP_CONFIG_ADDR = 0X41,
+ BNO055_AXIS_MAP_SIGN_ADDR = 0X42,
+
+ /* SIC registers */
+ BNO055_SIC_MATRIX_0_LSB_ADDR = 0X43,
+ BNO055_SIC_MATRIX_0_MSB_ADDR = 0X44,
+ BNO055_SIC_MATRIX_1_LSB_ADDR = 0X45,
+ BNO055_SIC_MATRIX_1_MSB_ADDR = 0X46,
+ BNO055_SIC_MATRIX_2_LSB_ADDR = 0X47,
+ BNO055_SIC_MATRIX_2_MSB_ADDR = 0X48,
+ BNO055_SIC_MATRIX_3_LSB_ADDR = 0X49,
+ BNO055_SIC_MATRIX_3_MSB_ADDR = 0X4A,
+ BNO055_SIC_MATRIX_4_LSB_ADDR = 0X4B,
+ BNO055_SIC_MATRIX_4_MSB_ADDR = 0X4C,
+ BNO055_SIC_MATRIX_5_LSB_ADDR = 0X4D,
+ BNO055_SIC_MATRIX_5_MSB_ADDR = 0X4E,
+ BNO055_SIC_MATRIX_6_LSB_ADDR = 0X4F,
+ BNO055_SIC_MATRIX_6_MSB_ADDR = 0X50,
+ BNO055_SIC_MATRIX_7_LSB_ADDR = 0X51,
+ BNO055_SIC_MATRIX_7_MSB_ADDR = 0X52,
+ BNO055_SIC_MATRIX_8_LSB_ADDR = 0X53,
+ BNO055_SIC_MATRIX_8_MSB_ADDR = 0X54,
+
+ /* Accelerometer Offset registers */
+ ACCEL_OFFSET_X_LSB_ADDR = 0X55,
+ ACCEL_OFFSET_X_MSB_ADDR = 0X56,
+ ACCEL_OFFSET_Y_LSB_ADDR = 0X57,
+ ACCEL_OFFSET_Y_MSB_ADDR = 0X58,
+ ACCEL_OFFSET_Z_LSB_ADDR = 0X59,
+ ACCEL_OFFSET_Z_MSB_ADDR = 0X5A,
+
+ /* Magnetometer Offset registers */
+ MAG_OFFSET_X_LSB_ADDR = 0X5B,
+ MAG_OFFSET_X_MSB_ADDR = 0X5C,
+ MAG_OFFSET_Y_LSB_ADDR = 0X5D,
+ MAG_OFFSET_Y_MSB_ADDR = 0X5E,
+ MAG_OFFSET_Z_LSB_ADDR = 0X5F,
+ MAG_OFFSET_Z_MSB_ADDR = 0X60,
+
+ /* Gyroscope Offset register s*/
+ GYRO_OFFSET_X_LSB_ADDR = 0X61,
+ GYRO_OFFSET_X_MSB_ADDR = 0X62,
+ GYRO_OFFSET_Y_LSB_ADDR = 0X63,
+ GYRO_OFFSET_Y_MSB_ADDR = 0X64,
+ GYRO_OFFSET_Z_LSB_ADDR = 0X65,
+ GYRO_OFFSET_Z_MSB_ADDR = 0X66,
+
+ /* Radius registers */
+ ACCEL_RADIUS_LSB_ADDR = 0X67,
+ ACCEL_RADIUS_MSB_ADDR = 0X68,
+ MAG_RADIUS_LSB_ADDR = 0X69,
+ MAG_RADIUS_MSB_ADDR = 0X6A
+ } adafruit_bno055_reg_t;
+
+ /** BNO055 power settings */
+ typedef enum {
+ POWER_MODE_NORMAL = 0X00,
+ POWER_MODE_LOWPOWER = 0X01,
+ POWER_MODE_SUSPEND = 0X02
+ } adafruit_bno055_powermode_t;
+
+ /** Remap settings **/
+ typedef enum {
+ REMAP_CONFIG_P0 = 0x21,
+ REMAP_CONFIG_P1 = 0x24, // default
+ REMAP_CONFIG_P2 = 0x24,
+ REMAP_CONFIG_P3 = 0x21,
+ REMAP_CONFIG_P4 = 0x24,
+ REMAP_CONFIG_P5 = 0x21,
+ REMAP_CONFIG_P6 = 0x21,
+ REMAP_CONFIG_P7 = 0x24
+ } adafruit_bno055_axis_remap_config_t;
+
+ /** Remap Signs **/
+ typedef enum {
+ REMAP_SIGN_P0 = 0x04,
+ REMAP_SIGN_P1 = 0x00, // default
+ REMAP_SIGN_P2 = 0x06,
+ REMAP_SIGN_P3 = 0x02,
+ REMAP_SIGN_P4 = 0x03,
+ REMAP_SIGN_P5 = 0x01,
+ REMAP_SIGN_P6 = 0x07,
+ REMAP_SIGN_P7 = 0x05
+ } adafruit_bno055_axis_remap_sign_t;
+
+ /** A structure to represent revisions **/
+ typedef struct {
+ uint8_t accel_rev; /**< acceleration rev */
+ uint8_t mag_rev; /**< magnetometer rev */
+ uint8_t gyro_rev; /**< gyroscrope rev */
+ uint16_t sw_rev; /**< SW rev */
+ uint8_t bl_rev; /**< bootloader rev */
+ } adafruit_bno055_rev_info_t;
+
+ /** Vector Mappings **/
+ typedef enum {
+ VECTOR_ACCELEROMETER = BNO055_ACCEL_DATA_X_LSB_ADDR,
+ VECTOR_MAGNETOMETER = BNO055_MAG_DATA_X_LSB_ADDR,
+ VECTOR_GYROSCOPE = BNO055_GYRO_DATA_X_LSB_ADDR,
+ VECTOR_EULER = BNO055_EULER_H_LSB_ADDR,
+ VECTOR_LINEARACCEL = BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR,
+ VECTOR_GRAVITY = BNO055_GRAVITY_DATA_X_LSB_ADDR
+ } adafruit_vector_type_t;
+
+ int32_t _sensorID;
+ adafruit_bno055_opmode_t currentMode;
+
+ public:
+ /**
+ * @brief Construct a new IMUSensor object
+ *
+ */
+ IMUSensor(std::string I2C_FILE);
+
+ /**
+ * @brief Inititlize the IMU
+ *
+ * @param data Data for initializing the sensor
+ * @return true Initialization Success
+ * @return false Initialization Failure
+ */
+ bool init() override;
+
+ //Data handlers, copied from adafruit implementation (not all of them)
+ void setModeTemp(adafruit_bno055_opmode_t mode);
+ void setModeHard(adafruit_bno055_opmode_t mode);
+ adafruit_bno055_opmode_t getMode();
+
+ imu::Vector<3> getVector(adafruit_vector_type_t vector_type);
+ imu::Quaternion getQuat();
+ int8_t getTemp();
+
+ //Configuration and status getters/setters
+ void setAxisRemap(adafruit_bno055_axis_remap_config_t remapcode);
+ void setAxisSign(adafruit_bno055_axis_remap_sign_t remapsign);
+ void getSystemStatus(uint8_t *system_status, uint8_t *self_test_result,
+ uint8_t *system_error);
+ void getCalibration(uint8_t *sys, uint8_t *gyro, uint8_t *accel,
+ uint8_t *mag);
+
+ /* Functions to deal with raw calibration data */
+ bool getSensorOffsets(uint8_t *calibData);
+ bool getSensorOffsets(adafruit_bno055_offsets_t &offsets_type);
+ void setSensorOffsets(const uint8_t *calibData);
+ void setSensorOffsets(const adafruit_bno055_offsets_t &offsets_type);
+ bool isFullyCalibrated();
+
+ /* Power managments functions */
+ void enterSuspendMode();
+ void enterNormalMode();
+
+}; \ No newline at end of file
diff --git a/include/unused/surfaceFitModel.hpp b/include/unused/surfaceFitModel.hpp
new file mode 100644
index 0000000..303633f
--- /dev/null
+++ b/include/unused/surfaceFitModel.hpp
@@ -0,0 +1,32 @@
+#pragma once
+#include <cmath>
+#include "eigen3/Eigen/Dense"
+
+
+using namespace Eigen;
+
+#define X_DEGREE 4 // Highest x-degree of current Surface Fit Model
+#define Y_DEGREE 3 // Highest y-degree of current Surface Fit Model
+
+class SurfaceFitModel {
+
+ private:
+ MatrixXd p; // Polynomial values
+
+ public:
+
+ /**
+ * @brief Construct a new Surface Fit Model object
+ *
+ */
+ SurfaceFitModel();
+
+ /**
+ * @brief
+ *
+ * @param x
+ * @param y
+ * @return double
+ */
+ double getFit(double x, double y);
+};
diff --git a/src/AltEst/algebra.cpp b/src/AltEst/algebra.cpp
new file mode 100644
index 0000000..653c3b9
--- /dev/null
+++ b/src/AltEst/algebra.cpp
@@ -0,0 +1,292 @@
+/*
+ algebra.cpp: This file contains a number of utilities useful for handling
+ 3D vectors
+
+ This work is an adaptation from vvector.h, written by Linas Vepstras. The
+ original code can be found at:
+
+ https://github.com/markkilgard/glut/blob/master/lib/gle/vvector.h
+
+ HISTORY:
+ Written by Linas Vepstas, August 1991
+ Added 2D code, March 1993
+ Added Outer products, C++ proofed, Linas Vepstas October 1993
+ Adapted for altitude estimation tasks by Juan Gallostra June 2018
+*/
+
+//#include <cmath>
+#include <stdio.h>
+
+#include "algebra.h"
+
+// Copy 3D vector
+void copyVector(float b[3],float a[3])
+{
+ b[0] = a[0];
+ b[1] = a[1];
+ b[2] = a[2];
+}
+
+
+// Vector difference
+void subtractVectors(float v21[3], float v2[3], float v1[3])
+{
+ v21[0] = v2[0] - v1[0];
+ v21[1] = v2[1] - v1[1];
+ v21[2] = v2[2] - v1[2];
+}
+
+// Vector sum
+void sumVectors(float v21[3], float v2[3], float v1[3])
+{
+ v21[0] = v2[0] + v1[0];
+ v21[1] = v2[1] + v1[1];
+ v21[2] = v2[2] + v1[2];
+}
+
+// scalar times vector
+void scaleVector(float c[3],float a, float b[3])
+{
+ (c)[0] = a*b[0];
+ (c)[1] = a*b[1];
+ (c)[2] = a*b[2];
+}
+
+// accumulate scaled vector
+void accumulateScaledVector(float c[3], float a, float b[3])
+{
+ (c)[0] += a*b[0];
+ (c)[1] += a*b[1];
+ (c)[2] += a*b[2];
+}
+
+// Vector dot product
+void dotProductVectors(float * c, float a[3], float b[3])
+{
+ *c = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
+}
+
+// Vector length
+void vectorLength(float * len, float a[3])
+{
+ float tmp;
+ tmp = a[0]*a[0] + a[1]*a[1]+a[2]*a[2];
+ *len = sqrt(tmp);
+}
+
+// Normalize vector
+void normalizeVector(float a[3])
+{
+ float len;
+ vectorLength(& len,a);
+ if (len != 0.0) {
+ len = 1.0 / len;
+ a[0] *= len;
+ a[1] *= len;
+ a[2] *= len;
+ }
+}
+
+// 3D Vector cross product yeilding vector
+void crossProductVectors(float c[3], float a[3], float b[3])
+{
+ c[0] = a[1] * b[2] - a[2] * b[1];
+ c[1] = a[2] * b[0] - a[0] * b[2];
+ c[2] = a[0] * b[1] - a[1] * b[0];
+}
+
+// initialize matrix
+void identityMatrix3x3(float m[3][3])
+{
+ m[0][0] = 1.0;
+ m[0][1] = 0.0;
+ m[0][2] = 0.0;
+
+ m[1][0] = 0.0;
+ m[1][1] = 1.0;
+ m[1][2] = 0.0;
+
+ m[2][0] = 0.0;
+ m[2][1] = 0.0;
+ m[2][2] = 1.0;
+}
+
+// matrix copy
+void copyMatrix3x3(float b[3][3], float a[3][3])
+{
+ b[0][0] = a[0][0];
+ b[0][1] = a[0][1];
+ b[0][2] = a[0][2];
+
+ b[1][0] = a[1][0];
+ b[1][1] = a[1][1];
+ b[1][2] = a[1][2];
+
+ b[2][0] = a[2][0];
+ b[2][1] = a[2][1];
+ b[2][2] = a[2][2];
+}
+
+// matrix transpose
+void transposeMatrix3x3(float b[3][3], float a[3][3])
+{
+ b[0][0] = a[0][0];
+ b[0][1] = a[1][0];
+ b[0][2] = a[2][0];
+
+ b[1][0] = a[0][1];
+ b[1][1] = a[1][1];
+ b[1][2] = a[2][1];
+
+ b[2][0] = a[0][2];
+ b[2][1] = a[1][2];
+ b[2][2] = a[2][2];
+}
+
+// multiply matrix by scalar
+void scaleMatrix3x3(float b[3][3], float s, float a[3][3])
+{
+ b[0][0] = (s) * a[0][0];
+ b[0][1] = (s) * a[0][1];
+ b[0][2] = (s) * a[0][2];
+
+ b[1][0] = (s) * a[1][0];
+ b[1][1] = (s) * a[1][1];
+ b[1][2] = (s) * a[1][2];
+
+ b[2][0] = (s) * a[2][0];
+ b[2][1] = (s) * a[2][1];
+ b[2][2] = (s) * a[2][2];
+}
+
+// multiply matrix by scalar and add result to another matrix
+void scaleAndAccumulateMatrix3x3(float b[3][3], float s, float a[3][3])
+{
+ b[0][0] += s * a[0][0];
+ b[0][1] += s * a[0][1];
+ b[0][2] += s * a[0][2];
+
+ b[1][0] += s * a[1][0];
+ b[1][1] += s * a[1][1];
+ b[1][2] += s * a[1][2];
+
+ b[2][0] += s * a[2][0];
+ b[2][1] += s * a[2][1];
+ b[2][2] += s * a[2][2];
+}
+
+// matrix product
+// c[x][y] = a[x][0]*b[0][y]+a[x][1]*b[1][y]+a[x][2]*b[2][y]+a[x][3]*b[3][y]
+void matrixProduct3x3(float c[3][3], float a[3][3], float b[3][3])
+{
+ c[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0];
+ c[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1];
+ c[0][2] = a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2];
+
+ c[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0];
+ c[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1];
+ c[1][2] = a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2];
+
+ c[2][0] = a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0];
+ c[2][1] = a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1];
+ c[2][2] = a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2];
+}
+
+// matrix times vector
+void matrixDotVector3x3(float p[3], float m[3][3], float v[3])
+{
+ p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2];
+ p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2];
+ p[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2];
+}
+
+// determinant of matrix
+// Computes determinant of matrix m, returning d
+void determinant3x3(float * d, float m[3][3])
+{
+ *d = m[0][0] * (m[1][1]*m[2][2] - m[1][2] * m[2][1]);
+ *d -= m[0][1] * (m[1][0]*m[2][2] - m[1][2] * m[2][0]);
+ *d += m[0][2] * (m[1][0]*m[2][1] - m[1][1] * m[2][0]);
+}
+
+// adjoint of matrix
+// Computes adjoint of matrix m, returning a
+// (Note that adjoint is just the transpose of the cofactor matrix)
+void adjoint3x3(float a[3][3], float m[3][3])
+{
+ a[0][0] = m[1][1]*m[2][2] - m[1][2]*m[2][1];
+ a[1][0] = - (m[1][0]*m[2][2] - m[2][0]*m[1][2]);
+ a[2][0] = m[1][0]*m[2][1] - m[1][1]*m[2][0];
+ a[0][1] = - (m[0][1]*m[2][2] - m[0][2]*m[2][1]);
+ a[1][1] = m[0][0]*m[2][2] - m[0][2]*m[2][0];
+ a[2][1] = - (m[0][0]*m[2][1] - m[0][1]*m[2][0]);
+ a[0][2] = m[0][1]*m[1][2] - m[0][2]*m[1][1];
+ a[1][2] = - (m[0][0]*m[1][2] - m[0][2]*m[1][0]);
+ a[2][2] = m[0][0]*m[1][1] - m[0][1]*m[1][0];
+}
+
+// compute adjoint of matrix and scale
+// Computes adjoint of matrix m, scales it by s, returning a
+void scaleAdjoint3x3(float a[3][3], float s, float m[3][3])
+{
+ a[0][0] = (s) * (m[1][1] * m[2][2] - m[1][2] * m[2][1]);
+ a[1][0] = (s) * (m[1][2] * m[2][0] - m[1][0] * m[2][2]);
+ a[2][0] = (s) * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
+
+ a[0][1] = (s) * (m[0][2] * m[2][1] - m[0][1] * m[2][2]);
+ a[1][1] = (s) * (m[0][0] * m[2][2] - m[0][2] * m[2][0]);
+ a[2][1] = (s) * (m[0][1] * m[2][0] - m[0][0] * m[2][1]);
+
+ a[0][2] = (s) * (m[0][1] * m[1][2] - m[0][2] * m[1][1]);
+ a[1][2] = (s) * (m[0][2] * m[1][0] - m[0][0] * m[1][2]);
+ a[2][2] = (s) * (m[0][0] * m[1][1] - m[0][1] * m[1][0]);
+}
+
+// inverse of matrix
+// Compute inverse of matrix a, returning determinant m and
+// inverse b
+void invert3x3(float b[3][3], float a[3][3])
+{
+ float tmp;
+ determinant3x3(& tmp, a);
+ tmp = 1.0 / (tmp);
+ scaleAdjoint3x3(b, tmp, a);
+}
+
+// skew matrix from vector
+void skew(float a[3][3], float v[3])
+{
+ a[0][1] = -v[2];
+ a[0][2] = v[1];
+ a[1][2] = -v[0];
+ a[1][0] = v[2];
+ a[2][0] = -v[1];
+ a[2][1] = v[0];
+ // set diagonal to 0
+ a[0][0] = 0.0;
+ a[1][1] = 0.0;
+ a[2][2] = 0.0;
+}
+
+void printMatrix3X3(float mmm[3][3])
+{
+ int i,j;
+ printf ("matrix mmm is \n");
+ if (mmm == NULL) {
+ printf (" Null \n");
+ } else {
+ for (i=0; i<3; i++) {
+ for (j=0; j<3; j++) {
+ printf ("%f ", mmm[i][j]);
+ }
+ printf (" \n");
+ }
+ }
+}
+
+void vecPrint(float a[3])
+{
+ float len;
+ vectorLength(& len, a);
+ printf(" a is %f %f %f length of a is %f \n", a[0], a[1], a[2], len);
+}
diff --git a/src/AltEst/algebra.h b/src/AltEst/algebra.h
new file mode 100644
index 0000000..382103e
--- /dev/null
+++ b/src/AltEst/algebra.h
@@ -0,0 +1,96 @@
+/*
+ algebra.h: This file contains a number of utilities useful for handling
+ 3D vectors
+
+ This work is an adaptation from vvector.h, written by Linas Vepstras. The
+ original code can be found at:
+
+ https://github.com/markkilgard/glut/blob/master/lib/gle/vvector.h
+
+ HISTORY:
+ Written by Linas Vepstas, August 1991
+ Added 2D code, March 1993
+ Added Outer products, C++ proofed, Linas Vepstas October 1993
+ Adapted for altitude estimation tasks by Juan Gallostra June 2018
+ Separated .h, .cpp by Simon D. Levy July 2018
+*/
+
+#pragma once
+
+//#include <cmath>
+#include <math.h>
+
+// Copy 3D vector
+void copyVector(float b[3],float a[3]);
+
+
+// Vector difference
+void subtractVectors(float v21[3], float v2[3], float v1[3]);
+
+// Vector sum
+void sumVectors(float v21[3], float v2[3], float v1[3]);
+
+// scalar times vector
+void scaleVector(float c[3],float a, float b[3]);
+
+// accumulate scaled vector
+void accumulateScaledVector(float c[3], float a, float b[3]);
+
+// Vector dot product
+void dotProductVectors(float * c, float a[3], float b[3]);
+
+// Vector length
+void vectorLength(float * len, float a[3]);
+
+// Normalize vector
+void normalizeVector(float a[3]);
+
+// 3D Vector cross product yeilding vector
+void crossProductVectors(float c[3], float a[3], float b[3]);
+
+// initialize matrix
+void identityMatrix3x3(float m[3][3]);
+
+// matrix copy
+void copyMatrix3x3(float b[3][3], float a[3][3]);
+
+// matrix transpose
+void transposeMatrix3x3(float b[3][3], float a[3][3]);
+
+// multiply matrix by scalar
+void scaleMatrix3x3(float b[3][3], float s, float a[3][3]);
+
+// multiply matrix by scalar and add result to another matrix
+void scaleAndAccumulateMatrix3x3(float b[3][3], float s, float a[3][3]);
+
+// matrix product
+// c[x][y] = a[x][0]*b[0][y]+a[x][1]*b[1][y]+a[x][2]*b[2][y]+a[x][3]*b[3][y]
+void matrixProduct3x3(float c[3][3], float a[3][3], float b[3][3]);
+
+// matrix times vector
+void matrixDotVector3x3(float p[3], float m[3][3], float v[3]);
+
+// determinant of matrix
+// Computes determinant of matrix m, returning d
+void determinant3x3(float * d, float m[3][3]);
+
+// adjoint of matrix
+// Computes adjoint of matrix m, returning a
+// (Note that adjoint is just the transpose of the cofactor matrix);
+void adjoint3x3(float a[3][3], float m[3][3]);
+
+// compute adjoint of matrix and scale
+// Computes adjoint of matrix m, scales it by s, returning a
+void scaleAdjoint3x3(float a[3][3], float s, float m[3][3]);
+
+// inverse of matrix
+// Compute inverse of matrix a, returning determinant m and
+// inverse b
+void invert3x3(float b[3][3], float a[3][3]);
+
+// skew matrix from vector
+void skew(float a[3][3], float v[3]);
+
+void printMatrix3X3(float mmm[3][3]);
+
+void vecPrint(float a[3]);
diff --git a/src/AltEst/altitude.cpp b/src/AltEst/altitude.cpp
new file mode 100644
index 0000000..8838b36
--- /dev/null
+++ b/src/AltEst/altitude.cpp
@@ -0,0 +1,58 @@
+/*
+ altitude.cpp: Altitude estimation via barometer/accelerometer fusion
+*/
+
+#include "filters.h"
+#include "algebra.h"
+#include "altitude.h"
+
+AltitudeEstimator::AltitudeEstimator(float sigmaAccel, float sigmaGyro, float sigmaBaro,
+ float ca, float accelThreshold)
+:kalman(ca, sigmaGyro, sigmaAccel), complementary(sigmaAccel, sigmaBaro, accelThreshold)
+{
+ this->sigmaAccel = sigmaAccel;
+ this->sigmaGyro = sigmaGyro;
+ this->sigmaBaro = sigmaBaro;
+ this->ca = ca;
+ this->accelThreshold = accelThreshold;
+}
+
+void AltitudeEstimator::estimate(float accel[3], float gyro[3], float baroHeight, uint32_t timestamp)
+{
+ float deltat = (float)(timestamp-previousTime)/1000000.0f;
+ float verticalAccel = kalman.estimate(pastGyro,
+ pastAccel,
+ deltat);
+ complementary.estimate(& estimatedVelocity,
+ & estimatedAltitude,
+ baroHeight,
+ pastAltitude,
+ pastVerticalVelocity,
+ pastVerticalAccel,
+ deltat);
+ // update values for next iteration
+ copyVector(pastGyro, gyro);
+ copyVector(pastAccel, accel);
+ pastAltitude = estimatedAltitude;
+ pastVerticalVelocity = estimatedVelocity;
+ pastVerticalAccel = verticalAccel;
+ previousTime = timestamp;
+}
+
+float AltitudeEstimator::getAltitude()
+{
+ // return the last estimated altitude
+ return estimatedAltitude;
+}
+
+float AltitudeEstimator::getVerticalVelocity()
+{
+ // return the last estimated vertical velocity
+ return estimatedVelocity;
+}
+
+float AltitudeEstimator::getVerticalAcceleration()
+{
+ // return the last estimated vertical acceleration
+ return pastVerticalAccel;
+}
diff --git a/src/AltEst/altitude.h b/src/AltEst/altitude.h
new file mode 100644
index 0000000..1ca6cb0
--- /dev/null
+++ b/src/AltEst/altitude.h
@@ -0,0 +1,55 @@
+/*
+ altitude.h: Altitude estimation via barometer/accelerometer fusion
+*/
+
+# pragma once
+
+#include "filters.h"
+#include "algebra.h"
+#include "pico/time.h"
+#include "pico/types.h"
+
+class AltitudeEstimator {
+
+ private:
+ // required parameters for the filters used for the estimations
+ // sensor's standard deviations
+ float sigmaAccel;
+ float sigmaGyro;
+ float sigmaBaro;
+ // Acceleration markov chain model state transition constant
+ float ca;
+ // Zero-velocity update acceleration threshold
+ float accelThreshold;
+ // gravity
+ float g = 9.81;
+ // For computing the sampling period
+ absolute_time_t prevTime = get_absolute_time();
+ uint32_t previousTime = to_us_since_boot(prevTime);
+ // required filters for altitude and vertical velocity estimation
+ KalmanFilter kalman;
+ ComplementaryFilter complementary;
+ // Estimated past vertical acceleration
+ float pastVerticalAccel = 0;
+ float pastVerticalVelocity = 0;
+ float pastAltitude = 0;
+ float pastGyro[3] = {0, 0, 0};
+ float pastAccel[3] = {0, 0, 0};
+ // estimated altitude and vertical velocity
+ float estimatedAltitude = 0;
+ float estimatedVelocity = 0;
+
+ public:
+
+ AltitudeEstimator(float sigmaAccel, float sigmaGyro, float sigmaBaro,
+ float ca, float accelThreshold);
+
+ void estimate(float accel[3], float gyro[3], float baroHeight, uint32_t timestamp);
+
+ float getAltitude();
+
+ float getVerticalVelocity();
+
+ float getVerticalAcceleration();
+
+}; // class AltitudeEstimator
diff --git a/src/AltEst/filters.cpp b/src/AltEst/filters.cpp
new file mode 100644
index 0000000..7902065
--- /dev/null
+++ b/src/AltEst/filters.cpp
@@ -0,0 +1,202 @@
+/*
+ filters.cpp: Filter class implementations
+ */
+
+//#include <cmath>
+#include <stdlib.h> // XXX eventually use fabs() instead of abs() ?
+
+#include "filters.h"
+
+void KalmanFilter::getPredictionCovariance(float covariance[3][3], float previousState[3], float deltat)
+{
+ // required matrices for the operations
+ float sigma[3][3];
+ float identity[3][3];
+ identityMatrix3x3(identity);
+ float skewMatrix[3][3];
+ skew(skewMatrix, previousState);
+ float tmp[3][3];
+ // Compute the prediction covariance matrix
+ scaleMatrix3x3(sigma, pow(sigmaGyro, 2), identity);
+ matrixProduct3x3(tmp, skewMatrix, sigma);
+ matrixProduct3x3(covariance, tmp, skewMatrix);
+ scaleMatrix3x3(covariance, -pow(deltat, 2), covariance);
+}
+
+void KalmanFilter::getMeasurementCovariance(float covariance[3][3])
+{
+ // required matrices for the operations
+ float sigma[3][3];
+ float identity[3][3];
+ identityMatrix3x3(identity);
+ float norm;
+ // Compute measurement covariance
+ scaleMatrix3x3(sigma, pow(sigmaAccel, 2), identity);
+ vectorLength(& norm, previousAccelSensor);
+ scaleAndAccumulateMatrix3x3(sigma, (1.0/3.0)*pow(ca, 2)*norm, identity);
+ copyMatrix3x3(covariance, sigma);
+}
+
+void KalmanFilter::predictState(float predictedState[3], float gyro[3], float deltat)
+{
+ // helper matrices
+ float identity[3][3];
+ identityMatrix3x3(identity);
+ float skewFromGyro[3][3];
+ skew(skewFromGyro, gyro);
+ // Predict state
+ scaleAndAccumulateMatrix3x3(identity, -deltat, skewFromGyro);
+ matrixDotVector3x3(predictedState, identity, currentState);
+ normalizeVector(predictedState);
+}
+
+void KalmanFilter::predictErrorCovariance(float covariance[3][3], float gyro[3], float deltat)
+{
+ // required matrices
+ float Q[3][3];
+ float identity[3][3];
+ identityMatrix3x3(identity);
+ float skewFromGyro[3][3];
+ skew(skewFromGyro, gyro);
+ float tmp[3][3];
+ float tmpTransposed[3][3];
+ float tmp2[3][3];
+ // predict error covariance
+ getPredictionCovariance(Q, currentState, deltat);
+ scaleAndAccumulateMatrix3x3(identity, -deltat, skewFromGyro);
+ copyMatrix3x3(tmp, identity);
+ transposeMatrix3x3(tmpTransposed, tmp);
+ matrixProduct3x3(tmp2, tmp, currErrorCovariance);
+ matrixProduct3x3(covariance, tmp2, tmpTransposed);
+ scaleAndAccumulateMatrix3x3(covariance, 1.0, Q);
+}
+
+void KalmanFilter::updateGain(float gain[3][3], float errorCovariance[3][3])
+{
+ // required matrices
+ float R[3][3];
+ float HTransposed[3][3];
+ transposeMatrix3x3(HTransposed, H);
+ float tmp[3][3];
+ float tmp2[3][3];
+ float tmp2Inverse[3][3];
+ // update kalman gain
+ // P.dot(H.T).dot(inv(H.dot(P).dot(H.T) + R))
+ getMeasurementCovariance(R);
+ matrixProduct3x3(tmp, errorCovariance, HTransposed);
+ matrixProduct3x3(tmp2, H, tmp);
+ scaleAndAccumulateMatrix3x3(tmp2, 1.0, R);
+ invert3x3(tmp2Inverse, tmp2);
+ matrixProduct3x3(gain, tmp, tmp2Inverse);
+}
+
+void KalmanFilter::updateState(float updatedState[3], float predictedState[3], float gain[3][3], float accel[3])
+{
+ // required matrices
+ float tmp[3];
+ float tmp2[3];
+ float measurement[3];
+ scaleVector(tmp, ca, previousAccelSensor);
+ subtractVectors(measurement, accel, tmp);
+ // update state with measurement
+ // predicted_state + K.dot(measurement - H.dot(predicted_state))
+ matrixDotVector3x3(tmp, H, predictedState);
+ subtractVectors(tmp, measurement, tmp);
+ matrixDotVector3x3(tmp2, gain, tmp);
+ sumVectors(updatedState, predictedState, tmp2);
+ normalizeVector(updatedState);
+}
+
+void KalmanFilter::updateErrorCovariance(float covariance[3][3], float errorCovariance[3][3], float gain[3][3])
+{
+ // required matrices
+ float identity[3][3];
+ identityMatrix3x3(identity);
+ float tmp[3][3];
+ float tmp2[3][3];
+ // update error covariance with measurement
+ matrixProduct3x3(tmp, gain, H);
+ matrixProduct3x3(tmp2, tmp, errorCovariance);
+ scaleAndAccumulateMatrix3x3(identity, -1.0, tmp2);
+ copyMatrix3x3(covariance, tmp2);
+}
+
+
+KalmanFilter::KalmanFilter(float ca, float sigmaGyro, float sigmaAccel)
+{
+ this->ca = ca;
+ this->sigmaGyro = sigmaGyro;
+ this->sigmaAccel = sigmaAccel;
+}
+
+float KalmanFilter::estimate(float gyro[3], float accel[3], float deltat)
+{
+ float predictedState[3];
+ float updatedState[3];
+ float errorCovariance[3][3];
+ float updatedErrorCovariance[3][3];
+ float gain[3][3];
+ float accelSensor[3];
+ float tmp[3];
+ float accelEarth;
+ scaleVector(accel, 9.81, accel); // Scale accel readings since they are measured in gs
+ // perform estimation
+ // predictions
+ predictState(predictedState, gyro, deltat);
+ predictErrorCovariance(errorCovariance, gyro, deltat);
+ // updates
+ updateGain(gain, errorCovariance);
+ updateState(updatedState, predictedState, gain, accel);
+ updateErrorCovariance(updatedErrorCovariance, errorCovariance, gain);
+ // Store required values for next iteration
+ copyVector(currentState, updatedState);
+ copyMatrix3x3(currErrorCovariance, updatedErrorCovariance);
+ // return vertical acceleration estimate
+ scaleVector(tmp, 9.81, updatedState);
+ subtractVectors(accelSensor, accel, tmp);
+ copyVector(previousAccelSensor, accelSensor);
+ dotProductVectors(& accelEarth, accelSensor, updatedState);
+ return accelEarth;
+}
+
+
+float ComplementaryFilter::ApplyZUPT(float accel, float vel)
+{
+ // first update ZUPT array with latest estimation
+ ZUPT[ZUPTIdx] = accel;
+ // and move index to next slot
+ uint8_t nextIndex = (ZUPTIdx + 1) % ZUPT_SIZE;
+ ZUPTIdx = nextIndex;
+ // Apply Zero-velocity update
+ for (uint8_t k = 0; k < ZUPT_SIZE; ++k) {
+ if (abs(ZUPT[k]) > accelThreshold) return vel;
+ }
+ return 0.0;
+}
+
+
+ComplementaryFilter::ComplementaryFilter(float sigmaAccel, float sigmaBaro, float accelThreshold)
+{
+ // Compute the filter gain
+ gain[0] = sqrt(2 * sigmaAccel / sigmaBaro);
+ gain[1] = sigmaAccel / sigmaBaro;
+ // If acceleration is below the threshold the ZUPT counter
+ // will be increased
+ this->accelThreshold = accelThreshold;
+ // initialize zero-velocity update
+ ZUPTIdx = 0;
+ for (uint8_t k = 0; k < ZUPT_SIZE; ++k) {
+ ZUPT[k] = 0;
+ }
+}
+
+void ComplementaryFilter::estimate(float * velocity, float * altitude, float baroAltitude,
+ float pastAltitude, float pastVelocity, float accel, float deltat)
+{
+ // Apply complementary filter
+ *altitude = pastAltitude + deltat*(pastVelocity + (gain[0] + gain[1]*deltat/2)*(baroAltitude-pastAltitude))+
+ accel*pow(deltat, 2)/2;
+ *velocity = pastVelocity + deltat*(gain[1]*(baroAltitude-pastAltitude) + accel);
+ // Compute zero-velocity update
+ *velocity = ApplyZUPT(accel, *velocity);
+}
diff --git a/src/AltEst/filters.h b/src/AltEst/filters.h
new file mode 100644
index 0000000..2e316a3
--- /dev/null
+++ b/src/AltEst/filters.h
@@ -0,0 +1,65 @@
+/*
+ filters.h: Filter class declarations
+ */
+
+#pragma once
+
+//#include <cmath>
+#include <math.h>
+#include <stdint.h>
+
+#include "algebra.h"
+
+class KalmanFilter {
+ private:
+ float currentState[3] = {0, 0, 1};
+ float currErrorCovariance[3][3] = {{100, 0, 0},{0, 100, 0},{0, 0, 100}};
+ float H[3][3] = {{9.81, 0, 0}, {0, 9.81, 0}, {0, 0, 9.81}};
+ float previousAccelSensor[3] = {0, 0, 0};
+ float ca;
+ float sigmaGyro;
+ float sigmaAccel;
+
+ void getPredictionCovariance(float covariance[3][3], float previousState[3], float deltat);
+
+ void getMeasurementCovariance(float covariance[3][3]);
+
+ void predictState(float predictedState[3], float gyro[3], float deltat);
+
+ void predictErrorCovariance(float covariance[3][3], float gyro[3], float deltat);
+
+ void updateGain(float gain[3][3], float errorCovariance[3][3]);
+
+ void updateState(float updatedState[3], float predictedState[3], float gain[3][3], float accel[3]);
+
+ void updateErrorCovariance(float covariance[3][3], float errorCovariance[3][3], float gain[3][3]);
+
+ public:
+
+ KalmanFilter(float ca, float sigmaGyro, float sigmaAccel);
+
+ float estimate(float gyro[3], float accel[3], float deltat);
+
+}; // Class KalmanFilter
+
+class ComplementaryFilter {
+
+ private:
+
+ // filter gain
+ float gain[2];
+ // Zero-velocity update
+ float accelThreshold;
+ static const uint8_t ZUPT_SIZE = 12;
+ uint8_t ZUPTIdx;
+ float ZUPT[ZUPT_SIZE];
+
+ float ApplyZUPT(float accel, float vel);
+
+ public:
+
+ ComplementaryFilter(float sigmaAccel, float sigmaBaro, float accelThreshold);
+
+ void estimate(float * velocity, float * altitude, float baroAltitude,
+ float pastAltitude, float pastVelocity, float accel, float deltat);
+}; // Class ComplementaryFilter
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0a50318..59d21de 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,49 +1,54 @@
add_executable(ads
- active_drag_system.cpp
-)
-
-add_executable(pru1
- pru1/sensors.cpp
-)
-
-add_executable(pru2
- pru2/servos.cpp
-)
-
-target_link_options(pru1 PRIVATE -static)
-target_link_options(pru2 PRIVATE -static)
-cmake_minimum_required(VERSION 3.16.3)
-
-include_directories( ../include )
-
-# Set Variables
-set(TARGET ActiveDragSystem)
-set(SOURCES active-drag-system.cpp ads.cpp actuationPlan.cpp surfaceFitModel.cpp rocketUtils.cpp sensorIMU.cpp sensorAltimeter.cpp motor.cpp logger.cpp kalmanfilter.cpp)
-
-
-# Create Executables & Link Dependencies
-add_executable(${TARGET} ${SOURCES})
-
-add_executable(${TARGET_B} ${SOURCES_B})
-target_link_libraries(${TARGET_B} PUBLIC gtest_main)
-add_test(NAME ${TARGET_B} COMMAND ${TARGET_B})
-
-add_executable(${TARGET_C} ${SOURCES_C})
-target_link_libraries(${TARGET_C} PUBLIC gtest_main)
-add_test(NAME ${TARGET_C} COMMAND ${TARGET_C})
-
-add_executable(${TARGET_D} ${SOURCES_D})
-target_link_libraries(${TARGET_D} PUBLIC gtest_main)
-add_test(NAME ${TARGET_D} COMMAND ${TARGET_D})
-
-add_executable(${TARGET_E} ${SOURCES_E})
-target_link_libraries(${TARGET_E} PUBLIC gtest_main)
-add_test(NAME ${TARGET_E} COMMAND ${TARGET_E})
-
-add_executable(${TARGET_F} ${SOURCES_F})
-target_link_libraries(${TARGET_F} PUBLIC gtest_main)
-add_test(NAME ${TARGET_F} COMMAND ${TARGET_F})
-
-add_executable(${TARGET_G} ${SOURCES_G})
-target_link_libraries(${TARGET_G} PUBLIC gtest_main)
-add_test(NAME ${TARGET_G} COMMAND ${TARGET_G})
+ active_drag_system.cpp
+ spi_flash.c
+ bno055.cpp
+ SimpleKalmanFilter.cpp
+ # kalmanfilter.cpp
+ pwm.cpp
+ AltEst/algebra.cpp
+ AltEst/altitude.cpp
+ AltEst/filters.cpp
+ )
+
+add_executable(read_flash
+ read_flash.c
+ spi_flash.c
+ )
+
+add_executable(servo_test
+ servo_test.cpp
+ pwm.cpp
+ )
+
+add_executable(alt_test
+ altimeter.cpp
+ )
+
+# pull in common dependencies
+target_link_libraries(ads pico_stdlib pico_multicore pico_sync hardware_i2c hardware_spi hardware_pwm pico_cyw43_arch_none ${Eigen_LIBRARIES})
+target_include_directories(ads PUBLIC ../include)
+
+target_link_libraries(read_flash pico_stdlib hardware_spi)
+target_include_directories(read_flash PUBLIC ../include)
+
+target_link_libraries(servo_test pico_stdlib hardware_pwm hardware_i2c)
+target_include_directories(servo_test PUBLIC ../include)
+
+target_link_libraries(alt_test pico_stdlib hardware_i2c hardware_gpio)
+
+pico_enable_stdio_usb(ads 1)
+pico_enable_stdio_uart(ads 0)
+
+pico_enable_stdio_usb(read_flash 1)
+pico_enable_stdio_uart(read_flash 0)
+
+pico_enable_stdio_usb(servo_test 1)
+pico_enable_stdio_uart(servo_test 0)
+
+pico_enable_stdio_usb(alt_test 1)
+pico_enable_stdio_uart(alt_test 0)
+# create map/bin/hex file etc.
+pico_add_extra_outputs(ads)
+pico_add_extra_outputs(read_flash)
+pico_add_extra_outputs(servo_test)
+pico_add_extra_outputs(alt_test)
diff --git a/src/SimpleKalmanFilter.cpp b/src/SimpleKalmanFilter.cpp
new file mode 100644
index 0000000..88ba5d4
--- /dev/null
+++ b/src/SimpleKalmanFilter.cpp
@@ -0,0 +1,48 @@
+/*
+ * SimpleKalmanFilter - a Kalman Filter implementation for single variable models.
+ * Created by Denys Sene, January, 1, 2017.
+ * Released under MIT License - see LICENSE file for details.
+ */
+
+#include "SimpleKalmanFilter.h"
+#include <math.h>
+
+SimpleKalmanFilter::SimpleKalmanFilter(float mea_e, float est_e, float q)
+{
+ _err_measure=mea_e;
+ _err_estimate=est_e;
+ _q = q;
+}
+
+float SimpleKalmanFilter::updateEstimate(float mea)
+{
+ _kalman_gain = _err_estimate/(_err_estimate + _err_measure);
+ _current_estimate = _last_estimate + _kalman_gain * (mea - _last_estimate);
+ _err_estimate = (1.0f - _kalman_gain)*_err_estimate + fabsf(_last_estimate-_current_estimate)*_q;
+ _last_estimate=_current_estimate;
+
+ return _current_estimate;
+}
+
+void SimpleKalmanFilter::setMeasurementError(float mea_e)
+{
+ _err_measure=mea_e;
+}
+
+void SimpleKalmanFilter::setEstimateError(float est_e)
+{
+ _err_estimate=est_e;
+}
+
+void SimpleKalmanFilter::setProcessNoise(float q)
+{
+ _q=q;
+}
+
+float SimpleKalmanFilter::getKalmanGain() {
+ return _kalman_gain;
+}
+
+float SimpleKalmanFilter::getEstimateError() {
+ return _err_estimate;
+}
diff --git a/src/active_drag_system.cpp b/src/active_drag_system.cpp
index 7b63faa..5d248e2 100644
--- a/src/active_drag_system.cpp
+++ b/src/active_drag_system.cpp
@@ -1,14 +1,649 @@
-#include <iostream>
-#include <cstdlib>
-#include "../include/surfaceFitModel.hpp"
-#include "../include/actuationPlan.hpp"
-#include "../include/ads.hpp"
+#include <algorithm>
+#include <stdio.h>
+#include "pico/multicore.h"
+#include "pico/platform.h"
+#include "pico/sem.h"
+#include "spi_flash.h"
+#include "boards/pico_w.h"
+#include "hardware/gpio.h"
+#include "hardware/i2c.h"
+#include "pico/stdlib.h"
+#include "pico/time.h"
+#include "pico/types.h"
+#include "pico/cyw43_arch.h"
+#include <math.h>
+#include "bno055.hpp"
+#include "AltEst/altitude.h"
+#include "pwm.hpp"
+#include "SimpleKalmanFilter.h"
+
+#define ALT_ADDR 0x60
+#define MAX_SCL 400000
+#define DATA_RATE_HZ 100
+#define LOOP_PERIOD (1.0f / DATA_RATE_HZ)
+#define INT1_PIN 6 // INT1 PIN on MPL3115A2 connected to GPIO PIN 9 (GP6)
+#define MOSFET_PIN 26 // MOSFET PIN connected to GPIO PIN 31 (GP26)
+
+#define GRAVITY -9.81
+#define LOG_RATE_HZ 4
+
+#define MOTOR_BURN_TIME 3900 // Burn time in milliseconds for M2500T
+
+typedef enum {
+ PAD,
+ BOOST,
+ COAST,
+ APOGEE,
+ RECOVERY,
+ END
+} state_t;
+
+BNO055 bno055;
+PWM pwm;
+static AltitudeEstimator vKF = AltitudeEstimator(0.0005, // sigma Accel
+ 0.0005, // sigma Gyro
+ 0.018, // sigma Baro
+ 0.5, // ca
+ 0.1);// accelThreshold
+
+void pad_callback(uint gpio, uint32_t event_mask);
+int64_t boost_callback(alarm_id_t id, void* user_data);
+int64_t apogee_callback(alarm_id_t id, void* user_data);
+int64_t coast_callback(alarm_id_t id, void* user_data);
+void recovery_callback(uint gpio, uint32_t event_mask);
+void init_altimeter();
+float get_deploy_percent(float velocity, float altitude);
+
+bool timer_callback(repeating_timer_t *rt);
+bool test_timer_callback(repeating_timer_t *rt);
+
+float get_altitude();
+float get_velocity();
+
+void snapshot();
+bool logging_callback(repeating_timer_t *rt);
+void logging_core();
+
+semaphore_t sem;
+
+volatile float altitude = 0.0f;
+volatile float prev_altitude = 0.0f;
+volatile float velocity = 0.0f;
+volatile state_t state = PAD;
+volatile float threshold_altitude = 30.0f;
+volatile float threshold_velocity = 30.0f;
+volatile uint8_t deployment_percent = 0;
+
+volatile vector3f linear_acceleration;
+volatile vector3f acceleration;
+volatile quarternion abs_quaternion;
+volatile vector3f velocity_vector;
+
+volatile vector3f euler_angles;
+volatile vector3f abs_lin_accel;
+volatile vector3f prev_abs_lin_accel;
+volatile vector3f rot_y_vec;
+volatile vector3f vel_at_angle;
+
+volatile vector3f accel_gravity;
+
+volatile CALIB_STATUS calib_status;
+uint8_t accel[6];
+uint8_t quat[8];
+
+repeating_timer_t data_timer;
+repeating_timer_t log_timer;
+
+float ground_altitude = 0.0f;
+
+SimpleKalmanFilter altitudeKF(2, 2, 0.01);
+SimpleKalmanFilter velocityKF(1, 1, 0.01);
+
+/**
+ * @brief Main function
+ *
+ * @return int error code
+ */
int main() {
+ // stdio_init_all();
+
+ cyw43_arch_init();
+ i2c_init(i2c_default, MAX_SCL);
+ gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
+ gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
+ gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
+
+ // Enable SPI 0 at 60 MHz and connect to GPIOs
+ spi_init(spi_default, 1000 * 1000 * 60);
+ gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
+
+ // Chip select is active-low, so we'll initialise it to a driven-high state
+ gpio_init(PICO_DEFAULT_SPI_CSN_PIN);
+ gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT);
+ gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
+
+ gpio_init(INT1_PIN);
+ gpio_pull_up(INT1_PIN);
+
+ alarm_pool_init_default();
+
+
+ // Initialize altimeter
+ init_altimeter();
+
+ // Initialize BNO055
+ bno055.init();
+
+ // Initialize PWM
+ pwm.init();
+
+ // Initialize MOSFET
+ gpio_init(MOSFET_PIN);
+ gpio_set_dir(MOSFET_PIN, GPIO_OUT);
+
+ // Initialize Kalman Filter
+ float measurement = get_altitude();
+ float v_measurement = get_velocity();
+ altitudeKF.updateEstimate(measurement);
+ velocityKF.updateEstimate(measurement);
+
+ ground_altitude = altitude;
+
+ sem_init(&sem, 1, 1);
+
+ if (!add_repeating_timer_us(-1000000 / DATA_RATE_HZ, &timer_callback, NULL, &data_timer)) {
+ // printf("Failed to add timer!\n");
+ return -1;
+ }
+
+ multicore_launch_core1(logging_core);
+
+ while (1) {
+ tight_loop_contents();
+ }
+}
+
+void logging_core() {
+ add_repeating_timer_us(-1000000 / LOG_RATE_HZ, &logging_callback, NULL, &log_timer);
+
+ while (1) {
+ tight_loop_contents();
+ }
+}
+
+/**
+ * @brief Initializes the altimeter
+ *
+ * @param altitude passes the altitude variable to the function
+ * @param threshold_altitude passes the threshold altitude variable to the function
+ */
+void init_altimeter() {
+
+ uint8_t config[2] = {0};
+
+ // Select control register(0x26)
+ // Active mode, OSR = 16, altimeter mode(0xB8)
+ config[0] = 0x26;
+ config[1] = 0x89;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select data configuration register(0x13)
+ // Data ready event enabled for altitude, pressure, temperature(0x07)
+ // config[0] = 0x13;
+ // config[1] = 0x07;
+ // i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Below configures the interrupt for the first transition from PAD to BOOST
+ // Initial Reading
+
+ sleep_ms(1000);
+
+ while (altitude == 0.0f) {
+ altitude = get_altitude();
+ }
+
+ threshold_altitude += altitude; // 30 meters above ground
+
+ // printf("threshold_altitude: %4.2f", threshold_altitude);
+
+ // Select control register 3 (0x28)
+ // Set bot interrupt pins to active low and enable internal pullups
+ config[0] = 0x28;
+ config[1] = 0x01;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select pressure target MSB register(0x16)
+ // Set altitude target to 30 meters above ground altitude
+ config[0] = 0x16;
+ config[1] = (uint8_t) (((int16_t)(threshold_altitude)) >> 8);
+ // printf("threshold_alt upper half: %X\n", config[1]);
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select pressure target LSB register(0x17)
+ // Set altitude target to 30 meters above ground altitude
+ config[0] = 0x17;
+ config[1] = (uint8_t) (((int16_t)(threshold_altitude)));
+ // printf("threshold_alt lower half: %X\n", config[1]);
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select interrupt enable register (0x29)
+ // Set interrupt enabled for altitude threshold(0x08)
+ config[0] = 0x29;
+ config[1] = 0x08;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select interrupt configuration register (0x2A)
+ // Set interrupt enabled for altitude threshold to route to INT1 pin(0x08)
+ config[0] = 0x2A;
+ config[1] = 0x08;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ gpio_set_irq_enabled_with_callback(INT1_PIN, GPIO_IRQ_LEVEL_LOW, true, &pad_callback);
+ // End of configuration of interrupt for first transition from PAD to BOOST
+}
+
+void snapshot() {
+ if (state != END) {
+ uint8_t entry[PACKET_SIZE];
+ absolute_time_t now = get_absolute_time();
+ uint64_t now_us = to_us_since_boot(now);
+ uint32_t alt_bits = *((uint32_t *)&altitude);
+ uint32_t vel_bits = *((uint32_t *)&velocity);
+ uint32_t acc_bits = *((uint32_t *)&abs_lin_accel.z);
+ entry[0] = now_us >> 56;
+ entry[1] = now_us >> 48;
+ entry[2] = now_us >> 40;
+ entry[3] = now_us >> 32;
+ entry[4] = now_us >> 24;
+ entry[5] = now_us >> 16;
+ entry[6] = now_us >> 8;
+ entry[7] = now_us;
+
+ switch (state) {
+ case PAD:
+ entry[8] = 'P';
+ break;
+ case BOOST:
+ entry[8] = 'B';
+ break;
+ case COAST:
+ entry[8] = 'C';
+ break;
+ case APOGEE:
+ entry[8] = 'A';
+ break;
+ case RECOVERY:
+ entry[8] = 'R';
+ break;
+ case END:
+ entry[8] = 'E';
+ break;
+ }
+
+ entry[9] = deployment_percent;
+ entry[10] = alt_bits >> 24;
+ entry[11] = alt_bits >> 16;
+ entry[12] = alt_bits >> 8;
+ entry[13] = alt_bits;
+ entry[14] = vel_bits >> 24;
+ entry[15] = vel_bits >> 16;
+ entry[16] = vel_bits >> 8;
+ entry[17] = vel_bits;
+
+ entry[18] = quat[0];
+ entry[19] = quat[1];
+ entry[20] = quat[2];
+ entry[21] = quat[3];
+ entry[22] = quat[4];
+ entry[23] = quat[5];
+ entry[24] = quat[6];
+ entry[25] = quat[7];
+
+ entry[26] = acc_bits >> 24;
+ entry[27] = acc_bits >> 16;
+ entry[28] = acc_bits >> 8;
+ entry[29] = acc_bits;
+ entry[30] = accel[4];
+ entry[31] = accel[5];
+ write_entry(entry);
+ }
+}
+
+bool logging_callback(repeating_timer_t *rt) {
+ static bool LED_STATUS = 0;
+ sem_acquire_blocking(&sem);
+ LED_STATUS = !LED_STATUS;
+ cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, LED_STATUS);
+ snapshot();
+ sem_release(&sem);
+ return true;
+}
+
+bool timer_callback(repeating_timer_t *rt) {
+ absolute_time_t last = get_absolute_time();
+ sem_acquire_blocking(&sem);
+ float measurement = get_altitude();
+ altitude = altitudeKF.updateEstimate(measurement);
+ // float in_velocity = (altitude - prev_altitude) * DATA_RATE_HZ;
+ // velocity = velocityKF.updateEstimate(in_velocity);
+ float acceldata[3];
+ float gyrodata[3];
+
+
+ // printf("Velocity_Delta: %4.2f\tVelocity_Prev: %4.2f\n", velocity, ((altitude - prev_altitude) * DATA_RATE_HZ));
+
+ bno055.read_lin_accel();
+ // printf("Linear Acceleration:\n" "x: %f\n" "y: %f\n" "z: %f\n",
+ // linear_acceleration.x, linear_acceleration.y, linear_acceleration.z);
+
+ bno055.read_abs_quaternion();
+ // printf("Absolute Quaternion:\n" "w: %f\n" "x: %f\n" "y: %f\n" "z: %f\n",
+ // abs_quaternion.w, abs_quaternion.x, abs_quaternion.y, abs_quaternion.z);
+
+ bno055.read_euler_angles();
+ // printf("Euler Angles:\n" "Roll: %f\n" "Pitch: %f\n" "Yaw: %f\n",
+ // euler_angles.x, euler_angles.y, euler_angles.z);
+
+ // Linear Acceleration and Absolute Quaternion are used to calculate Absolute Linear Acceleration
+ // They must be read before calling this function
+ bno055.calculate_abs_linear_acceleration();
+ // printf("Absolute Linear Acceleration:\n" "x: %f\n" "y: %f\n" "z: %f\n",
+ // abs_lin_accel.x, abs_lin_accel.y, abs_lin_accel.z);
- SurfaceFitModel sfm = SurfaceFitModel();
- ActuationPlan plan = ActuationPlan(sfm);
- ADS ads = ADS(plan);
- ads.run();
- return EXIT_SUCCESS;
+ acceldata[0] = abs_lin_accel.x;
+ acceldata[1] = abs_lin_accel.y;
+ acceldata[2] = abs_lin_accel.z - 0.4f;
+ gyrodata[0] = 0;
+ gyrodata[1] = 0;
+ gyrodata[2] = 0;
+
+ vKF.estimate(acceldata, gyrodata, altitude, to_us_since_boot(last));
+ velocity = vKF.getVerticalVelocity();
+ // printf("Measurement: %4.2f, Altitude: %4.2f, Velocity: %4.2f\n", measurement, altitude, velocity);
+ // This is wrong but i'm going home.
+ // velocity_vector.x = (prev_abs_lin_accel.x - abs_lin_accel.x) / 0.01f);
+ // velocity_vector.y = (prev_abs_lin_accel.y - abs_lin_accel.y) / 0.01f);
+ // velocity_vector.z = (prev_abs_lin_accel.z - abs_lin_accel.z) / 0.01f);
+ // printf("Velocity Vector:\n" "x: %f\n" "y: %f\n" "z: %f\n",
+ // velocity_vector.x, velocity_vector.y, velocity_vector.z);
+
+ prev_abs_lin_accel.x = abs_lin_accel.x;
+ prev_abs_lin_accel.y = abs_lin_accel.y;
+ prev_abs_lin_accel.z = abs_lin_accel.z;
+
+ bno055.accel_to_gravity();
+ float agl = altitude - ground_altitude;
+
+ deployment_percent = (uint8_t)(std::min(std::max(30.0f, get_deploy_percent(velocity, agl)), 100.0f));
+
+ switch(state) {
+ case PAD:
+ gpio_put(MOSFET_PIN, 0);
+ pwm.set_servo_percent(0);
+ deployment_percent = 0;
+ break;
+ case BOOST:
+ gpio_put(MOSFET_PIN, 1);
+ pwm.set_servo_percent(0);
+ deployment_percent = 0;
+ break;
+ case COAST:
+ gpio_put(MOSFET_PIN, 1);
+ pwm.set_servo_percent(deployment_percent);
+ break;
+ case APOGEE:
+ gpio_put(MOSFET_PIN, 1);
+ pwm.set_servo_percent(0);
+ deployment_percent = 0;
+ break;
+ case RECOVERY:
+ gpio_put(MOSFET_PIN, 1);
+ pwm.set_servo_percent(0);
+ deployment_percent = 0;
+ break;
+ case END:
+ gpio_put(MOSFET_PIN, 1);
+ pwm.set_servo_percent(0);
+ deployment_percent = 0;
+ break;
+ }
+ prev_altitude = altitude;
+ sem_release(&sem);
+ return true;
}
+
+/**
+ * @brief Test function for timer callback outputs data in ROS2 format
+ *
+ * @param rt
+ * @return true
+ * @return false
+ */
+// bool test_timer_callback(repeating_timer_t *rt) {
+// static float prev_altitude = altitude;
+// absolute_time_t last = get_absolute_time();
+// altitude = get_altitude();
+// velocity = ((altitude - prev_altitude) / 0.01f);
+// prev_altitude = altitude;
+//
+// bno055.read_lin_accel();
+// bno055.read_abs_quaternion();
+//
+// absolute_time_t now = get_absolute_time();
+// int64_t time_delta = absolute_time_diff_us(last, now);
+//
+// std::cout << altitude << " " << abs_quaternion.w << " "
+// << abs_quaternion.x << " "
+// << abs_quaternion.y << " "
+// << abs_quaternion.z << " "
+// << linear_acceleration.x << " "
+// << linear_acceleration.y << " "
+// << linear_acceleration.z << std::endl;
+//
+// /* switch (state) {
+// case PAD:
+// printf("P\n");
+// break;
+// case BOOST:
+// printf("B\n");
+// break;
+// case COAST:
+// printf("C\n");
+// break;
+// case APOGEE:
+// printf("A\n");
+// break;
+// case RECOVERY:
+// printf("R\n");
+// break;
+// case END:
+// printf("E\n");
+// break;
+// }*/
+//
+// // absolute_time_t now = get_absolute_time();
+// // int64_t time_delta = absolute_time_diff_us(last, now);
+// // printf("Time Delta: %" PRIi64"\n", time_delta);
+// // std::flush(std::cout);
+// prev_altitude = altitude;
+// return true;
+// }
+
+/**
+ * @brief Call back function for when rocket is on the pad
+ *
+ * @param gpio pin number of interrupt
+ * @param event_mask interrupt condition, value is set by PICO_SDK
+ * GPIO_IRQ_LEVEL_LOW = 0x1u,
+ * GPIO_IRQ_LEVEL_HIGH = 0x2u,
+ * GPIO_IRQ_EDGE_FALL = 0x4u,
+ * GPIO_IRQ_EDGE_RISE = 0x8u,
+ * @link https://www.raspberrypi.com/documentation/pico-sdk/hardware/gpio.html#ga6347e27da3ab34f1ea65b5ae16ab724f
+ */
+void pad_callback(uint gpio, uint32_t event_mask) {
+
+ /// @link https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#ga6165f07f4b619dd08ea6dc97d069e78a
+ /// Each pin only supports one call back, so by calling this we overwrite the previous one
+ gpio_set_irq_enabled_with_callback(INT1_PIN, GPIO_IRQ_LEVEL_LOW, false, &pad_callback);
+ uint8_t config[2] = {0};
+ // Select interrupt enable register (0x29)
+ // Set interrupt enabled for altitude threshold(0x08)
+ config[0] = 0x29;
+ config[1] = 0x00;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select interrupt configuration register (0x2A)
+ // Set interrupt enabled for altitude threshold to route to INT1 pin(0x08)
+ config[0] = 0x2A;
+ config[1] = 0x00;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ sem_acquire_blocking(&sem);
+ state = BOOST;
+ // start motor burn timer with this function as callback
+ add_alarm_in_ms(MOTOR_BURN_TIME, &boost_callback, NULL, false);
+ snapshot();
+ sem_release(&sem);
+}
+
+int64_t boost_callback(alarm_id_t id, void* user_data) {
+ // Configure accelerometer and/or altimeter to generate interrupt
+ // for when velocity is negative with this function as callback to
+ // transition to APOGEE
+ sem_acquire_blocking(&sem);
+ state = COAST;
+ snapshot();
+ sem_release(&sem);
+ add_alarm_in_ms(1000, &coast_callback, NULL, false);
+ return 0;
+}
+
+int64_t coast_callback(alarm_id_t id, void* user_data) {
+ // Want to somehow immediately transition to RECOVERY from APOGEE (extremely short timer?)
+ if (velocity <= 0.0f) {
+ sem_acquire_blocking(&sem);
+ state = APOGEE;
+ snapshot();
+ sem_release(&sem);
+ add_alarm_in_ms(1, &apogee_callback, NULL, false);
+ } else {
+ add_alarm_in_ms(250, &coast_callback, NULL, false);
+ }
+ return 0;
+}
+
+int64_t apogee_callback(alarm_id_t id, void* user_data) {
+ // Set altimeter interrupt to occur for when rocket touches back to the ground
+
+ uint8_t config[2] = {0};
+ // Select pressure target MSB register(0x16)
+ // Set altitude target to 10 meters above ground altitude
+ float recov_altitude = ground_altitude + 10.0f;
+ // Select pressure target MSB register(0x16)
+ // Set altitude target to 30 meters above ground altitude
+ config[0] = 0x16;
+ config[1] = (uint8_t) (((int16_t)(recov_altitude)) >> 8);
+ // printf("threshold_alt upper half: %X\n", config[1]);
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select pressure target LSB register(0x17)
+ // Set altitude target to 30 meters above ground altitude
+ config[0] = 0x17;
+ config[1] = (uint8_t) (((int16_t)(recov_altitude)));
+ // printf("threshold_alt lower half: %X\n", config[1]);
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select interrupt enable register (0x29)
+ // Set interrupt enabled for altitude threshold(0x08)
+ config[0] = 0x29;
+ config[1] = 0x08;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ // Select interrupt configuration register (0x2A)
+ // Set interrupt enabled for altitude threshold to route to INT1 pin(0x08)
+ config[0] = 0x2A;
+ config[1] = 0x08;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+
+ sem_acquire_blocking(&sem);
+ state = RECOVERY;
+ snapshot();
+ sem_release(&sem);
+ gpio_set_irq_enabled_with_callback(INT1_PIN, GPIO_IRQ_LEVEL_LOW, true, &recovery_callback);
+ return 0;
+}
+
+void recovery_callback(uint gpio, uint32_t event_mask) {
+ // Essentially just a signal to stop logging data
+ sem_acquire_blocking(&sem);
+ state = END;
+ snapshot();
+ sem_acquire_blocking(&sem);
+}
+
+float get_altitude() {
+ uint8_t reg = 0x01;
+ uint8_t data[5];
+ i2c_write_blocking(i2c_default, ALT_ADDR, &reg, 1, true);
+ i2c_read_blocking(i2c_default, ALT_ADDR, data, 5, false);
+ // Exactly how MPL3115A2 datasheet says to retrieve altitude
+ float altitude = (float) ((int16_t) ((data[0] << 8) | data[1])) + (float) (data[2] >> 4) * 0.0625;
+ // uint32_t temp_alt = (data[1] << 24) | (data[2] << 16) | (data[3] << 8);
+ // float altitude = temp_alt / 65536.0f;
+ return altitude;
+}
+
+/**
+ * @brief Calculates the fitted Coeficient of Drag using the Surface Fit Model for the current rocket design.
+ * @param velocity Velocity
+ * @param altitude Altitude
+ *
+ * @return: Drag Coefficient (CD)
+ */
+float get_deploy_percent(float velocity, float altitude) {
+ // Lookup table (Data from 'dragSurfFit.m' for Surface Fit Model Formula)
+ float p00 = -8.498e+04;
+ float p10 = 924.4;
+ float p01 = 69.98;
+ float p20 = -3.62;
+ float p11 = -0.6196;
+ float p02 = -0.01897;
+ float p30 = 0.005983;
+ float p21 = 0.001756;
+ float p12 = 0.0001271;
+ float p03 = 1.693e-06;
+ float p40 = -3.451e-06;
+ float p31 = -1.582e-06;
+ float p22 = -2.004e-07;
+ float p13 = -7.476e-09;
+
+
+ /* MATLAB Code:
+ * return p00 + p10 * V + p01 * H + p20 * V ** 2 + \
+ * p11 * V * H + p02 * H ** 2 + p30 * V ** 3 + \
+ * p21 * V ** 2 * H + p12 * V * H ** 2 + p03 * H ** 3 + \
+ * p40 * V ** 4 + p31 * V ** 3 * H + p22 * V ** 2 * H ** 2 + \
+ * p13 * V * H ** 3
+ */
+
+ return (p00 + p10 * velocity + p01 * altitude + p20 * std::pow(velocity, 2) + p11 * velocity * altitude + p02 * std::pow(altitude, 2)
+ + p30 * std::pow(velocity, 3) + p21 * std::pow(velocity, 2) * altitude + p12 * velocity * std::pow(altitude, 2) + p03 * std::pow(altitude, 3)
+ + p40 * std::pow(velocity, 4) + p31 * std::pow(velocity, 3) * altitude + p22 * std::pow(velocity, 2) * std::pow(altitude, 2) + p13 * velocity * std::pow(altitude, 3));
+
+
+}
+
+float get_velocity() {
+ uint8_t reg = 0x07;
+ uint8_t data[5];
+ i2c_write_blocking(i2c_default, ALT_ADDR, &reg, 1, true);
+ i2c_read_blocking(i2c_default, ALT_ADDR, data, 5, false);
+ float delta = (float) ((int16_t) ((data[0] << 8) | data[1])) + (float) (data[2] >> 4) * 0.0625;
+ float vel = delta * DATA_RATE_HZ;
+ return vel;
+}
+
diff --git a/src/altimeter.cpp b/src/altimeter.cpp
new file mode 100644
index 0000000..a5c8849
--- /dev/null
+++ b/src/altimeter.cpp
@@ -0,0 +1,50 @@
+#include <stdio.h>
+
+#include "hardware/gpio.h"
+#include "boards/pico_w.h"
+#include "hardware/i2c.h"
+#include "pico/stdio.h"
+#include "pico/time.h"
+
+#define ALT_ADDR 0x60
+#define MAX_SCL 400000
+#define DATA_RATE_HZ 15
+
+float altitude = 0.0f;
+float get_altitude();
+
+int main() {
+ stdio_init_all();
+
+ i2c_init(i2c_default, MAX_SCL);
+ gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
+ gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
+ gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
+
+ uint8_t config[2] = {0};
+
+ // Select control register(0x26)
+ // Active mode, OSR = 16, altimeter mode(0xB8)
+ config[0] = 0x26;
+ config[1] = 0xB9;
+ i2c_write_blocking(i2c_default, ALT_ADDR, config, 2, true);
+ sleep_ms(1500);
+
+ while (1) {
+ sleep_ms(1000);
+ altitude = get_altitude();
+ printf("Altitude: %4.2f\n", altitude);
+ }
+}
+
+float get_altitude() {
+ uint8_t reg = 0x01;
+ uint8_t data[5];
+ i2c_write_blocking(i2c_default, ALT_ADDR, &reg, 1, true);
+ i2c_read_blocking(i2c_default, ALT_ADDR, data, 5, false);
+ // Exactly how MPL3115A2 datasheet says to retrieve altitude
+ float altitude = (float) ((int16_t) ((data[0] << 8) | data[1])) + (float) (data[2] >> 4) * 0.0625;
+ return altitude;
+}
+
diff --git a/src/bno055.cpp b/src/bno055.cpp
new file mode 100644
index 0000000..fb51986
--- /dev/null
+++ b/src/bno055.cpp
@@ -0,0 +1,201 @@
+#include "bno055.hpp"
+
+/// @link [Pico BNO055 Example](https://learnembeddedsystems.co.uk/bno005-i2c-example-code)
+
+BNO055::BNO055() {
+ bno055_address = BNO055_ADDRESS_A;
+ _sensorID = BNO055_ID;
+ default_mode = OPERATION_MODE_NDOF;
+}
+
+void BNO055::reset_bno055() {
+ uint8_t data[2];
+ data[0] = BNO055_SYS_TRIGGER_ADDR;
+ data[1] = 0x20; // Reset system
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+ sleep_ms(1000); // Wait 650ms for the sensor to reset
+}
+
+void BNO055::init() {
+ sleep_ms(1000); // Wait 650ms for the sensor to reset
+ uint8_t chip_id_addr = BNO055_CHIP_ID_ADDR;
+ uint8_t id[1];
+ i2c_write_blocking(i2c_default, bno055_address, &chip_id_addr, 1, false);
+ i2c_read_blocking(i2c_default, bno055_address, id, 1, false);
+ if (!id[0] == _sensorID) {
+ printf("BNO055 not detected\n");
+ }
+
+ // Use internal oscillator
+ uint8_t data[2];
+ data[0] = BNO055_SYS_TRIGGER_ADDR;
+ data[1] = 0x40; // Set to use internal oscillator
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+
+ // Reset all interrupt status bits
+ data[0] = BNO055_SYS_TRIGGER_ADDR;
+ data[1] = 0x01; // Reset interrupt status
+ // 0x05 = Reset system
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+
+ // Set to normal power mode
+ data[0] = BNO055_PWR_MODE_ADDR;
+ data[1] = 0x00; // Normal power mode
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+ sleep_ms(50); // Wait 50ms for the sensor to switch to normal power mode
+
+ // Page 25 of the datasheet
+ // Default Axis Config
+ data[0] = BNO055_AXIS_MAP_CONFIG_ADDR;
+ data[1] = 0x24; // P1=Z, P2=Y, P3=X
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+
+ // Default Axis Sign
+ data[0] = BNO055_AXIS_MAP_SIGN_ADDR;
+ data[1] = 0x00; // P1=Positive, P2=Positive, P3=Positive
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+
+ // Set units to m/s^2
+ data[0] = BNO055_UNIT_SEL_ADDR;
+ data[1] = 0x00; // Windows, Celsius, Degrees, DPS, m/s^2
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, true);
+ sleep_ms(30);
+
+ //The default operation mode after power-on is CONFIGMODE
+ // Set mode to NDOF
+ // Takes 7ms to switch from CONFIG mode; see page 21 on datasheet (3.3)
+ data[0] = BNO055_OPR_MODE_ADDR;
+ data[1] = default_mode; // NDOF
+ i2c_write_blocking(i2c_default, bno055_address, data, 2, false);
+ sleep_ms(100);
+
+
+}
+
+void BNO055::read_calib_status() {
+ uint8_t calib_stat_reg = BNO055_CALIB_STAT_ADDR;
+ uint8_t calib_stat[1];
+ i2c_write_blocking(i2c_default, bno055_address, &calib_stat_reg, 1, true);
+ i2c_read_blocking(i2c_default, bno055_address, calib_stat, 1, false);
+ calib_status.mag = ((calib_stat[0] & 0b00000011) >> 0);
+ calib_status.accel = ((calib_stat[0] & 0b00001100) >> 2);
+ calib_status.gyro = ((calib_stat[0] & 0b00110000) >> 4);
+ calib_status.sys = ((calib_stat[0] & 0b11000000) >> 6);
+}
+
+void BNO055::read_lin_accel() {
+ uint8_t lin_accel_reg = BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR;
+ i2c_write_blocking(i2c_default, bno055_address, &lin_accel_reg, 1, true);
+ i2c_read_blocking(i2c_default, bno055_address, accel, 6, false);
+ int16_t x, y, z;
+ x = y = z = 0;
+ x = ((int16_t)accel[0]) | (((int16_t)accel[1]) << 8);
+ y = ((int16_t)accel[2]) | (((int16_t)accel[3]) << 8);
+ z = ((int16_t)accel[4]) | (((int16_t)accel[5]) << 8);
+ linear_acceleration.x = ((float)x) / 100.0;
+ linear_acceleration.y = ((float)y) / 100.0;
+ linear_acceleration.z = ((float)z) / 100.0;
+}
+
+void BNO055::clamp_close_zero(volatile float &val) {
+ if (val < 0.01 && val > -0.01) {
+ val = 0.0;
+ }
+}
+
+void BNO055::accel_to_gravity() {
+ accel_gravity.x = abs_lin_accel.x / 9.81;
+ accel_gravity.y = abs_lin_accel.y / 9.81;
+ accel_gravity.z = abs_lin_accel.z / 9.81;
+}
+
+void BNO055::read_abs_quaternion() {
+ uint8_t quat_reg = BNO055_QUATERNION_DATA_W_LSB_ADDR;
+ i2c_write_blocking(i2c_default, bno055_address, &quat_reg, 1, true);
+ i2c_read_blocking(i2c_default, bno055_address, quat, 8, false);
+ int16_t w, x, y, z;
+ w = x = y = z = 0;
+ w = ((int16_t)quat[0]) | (((int16_t)quat[1]) << 8);
+ x = ((int16_t)quat[2]) | (((int16_t)quat[3]) << 8);
+ y = ((int16_t)quat[4]) | (((int16_t)quat[5]) << 8);
+ z = ((int16_t)quat[6]) | (((int16_t)quat[7]) << 8);
+ abs_quaternion.w = ((float)w) / 16384.0; // 2^14 LSB
+ abs_quaternion.x = ((float)x) / 16384.0;
+ abs_quaternion.y = ((float)y) / 16384.0;
+ abs_quaternion.z = ((float)z) / 16384.0;
+}
+
+void BNO055::read_euler_angles() {
+ uint8_t euler[6];
+ uint8_t euler_reg = BNO055_EULER_H_LSB_ADDR;
+ i2c_write_blocking(i2c_default, bno055_address, &euler_reg, 1, true);
+ i2c_read_blocking(i2c_default, bno055_address, euler, 6, false);
+ /// @note heading = yaw
+ int16_t heading, roll, pitch;
+ heading = roll = pitch = 0;
+ heading = ((int16_t)euler[0]) | (((int16_t)euler[1]) << 8);
+ roll = ((int16_t)euler[2]) | (((int16_t)euler[3]) << 8);
+ pitch = ((int16_t)euler[4]) | (((int16_t)euler[5]) << 8);
+ euler_angles.x = ((float)roll) / 16.0;
+ euler_angles.y = ((float)pitch) / 16.0;
+ euler_angles.z = ((float)heading) / 16.0;
+}
+
+void BNO055::read_accel() {
+ uint8_t accel[6];
+ uint8_t accel_reg = BNO055_ACCEL_DATA_X_LSB_ADDR;
+ i2c_write_blocking(i2c_default, bno055_address, &accel_reg, 1, true);
+ i2c_read_blocking(i2c_default, bno055_address, accel, 6, false);
+ int16_t x, y, z;
+ x = y = z = 0;
+ x = ((int16_t)accel[0]) | (((int16_t)accel[1]) << 8);
+ y = ((int16_t)accel[2]) | (((int16_t)accel[3]) << 8);
+ z = ((int16_t)accel[4]) | (((int16_t)accel[5]) << 8);
+ acceleration.x = ((float)x) / 100.0;
+ acceleration.y = ((float)y) / 100.0;
+ acceleration.z = ((float)z) / 100.0;
+}
+
+void BNO055::quaternion_to_euler() {
+ Eigen::Quaternion<float> q;
+ q.w() = abs_quaternion.w;
+ q.x() = abs_quaternion.x;
+ q.y() = abs_quaternion.y;
+ q.z() = abs_quaternion.z;
+ q.normalize();
+ Eigen::Matrix3f m = q.toRotationMatrix();
+ euler_angles.x = atan2f(m(2,1), m(2,2));
+ euler_angles.y = asinf(-m(2,0));
+ euler_angles.z = atan2f(m(1,0), m(0,0));
+}
+
+void BNO055::calculate_abs_linear_acceleration() {
+ Eigen::Quaternion<float> q;
+ q.w() = abs_quaternion.w;
+ q.x() = abs_quaternion.x;
+ q.y() = abs_quaternion.y;
+ q.z() = abs_quaternion.z;
+ // q.normalize();
+ Eigen::Matrix3f rotation_matrix = q.toRotationMatrix();
+ Eigen::Vector3f lin_accel;
+ lin_accel.x() = linear_acceleration.x;
+ lin_accel.y() = linear_acceleration.y;
+ lin_accel.z() = linear_acceleration.z;
+ abs_lin_accel.x = lin_accel.x() * rotation_matrix(0, 0) + lin_accel.y() * rotation_matrix(0, 1) + lin_accel.z() * rotation_matrix(0, 2);
+ abs_lin_accel.y = lin_accel.x() * rotation_matrix(1, 0) + lin_accel.y() * rotation_matrix(1, 1) + lin_accel.z() * rotation_matrix(1, 2);
+ abs_lin_accel.z = -1.0f * (lin_accel.x() * rotation_matrix(2, 0) + lin_accel.y() * rotation_matrix(2, 1) + lin_accel.z() * rotation_matrix(2, 2));
+}
+
+void BNO055::get_rotation_vector() {
+ Eigen::Quaternion<float> q;
+ q.w() = abs_quaternion.w;
+ q.x() = abs_quaternion.x;
+ q.y() = abs_quaternion.y;
+ q.z() = abs_quaternion.z;
+ q.normalize();
+ Eigen::Matrix3f rotation_matrix = q.toRotationMatrix();
+ rot_y_vec.x = rotation_matrix(1, 0);
+ rot_y_vec.y = rotation_matrix(1, 1);
+ rot_y_vec.z = rotation_matrix(1, 2);
+}
+
diff --git a/src/kalmanfilter.cpp b/src/kalmanfilter.cpp
index 735e8c8..8174513 100644
--- a/src/kalmanfilter.cpp
+++ b/src/kalmanfilter.cpp
@@ -1,3 +1,4 @@
+#include <climits>
#include "../include/kalmanfilter.hpp"
// Private----------------------------------------------------------------------
@@ -25,7 +26,7 @@ void KalmanFilter::matrixInit() {
process_noise_covariance(1,1) = 0.1;
// Setup Measurement Covariance
- measurement_covariance << 0.1;
+ measurement_covariance << 1e-12;
}
diff --git a/src/pwm.cpp b/src/pwm.cpp
new file mode 100644
index 0000000..3a78d23
--- /dev/null
+++ b/src/pwm.cpp
@@ -0,0 +1,35 @@
+#include "pwm.hpp"
+
+void PWM::init() {
+ // Tell GPIO 0 they are allocated to the PWM
+ gpio_set_function(SERVO_PIN, GPIO_FUNC_PWM);
+ // Find out which PWM slice is connected to GPIO 0 (it's slice 0)
+ uint slice_num = pwm_gpio_to_slice_num(SERVO_PIN);
+
+ // Configure PWM slice and set it running
+ pwm_config cfg = pwm_get_default_config();
+ // Set the PWM clock divider to 38.15
+ pwm_config_set_clkdiv(&cfg, CLOCK_DIV_RATE);
+ // Set the PWM wrap value to 65535
+ pwm_set_wrap(slice_num, WRAP_VALUE);
+ // Set the PWM duty cycle to 0 and enable the PWM
+ pwm_init(slice_num, &cfg, true);
+
+ // Enable the PWM again cause idk
+ pwm_set_enabled(slice_num, true);
+}
+
+void PWM::set_duty_cycle(int duty_cycle_percent) {
+ // Calculate the raw value
+ uint32_t raw_value = WRAP_VALUE * (duty_cycle_percent / 100.0);
+
+ // Set the duty cycle
+ pwm_set_gpio_level(SERVO_PIN, raw_value);
+}
+
+void PWM::set_servo_percent(int percent) {
+ // Calculate the value by clamping the percent from 0 to 100
+ // to the SERVO_MIN and SERVO_MAX
+ uint32_t value = ((percent * SERVO_RANGE) / 100) + SERVO_MIN;
+ PWM::set_duty_cycle(value);
+}
diff --git a/src/read_flash.c b/src/read_flash.c
new file mode 100644
index 0000000..71d2870
--- /dev/null
+++ b/src/read_flash.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <inttypes.h>
+#include "boards/pico_w.h"
+#include "hardware/spi.h"
+#include "spi_flash.h"
+
+int main() {
+ stdio_init_all();
+ getchar();
+ // Enable SPI 0 at 1 MHz and connect to GPIOs
+ spi_init(spi_default, 1000 * 1000 * 60);
+ gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
+
+ // Chip select is active-low, so we'll initialise it to a driven-high state
+ gpio_init(PICO_DEFAULT_SPI_CSN_PIN);
+ gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT);
+ gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
+
+ uint8_t entry[PACKET_SIZE];
+
+ // flash_erase(spi_default, PICO_DEFAULT_SPI_CSN_PIN);
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, base_addr, page_buffer, FLASH_PAGE_SIZE);
+ for (uint16_t i = 0; i < FLASH_PAGE_SIZE; i += PACKET_SIZE) {
+ if (page_buffer[i] == 0xFF) {
+ base_addr += i;
+ break;
+ }
+ if ((i + PACKET_SIZE) == FLASH_PAGE_SIZE) {
+ base_addr += FLASH_PAGE_SIZE;
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, base_addr, page_buffer, FLASH_PAGE_SIZE);
+ i = 0;
+ }
+ }
+
+ printf("\nRead Data:\n");
+ printf("time (us) | state | dep pcnt | alt (m) | vel (m/s) | quat_w | quat_x | quat_y | quat_z | lin_ax | lin_ay | lin_az\n");
+ for (uint32_t i = 0; i < base_addr; i += PACKET_SIZE) {
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, i, entry, PACKET_SIZE);
+ uint64_t now_us = (((uint64_t)entry[0] << 56) | ((uint64_t)entry[1] << 48) | \
+ ((uint64_t)entry[2] << 40) | ((uint64_t)entry[3] << 32) | \
+ ((uint64_t)entry[4] << 24) | ((uint64_t)entry[5] << 16) | \
+ ((uint64_t)entry[6] << 8) | ((uint64_t)entry[7]));
+
+ uint8_t state = entry[8];
+ uint8_t deploy_percent = entry[9];
+
+ uint32_t alt_bits = (entry[10] << 24) | (entry[11] << 16) | (entry[12] << 8) | (entry[13]);
+ uint32_t vel_bits = (entry[14] << 24) | (entry[15] << 16) | (entry[16] << 8) | (entry[17]);
+ float altitude = *(float *)(&alt_bits);
+ float velocity = *(float *)(&vel_bits);
+
+ int16_t w = ((int16_t)entry[18]) | (((int16_t)entry[19]) << 8);
+ int16_t x = ((int16_t)entry[20]) | (((int16_t)entry[21]) << 8);
+ int16_t y = ((int16_t)entry[22]) | (((int16_t)entry[23]) << 8);
+ int16_t z = ((int16_t)entry[24]) | (((int16_t)entry[25]) << 8);
+ float qw = ((float)w) / 16384.0; // 2^14 LSB
+ float qx = ((float)x) / 16384.0;
+ float qy = ((float)y) / 16384.0;
+ float qz = ((float)z) / 16384.0;
+ int16_t ax = ((int16_t)entry[26]) | (((int16_t)entry[27]) << 8);
+ int16_t ay = ((int16_t)entry[28]) | (((int16_t)entry[29]) << 8);
+ int16_t az = ((int16_t)entry[30]) | (((int16_t)entry[31]) << 8);
+ float lax = ((float)x) / 100.0;
+ float lay = ((float)y) / 100.0;
+ float laz = ((float)z) / 100.0;
+ printf("%"PRIu64" | %c | %"PRIu8" | %4.2f | %4.2f | %4.2f | %4.2f| %4.2f | %4.2f | %4.2f | %4.2f |%4.2f\n", \
+ now_us, state, deploy_percent, altitude, velocity, qw, qx, qy, qz, lax, lay, laz);
+ }
+}
diff --git a/src/servo_test.cpp b/src/servo_test.cpp
new file mode 100644
index 0000000..c5e8e6e
--- /dev/null
+++ b/src/servo_test.cpp
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <inttypes.h>
+#include "pico/stdio.h"
+#include "pwm.hpp"
+
+#define MOSFET_PIN 1
+
+PWM pwm;
+
+int main() {
+ stdio_init_all();
+ // Initialize MOSFET
+ gpio_init(MOSFET_PIN);
+ gpio_set_dir(MOSFET_PIN, GPIO_OUT);
+ gpio_put(MOSFET_PIN, 1);
+ pwm.init();
+ uint8_t duty_cycle = 13;
+ while (1) {
+ getchar();
+ if (duty_cycle == 2) {
+ duty_cycle = 13;
+ }
+ pwm.set_duty_cycle(duty_cycle);
+ printf("Currenty Duty Cycle: %" PRIu8 "\n", duty_cycle);
+ duty_cycle--;
+ }
+}
diff --git a/src/spi_flash.c b/src/spi_flash.c
new file mode 100644
index 0000000..32babc5
--- /dev/null
+++ b/src/spi_flash.c
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+// Example of reading/writing an external serial flash using the PL022 SPI interface
+
+#include "spi_flash.h"
+
+static inline void cs_select(uint cs_pin) {
+ asm volatile("nop \n nop \n nop"); // FIXME
+ gpio_put(cs_pin, 0);
+ asm volatile("nop \n nop \n nop"); // FIXME
+}
+
+static inline void cs_deselect(uint cs_pin) {
+ asm volatile("nop \n nop \n nop"); // FIXME
+ gpio_put(cs_pin, 1);
+ asm volatile("nop \n nop \n nop"); // FIXME
+}
+
+void __not_in_flash_func(flash_read)(spi_inst_t *spi, uint cs_pin, uint32_t addr, uint8_t* dest, size_t len) {
+ cs_select(cs_pin);
+ uint8_t cmdbuf[4] = {
+ FLASH_CMD_READ,
+ addr >> 16,
+ addr >> 8,
+ addr
+ };
+ spi_write_blocking(spi, cmdbuf, 4);
+ spi_read_blocking(spi, 0, dest, len);
+ cs_deselect(cs_pin);
+}
+
+void __not_in_flash_func(flash_write_enable)(spi_inst_t *spi, uint cs_pin) {
+ cs_select(cs_pin);
+ uint8_t cmd = FLASH_CMD_WRITE_EN;
+ spi_write_blocking(spi, &cmd, 1);
+ cs_deselect(cs_pin);
+}
+
+void __not_in_flash_func(flash_wait_done)(spi_inst_t *spi, uint cs_pin) {
+ uint8_t status;
+ do {
+ cs_select(cs_pin);
+ uint8_t buf[2] = {FLASH_CMD_STATUS, 0};
+ spi_write_read_blocking(spi, buf, buf, 2);
+ cs_deselect(cs_pin);
+ status = buf[1];
+ } while (status & FLASH_STATUS_BUSY_MASK);
+}
+
+void __not_in_flash_func(flash_sector_erase)(spi_inst_t *spi, uint cs_pin, uint32_t addr) {
+ uint8_t cmdbuf[4] = {
+ FLASH_CMD_SECTOR_ERASE,
+ addr >> 16,
+ addr >> 8,
+ addr
+ };
+ flash_write_enable(spi, cs_pin);
+ cs_select(cs_pin);
+ spi_write_blocking(spi, cmdbuf, 4);
+ cs_deselect(cs_pin);
+ flash_wait_done(spi, cs_pin);
+}
+
+void __not_in_flash_func(flash_block_erase)(spi_inst_t *spi, uint cs_pin, uint32_t addr) {
+ uint8_t cmdbuf[4] = {
+ FLASH_CMD_BLOCK_ERASE,
+ addr >> 16,
+ addr >> 8,
+ addr
+ };
+ flash_write_enable(spi, cs_pin);
+ cs_select(cs_pin);
+ spi_write_blocking(spi, cmdbuf, 4);
+ cs_deselect(cs_pin);
+ flash_wait_done(spi, cs_pin);
+}
+
+
+void __not_in_flash_func(flash_erase)(spi_inst_t *spi, uint cs_pin) {
+ uint8_t cmdbuf[1] = {
+ FLASH_CMD_CHIP_ERASE
+ };
+ flash_write_enable(spi, cs_pin);
+ cs_select(cs_pin);
+ spi_write_blocking(spi, cmdbuf, 1);
+ cs_deselect(cs_pin);
+ flash_wait_done(spi, cs_pin);
+}
+
+void __not_in_flash_func(flash_page_program)(spi_inst_t *spi, uint cs_pin, uint32_t addr, uint8_t* src) {
+ uint8_t cmdbuf[4] = {
+ FLASH_CMD_PAGE_PROGRAM,
+ addr >> 16,
+ addr >> 8,
+ addr
+ };
+ flash_write_enable(spi, cs_pin);
+ cs_select(cs_pin);
+ spi_write_blocking(spi, cmdbuf, 4);
+ spi_write_blocking(spi, src, FLASH_PAGE_SIZE);
+ cs_deselect(cs_pin);
+ flash_wait_done(spi, cs_pin);
+}
+
+void __not_in_flash_func(flash_write)(spi_inst_t *spi, uint cs_pin, uint32_t addr, uint8_t* src, size_t size) {
+ uint8_t cmdbuf[4] = {
+ FLASH_CMD_PAGE_PROGRAM,
+ addr >> 16,
+ addr >> 8,
+ addr
+ };
+ flash_write_enable(spi, cs_pin);
+ cs_select(cs_pin);
+ spi_write_blocking(spi, cmdbuf, 4);
+ spi_write_blocking(spi, src, size);
+ cs_deselect(cs_pin);
+ flash_wait_done(spi, cs_pin);
+}
+
+void write_entry(uint8_t* data_entry) {
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, base_addr, page_buffer, FLASH_PAGE_SIZE);
+ for (uint16_t i = 0; i < FLASH_PAGE_SIZE; i += PACKET_SIZE) {
+ if (page_buffer[i] == 0xFF) {
+ base_addr += i;
+ break;
+ }
+ if ((i + PACKET_SIZE) == FLASH_PAGE_SIZE) {
+ base_addr += FLASH_PAGE_SIZE;
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, base_addr, page_buffer, FLASH_PAGE_SIZE);
+ i = 0;
+ }
+ }
+ flash_write(spi_default, PICO_DEFAULT_SPI_CSN_PIN, base_addr, data_entry, PACKET_SIZE);
+ base_addr += PACKET_SIZE;
+}
+
+
+#ifdef FLASH_TEST
+ #include "pico/multicore.h"
+ #include <stdint.h>
+ #include <inttypes.h>
+ #include <stdio.h>
+
+
+ void printbuf(uint8_t buf[FLASH_PAGE_SIZE]) {
+ for (int i = 0; i < FLASH_PAGE_SIZE; ++i) {
+ if (i % 16 == 15)
+ printf("%02x\n", buf[i]);
+ else
+ printf("%02x ", buf[i]);
+ }
+ }
+
+
+
+ void core1_entry() {
+ printf("SPI flash example\n");
+
+ // Enable SPI 0 at 1 MHz and connect to GPIOs
+ spi_init(spi_default, 1000 * 1000 * 60);
+ gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
+ gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
+
+ // Chip select is active-low, so we'll initialise it to a driven-high state
+ gpio_init(PICO_DEFAULT_SPI_CSN_PIN);
+ gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT);
+ gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
+
+ // flash_erase(spi_default, PICO_DEFAULT_SPI_CSN_PIN);
+ // flash_sector_erase(spi_default, PICO_DEFAULT_SPI_CSN_PIN, 0);
+ printf("Before program:\n");
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, 0, page_buffer, FLASH_PAGE_SIZE);
+ printbuf(page_buffer);
+
+ uint8_t entry[PACKET_SIZE];
+
+ printf("Written Data:\n");
+ printf("time (us)\t|\tstate\t|\tdep pcnt\t|\talt (m)\t|\tvel (m/s)\t|\tempty\n");
+ for (uint16_t i = 0; i < 500; i++) {
+ absolute_time_t now = get_absolute_time();
+ uint64_t now_us= to_us_since_boot(now);
+ float altitude = 10.0f * i;
+ float velocity = 5.0f * i;
+ uint8_t deploy_percent = (i*1000) / 200;
+ printf("%"PRIu64"\t|\t%"PRIu8"\t|\t%"PRIu8"\t|\t%4.2f\t|\t%4.2f\t|\tDAWSYN_SCHRAIB\n", now_us, (uint8_t)(i), deploy_percent, altitude, velocity);
+ uint32_t alt_bits = *((uint32_t *)&altitude);
+ uint32_t vel_bits = *((uint32_t *)&velocity);
+ entry[0] = now_us >> 56;
+ entry[1] = now_us >> 48;
+ entry[2] = now_us >> 40;
+ entry[3] = now_us >> 32;
+ entry[4] = now_us >> 24;
+ entry[5] = now_us >> 16;
+ entry[6] = now_us >> 8;
+ entry[7] = now_us;
+ entry[8] = i;
+ entry[9] = deploy_percent;
+ entry[10] = alt_bits >> 24;
+ entry[11] = alt_bits >> 16;
+ entry[12] = alt_bits >> 8;
+ entry[13] = alt_bits;
+ entry[14] = vel_bits >> 24;
+ entry[15] = vel_bits >> 16;
+ entry[16] = vel_bits >> 8;
+ entry[17] = vel_bits;
+ entry[18] = 'D';
+ entry[19] = 'A';
+ entry[20] = 'W';
+ entry[21] = 'S';
+ entry[22] = 'Y';
+ entry[23] = 'N';
+ entry[24] = '_';
+ entry[25] = 'S';
+ entry[26] = 'C';
+ entry[27] = 'H';
+ entry[28] = 'R';
+ entry[29] = 'A';
+ entry[30] = 'I';
+ entry[31] = 'B';
+ write_entry(entry);
+ }
+
+ printf("After program:\n");
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, 0, page_buffer, FLASH_PAGE_SIZE);
+ printbuf(page_buffer);
+
+ printf("\nRead Data:\n");
+ printf("time (us)\t|\tstate\t|\tdep pcnt\t|\talt (m)\t|\tvel (m/s)\t|\tempty\n");
+ for (uint32_t i = 0; i < base_addr; i += PACKET_SIZE) {
+ flash_read(spi_default, PICO_DEFAULT_SPI_CSN_PIN, i, entry, PACKET_SIZE);
+ uint64_t now_us = (((uint64_t)entry[0] << 56) | ((uint64_t)entry[1] << 48) | \
+ ((uint64_t)entry[2] << 40) | ((uint64_t)entry[3] << 32) | \
+ ((uint64_t)entry[4] << 24) | ((uint64_t)entry[5] << 16) | \
+ ((uint64_t)entry[6] << 8) | ((uint64_t)entry[7]));
+
+ uint8_t state = entry[8];
+ uint8_t deploy_percent = entry[9];
+
+ uint32_t alt_bits = (entry[10] << 24) | (entry[11] << 16) | (entry[12] << 8) | (entry[13]);
+ uint32_t vel_bits = (entry[14] << 24) | (entry[15] << 16) | (entry[16] << 8) | (entry[17]);
+ float altitude = *(float *)(&alt_bits);
+ float velocity = *(float *)(&vel_bits);
+ printf("%"PRIu64"\t|\t%"PRIu8"\t|\t%"PRIu8"\t|\t%4.2f\t|\t%4.2f\t|\t%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", \
+ now_us, state, deploy_percent, altitude, velocity, \
+ entry[18],entry[19],entry[20],entry[21],entry[22],entry[23],entry[24],entry[25],entry[26],entry[27],entry[28],entry[29],entry[30],entry[31]);
+ }
+
+ }
+
+ int main() {
+ // Enable UART so we can print status output
+ stdio_init_all();
+ getchar();
+
+ multicore_launch_core1(core1_entry);
+
+ while (1) {
+ tight_loop_contents();
+ }
+
+ }
+#endif
diff --git a/src/unused/actuationPlan.cpp b/src/unused/actuationPlan.cpp
new file mode 100644
index 0000000..a987478
--- /dev/null
+++ b/src/unused/actuationPlan.cpp
@@ -0,0 +1,60 @@
+#include "../include/actuationPlan.hpp"
+
+ActuationPlan::ActuationPlan() {}
+
+ActuationPlan::ActuationPlan(SurfaceFitModel sFitModel) : sFitModel(sFitModel) {
+
+}
+
+
+void ActuationPlan::runPlan(Vehicle& rocket) {
+
+
+ if (rocket.imuReadFail || rocket.altiReadFail) {
+ rocket.deployment_angle = deploy_percentage_to_angle(0); // No fin deployment
+ }
+
+ rocket.fail_time = time(nullptr);
+
+ // 2024 Mission---------------------------------------------------------------------
+ if (rocket.status == GLIDE) {
+
+ // Fin deployment based on current drag coefficient value
+ try {
+ double cd = sFitModel.getFit(rocket.filtered_velocity, rocket.filtered_altitude);
+ cd = std::min(std::max(0.0, cd), 100.0);
+ rocket.deployment_angle = deploy_percentage_to_angle(cd);
+ }
+
+ // Full deployment during coasting
+ catch (...) {
+ rocket.deployment_angle = deploy_percentage_to_angle(0);
+
+ if ((time(nullptr) - rocket.deploy_time) > 2 && (time(nullptr) - rocket.deploy_time) < 7) {
+ rocket.deployment_angle = deploy_percentage_to_angle(100);
+ }
+ }
+ }
+
+ else if (rocket.status == APOGEE) {
+
+ rocket.deployment_angle = deploy_percentage_to_angle(50);
+ }
+
+ else {
+
+ rocket.deploy_time = time(nullptr);
+ }
+ // End 2024 Mission------------------------------------------------------------------
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/unused/ads.cpp b/src/unused/ads.cpp
new file mode 100644
index 0000000..5484970
--- /dev/null
+++ b/src/unused/ads.cpp
@@ -0,0 +1,286 @@
+#include "../include/ads.hpp"
+
+
+// Private----------------------------------------------------------------------
+void ADS::logSummary() {
+
+ std::string output_string = "" + state_for_log[rocket.status];
+
+ if (!rocket.altiInitFail && !rocket.altiReadFail) {
+
+ output_string += format_data(" ", rocket.filtered_altitude, 3);
+ }
+
+ output_string += format_data(" ", rocket.deployment_angle, 2);
+
+ if (!rocket.imuInitFail && !rocket.imuReadFail) {
+
+ output_string += format_data(" ", rocket.acceleration[2], 2);
+ output_string += format_data(" ", rocket.filtered_velocity, 2);
+ }
+
+ Logger::Get().log(output_string);
+}
+
+
+void ADS::updateOnPadAltitude() {
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+ double avg_alt = 0;
+ double alt_read_count = 0;
+
+ while (alt_read_count < COUNT_LIMIT) {
+
+ altimeter.getData(&rocket.current_altitude);
+ alt_read_count++;
+ avg_alt = (avg_alt * (alt_read_count - 1) + rocket.current_altitude) / alt_read_count;
+ }
+
+ Logger::Get().log(format_data("pad altitude initialization complete - ", avg_alt, 3));
+ rocket.ON_PAD_altitude = avg_alt;
+}
+
+
+void ADS::updateSensorData() {
+
+ if (!rocket.imuInitFail) {
+
+ try {
+ imu.getData((void*)&rocket);
+ }
+
+ catch (...) {
+ std::exception_ptr e = std::current_exception();
+ Logger::Get().logErr(e.__cxa_exception_type()->name());
+ rocket.imuReadFail = true;
+ }
+ }
+
+ rocket.previous_altitude = rocket.current_altitude; // Why was this placed here????
+
+ if (!rocket.altiInitFail) {
+
+ try {
+ altimeter.getData((void*)&rocket.current_altitude);
+ if (rocket.ON_PAD_fail) {
+ rocket.ON_PAD_altitude = rocket.current_altitude;
+ rocket.ON_PAD_fail = false;
+ }
+
+ rocket.altiReadFail = false;
+ }
+
+ catch (...) {
+ std::exception_ptr e = std::current_exception();
+ Logger::Get().logErr(e.__cxa_exception_type()->name());
+ rocket.altiReadFail = true;
+ }
+ }
+}
+
+
+void ADS::updateRocketState() {
+
+ // Filter sensor data
+ VectorXf control_input(1);
+ VectorXf measurement(1);
+ control_input << rocket.acceleration[2];
+ measurement << rocket.current_altitude;
+ VectorXf filtered = kf.run(control_input, measurement, rocket.dt);
+ rocket.filtered_altitude = filtered(0);
+ rocket.filtered_velocity = filtered(1);
+
+ if (rocket.apogee_altitude < rocket.filtered_altitude) {
+ rocket.apogee_altitude = rocket.filtered_altitude;
+ }
+
+ // (VEHICLE ON PAD)
+ if (rocket.status == ON_PAD) {
+
+ // If launch detected
+ if (rocket.acceleration[2] >= BOOST_ACCEL_THRESH * G_0
+ && rocket.filtered_altitude >= BOOST_HEIGHT_THRESH + rocket.ON_PAD_altitude) {
+ Logger::Get().log(format_data("LOM at -- ", (double)(rocket.liftoff_time - rocket.start_time), 3));
+ }
+
+ if (TEST_MODE && time(nullptr) - rocket.start_time >= 15) {
+ Logger::Get().log(format_data("TEST LOM at -- ", (double)(rocket.liftoff_time - rocket.start_time), 3));
+ }
+
+ if (time(nullptr) - rocket.relog_time > 2*60*60
+ && rocket.status == ON_PAD) {
+ std::cout << "OverWR Success" << std::endl;
+ }
+ }
+
+ // (VEHICLE BOOSTING)
+ else if (rocket.status == BOOST) {
+
+ if (rocket.acceleration[2] <= GLIDE_ACCEL_THRESH * G_0
+ || time(nullptr) - rocket.liftoff_time >= TIME_BO) {
+ rocket.status = GLIDE;
+ }
+
+ }
+
+ // (VEHICLE IN GLIDE)
+ else if (rocket.status == GLIDE) {
+
+ if (rocket.filtered_altitude < rocket.apogee_altitude - APOGEE_FSM_CHANGE
+ || time(nullptr) - rocket.liftoff_time >= TIME_BO + TIME_APO) {
+ rocket.status = APOGEE;
+ Logger::Get().log(format_data("APO: ", (double)(rocket.apogee_altitude), 2));
+ }
+ }
+
+ // (VEHICLE AT APOGEE)
+ else if (rocket.status == APOGEE) {
+
+ if (rocket.filtered_altitude <= FSM_DONE_SURFACE_ALTITUDE + rocket.ON_PAD_altitude) {
+ rocket.status = DONE;
+ return;
+ }
+ }
+}
+
+
+// Public----------------------------------------------------------------------
+ADS::ADS(ActuationPlan plan) : plan(plan) {
+
+ rocket.status = ON_PAD;
+
+ rocket.apogee_altitude = 0;
+ rocket.previous_altitude = 0;
+ rocket.current_altitude = 0;
+ rocket.filtered_altitude = 0;
+
+ rocket.filtered_velocity = 0;
+
+ rocket.duty_span = DUTY_MAX - DUTY_MIN;
+ rocket.deployment_angle = deploy_percentage_to_angle(INIT_DEPLOYMENT);
+
+ rocket.dt = 0.1;
+
+ rocket.imuInitFail = false;
+ rocket.imuReadFail = false;
+ rocket.altiInitFail = false;
+ rocket.altiReadFail = false;
+
+ rocket.ON_PAD_altitude = 0;
+ rocket.ON_PAD_fail = false;
+
+ rocket.start_time = time(nullptr);
+ rocket.fail_time = rocket.start_time;
+ rocket.relog_time = rocket.start_time;
+ rocket.led_time = rocket.start_time;
+
+ imu = IMUSensor();
+ altimeter = AltimeterSensor();
+ motor = Motor();
+ kf = KalmanFilter(2, 1, 1, rocket.dt);
+
+ Logger::Get().openLog(LOG_FILENAME);
+
+ motor.init(&rocket);
+
+ imu.init(nullptr);
+ altimeter.init(nullptr);
+
+ if (TEST_MODE) {
+
+ Logger::Get().log("TEST Record Start --");
+ }
+}
+
+
+
+void ADS::run() {
+
+ if (!rocket.altiInitFail) {
+ try {
+ updateOnPadAltitude();
+ }
+
+ catch (...) {
+ std::exception_ptr e = std::current_exception();
+ Logger::Get().logErr(e.__cxa_exception_type()->name());
+ rocket.ON_PAD_fail = true;
+ }
+ }
+
+ rocket.loop_time = time(nullptr);
+ while (rocket.status != DONE) {
+
+ updateSensorData();
+
+ if (!rocket.imuInitFail && !rocket.altiInitFail) {
+
+ updateRocketState();
+
+ // Run the Actuation Plan----------------------------------
+ plan.runPlan(rocket);
+
+ if (rocket.imuReadFail || rocket.altiReadFail) {
+
+ if (rocket.imuReadFail) {
+ imu.init(nullptr); // Restart
+ Logger::Get().log("Altimeter reset attempt");
+ }
+
+ if (rocket.altiReadFail) {
+ altimeter.init(nullptr); // Restart
+ Logger::Get().log("IMU reset attempt");
+ }
+ }
+ }
+
+ // Altimeter or IMU setup failed. Attempt to reinitialize
+ else {
+
+ if (time(nullptr) - rocket.fail_time >= TIME_END) {
+ rocket.status = DONE;
+ }
+
+ if (rocket.altiInitFail || rocket.altiReadFail) {
+ imu.init(nullptr); // Restart
+ Logger::Get().log("Altimeter reset attempt");
+ }
+
+ if (rocket.imuInitFail || rocket.imuReadFail) {
+ altimeter.init(nullptr); // Restart
+ Logger::Get().log("IMU reset attempt");
+ }
+
+ rocket.deployment_angle = deploy_percentage_to_angle(INIT_DEPLOYMENT);
+ }
+
+ // Actuate Servos
+ motor.writeData(&rocket);
+
+ logSummary();
+
+ // Blink Beaglebone LED 1
+ if (time(nullptr) - rocket.led_time > LED_GAP_TIME) {
+ led_out(&rocket);
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ rocket.dt = time(nullptr) - rocket.loop_time;
+ rocket.loop_time = time(nullptr);
+ }
+
+ Logger::Get().closeLog();
+ std::cout << "Done" << std::endl;
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/unused/logger.cpp b/src/unused/logger.cpp
new file mode 100644
index 0000000..a857be8
--- /dev/null
+++ b/src/unused/logger.cpp
@@ -0,0 +1,132 @@
+#include "../include/logger.hpp"
+
+// Private----------------------------------------------------------------------
+std::string Logger::getDate() {
+
+ t = time(nullptr);
+ now = localtime(&t);
+ return "(" + days[now->tm_wday] + " " + months[now->tm_mon] + " "
+ + std::to_string(now->tm_mday) + " " + std::to_string(now->tm_year + 1900) + ")";
+}
+
+std::string Logger::getTime() {
+
+ t = time(nullptr);
+ now = localtime(&t);
+ std::string hour = std::to_string(now->tm_hour);
+ std::string min = std::to_string(now->tm_min);
+ std::string sec = std::to_string(now->tm_sec);
+ //string hour = "0" + to_string(now->tm_hour);
+
+ if (now->tm_hour < 10) {
+ hour = "0" + std::to_string(now->tm_hour);
+ }
+
+ if (now->tm_min < 10) {
+ min = "0" + std::to_string(now->tm_min);
+ }
+
+ if (now->tm_sec < 10) {
+ sec = "0" + std::to_string(now->tm_sec);
+ }
+
+ return hour + ":" + min +
+ ":" + sec;
+}
+
+
+// Public----------------------------------------------------------------------
+Logger& Logger::Get() {
+
+ static Logger loggerSingleton;
+ return loggerSingleton;
+}
+
+//Logger Logger::loggerSingleton;
+
+
+bool Logger::openLog(std::string _filename) {
+
+ filename = _filename;
+
+ if (file_open) {
+ return false;
+ }
+
+ file.open(filename, std::ios::in | std::ios::out | std::ios::app);
+
+ if (!file) {
+ return false;
+ }
+
+ file_open = true;
+ std::string date = getDate();
+ std::string timestamp = getTime();
+ file << timestamp << infoTag << "Log Start---- " << date << std::endl;
+
+ return true;
+}
+
+
+void Logger::closeLog() {
+
+ std::string timestamp = getTime();
+ file << timestamp << infoTag << "Log End----\n\n";
+
+ file.close();
+ file_open = false;
+}
+
+
+bool Logger::log(std::string data) {
+
+ if (!file) {
+ return false;
+ }
+
+ if (!file_open) {
+ return false;
+ }
+ std::string timestamp = getTime();
+ file << timestamp << infoTag << data << std::endl;
+ return true;
+}
+
+bool Logger::logErr(std::string data) {
+
+ if (!file) {
+ return false;
+ }
+
+ if (!file_open) {
+ return false;
+ }
+
+ std::string timestamp = getTime();
+ file << timestamp << errorTag << data << std::endl;
+ return true;
+}
+
+
+bool Logger::printLog() {
+
+ if (file.is_open()) {
+ std::cout << "Log still open. Please close Log." << std::endl;
+ return false;
+ }
+
+ file.open(filename, std::ios::in);
+
+ if (!file.is_open()) {
+ return false;
+ }
+
+ std::string line;
+ while(getline(file, line)) {
+ std::cout << line << std::endl;
+ }
+
+ file.close();
+
+ return true;
+}
diff --git a/src/unused/motor.cpp b/src/unused/motor.cpp
new file mode 100644
index 0000000..84785a9
--- /dev/null
+++ b/src/unused/motor.cpp
@@ -0,0 +1,46 @@
+#include "../include/motor.hpp"
+
+
+
+Motor::Motor() {
+
+
+}
+
+bool Motor::init(void* data) {
+
+ Vehicle *vehicle = (Vehicle *) data;
+ double duty = 100 - ((MIN_ANGLE / 180) * vehicle->duty_span + DUTY_MIN);
+
+ // Initialize stuff
+ // .....
+ // .....
+
+
+ data = (void*) vehicle; // Is this necessary?
+ return true;
+}
+
+
+bool Motor::writeData(void* data) {
+
+ Vehicle *vehicle = (Vehicle *) data;
+ double duty = 100 - ((vehicle->deployment_angle / 180) * vehicle->duty_span + DUTY_MIN);
+
+ // Send the Data somewhere
+ // ..... Pin
+ // ..... Duty
+ // ..... PWM frequency Hz
+ // ..... Polarity
+
+
+ if (1 == 2) {
+ Logger::Get().logErr("Some type of Error");
+ return false;
+ }
+
+ data = (void*) vehicle; // Is this necessary?
+ return true;
+}
+
+
diff --git a/src/unused/rocketUtils.cpp b/src/unused/rocketUtils.cpp
new file mode 100644
index 0000000..45fcfc3
--- /dev/null
+++ b/src/unused/rocketUtils.cpp
@@ -0,0 +1,35 @@
+#include "../include/rocketUtils.hpp"
+
+double deploy_percentage_to_angle(double percentage) {
+
+ return (MAX_ANGLE - MIN_ANGLE) / 100.0 * percentage + MIN_ANGLE;
+}
+
+
+std::string format_data(std::string prefix, double data, int precision) {
+
+ std::stringstream stream;
+ stream << std::fixed << std::setprecision(precision) << data;
+ std::string s = stream.str();
+ return prefix + s;
+}
+
+bool led_out(Vehicle *vehicle) {
+
+ std::ofstream file;
+ file.open(LED_FILENAME);
+ if (!file.is_open()) {
+ return false;
+ }
+
+ file << std::to_string(vehicle->led_brightness);
+ file.close();
+
+ vehicle->led_time = time(nullptr);
+ vehicle->led_brightness = (vehicle->led_brightness + 1) % 2;
+
+ return true;
+}
+
+std::string state_for_log[5] = {"ON_PAD", "BOOST", "GLIDE", "APOGEE", "DONE"};
+ \ No newline at end of file
diff --git a/src/unused/sensorAltimeter.cpp b/src/unused/sensorAltimeter.cpp
new file mode 100644
index 0000000..8ec065d
--- /dev/null
+++ b/src/unused/sensorAltimeter.cpp
@@ -0,0 +1,115 @@
+#include "sensorAltimeter.hpp"
+
+AltimeterSensor::AltimeterSensor(std::string I2C_FILE_in) {
+ I2C_FILE = I2C_FILE_in;
+ deviceAddress = 0x60;
+}
+
+//Startup routine copied from Adafruit library, as is most of the data getting methods
+//Adaptation is largely editing for readability and porting from Adafruit_I2C to BBB I2C (sensorI2C.hpp implementation)
+bool AltimeterSensor::init() {
+
+ // Vehicle *vehicle = (Vehicle *) data;
+ // // Do Stuff
+ // data = (void*) vehicle;
+
+ //Pass file string from parent to setup function, actual I2C bus gets stored internally.
+ setupI2C(I2C_FILE);
+
+ // Check a register with a hard-coded value to see if comms are working
+ uint8_t whoami = readSingleRegister(MPL3115A2_WHOAMI);
+ if (whoami != 0xC4) {
+ fprintf(stderr, "MPL INITIALIZATION DID NOT PASS WHOAMI DEVICE CHECK!, got: %X, expected: 0xC4\n", whoami);
+ return false;
+ }
+
+ //Send device dedicated reset byte to CTRL1 Register
+ writeRegister(MPL3115A2_CTRL_REG1, MPL3115A2_CTRL_REG1_RST);
+ //Wait for reset to wipe its way through device and reset appropriate bit of CTRL1 Register
+ while (readSingleRegister(MPL3115A2_CTRL_REG1) & MPL3115A2_CTRL_REG1_RST);
+
+ //Set oversampling (?) and altitude mode by default
+ currentMode = MPL3115A2_ALTIMETER;
+ ctrl_reg1.reg = MPL3115A2_CTRL_REG1_OS128 | MPL3115A2_CTRL_REG1_ALT;
+ writeRegister(MPL3115A2_CTRL_REG1, ctrl_reg1.reg);
+
+ //Configure data return types, I don't really understand this chunk but Adafruit does it this way so we will too I guess
+ writeRegister(MPL3115A2_PT_DATA_CFG, MPL3115A2_PT_DATA_CFG_TDEFE |
+ MPL3115A2_PT_DATA_CFG_PDEFE |
+ MPL3115A2_PT_DATA_CFG_DREM);
+
+ return true;
+}
+
+//EXPECTED THAT USER WILL NEVER SET MODE TO PRESSURE AFTER INITIAL CONFIGURATION
+void AltimeterSensor::setMode(mpl3115a2_mode_t mode) {
+ ctrl_reg1.reg = readSingleRegister(MPL3115A2_CTRL_REG1);
+ ctrl_reg1.bit.ALT = mode;
+ writeRegister(MPL3115A2_CTRL_REG1, ctrl_reg1.reg);
+ currentMode = mode;
+}
+
+double AltimeterSensor::getAltitude() {
+ //Request new data reading
+ requestOneShotReading();
+ //If new data is available, read it and store it to internal fields
+ if (isNewDataAvailable()) {
+ //Logger flag here for new data?
+ updateCurrentDataBuffer();
+ }
+ //Return internal field, whether updated or not
+ return internalAltitude;
+}
+
+double AltimeterSensor::getTemperature() {
+ //Request new data reading
+ requestOneShotReading();
+ //If new data is available, read it and store it to internal fields
+ if (isNewDataAvailable()) {
+ //Logger flag here for new data?
+ updateCurrentDataBuffer();
+ }
+ //Return internal field, whether updated or not
+ return internalTemperature;
+}
+
+void AltimeterSensor::requestOneShotReading() {
+ //Request current status of oneshot reading
+ ctrl_reg1.reg = readSingleRegister(MPL3115A2_CTRL_REG1);
+ //If oneshot is complete, proc a new one; if it isn't, do nothing.
+ //THIS PRODUCES DUPLICATE DATA IF READING REQUESTS FROM BB DON'T LINE UP WITH READING COMPLETION ON SENSOR.
+ if (!ctrl_reg1.bit.OST) {
+ // initiate one-shot measurement
+ ctrl_reg1.bit.OST = 1;
+ writeRegister(MPL3115A2_CTRL_REG1, ctrl_reg1.reg);
+ }
+}
+
+bool AltimeterSensor::isNewDataAvailable() {
+ //Returns PTDR bit of status register, 1 if new data for Temp OR Alt/Pres is available
+ //There *are* registers available for exclusively temperature *or* pressure/altitude, but
+ //for simplicity's sake we'll use the combined one for now.
+ return ((readSingleRegister(MPL3115A2_REGISTER_STATUS) & MPL3115A2_REGISTER_STATUS_PTDR) != 0);
+}
+
+//Adafruit returns specific field based on input parameter, this method updates all internal fields at once instead
+void AltimeterSensor::updateCurrentDataBuffer() {
+ uint8_t buffer[5] = {MPL3115A2_REGISTER_PRESSURE_MSB, 0, 0, 0, 0};
+ readMultipleRegisters(MPL3115A2_REGISTER_PRESSURE_MSB, 5);
+
+ //Pressure is no longer used, assumed rocket is only logging altitude
+ // uint32_t pressure;
+ // pressure = uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 |
+ // uint32_t(buffer[2]);
+ // return double(pressure) / 6400.0;
+
+ //Altitude Conversion
+ int32_t alt;
+ alt = uint32_t(buffer[0]) << 24 | uint32_t(buffer[1]) << 16 |
+ uint32_t(buffer[2]) << 8;
+ internalAltitude = double(alt) / 65536.0;
+
+ int16_t t;
+ t = uint16_t(buffer[3]) << 8 | uint16_t(buffer[4]);
+ internalTemperature = double(t) / 256.0;
+}
diff --git a/src/unused/sensorIMU.cpp b/src/unused/sensorIMU.cpp
new file mode 100644
index 0000000..941ea35
--- /dev/null
+++ b/src/unused/sensorIMU.cpp
@@ -0,0 +1,385 @@
+#include "../include/sensorIMU.hpp"
+
+IMUSensor::IMUSensor(std::string I2C_FILE) {
+ this -> I2C_FILE = I2C_FILE;
+}
+
+bool IMUSensor::init(void* data) {
+
+ //I2C_File passed on object creation, stored in sensorI2C parent
+ setupI2C(I2C_FILE);
+
+ //In the adafruit code there's a big step of waiting for timeout and connection stuff for up to a full second
+ //I don't do that here because the BBB takes like 17 years to boot so we'll just hope it goes faster than that
+
+ //Sanity check for factory device ID
+ uint8_t id = readSingleRegister(BNO055_CHIP_ID_ADDR);
+ if (id != BNO055_ID) {
+ fprintf(stderr, "DEVICE ID DID NOT PASS SANITY CHECK FOR BNO IMU!");
+ return false;
+ }
+
+ //Set default operating mode of IMU into config from startup (will be set properly after config phase)
+ setModeHard(OPERATION_MODE_CONFIG);
+
+ //Writes 1 to the system reset bit in the trigger register
+ writeRegister(BNO055_SYS_TRIGGER_ADDR, 0x20);
+ //Wait for reset to complete by doing sanity check again
+ while (readSingleRegister(BNO055_CHIP_ID_ADDR) != BNO055_ID);
+
+ //Set power mode for sensor
+ writeRegister(BNO055_PWR_MODE_ADDR, POWER_MODE_NORMAL);
+
+ //Sensor chip uses two "pages" to multiplex register values
+ //Page 0 contains the sensor data (not configuration), which is what we want
+ writeRegister(BNO055_PAGE_ID_ADDR, 0);
+
+ //Genuinely no idea why Adafruit does this, ensuring all triggers are off before mode config I guess
+ writeRegister(BNO055_SYS_TRIGGER_ADDR, 0x0);
+
+ setModeTemp(default_mode);
+
+ return true;
+}
+
+//Sets mode so it can be undone for temporary changes, like operation setting
+void IMUSensor::setModeTemp(adafruit_bno055_opmode_t mode) {
+ currentMode = mode;
+ writeRegister(BNO055_OPR_MODE_ADDR, currentMode);
+}
+
+//Sets mode *AND* internal state variable
+void IMUSensor::setModeTemp(adafruit_bno055_opmode_t mode) {
+ writeRegister(BNO055_OPR_MODE_ADDR, currentMode);
+}
+
+adafruit_bno055_opmode_t IMUSensor::getMode() {
+ return (adafruit_bno055_opmode_t)readSingleRegister(BNO055_OPR_MODE_ADDR);
+}
+
+imu::Vector<3> IMUSensor::getVector(adafruit_vector_type_t vector_type) {
+ imu::Vector<3> xyz;
+ uint8_t buffer[6] = readMultipleRegisters((adafruit_bno055_reg_t)vector_type, 6);
+
+ int16_t x, y, z;
+ x = y = z = 0;
+
+ /* Read vector data (6 bytes) */
+ x = ((int16_t)buffer[0]) | (((int16_t)buffer[1]) << 8);
+ y = ((int16_t)buffer[2]) | (((int16_t)buffer[3]) << 8);
+ z = ((int16_t)buffer[4]) | (((int16_t)buffer[5]) << 8);
+
+ /*!
+ * Convert the value to an appropriate range (section 3.6.4)
+ * and assign the value to the Vector type
+ */
+ switch (vector_type) {
+ case VECTOR_MAGNETOMETER:
+ /* 1uT = 16 LSB */
+ xyz[0] = ((double)x) / 16.0;
+ xyz[1] = ((double)y) / 16.0;
+ xyz[2] = ((double)z) / 16.0;
+ break;
+ case VECTOR_GYROSCOPE:
+ /* 1dps = 16 LSB */
+ xyz[0] = ((double)x) / 16.0;
+ xyz[1] = ((double)y) / 16.0;
+ xyz[2] = ((double)z) / 16.0;
+ break;
+ case VECTOR_EULER:
+ /* 1 degree = 16 LSB */
+ xyz[0] = ((double)x) / 16.0;
+ xyz[1] = ((double)y) / 16.0;
+ xyz[2] = ((double)z) / 16.0;
+ break;
+ case VECTOR_ACCELEROMETER:
+ /* 1m/s^2 = 100 LSB */
+ xyz[0] = ((double)x) / 100.0;
+ xyz[1] = ((double)y) / 100.0;
+ xyz[2] = ((double)z) / 100.0;
+ break;
+ case VECTOR_LINEARACCEL:
+ /* 1m/s^2 = 100 LSB */
+ xyz[0] = ((double)x) / 100.0;
+ xyz[1] = ((double)y) / 100.0;
+ xyz[2] = ((double)z) / 100.0;
+ break;
+ case VECTOR_GRAVITY:
+ /* 1m/s^2 = 100 LSB */
+ xyz[0] = ((double)x) / 100.0;
+ xyz[1] = ((double)y) / 100.0;
+ xyz[2] = ((double)z) / 100.0;
+ break;
+ }
+
+ return xyz;
+}
+imu::Quaternion IMUSensor::getQuat() {
+ uint8_t buffer[8] = readMultipleRegisters(BNO055_QUATERNION_DATA_W_LSB_ADDR, 8);
+
+ int16_t x, y, z, w;
+ x = y = z = w = 0;
+
+ //Bit shift data into the right places and store it
+ w = (((uint16_t)buffer[1]) << 8) | ((uint16_t)buffer[0]);
+ x = (((uint16_t)buffer[3]) << 8) | ((uint16_t)buffer[2]);
+ y = (((uint16_t)buffer[5]) << 8) | ((uint16_t)buffer[4]);
+ z = (((uint16_t)buffer[7]) << 8) | ((uint16_t)buffer[6]);
+
+ /*!
+ * Assign to Quaternion
+ * See
+ * https://cdn-shop.adafruit.com/datasheets/BST_BNO055_DS000_12.pdf
+ * 3.6.5.5 Orientation (Quaternion)
+ */
+ const double scale = (1.0 / (1 << 14));
+ imu::Quaternion quat(scale * w, scale * x, scale * y, scale * z);
+ return quat;
+}
+
+int8_t IMUSensor::getTemp() {
+ int8_t temp = (int8_t)(readSingleRegister(BNO055_TEMP_ADDR));
+ return temp;
+}
+
+void IMUSensor::setAxisRemap(adafruit_bno055_axis_remap_config_t remapcode) {
+ //Put into proper config for mapping stuff
+ setModeTemp(OPERATION_MODE_CONFIG);
+ writeRegister(BNO055_AXIS_MAP_CONFIG_ADDR, remapcode);
+
+ //Return mode to operating mode
+ setModeTemp(currentMode);
+}
+
+void IMUSensor::setAxisSign(adafruit_bno055_axis_remap_sign_t remapsign) {
+ //See above method, pretty much the exact same
+ setModeTemp(OPERATION_MODE_CONFIG);
+ writeRegister(BNO055_AXIS_MAP_SIGN_ADDR, remapsign);
+ setModeTemp(currentMode);
+}
+
+//This method is weird; it intakes several existing byte pointers to see what action it should take. Luckily, we shouldn't have to use it.
+void IMUSensor::getSystemStatus(uint8_t *system_status, uint8_t *self_test_result, uint8_t *system_error) {
+ //Make sure IMU is on proper register page to get system status
+ writeRegister(BNO055_PAGE_ID_ADDR, 0);
+
+ //If system status requested, read the status.
+ if (system_status != 0) *system_status = readSingleRegister(BNO055_SYS_STAT_ADDR);
+ //If self test result requested, pull the self test results.
+ if (self_test_result != 0) *self_test_result = readSingleRegister(BNO055_SELFTEST_RESULT_ADDR);
+ //Finally, if there's an error pull and stash it.
+ if (system_error != 0) *system_error = readSingleRegister(BNO055_SYS_ERR_ADDR);
+}
+
+//Same as above method, byte pointers are fed into it as parameters that get populated by method.
+void IMUSensor::getCalibration(uint8_t *sys, uint8_t *gyro, uint8_t *accel, uint8_t *mag) {
+ uint8_t calData = readSingleRegister(BNO055_CALIB_STAT_ADDR);
+ if (sys != NULL) {
+ *sys = (calData >> 6) & 0x03;
+ }
+ if (gyro != NULL) {
+ *gyro = (calData >> 4) & 0x03;
+ }
+ if (accel != NULL) {
+ *accel = (calData >> 2) & 0x03;
+ }
+ if (mag != NULL) {
+ *mag = calData & 0x03;
+ }
+}
+
+/* Functions to deal with raw calibration data */
+bool IMUSensor::getSensorOffsets(uint8_t *calibData) {
+ if (isFullyCalibrated()) {
+ setModeTemp(OPERATION_MODE_CONFIG);
+
+ calibData = readMultipleRegisters(ACCEL_OFFSET_X_LSB_ADDR, NUM_BNO055_OFFSET_REGISTERS);
+
+ setModeTemp(currentMode);
+ return true;
+ }
+ return false;
+}
+
+//Fully populated offset getter using type of offset, not just calibration data
+bool IMUSensor::getSensorOffsets(adafruit_bno055_offsets_t &offsets_type) {
+ if (isFullyCalibrated()) {
+ setModeTemp(OPERATION_MODE_CONFIG);
+
+ /* Accel offset range depends on the G-range:
+ +/-2g = +/- 2000 mg
+ +/-4g = +/- 4000 mg
+ +/-8g = +/- 8000 mg
+ +/-1§g = +/- 16000 mg */
+ offsets_type.accel_offset_x = (readSingleRegister(ACCEL_OFFSET_X_MSB_ADDR) << 8) |
+ (readSingleRegister(ACCEL_OFFSET_X_LSB_ADDR));
+ offsets_type.accel_offset_y = (readSingleRegister(ACCEL_OFFSET_Y_MSB_ADDR) << 8) |
+ (readSingleRegister(ACCEL_OFFSET_Y_LSB_ADDR));
+ offsets_type.accel_offset_z = (readSingleRegister(ACCEL_OFFSET_Z_MSB_ADDR) << 8) |
+ (readSingleRegister(ACCEL_OFFSET_Z_LSB_ADDR));
+
+ /* Magnetometer offset range = +/- 6400 LSB where 1uT = 16 LSB */
+ offsets_type.mag_offset_x =
+ (readSingleRegister(MAG_OFFSET_X_MSB_ADDR) << 8) | (readSingleRegister(MAG_OFFSET_X_LSB_ADDR));
+ offsets_type.mag_offset_y =
+ (readSingleRegister(MAG_OFFSET_Y_MSB_ADDR) << 8) | (readSingleRegister(MAG_OFFSET_Y_LSB_ADDR));
+ offsets_type.mag_offset_z =
+ (readSingleRegister(MAG_OFFSET_Z_MSB_ADDR) << 8) | (readSingleRegister(MAG_OFFSET_Z_LSB_ADDR));
+
+ /* Gyro offset range depends on the DPS range:
+ 2000 dps = +/- 32000 LSB
+ 1000 dps = +/- 16000 LSB
+ 500 dps = +/- 8000 LSB
+ 250 dps = +/- 4000 LSB
+ 125 dps = +/- 2000 LSB
+ ... where 1 DPS = 16 LSB */
+ offsets_type.gyro_offset_x =
+ (readSingleRegister(GYRO_OFFSET_X_MSB_ADDR) << 8) | (readSingleRegister(GYRO_OFFSET_X_LSB_ADDR));
+ offsets_type.gyro_offset_y =
+ (readSingleRegister(GYRO_OFFSET_Y_MSB_ADDR) << 8) | (readSingleRegister(GYRO_OFFSET_Y_LSB_ADDR));
+ offsets_type.gyro_offset_z =
+ (readSingleRegister(GYRO_OFFSET_Z_MSB_ADDR) << 8) | (readSingleRegister(GYRO_OFFSET_Z_LSB_ADDR));
+
+ /* Accelerometer radius = +/- 1000 LSB */
+ offsets_type.accel_radius =
+ (readSingleRegister(ACCEL_RADIUS_MSB_ADDR) << 8) | (readSingleRegister(ACCEL_RADIUS_LSB_ADDR));
+
+ /* Magnetometer radius = +/- 960 LSB */
+ offsets_type.mag_radius =
+ (readSingleRegister(MAG_RADIUS_MSB_ADDR) << 8) | (readSingleRegister(MAG_RADIUS_LSB_ADDR));
+
+ setModeTemp(currentMode);
+ return true;
+ }
+ return false;
+}
+
+void IMUSensor::setSensorOffsets(const uint8_t *calibData) {
+ setModeTemp(OPERATION_MODE_CONFIG);
+
+ /* Note: Configuration will take place only when user writes to the last
+ byte of each config data pair (ex. ACCEL_OFFSET_Z_MSB_ADDR, etc.).
+ Therefore the last byte must be written whenever the user wants to
+ changes the configuration. */
+
+ /* A writeLen() would make this much cleaner */
+ writeRegister(ACCEL_OFFSET_X_LSB_ADDR, calibData[0]);
+ writeRegister(ACCEL_OFFSET_X_MSB_ADDR, calibData[1]);
+ writeRegister(ACCEL_OFFSET_Y_LSB_ADDR, calibData[2]);
+ writeRegister(ACCEL_OFFSET_Y_MSB_ADDR, calibData[3]);
+ writeRegister(ACCEL_OFFSET_Z_LSB_ADDR, calibData[4]);
+ writeRegister(ACCEL_OFFSET_Z_MSB_ADDR, calibData[5]);
+
+ writeRegister(MAG_OFFSET_X_LSB_ADDR, calibData[6]);
+ writeRegister(MAG_OFFSET_X_MSB_ADDR, calibData[7]);
+ writeRegister(MAG_OFFSET_Y_LSB_ADDR, calibData[8]);
+ writeRegister(MAG_OFFSET_Y_MSB_ADDR, calibData[9]);
+ writeRegister(MAG_OFFSET_Z_LSB_ADDR, calibData[10]);
+ writeRegister(MAG_OFFSET_Z_MSB_ADDR, calibData[11]);
+
+ writeRegister(GYRO_OFFSET_X_LSB_ADDR, calibData[12]);
+ writeRegister(GYRO_OFFSET_X_MSB_ADDR, calibData[13]);
+ writeRegister(GYRO_OFFSET_Y_LSB_ADDR, calibData[14]);
+ writeRegister(GYRO_OFFSET_Y_MSB_ADDR, calibData[15]);
+ writeRegister(GYRO_OFFSET_Z_LSB_ADDR, calibData[16]);
+ writeRegister(GYRO_OFFSET_Z_MSB_ADDR, calibData[17]);
+
+ writeRegister(ACCEL_RADIUS_LSB_ADDR, calibData[18]);
+ writeRegister(ACCEL_RADIUS_MSB_ADDR, calibData[19]);
+
+ writeRegister(MAG_RADIUS_LSB_ADDR, calibData[20]);
+ writeRegister(MAG_RADIUS_MSB_ADDR, calibData[21]);
+
+ setModeTemp(currentMode);
+}
+
+void IMUSensor::setSensorOffsets(const adafruit_bno055_offsets_t &offsets_type) {
+ setModeTemp(OPERATION_MODE_CONFIG);
+
+ /* Note: Configuration will take place only when user writes to the last
+ byte of each config data pair (ex. ACCEL_OFFSET_Z_MSB_ADDR, etc.).
+ Therefore the last byte must be written whenever the user wants to
+ changes the configuration. */
+
+ writeRegister(ACCEL_OFFSET_X_LSB_ADDR, (offsets_type.accel_offset_x) & 0x0FF);
+ writeRegister(ACCEL_OFFSET_X_MSB_ADDR, (offsets_type.accel_offset_x >> 8) & 0x0FF);
+ writeRegister(ACCEL_OFFSET_Y_LSB_ADDR, (offsets_type.accel_offset_y) & 0x0FF);
+ writeRegister(ACCEL_OFFSET_Y_MSB_ADDR, (offsets_type.accel_offset_y >> 8) & 0x0FF);
+ writeRegister(ACCEL_OFFSET_Z_LSB_ADDR, (offsets_type.accel_offset_z) & 0x0FF);
+ writeRegister(ACCEL_OFFSET_Z_MSB_ADDR, (offsets_type.accel_offset_z >> 8) & 0x0FF);
+
+ writeRegister(MAG_OFFSET_X_LSB_ADDR, (offsets_type.mag_offset_x) & 0x0FF);
+ writeRegister(MAG_OFFSET_X_MSB_ADDR, (offsets_type.mag_offset_x >> 8) & 0x0FF);
+ writeRegister(MAG_OFFSET_Y_LSB_ADDR, (offsets_type.mag_offset_y) & 0x0FF);
+ writeRegister(MAG_OFFSET_Y_MSB_ADDR, (offsets_type.mag_offset_y >> 8) & 0x0FF);
+ writeRegister(MAG_OFFSET_Z_LSB_ADDR, (offsets_type.mag_offset_z) & 0x0FF);
+ writeRegister(MAG_OFFSET_Z_MSB_ADDR, (offsets_type.mag_offset_z >> 8) & 0x0FF);
+
+ writeRegister(GYRO_OFFSET_X_LSB_ADDR, (offsets_type.gyro_offset_x) & 0x0FF);
+ writeRegister(GYRO_OFFSET_X_MSB_ADDR, (offsets_type.gyro_offset_x >> 8) & 0x0FF);
+ writeRegister(GYRO_OFFSET_Y_LSB_ADDR, (offsets_type.gyro_offset_y) & 0x0FF);
+ writeRegister(GYRO_OFFSET_Y_MSB_ADDR, (offsets_type.gyro_offset_y >> 8) & 0x0FF);
+ writeRegister(GYRO_OFFSET_Z_LSB_ADDR, (offsets_type.gyro_offset_z) & 0x0FF);
+ writeRegister(GYRO_OFFSET_Z_MSB_ADDR, (offsets_type.gyro_offset_z >> 8) & 0x0FF);
+
+ writeRegister(ACCEL_RADIUS_LSB_ADDR, (offsets_type.accel_radius) & 0x0FF);
+ writeRegister(ACCEL_RADIUS_MSB_ADDR, (offsets_type.accel_radius >> 8) & 0x0FF);
+
+ writeRegister(MAG_RADIUS_LSB_ADDR, (offsets_type.mag_radius) & 0x0FF);
+ writeRegister(MAG_RADIUS_MSB_ADDR, (offsets_type.mag_radius >> 8) & 0x0FF);
+
+ setModeTemp(currentMode);
+
+}
+
+bool IMUSensor::isFullyCalibrated() {
+ uint8_t system, gyro, accel, mag;
+ getCalibration(&system, &gyro, &accel, &mag);
+
+ switch (currentMode) {
+ case OPERATION_MODE_ACCONLY:
+ return (accel == 3);
+ case OPERATION_MODE_MAGONLY:
+ return (mag == 3);
+ case OPERATION_MODE_GYRONLY:
+ case OPERATION_MODE_M4G: /* No magnetometer calibration required. */
+ return (gyro == 3);
+ case OPERATION_MODE_ACCMAG:
+ case OPERATION_MODE_COMPASS:
+ return (accel == 3 && mag == 3);
+ case OPERATION_MODE_ACCGYRO:
+ case OPERATION_MODE_IMUPLUS:
+ return (accel == 3 && gyro == 3);
+ case OPERATION_MODE_MAGGYRO:
+ return (mag == 3 && gyro == 3);
+ default:
+ return (system == 3 && gyro == 3 && accel == 3 && mag == 3);
+ }
+}
+
+/* Power managments functions */
+void IMUSensor::enterSuspendMode() {
+ /* Switch to config mode (just in case since this is the default) */
+ setModeTemp(OPERATION_MODE_CONFIG);
+ writeRegister(BNO055_PWR_MODE_ADDR, 0x02);
+ /* Set the requested operating mode (see section 3.3) */
+ setModeTemp(currentMode);
+}
+
+void IMUSensor::enterNormalMode() {
+ /* Switch to config mode (just in case since this is the default) */
+ setModeTemp(OPERATION_MODE_CONFIG);
+ writeRegister(BNO055_PWR_MODE_ADDR, 0x00);
+ /* Set the requested operating mode (see section 3.3) */
+ setModeTemp(modeback);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/unused/surfaceFitModel.cpp b/src/unused/surfaceFitModel.cpp
new file mode 100644
index 0000000..d48da49
--- /dev/null
+++ b/src/unused/surfaceFitModel.cpp
@@ -0,0 +1,40 @@
+#include "../include/surfaceFitModel.hpp"
+
+SurfaceFitModel::SurfaceFitModel() {
+
+ p = MatrixXd::Zero(X_DEGREE + 1, Y_DEGREE + 1);
+
+ p(0, 0) = -781536.384794701;
+ p(1, 0) = 8623.59011973048;
+ p(0, 1) = 643.65918253;
+ p(2, 0) = -34.3646691281487;
+ p(1, 1) = -5.46066535343611;
+ p(0, 2) = -0.177121900557321;
+ p(3, 0) = 0.0573287698655951;
+ p(2, 1) = 0.0150031142038895;
+ p(1, 2) = 0.00101871763126609;
+ p(0, 3) = 1.63862900553892e-05;
+ p(4, 0) = -3.21785828407871e-05;
+ p(3, 1) = -1.3161091180883e-05;
+ p(2, 2) = -1.42505256569339e-06;
+ p(1, 3) = -4.76209793830867e-08;
+}
+
+
+double SurfaceFitModel::getFit(double x, double y) {
+
+ return p(0, 0) + p(1, 0) * x + p(0, 1) * y + p(2, 0) * pow(x, 2) +
+ p(1, 1) * x * y + p(0, 2) * pow(y, 2) + p(3, 0) * pow(x, 3) +
+ p(2, 1) * pow(x, 2) * y + p(1, 2) * x * pow(y, 2) + p(0, 3) * pow(y, 3) +
+ p(4, 0) * pow(x, 4) + p(3, 1) * pow(x, 3) * y + p(2, 2) * pow(x, 2) * pow(y, 2) +
+ p(1, 3) * x * pow(y, 3);
+}
+
+
+
+
+
+
+
+
+
diff --git a/test/kalmanFilterTest.cpp b/test/kalmanFilterTest.cpp
index e3dc1e4..bb0acda 100644
--- a/test/kalmanFilterTest.cpp
+++ b/test/kalmanFilterTest.cpp
@@ -1,6 +1,6 @@
#include <iostream>
#include <gtest/gtest.h>
-#include "eigen3/Eigen/Dense"
+#include <Eigen/Dense>
#include "../include/kalmanfilter.hpp"
using namespace Eigen;