diff options
Diffstat (limited to 'src/active_drag_system.cpp')
| -rw-r--r-- | src/active_drag_system.cpp | 1131 |
1 files changed, 770 insertions, 361 deletions
diff --git a/src/active_drag_system.cpp b/src/active_drag_system.cpp index 6f5d57e..ce898bb 100644 --- a/src/active_drag_system.cpp +++ b/src/active_drag_system.cpp @@ -1,436 +1,845 @@ -#include "cyw43_configport.h" -#include "pico/multicore.h" -#include "pico/platform.h" -#include "pico/sem.h" -#include "boards/pico_w.h" +#include <pico.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + #include "hardware/gpio.h" #include "hardware/i2c.h" #include "hardware/adc.h" +#include <hardware/timer.h> +#include "pico/rand.h" #include "pico/stdlib.h" +#include "pico/stdio.h" +#include "pico/multicore.h" #include "pico/time.h" -#include "pico/types.h" -#include "pico/cyw43_arch.h" -#include <inttypes.h> -#include <math.h> +#include <pico/error.h> +#include <pico/types.h> +#include "math.h" + +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "portmacro.h" +#include "projdefs.h" +#include "serial.hpp" +#include "task.h" +#include "semphr.h" + +extern "C" { +#include <fix16.h> +#include <fixmatrix.h> +#include <fixkalman.h> +} +#include "adxl375.hpp" +#include "ms5607.hpp" +#include "iim42653.hpp" +#include "mmc5983ma.hpp" #include "pwm.hpp" -#include "imu.hpp" -#include "altimeter.hpp" -#include "kalman_filter.hpp" -#include "spi_flash.h" - -#define MPL3115A2_ADDR 0x60 - -#define BNO055_ADDR 0x28 -#define BNO055_ID 0xA0 - -#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 LOG_RATE_HZ 8 -#define HEART_RATE_HZ 5 - -#define MOTOR_BURN_TIME 3900 // Burn time in milliseconds for M2500T - -#define PAD_SECONDS 8 -#define PAD_BUFFER_SIZE (PACKET_SIZE * LOG_RATE_HZ * PAD_SECONDS) - -typedef enum { - PAD = 0, - BOOST, - COAST, - APOGEE, - RECOVERY, - END -} state_t; +#include "pico_logger.h" +#include "log_format.hpp" +#include "serial.hpp" +#include "heartbeat.hpp" + +/****************************** FREERTOS **************************************/ +#define SENSOR_EVENT_HANDLER_PRIORITY ( tskIDLE_PRIORITY + 7 ) +#define SENSOR_SAMPLE_PRIORITY ( tskIDLE_PRIORITY + 6 ) +#define KALMAN_TASK_PRIORITY ( tskIDLE_PRIORITY + 6 ) +#define ROCKET_TASK_PRIORITY ( tskIDLE_PRIORITY + 4 ) +#define ROCKET_EVENT_HANDLER_PRIORITY ( tskIDLE_PRIORITY + 3 ) +#define HEARTBEAT_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define LOGGING_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define SERIAL_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +void vApplicationTickHook(void) { /* optional */ } +void vApplicationMallocFailedHook(void) { /* optional */ } +void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { for( ;; ); } + +static void logging_task(void* pvParameters); + +#define ROCKET_TASK_RATE_HZ 50 +volatile TaskHandle_t rocket_task_handle = NULL; +static void rocket_task(void* pvParameters); + +#define KALMAN_TASK_RATE_HZ 50 +volatile TaskHandle_t kalman_task_handle = NULL; +static void kalman_task(void* pvParameters); + +int64_t launch_event_callback(alarm_id_t id, void* user_data); +int64_t coast_event_callback(alarm_id_t id, void* user_data); +int64_t end_event_callback(alarm_id_t id, void* user_data); + +volatile TaskHandle_t launch_event_handler_task_handle = NULL; +volatile TaskHandle_t coast_event_handler_task_handle = NULL; +volatile TaskHandle_t end_event_handler_task_handle = NULL; +static void launch_event_handler_task(void* pvParameters); +static void coast_event_handler_task(void* pvParameters); +static void end_event_handler_task(void* pvParameters); + +/****************************** FREERTOS **************************************/ + +/****************************** SERIAL CONSOLE ********************************/ +static void read_cmd_func(); +static void write_cmd_func(); +static void erase_cmd_func(); +static void show_cmd_func(); +static void deploy_cmd_func(); +static void kalman_cmd_func(); + +const char* executeable_name = "active-drag-system.uf2"; +const size_t num_user_cmds = 6; +const command_t user_commands[] = { {.name = "read", + .len = 4, + .function = &read_cmd_func}, + {.name = "write", + .len = 5, + .function = &write_cmd_func}, + {.name = "erase", + .len = 5, + .function = &erase_cmd_func}, + {.name = "show", + .len = 4, + .function = &show_cmd_func}, + {.name = "deploy", + .len = 6, + .function = &deploy_cmd_func}, + {.name = "kalman", + .len = 6, + .function = &kalman_cmd_func} }; +/****************************** SERIAL CONSOLE ********************************/ + +/****************************** LOGGING ***************************************/ +volatile bool use_circular_buffer = true; +volatile TaskHandle_t logging_handle = NULL; +volatile log_entry_t log_entry; + +Logger logger(PACKET_SIZE, LOG_BASE_ADDR, print_log_entry); + +static void populate_log_entry(log_entry_t* log_entry); +/****************************** LOGGING ***************************************/ + +volatile bool serial_data_output = false; + +MS5607 alt(i2c_default); +ADXL375 adxl375(i2c_default); +IIM42653 iim42653(i2c_default); +MMC5983MA mmc5983ma(i2c_default); PWM pwm; -kalman_filter *kf; -VectorXf control(1); -VectorXf measurement(1); -VectorXf res(2); - -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); -float get_deploy_percent(float velocity, float altitude); - -bool timer_callback(repeating_timer_t *rt); - -void populate_entry(); -bool logging_buffer_callback(repeating_timer_t *rt); -bool logging_flash_callback(repeating_timer_t *rt); -bool heartbeat_callback(repeating_timer_t *rt); -void logging_core(); - -semaphore_t sem; - -volatile float 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 uint8_t led_counter; -volatile uint32_t pad_buffer_offset = 0; -Eigen::Vector3f linear_acceleration; -Eigen::Vector4f quaternion; -Eigen::Vector3f euler_angles; - -volatile calibration_status_t calib_status; - -repeating_timer_t data_timer; -repeating_timer_t log_timer; -repeating_timer_t heartbeat_timer; - -float ground_altitude = 0.0f; +#define MOTOR_BURN_TIME 6200 +volatile state_t rocket_state = PAD; +volatile uint8_t deployment_percent = 0; -altimeter altimeter(i2c_default, MPL3115A2_ADDR); -imu imu(i2c_default, BNO055_ADDR, BNO055_ID, NDOF); +volatile int32_t ground_altitude = 0; +volatile int32_t threshold_altitude = 30; + +/****************************** KALMAN ****************************************/ +#define KALMAN_NAME verticality +#define KALMAN_NUM_STATES 2 +#define KALMAN_NUM_INPUTS 1 +kalman16_t kf; + +#define KALMAN_MEASUREMENT_NAME altitude +#define KALMAN_NUM_MEASUREMENTS 1 +kalman16_observation_t kfm; + +#define matrix_set(matrix, row, column, value) \ + matrix->data[row][column] = value + +#define matrix_set_symmetric(matrix, row, column, value) \ + matrix->data[row][column] = value; \ + matrix->data[column][row] = value + +#ifndef FIXMATRIX_MAX_SIZE +#error FIXMATRIX_MAX_SIZE must be defined and greater or equal to the number of states, inputs and measurements. +#endif + +#if (FIXMATRIX_MAX_SIZE < KALMAN_NUM_STATES) || (FIXMATRIX_MAX_SIZE < KALMAN_NUM_INPUTS) || (FIXMATRIX_MAX_SIZE < KALMAN_NUM_MEASUREMENTS) +#error FIXMATRIX_MAX_SIZE must be greater or equal to the number of states, inputs and measurements. +#endif + +static void kalman_verticality_init() { + /************************************************************************/ + /* initialize the filter structures */ + /************************************************************************/ + kalman_filter_initialize(&kf, KALMAN_NUM_STATES, KALMAN_NUM_INPUTS); + kalman_observation_initialize(&kfm, KALMAN_NUM_STATES, KALMAN_NUM_MEASUREMENTS); + + /************************************************************************/ + /* set initial state */ + /************************************************************************/ + mf16 *x = kalman_get_state_vector(&kf); + x->data[0][0] = 0; // s_i + x->data[1][0] = 0; // v_i + + /************************************************************************/ + /* set state transition */ + /************************************************************************/ + mf16 *A = kalman_get_state_transition(&kf); + + // set time constant +#if (DEBUG == 1) + const fix16_t T = fix16_from_float(0.02f); +#else + const fix16_t T = fix16_from_float(0.02f); +#endif + const fix16_t Tsquare = fix16_sq(T); + + // helper + const fix16_t fix16_half = fix16_from_float(0.5); + + // transition of x to s + matrix_set(A, 0, 0, fix16_one); // 1 + matrix_set(A, 0, 1, T); // T + + // transition of x to v + matrix_set(A, 1, 0, 0); // 0 + matrix_set(A, 1, 1, fix16_one); // 1 + + + /************************************************************************/ + /* set covariance */ + /************************************************************************/ + mf16 *P = kalman_get_system_covariance(&kf); + +// matrix_set_symmetric(P, 0, 0, fix16_half); // var(s) +// matrix_set_symmetric(P, 0, 1, 0); // cov(s,v) +// matrix_set_symmetric(P, 0, 2, 0); // cov(s,g) +// +// matrix_set_symmetric(P, 1, 1, fix16_one); // var(v) +// matrix_set_symmetric(P, 1, 2, 0); // cov(v,g) +// +// matrix_set_symmetric(P, 2, 2, fix16_one); // var(g) + + /************************************************************************/ + /* set input covariance */ + /************************************************************************/ + mf16 *Q = kalman_get_input_covariance(&kf); +// mf16_fill_diagonal(Q, fix16_one); + mf16_mul_bt(Q, A, A); + mf16_mul_s(Q, Q, fix16_from_int(10*10)); + + /************************************************************************/ + /* set control input transformation */ + /************************************************************************/ + mf16 *B = kalman_get_input_transition(&kf); + matrix_set(B, 0, 0, fix16_mul(fix16_half, Tsquare)); // u = 0*s + matrix_set(B, 1, 0, T); // + 0*v + + /************************************************************************/ + /* set measurement transformation */ + /************************************************************************/ + mf16 *H = kalman_get_observation_transformation(&kfm); + matrix_set(H, 0, 0, fix16_one); // z = 1*s + matrix_set(H, 0, 1, 0); // + 0*v + + /************************************************************************/ + /* set process noise */ + /************************************************************************/ + mf16 *R = kalman_get_observation_process_noise(&kfm); + + matrix_set(R, 0, 0, fix16_from_int(36)); // var(s) +} -uint8_t *altimeter_buffer; -uint8_t *acceleration_buffer; -uint8_t *quaternion_buffer; +volatile bool derate_baro_sensor = false; +const fix16_t baro_velocity_derate = F16(160.f); + +volatile fix16_t altitude_filt = 0; +volatile fix16_t velocity_filt = 0; + +volatile fix16_t drag_force = 0; +volatile fix16_t apogee_prediction = 0; +volatile fix16_t desired_drag_force = 0; +volatile fix16_t desired_deployment = 0; + +void kalman_update(fix16_t altitude, fix16_t vertical_acceleration) { + static mf16* control = kalman_get_input_vector(&kf); + static mf16* measurement = kalman_get_observation_vector(&kfm); + static mf16* Q = kalman_get_input_covariance(&kf); + static mf16 *R = kalman_get_observation_process_noise(&kfm); + static mf16* state_vector = kalman_get_state_vector(&kf); + + if (state_vector->data[1][0] >= baro_velocity_derate && !derate_baro_sensor) { + mf16_div_s(Q, Q, fix16_from_int(3)); + mf16_mul_s(R, R, fix16_from_int(4)); + derate_baro_sensor = true; + } else if (state_vector->data[1][0] < baro_velocity_derate && derate_baro_sensor) { + mf16_mul_s(Q, Q, fix16_from_int(3)); + mf16_div_s(R, R, fix16_from_int(4)); + derate_baro_sensor = false; + } + matrix_set(control, 0, 0, vertical_acceleration); + kalman_predict(&kf); + matrix_set(measurement, 0, 0, altitude); + kalman_correct(&kf, &kfm); +} -uint8_t entry_buffer[PACKET_SIZE]; +fix16_t calculate_drag_force(fix16_t deployment_percentage, fix16_t vertical_velocity); +fix16_t predict_apogee(fix16_t altitude, fix16_t vertical_velocity, fix16_t drag_force); +fix16_t calculate_deployment_percentage(fix16_t drag_force, fix16_t vertical_velocity); +fix16_t calculate_desired_drag_force(fix16_t altitude, fix16_t vertical_velocity); -uint8_t *pad_buffer; +/****************************** KALMAN ****************************************/ int main() { + stdio_init_all(); + adc_init(); adc_set_temp_sensor_enabled(true); - cyw43_arch_init(); + heartbeat_initialize(PICO_DEFAULT_LED_PIN); - i2c_init(i2c_default, MAX_SCL); + i2c_init(i2c_default, 400000); 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(); - altimeter.initialize(30.0f, INT1_PIN, &pad_callback); + gpio_init(MICRO_DEFAULT_SERVO_ENABLE); + gpio_set_dir(MICRO_DEFAULT_SERVO_ENABLE, GPIO_OUT); + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 0); + + sleep_ms(2500); + + info_cmd_func(); + stdio_flush(); - imu.initialize(); - imu.linear_acceleration(linear_acceleration); - imu.quaternion(quaternion); - imu.quaternion_euler(euler_angles, quaternion); + logger.initialize(true); + logger.initialize_circular_buffer(PAD_BUFFER_SIZE); + alt.initialize(); + sleep_ms(500); + adxl375.initialize(); + sleep_ms(500); + iim42653.initialize(); + sleep_ms(500); + mmc5983ma.initialize(); + sleep_ms(500); pwm.init(); - // Initialize MOSFET - gpio_init(MOSFET_PIN); - gpio_set_dir(MOSFET_PIN, GPIO_OUT); + kalman_verticality_init(); - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); + xTaskCreate(heartbeat_task, "heartbeat", 256, NULL, HEARTBEAT_TASK_PRIORITY, NULL); + xTaskCreate(serial_task, "serial", 8192, NULL, SERIAL_TASK_PRIORITY, NULL); - pad_buffer = (uint8_t*)malloc(PAD_BUFFER_SIZE); + xTaskCreate(MS5607::update_ms5607_task, "update_ms5607", 256, &alt, SENSOR_SAMPLE_PRIORITY, &(alt.update_task_handle)); + xTaskCreate(ADXL375::update_adxl375_task, "update_adxl375", 256, &adxl375, SENSOR_SAMPLE_PRIORITY, &(adxl375.update_task_handle)); + xTaskCreate(IIM42653::update_iim42653_task, "update_iim42653", 256, &iim42653, SENSOR_SAMPLE_PRIORITY, &(iim42653.update_task_handle)); + xTaskCreate(MMC5983MA::update_mmc5983ma_task, "update_mmc5983ma", 256, &mmc5983ma, SENSOR_SAMPLE_PRIORITY, &(mmc5983ma.update_task_handle)); - // Initialize Kalman Filter - kf = new kalman_filter(2, 1, 1, 0.01); - VectorXf state_vec(2); - MatrixXf state_cov(2, 2); - state_vec << altimeter.get_altitude_converted(), linear_acceleration.z(); - state_cov << 0.018, 0.0, 0.0, 0.0005; - kf->state_initialize(state_vec, state_cov); - ground_altitude = altimeter.get_altitude_converted(); + xTaskCreate(MS5607::ms5607_sample_handler, "ms5607_sample_handler", 256, &alt, SENSOR_EVENT_HANDLER_PRIORITY, &(alt.sample_handler_task_handle)); - altimeter.expose_buffer(&altimeter_buffer); - imu.expose_acceleration_buffer(&acceleration_buffer); - imu.expose_quaternion_buffer(&quaternion_buffer); + xTaskCreate(rocket_task, "rocket_task", 512, NULL, SENSOR_SAMPLE_PRIORITY, const_cast<TaskHandle_t *>(&rocket_task_handle)); +#if (DEBUG != 1) + xTaskCreate(kalman_task, "kalman_task", 512, NULL, KALMAN_TASK_PRIORITY, const_cast<TaskHandle_t *>(&kalman_task_handle)); + vTaskCoreAffinitySet(kalman_task_handle, 0x01); +#endif + vTaskCoreAffinitySet( alt.update_task_handle, 0x01 ); + vTaskCoreAffinitySet( adxl375.update_task_handle, 0x01 ); + vTaskCoreAffinitySet( iim42653.update_task_handle, 0x01 ); + vTaskCoreAffinitySet( mmc5983ma.update_task_handle, 0x01 ); - sem_init(&sem, 1, 1); + vTaskCoreAffinitySet(rocket_task_handle, 0x01); - add_repeating_timer_us(-1000000 / DATA_RATE_HZ, &timer_callback, NULL, &data_timer); + vTaskCoreAffinitySet( alt.sample_handler_task_handle, 0x01 ); - multicore_launch_core1(logging_core); + xTaskCreate(logging_task, "logging", 256, NULL, LOGGING_PRIORITY, const_cast<TaskHandle_t *>(&logging_handle)); + vTaskCoreAffinitySet(logging_handle, 0x02); + + vTaskStartScheduler(); while (1) { tight_loop_contents(); } } -// PRIMARY THREAD RELATED FUNCTIONS AND CALLBACKS -//=============================================================================== - -bool timer_callback(repeating_timer_t *rt) { - sem_acquire_blocking(&sem); - imu.linear_acceleration(linear_acceleration); - imu.quaternion(quaternion); - - control(0) = linear_acceleration.z(); - measurement(0) = altimeter.get_altitude_converted(); - res = kf->run(control, measurement, 0.01f); - altitude = res(0); - velocity = res(1); - - - deployment_percent = (uint8_t)(std::min(std::max(30.0f, get_deploy_percent(velocity, (altitude - ground_altitude))), 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; - } - sem_release(&sem); - return true; +/****************************** LOGGING ***************************************/ +static void populate_log_entry(log_entry_t* log_entry) { + log_entry->time_us = time_us_64(); + + adc_select_input(4); + log_entry->temperature_chip = adc_read(); + log_entry->state = rocket_state; + log_entry->deploy_percent = deployment_percent; + log_entry->pressure = alt.get_pressure(); + log_entry->altitude = alt.get_altitude(); + log_entry->altitude_filt = altitude_filt; + log_entry->velocity_filt = velocity_filt; + log_entry->temperature_alt = alt.get_temperature(); + + log_entry->ax = iim42653.get_ax(); + log_entry->ay = iim42653.get_ay(); + log_entry->az = iim42653.get_az(); + log_entry->gx = iim42653.get_gx(); + log_entry->gy = iim42653.get_gy(); + log_entry->gz = iim42653.get_gz(); + + log_entry->mag_x = mmc5983ma.get_ax(); + log_entry->mag_y = mmc5983ma.get_ay(); + log_entry->mag_z = mmc5983ma.get_az(); + + log_entry->high_g_x = adxl375.get_ax(); + log_entry->high_g_y = adxl375.get_ay(); + log_entry->high_g_z = adxl375.get_az(); + + log_entry->drag_force = drag_force; + log_entry->apogee_prediction = apogee_prediction; + log_entry->desired_drag_force = desired_drag_force; + + log_entry->data0 = get_rand_32(); } -/** - * @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) { - sem_acquire_blocking(&sem); - altimeter.unset_threshold_altitude(INT1_PIN); - state = BOOST; - sem_release(&sem); - // start motor burn timer with boost transition function as callback - add_alarm_in_ms(MOTOR_BURN_TIME, &boost_callback, NULL, false); +static void logging_task(void* pvParameters) { + TickType_t xLastWakeTime; + const TickType_t xFrequency = pdMS_TO_TICKS(1000 / LOG_RATE_HZ); + + xLastWakeTime = xTaskGetTickCount(); + while (1) { + vTaskDelayUntil(&xLastWakeTime, xFrequency); + populate_log_entry(const_cast<log_entry_t *>(&log_entry)); + if (serial_data_output) { + print_log_entry(reinterpret_cast<const uint8_t *>(const_cast<log_entry_t *>(&log_entry))); + stdio_flush(); + } + + if (use_circular_buffer) { + logger.write_circular_buffer(reinterpret_cast<const uint8_t *>(const_cast<log_entry_t *>(&log_entry))); + if (rocket_state != PAD) { + logger.flush_circular_buffer(true); + use_circular_buffer = false; + } + } else { + logger.write_memory(reinterpret_cast<const uint8_t *>(const_cast<log_entry_t *>(&log_entry)), false); + } + if ((xLastWakeTime + xFrequency) < xTaskGetTickCount()) { + xLastWakeTime = xTaskGetTickCount(); + } + } } +/****************************** LOGGING ***************************************/ -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; - populate_entry(); - write_entry(entry_buffer); - sem_release(&sem); - add_alarm_in_ms(1000, &coast_callback, NULL, false); - return 0; +/****************************** SERIAL CONSOLE ********************************/ +static void read_cmd_func() { + if (logging_handle != NULL) { + vTaskSuspend(logging_handle); + } + logger.read_memory(); + if (logging_handle != NULL) { + vTaskResume(logging_handle); + } } -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; - populate_entry(); - write_entry(entry_buffer); - sem_release(&sem); - add_alarm_in_ms(1, &apogee_callback, NULL, false); +static void write_cmd_func() { + if (logging_handle != NULL) { + vTaskSuspend(logging_handle); + } + uint64_t start = time_us_64(); + log_entry_t log_entry; + populate_log_entry(&log_entry); + printf("\nWriting the following entry!\n"); + print_log_entry(reinterpret_cast<const uint8_t *>(&log_entry)); + if (use_circular_buffer) { + logger.write_circular_buffer(reinterpret_cast<const uint8_t *>(&log_entry)); } else { - add_alarm_in_ms(250, &coast_callback, NULL, false); + logger.write_memory(reinterpret_cast<const uint8_t *>(&log_entry), true); + } + uint64_t end = time_us_64(); + printf("\nTook %" PRIu64 " us to write that entry!\n", (end - start)); + if (logging_handle != NULL) { + vTaskResume(logging_handle); } - return 0; } -int64_t apogee_callback(alarm_id_t id, void* user_data) { - state = RECOVERY; - // Set altimeter interrupt to occur for when rocket touches back to the ground - altimeter.set_threshold_altitude((ground_altitude + 10.0f), INT1_PIN, &recovery_callback); +static void erase_cmd_func() { + if (logging_handle != NULL) { + vTaskSuspend(logging_handle); + } + logger.erase_memory(); + if (logging_handle != NULL) { + vTaskResume(logging_handle); + } +} - sem_acquire_blocking(&sem); - populate_entry(); - write_entry(entry_buffer); - sem_release(&sem); - return 0; +static void show_cmd_func() { + serial_data_output = !serial_data_output; +} + +static void deploy_cmd_func() { + vTaskSuspend(rocket_task_handle); + printf("Enabling servo!\n"); + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 1); + vTaskDelay(pdMS_TO_TICKS(1000)); + printf("Setting servo to 80%\n"); + pwm.set_servo_percent(80); + vTaskDelay(pdMS_TO_TICKS(5000)); + printf("Setting servo to 0%\n"); + pwm.set_servo_percent(0); + vTaskDelay(pdMS_TO_TICKS(5000)); + printf("Setting servo to 80%\n"); + pwm.set_servo_percent(80); + vTaskDelay(pdMS_TO_TICKS(5000)); + printf("Setting servo to 0%\n"); + pwm.set_servo_percent(0); + vTaskDelay(pdMS_TO_TICKS(5000)); + printf("Disabling servo!\n"); + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 0); + vTaskResume(rocket_task_handle); } -void recovery_callback(uint gpio, uint32_t event_mask) { - // Essentially just a signal to stop logging data - sem_acquire_blocking(&sem); - state = END; - populate_entry(); - write_entry(entry_buffer); - sem_acquire_blocking(&sem); +#if (DEBUG == 1) +#include "ohio_test_data.h" +#endif + +static void kalman_cmd_func() { + static mf16* state_vector = kalman_get_state_vector(&kf); + printf("Perfoming Kalman Filter Test! Stand back!\n"); + printf("*******************************************\n"); + printf("\naltitude,velocity,acceleration,drag_force,apogee_prediction,desired_drag_force,desired_deployment\n"); + + fix16_t drag_force_l = 0; + fix16_t apogee_prediction_l = 0; + fix16_t desired_drag_force_l = 0; + fix16_t desired_deployment_l = 0; +#if (DEBUG == 1) + for (uint32_t i = 0; i < 7500; i++) { + kalman_update(altitude_test_data[i], fix16_mul(fix16_sub(acceleration_data[i], fix16_one), F16(9.81f))); + + if (i >= 693 && i <= 1632) { + drag_force_l = calculate_drag_force(fix16_from_int(80), state_vector->data[1][0]); + } else { + drag_force_l = calculate_drag_force(0, state_vector->data[1][0]); + } + apogee_prediction_l = predict_apogee(state_vector->data[0][0], state_vector->data[1][0], drag_force_l); + desired_drag_force_l = calculate_desired_drag_force(state_vector->data[0][0], state_vector->data[1][0]); + desired_deployment_l = calculate_deployment_percentage(desired_drag_force_l, state_vector->data[1][0]); + desired_deployment_l = fix16_clamp(desired_deployment_l, 0, fix16_from_int(100)); + printf("%4.2f,%4.2f,%4.2f,%4.2f,%4.2f,%4.2f,%4.2f\n", fix16_to_float(state_vector->data[0][0]), fix16_to_float(state_vector->data[1][0]), fix16_to_float(fix16_mul(acceleration_data[i], F16(9.81f))), fix16_to_float(drag_force_l), fix16_to_float(apogee_prediction_l), fix16_to_float(desired_drag_force_l), fix16_to_float(desired_deployment_l)); + stdio_flush(); + } +#else + kalman_update(0, 0); +#endif +} +/****************************** SERIAL CONSOLE ********************************/ + + +static void rocket_task(void* pvParameters) { + TickType_t xLastWakeTime; + const TickType_t xFrequency = pdMS_TO_TICKS(1000 / ROCKET_TASK_RATE_HZ); + + vTaskDelay(pdMS_TO_TICKS(1000)); + ground_altitude = alt.get_altitude(); + + // Sign of life + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 1); + pwm.set_servo_percent(0); + vTaskDelay(pdMS_TO_TICKS(1000)); + pwm.set_servo_percent(20); + vTaskDelay(pdMS_TO_TICKS(3000)); + pwm.set_servo_percent(0); + vTaskDelay(pdMS_TO_TICKS(3000)); + pwm.set_servo_percent(20); + vTaskDelay(pdMS_TO_TICKS(3000)); + pwm.set_servo_percent(0); + vTaskDelay(pdMS_TO_TICKS(3000)); + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 0); + + xTaskCreate(launch_event_handler_task, "launch_event_handler", 512, NULL, ROCKET_EVENT_HANDLER_PRIORITY, const_cast<TaskHandle_t *>(&launch_event_handler_task_handle)); + vTaskCoreAffinitySet(launch_event_handler_task_handle, 0x01); + alt.set_threshold_altitude(ground_altitude + (threshold_altitude * ALTITUDE_SCALE), &launch_event_callback); + + xLastWakeTime = xTaskGetTickCount(); + while (1) { + vTaskDelayUntil(&xLastWakeTime, xFrequency); + + drag_force = calculate_drag_force(fix16_from_int(deployment_percent), velocity_filt); + apogee_prediction = predict_apogee(altitude_filt, velocity_filt, drag_force); + desired_drag_force = calculate_desired_drag_force(altitude_filt, velocity_filt); + desired_deployment = calculate_deployment_percentage(desired_drag_force, velocity_filt); + desired_deployment = fix16_clamp(desired_deployment, 0, fix16_from_int(100)); + + switch(rocket_state) { + case PAD: + deployment_percent = 0; + pwm.set_servo_percent(deployment_percent); + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 0); + break; + case BOOST: + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 1); + deployment_percent = 0; + pwm.set_servo_percent(deployment_percent); + break; + case COAST: + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 1); + if (velocity_filt <= 0) { + rocket_state = APOGEE; + populate_log_entry(const_cast<log_entry_t *>(&log_entry)); + logger.write_memory(reinterpret_cast<const uint8_t *>(const_cast<log_entry_t *>(&log_entry)), false); + deployment_percent = 0; + pwm.set_servo_percent(deployment_percent); + rocket_state = RECOVERY; + xTaskCreate(end_event_handler_task, "end_event_handler", 1024, NULL, ROCKET_EVENT_HANDLER_PRIORITY, const_cast<TaskHandle_t *>(&end_event_handler_task_handle)); + vTaskCoreAffinitySet(end_event_handler_task_handle, 0x01); + add_alarm_in_ms(450000, end_event_callback, NULL, false); + } + deployment_percent = desired_deployment; + if ((alt.get_altitude() - ground_altitude)> (2895 * ALTITUDE_SCALE)) { + deployment_percent = 100; + } + pwm.set_servo_percent(deployment_percent); + break; + case APOGEE: + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 1); + deployment_percent = 0; + pwm.set_servo_percent(deployment_percent); + break; + case RECOVERY: + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 1); + deployment_percent = 0; + pwm.set_servo_percent(deployment_percent); + break; + case END: + deployment_percent = 0; + pwm.set_servo_percent(deployment_percent); + vTaskDelay(pdMS_TO_TICKS(1000)); + gpio_put(MICRO_DEFAULT_SERVO_ENABLE, 0); + vTaskDelete(logging_handle); + vTaskDelete(end_event_handler_task_handle); + vTaskDelete(rocket_task_handle); + break; + } + + if ((xLastWakeTime + xFrequency) < xTaskGetTickCount()) { + xLastWakeTime = xTaskGetTickCount(); + } + } } -/** - * @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)); +static void kalman_task(void* pvParameters) { + TickType_t xLastWakeTime; + const TickType_t xFrequency = pdMS_TO_TICKS(1000 / KALMAN_TASK_RATE_HZ); + const fix16_t accel_scale = fix16_div(F16(9.81f), F16(S_IIM42653_ACCEL_SENSITIVITY_FACTOR)); + + mf16* state_vector = kalman_get_state_vector(&kf); + fix16_t altitude_agl = 0; + fix16_t vertical_accel = 1; + + xLastWakeTime = xTaskGetTickCount(); + while (1) { + vTaskDelayUntil(&xLastWakeTime, xFrequency); + taskENTER_CRITICAL(); + altitude_agl = fix16_div(fix16_from_int(alt.get_altitude() - ground_altitude), fix16_from_int(ALTITUDE_SCALE)); + vertical_accel = fix16_mul(fix16_sub(fix16_from_int(iim42653.get_az()), fix16_one), accel_scale); + kalman_update(altitude_agl, vertical_accel); + altitude_filt = state_vector->data[0][0]; + velocity_filt = state_vector->data[1][0]; + taskEXIT_CRITICAL(); + if ((xLastWakeTime + xFrequency) < xTaskGetTickCount()) { + xLastWakeTime = xTaskGetTickCount(); + } + } +} +static void launch_event_handler_task(void* pvParameters) { + const TickType_t xInterruptFrequency = pdMS_TO_TICKS( 1000 ); + const TickType_t xMaxExpectedBlockTime = xInterruptFrequency + pdMS_TO_TICKS( 500 ); + uint32_t ulEventsToProcess; + while (1) { + /* Wait to receive a notification sent directly to this task from the + interrupt service routine. */ + ulEventsToProcess = ulTaskNotifyTake( pdTRUE, xMaxExpectedBlockTime ); + if( ulEventsToProcess != 0 ) { + /* To get here at least one event must have occurred. Loop here + until all the pending events have been processed */ + while( ulEventsToProcess > 0 ) { + vTaskSuspendAll(); + rocket_state = BOOST; + xTaskCreate(coast_event_handler_task, "coast_event_handler", 512, NULL, ROCKET_EVENT_HANDLER_PRIORITY, const_cast<TaskHandle_t *>(&coast_event_handler_task_handle)); + vTaskCoreAffinitySet(coast_event_handler_task_handle, 0x01); + add_alarm_in_ms(MOTOR_BURN_TIME, coast_event_callback, NULL, false); + vTaskDelete(launch_event_handler_task_handle); + launch_event_handler_task_handle = NULL; + xTaskResumeAll(); + } + } + } } -// LOGGING THREAD RELATED FUNCTIONS AND CALLBACKS -//=============================================================================== +static void coast_event_handler_task(void* pvParameters) { + const TickType_t xInterruptFrequency = pdMS_TO_TICKS( MOTOR_BURN_TIME ); + const TickType_t xMaxExpectedBlockTime = xInterruptFrequency + pdMS_TO_TICKS( 500 ); + uint32_t ulEventsToProcess; + while (1) { + /* Wait to receive a notification sent directly to this task from the + interrupt service routine. */ + ulEventsToProcess = ulTaskNotifyTake( pdTRUE, xMaxExpectedBlockTime ); + if( ulEventsToProcess != 0 ) { + /* To get here at least one event must have occurred. Loop here + until all the pending events have been processed */ + while( ulEventsToProcess > 0 ) { + vTaskSuspendAll(); + rocket_state = COAST; + vTaskDelete(coast_event_handler_task_handle); + coast_event_handler_task_handle = NULL; + xTaskResumeAll(); + } + } + } +} -void logging_core() { - add_repeating_timer_us(-1000000 / LOG_RATE_HZ, &logging_buffer_callback, NULL, &log_timer); - add_repeating_timer_us(-1000000 / HEART_RATE_HZ, &heartbeat_callback, NULL, &heartbeat_timer); +static void end_event_handler_task(void* pvParameters) { + const TickType_t xInterruptFrequency = pdMS_TO_TICKS( 30000 ); + const TickType_t xMaxExpectedBlockTime = xInterruptFrequency + pdMS_TO_TICKS( 500 ); + uint32_t ulEventsToProcess; while (1) { - tight_loop_contents(); + /* Wait to receive a notification sent directly to this task from the + interrupt service routine. */ + ulEventsToProcess = ulTaskNotifyTake( pdTRUE, xMaxExpectedBlockTime ); + if( ulEventsToProcess != 0 ) { + /* To get here at least one event must have occurred. Loop here + until all the pending events have been processed */ + while( ulEventsToProcess > 0 ) { + rocket_state = END; + vTaskDelete(end_event_handler_task_handle); + end_event_handler_task_handle = NULL; + } + } } } -void populate_entry() { - absolute_time_t now = get_absolute_time(); - uint64_t now_us = to_us_since_boot(now); - uint32_t vel_bits = *((uint32_t *)&velocity); - entry_buffer[0] = now_us >> 56; - entry_buffer[1] = now_us >> 48; - entry_buffer[2] = now_us >> 40; - entry_buffer[3] = now_us >> 32; - entry_buffer[4] = now_us >> 24; - entry_buffer[5] = now_us >> 16; - entry_buffer[6] = now_us >> 8; - entry_buffer[7] = now_us; +int64_t launch_event_callback(alarm_id_t id, void* user_data) { + BaseType_t xHigherPriorityTaskWoken; + xHigherPriorityTaskWoken = pdFALSE; + // Defer ISR handling to separate handler within FreeRTOS context + vTaskNotifyGiveFromISR(launch_event_handler_task_handle, &xHigherPriorityTaskWoken ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + return 0; +} - adc_select_input(4); - uint16_t temperature = adc_read(); - entry_buffer[8] = ((*(uint8_t *)(&state)) << 4) | (temperature >> 8); - entry_buffer[9] = temperature; - - entry_buffer[10] = deployment_percent; - entry_buffer[11] = altimeter_buffer[0]; - entry_buffer[12] = altimeter_buffer[1]; - entry_buffer[13] = altimeter_buffer[2]; - entry_buffer[14] = vel_bits >> 24; - entry_buffer[15] = vel_bits >> 16; - entry_buffer[16] = vel_bits >> 8; - entry_buffer[17] = vel_bits; - entry_buffer[18] = acceleration_buffer[0]; - entry_buffer[19] = acceleration_buffer[1]; - entry_buffer[20] = acceleration_buffer[2]; - entry_buffer[21] = acceleration_buffer[3]; - entry_buffer[22] = acceleration_buffer[4]; - entry_buffer[23] = acceleration_buffer[5]; - entry_buffer[24] = quaternion_buffer[0]; - entry_buffer[25] = quaternion_buffer[1]; - entry_buffer[26] = quaternion_buffer[2]; - entry_buffer[27] = quaternion_buffer[3]; - entry_buffer[28] = quaternion_buffer[4]; - entry_buffer[29] = quaternion_buffer[5]; - entry_buffer[30] = quaternion_buffer[6]; - entry_buffer[31] = quaternion_buffer[7]; +int64_t coast_event_callback(alarm_id_t id, void* user_data) { + BaseType_t xHigherPriorityTaskWoken; + xHigherPriorityTaskWoken = pdFALSE; + // Defer ISR handling to separate handler within FreeRTOS context + vTaskNotifyGiveFromISR(coast_event_handler_task_handle, &xHigherPriorityTaskWoken ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + return 0; } -bool logging_buffer_callback(repeating_timer_t *rt) { - sem_acquire_blocking(&sem); - populate_entry(); - sem_release(&sem); - for (uint32_t i = 0; i < PACKET_SIZE; i++) { - pad_buffer[i + pad_buffer_offset] = entry_buffer[i]; - } - pad_buffer_offset += PACKET_SIZE; - pad_buffer_offset %= PAD_BUFFER_SIZE; - - if (state != PAD) { - uint32_t idx = ((pad_buffer_offset + PACKET_SIZE) % PAD_BUFFER_SIZE); - sem_acquire_blocking(&sem); - do { - write_entry(pad_buffer + idx); - idx += PACKET_SIZE; - idx %= PAD_BUFFER_SIZE; - } while (idx != pad_buffer_offset); - sem_release(&sem); - cancel_repeating_timer(&log_timer); - free(pad_buffer); - add_repeating_timer_us(-1000000 / LOG_RATE_HZ, &logging_flash_callback, NULL, &log_timer); - } - return true; +int64_t end_event_callback(alarm_id_t id, void* user_data) { + BaseType_t xHigherPriorityTaskWoken; + xHigherPriorityTaskWoken = pdFALSE; + // Defer ISR handling to separate handler within FreeRTOS context + vTaskNotifyGiveFromISR(end_event_handler_task_handle, &xHigherPriorityTaskWoken ); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + return 0; } -bool logging_flash_callback(repeating_timer_t *rt) { - sem_acquire_blocking(&sem); - populate_entry(); - write_entry(entry_buffer); - sem_release(&sem); - if (state == END) { - cancel_repeating_timer(&log_timer); - } - return true; +fix16_t calculate_drag_force(fix16_t deployment_percentage, fix16_t vertical_velocity) { + static const fix16_t p00 = F16(125.f); + static const fix16_t p10 = F16(-3.286f); + static const fix16_t p01 = F16(-1.803f); + static const fix16_t p20 = F16(0.01675f); + static const fix16_t p11 = F16(0.02687f); + static const fix16_t p02 = F16(0.008441f); + + fix16_t term1 = fix16_mul(p10, deployment_percentage); + fix16_t term2 = fix16_mul(p01, vertical_velocity); + fix16_t term3 = fix16_mul(p20, fix16_sq(deployment_percentage)); + fix16_t term4 = fix16_mul(fix16_mul(p11, vertical_velocity), deployment_percentage); + fix16_t term5 = fix16_mul(fix16_mul(p02, vertical_velocity), vertical_velocity); + + fix16_t drag_force = fix16_add(p00, term1); + drag_force = fix16_add(drag_force, term2); + drag_force = fix16_add(drag_force, term3); + drag_force = fix16_add(drag_force, term4); + drag_force = fix16_add(drag_force, term5); + + return drag_force; } -bool heartbeat_callback(repeating_timer_t *rt) { - const bool sequence[] = {true, false, true, false, false}; - const uint8_t sequence_length = 5; +fix16_t predict_apogee(fix16_t altitude, fix16_t vertical_velocity, fix16_t drag_force) { + static const fix16_t gravity = F16(9.81f); + static const fix16_t mass = F16(21.8f); + + fix16_t nal_log_internal = fix16_div(gravity, fix16_add(gravity, fix16_div(drag_force, mass))); + fix16_t nal_log_scale = fix16_mul(fix16_mul(fix16_div(vertical_velocity, fix16_mul(F16(2), drag_force)), vertical_velocity), mass); + fix16_t apogee_prediction = fix16_sub(altitude, fix16_mul(nal_log_scale, fix16_log(nal_log_internal))); + return apogee_prediction; +} - bool led_status = sequence[led_counter]; - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_status); - led_counter++; - led_counter %= sequence_length; - return true; +fix16_t calculate_deployment_percentage(fix16_t drag_force, fix16_t vertical_velocity) { + static const fix16_t p00 = F16(79.05f); + static const fix16_t p10 = F16(1.057f); + static const fix16_t p01 = F16(-1.049f); + static const fix16_t p20 = F16(-7.296e-5f); + static const fix16_t p11 = F16(-0.003321f); + static const fix16_t p02 = F16(0.002322f); + + + fix16_t term1 = fix16_mul(p10, drag_force); + fix16_t term2 = fix16_mul(p01, vertical_velocity); + fix16_t term3 = fix16_mul(fix16_mul(p20, drag_force), drag_force); + fix16_t term4 = fix16_mul(fix16_mul(p11, drag_force), vertical_velocity); + fix16_t term5 = fix16_mul(fix16_mul(p02, vertical_velocity), vertical_velocity); + + fix16_t deployment_percentage = fix16_add(p00, term1); + deployment_percentage = fix16_add(deployment_percentage, term2); + deployment_percentage = fix16_add(deployment_percentage, term3); + deployment_percentage = fix16_add(deployment_percentage, term4); + deployment_percentage = fix16_add(deployment_percentage, term5); + deployment_percentage = fix16_clamp(deployment_percentage, 0, fix16_from_int(100)); + return deployment_percentage; } +fix16_t calculate_desired_drag_force(fix16_t altitude, fix16_t vertical_velocity) { + static const fix16_t p00 = F16(-2.042e+01); + static const fix16_t p10 = F16(2.879e+01); + static const fix16_t p01 = F16(2.391e+02); + static const fix16_t p20 = F16(-1.265e+01); + static const fix16_t p11 = F16(-2.499e+02); + static const fix16_t p02 = F16(-1.063e+03); + static const fix16_t p30 = F16(1.774); + static const fix16_t p21 = F16(7.604e+01); + static const fix16_t p12 = F16(7.028e+02); + static const fix16_t p03 = F16(2.135e+03); + static const fix16_t p31 = F16(-6.349); + static const fix16_t p22 = F16(-1.049e+02); + static const fix16_t p13 = F16(-6.41e+02); + static const fix16_t p04 = F16(-1.604e+03); + + fix16_t altitude_km = fix16_div(altitude, fix16_from_int(1000)); + fix16_t vertical_velocity_km = fix16_div(vertical_velocity_km, fix16_from_int(1000)); + + fix16_t term01 = fix16_mul(p10, altitude_km); + fix16_t term02 = fix16_mul(p01, vertical_velocity_km); + fix16_t term03 = fix16_mul(fix16_mul(p20, altitude_km), altitude_km); + fix16_t term04 = fix16_mul(fix16_mul(p11, altitude_km), vertical_velocity_km); + fix16_t term05 = fix16_mul(fix16_mul(p02, vertical_velocity_km), vertical_velocity_km); + fix16_t term06 = fix16_mul(fix16_mul(fix16_mul(p30, altitude_km), altitude_km), altitude_km); + fix16_t term07 = fix16_mul(fix16_mul(fix16_mul(p21, altitude_km), altitude_km), vertical_velocity_km); + fix16_t term08 = fix16_mul(fix16_mul(fix16_mul(p12, altitude_km), vertical_velocity_km), vertical_velocity_km); + fix16_t term09 = fix16_mul(fix16_mul(fix16_mul(p03, vertical_velocity_km), vertical_velocity_km), vertical_velocity_km); + fix16_t term10 = fix16_mul(fix16_mul(fix16_mul(fix16_mul(p31, altitude_km), altitude_km), altitude_km), vertical_velocity_km); + fix16_t term11 = fix16_mul(fix16_mul(fix16_mul(fix16_mul(p22, altitude_km), altitude_km), vertical_velocity_km), vertical_velocity_km); + fix16_t term12 = fix16_mul(fix16_mul(fix16_mul(fix16_mul(p13, altitude_km), vertical_velocity_km), vertical_velocity_km), vertical_velocity_km); + fix16_t term13 = fix16_mul(fix16_mul(fix16_mul(fix16_mul(p04, vertical_velocity_km), vertical_velocity_km), vertical_velocity_km), vertical_velocity_km); + + fix16_t desired_drag_force = fix16_add(p00, term01); + desired_drag_force = fix16_add(desired_drag_force, term02); + desired_drag_force = fix16_add(desired_drag_force, term03); + desired_drag_force = fix16_add(desired_drag_force, term04); + desired_drag_force = fix16_add(desired_drag_force, term05); + desired_drag_force = fix16_add(desired_drag_force, term06); + desired_drag_force = fix16_add(desired_drag_force, term07); + desired_drag_force = fix16_add(desired_drag_force, term08); + desired_drag_force = fix16_add(desired_drag_force, term09); + desired_drag_force = fix16_add(desired_drag_force, term10); + desired_drag_force = fix16_add(desired_drag_force, term11); + desired_drag_force = fix16_add(desired_drag_force, term12); + desired_drag_force = fix16_add(desired_drag_force, term13); + + desired_drag_force = fix16_mul(desired_drag_force, fix16_from_int(1000)); + + return desired_drag_force; +} |
