From 26b3c857d59548828d3216b2cf9b0c31657f25b4 Mon Sep 17 00:00:00 2001 From: Atharva <77076007+ATGr8@users.noreply.github.com> Date: Tue, 4 Oct 2022 10:23:46 -0700 Subject: [PATCH 01/10] Operation/adding initial functions for camera (#209) * updated return codes for FRAM, I2C, and UART wrappers * updated readme to reflect updates to error code standards * removed a dumb whitespace char between function name and its inputs * updated README section on data types * added a word * another update * wuick commit * Added stub header files for Antenna, EPS, Trx. No API, just I2C slave addrs * creatd the camera header file * Source code of the camera function * Working progress on sending telecommand to the camera to take an image * deleted redundant files left over from the merge' * In progress we added string.h * working progress on defining telecommands and their parameters * Added Telemetry frames * Writing telecommands is completed. No errors with telecommands.Still working on telemetry format * Changed up telemetry approach, added defines * continued working on the telemetry's * finsihed telemetry functions for camera * added a TODO for the telemetry tasks * Added filtering function to filter out useless images. Added some code to capture and filter * Added RImage.c and .h -> added all TC and TLMs with defines * added RADCS files and included libraries in RImage.c * RImage.h is updated. Image processing function is moved to RI * finished revision A for RImage and RADCS and re-established a base for RCamera * finished all tc and tlm functions, ready to creating functions * made the adcs function and it has a clean build * camera.c and image.c is updated. Capture and download function is also updated. * Cleaned up the code a bit and and finished telemetry function * image processing function updated * finish the config function and download function * Finished the capture and filtering functions * added error to return instead of SUCCESS * general touch up * for loop for average grayScale of one frame * initial grayScale filter, previous implementation in comments * added if statements to check overall average in a range * changed function name to SaturationFilter * addeed a hex to dec converter and used it in the saturation filter * styling corrections * changed the range for the filter * added error check and made minor styling changes * made changes to pointers of arrays that were causing issues * syntax clean up for PR * resolved all errors and warnings on build * added comments, removed TODOs * changed full image struct to not define a pointer but an array instead, giving clean build * fixed saturation filter with indexing and made style changes * fixed some typos and pointers; started to tackle detection function * improved code to capture & download image, and for the camera detection algorithm; fixed some potential memory leaks * fixed imageFrame pointer issue; removed risky malloc cast * fixed the detection calculation to return floating point results * fixed major camera/UART issue by escaping & unescaping characters (see ICD); cleaned up code by moving common definitions & functions in a new file (RCameraCommon) * optimized code by removing duplication; removed unused definitions and fixed typos * allocated image memory dynamically based on size; moved size and frame count in image struct * added parameters to captureImage; added retry given unscheduled detection results; renamed some functions * added a reset function * added comments pointing to which camera functions to use Co-authored-by: tyrelkostyk Co-authored-by: Shiva Moghtaderi Co-authored-by: Addi Amaya Co-authored-by: Addi Amaya Co-authored-by: Atharva Co-authored-by: Austin Co-authored-by: w-moretti <=> Co-authored-by: William Moretti --- radsat-sk/.cproject | 1 + radsat-sk/operation/subsystems/camera/RADCS.c | 198 ++++ radsat-sk/operation/subsystems/camera/RADCS.h | 40 + .../operation/subsystems/camera/RCamera.c | 973 ++++++++++++++++++ .../operation/subsystems/camera/RCamera.h | 84 ++ .../subsystems/camera/RCameraCommon.c | 142 +++ .../subsystems/camera/RCameraCommon.h | 68 ++ .../operation/subsystems/camera/RImage.c | 417 ++++++++ .../operation/subsystems/camera/RImage.h | 42 + radsat-sk/src/tasks/RAdcsCaptureTask.c | 9 +- radsat-sk/src/tasks/RImageCaptureTask.c | 11 +- 11 files changed, 1979 insertions(+), 6 deletions(-) create mode 100644 radsat-sk/operation/subsystems/camera/RADCS.c create mode 100644 radsat-sk/operation/subsystems/camera/RADCS.h create mode 100644 radsat-sk/operation/subsystems/camera/RCamera.c create mode 100644 radsat-sk/operation/subsystems/camera/RCamera.h create mode 100644 radsat-sk/operation/subsystems/camera/RCameraCommon.c create mode 100644 radsat-sk/operation/subsystems/camera/RCameraCommon.h create mode 100644 radsat-sk/operation/subsystems/camera/RImage.c create mode 100644 radsat-sk/operation/subsystems/camera/RImage.h diff --git a/radsat-sk/.cproject b/radsat-sk/.cproject index d93271cf..bedd8f2b 100644 --- a/radsat-sk/.cproject +++ b/radsat-sk/.cproject @@ -87,6 +87,7 @@ + diff --git a/radsat-sk/operation/subsystems/camera/RADCS.c b/radsat-sk/operation/subsystems/camera/RADCS.c new file mode 100644 index 00000000..30f40037 --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RADCS.c @@ -0,0 +1,198 @@ +/** + * @file RADCS.c + * @date March 26, 2022 + * @author Addi Amaya (Caa746) + */ + +#include +#include +#include +#include +#include +#include +#include + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ + +#define TELECOMMAND_20 ((uint8_t) 0x14) +#define TELECOMMAND_20_LEN ((uint8_t) 3) + +#define TELEMETRY_22_TO_25_LEN ((uint8_t) 10) + + +// TODO: REMOVE. Test purposes only. +static void printDetectionData(tlm_detection_result_and_trigger_adcs_t *data); +char capture_results[6][25] = { + "Startup", + "Capture Pending", + "Success - Own SRAM", + "Success - Other SRAM", + "Camera timeout", + "SRAM overcurrent" +}; +char detection_results[8][25] = { + "Startup", + "Not scheduled", + "Detection Pending", + "Error - Too many edges", + "Error - Not enough edges", + "Error - Bad fit", + "Error - Sun not found", + "Success" +}; +void printDetectionData(tlm_detection_result_and_trigger_adcs_t *data) { + printf("\n--- Detection Data ---\n"); + printf("Alpha Angle = %d\n", data->alpha); + printf("Beta Angle = %d\n", data->beta); + printf("Capture Result = %d (%s)\n", data->captureResult, capture_results[data->captureResult]); + printf("Detection Result = %d (%s)\n", data->detectionResult, detection_results[data->detectionResult]); + printf("----------------------\n"); +} + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +/* + * Used to send image capture & detect telecommand (TC ID 20) + * + * @param camera defines which camera to use for capture and detection, camera 1 = 0, camera 2 = 1 + * @param sram defines which SRAM to use on Cubesense, SRAM1 = 0, SRAM2 = 1 + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tcImageCaptureAndDetection(uint8_t camera, uint8_t sram) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_20_LEN); + sizeOfBuffer = TELECOMMAND_20_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_20; + + // Fill buffer with SRAM + telecommandBuffer[TELECOMMAND_OFFSET_0] = camera; + + // Fill buffer with Location in SRAM + telecommandBuffer[TELECOMMAND_OFFSET_1] = sram; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telecommandBuffer, sizeOfBuffer); // No escaping needed + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Used to request the detection results and trigger a new detection (TLM ID 22 to 25) + * + * @param telemetry_reply defines where the detection results will be stored + * @param sensorSelection defines the selected sensor and SRAM to get the detection results from + * @return error of telemetry request attempt. 0 on success, otherwise failure + * */ +int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemetry_reply, SensorResultAndDetection sensorSelection) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = sensorSelection; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_6); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_22_TO_25_LEN); + + if (error != 0) { + free(telemetryBuffer); + return E_GENERIC; + } + + // Fill telemetry reply, data from uart read starts at index two + memcpy(&telemetry_reply->alpha, &telemetryBuffer[TELEMETRY_OFFSET_0], sizeof(telemetry_reply->alpha)); + memcpy(&telemetry_reply->beta, &telemetryBuffer[TELEMETRY_OFFSET_2], sizeof(telemetry_reply->beta)); + telemetry_reply->captureResult = telemetryBuffer[TELEMETRY_OFFSET_4]; + telemetry_reply->detectionResult = telemetryBuffer[TELEMETRY_OFFSET_5]; + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + printDetectionData(telemetry_reply); + + return SUCCESS; +} + +/* + * Used to interpret detection results into a 3D vector + * + * @param alpha result in centidegrees after executing TLM22 or 25 + * @param beta result in centidegrees after executing TLM22 or 25 + * @return struct containing the components of the 3D Vector + */ +interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t beta) { + float theta; + float phi; + interpret_detection_result_t data = {0}; + + theta = sqrt(pow((float)alpha/100, 2) + pow((float)beta/100, 2)) * M_PI/180; + phi = atan2(beta, alpha); + + data.X_AXIS = sin(theta) * cos(phi); + data.Y_AXIS = sin(theta) * sin(phi); + data.Z_AXIS = cos(theta); + + return data; +} diff --git a/radsat-sk/operation/subsystems/camera/RADCS.h b/radsat-sk/operation/subsystems/camera/RADCS.h new file mode 100644 index 00000000..a6b16ffd --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RADCS.h @@ -0,0 +1,40 @@ +/** + * @file RImage.h + * @date March 26, 2022 + * @author Addi Amaya (Caa746) + */ + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ + +/* Enum of telemetry request ID for sensor result and new detection */ +typedef enum _SensorResultAndDetection { + sensor1_sram1 = 0x96, // TLM 22 + sensor2_sram2 = 0x97, // TLM 23 + sensor1_sram2 = 0x98, // TLM 24 + sensor2_sram1 = 0x99 // TLM 25 +} SensorResultAndDetection; + +/* Struct for telemetry Detection result and Trigger, ID 20-25 */ +typedef struct _tlm_detection_result_and_trigger_adcs_t { + uint16_t alpha; + uint16_t beta; + uint8_t captureResult; + uint8_t detectionResult; +} tlm_detection_result_and_trigger_adcs_t; + +/* Struct to define 3D vector */ +typedef struct _interpret_detection_result_t { + float X_AXIS; + float Y_AXIS; + float Z_AXIS; +} interpret_detection_result_t; + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +int tcImageCaptureAndDetection(uint8_t camera, uint8_t sram); +int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemetry_reply, SensorResultAndDetection sensorSelection); +interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t beta); diff --git a/radsat-sk/operation/subsystems/camera/RCamera.c b/radsat-sk/operation/subsystems/camera/RCamera.c new file mode 100644 index 00000000..85bf2d2f --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RCamera.c @@ -0,0 +1,973 @@ +/** + * @file RCamera.c + * @date December 23, 2021 + * @author Shiva Moghtaderi (shm153), Addi Amaya (caa746) and Atharva Kulkarni (iya789) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*************************************************************************************************** + PRIVATE DEFINITIONS AND VARIABLES +***************************************************************************************************/ + +/* Interval in milliseconds before retrying a frame info request (40ms seems to be the lower limit) */ +#define IMAGE_FRAME_INTERVAL_MS 40 + +/* Maximum number of requests before timing out (set to 2 seconds) */ +#define IMAGE_FRAME_MAX_RETRY (2000 / IMAGE_FRAME_INTERVAL_MS) + +/* Telecommand ID numbers and Related Parameters */ +#define TELECOMMAND_0 ((uint8_t) 0x00) +#define TELECOMMAND_40 ((uint8_t) 0x28) +#define TELECOMMAND_41 ((uint8_t) 0x29) +#define TELECOMMAND_42 ((uint8_t) 0x2A) +#define TELECOMMAND_43 ((uint8_t) 0x2B) +#define TELECOMMAND_44 ((uint8_t) 0x2C) +#define TELECOMMAND_45 ((uint8_t) 0x2D) + +#define TELECOMMAND_0_LEN ((uint8_t) 2) +#define TELECOMMAND_40_AND_41_LEN ((uint8_t) 2) +#define TELECOMMAND_42_AND_44_LEN ((uint8_t) 2) +#define TELECOMMAND_43_AND_45_LEN ((uint8_t) 6) + +/* Telemetry ID numbers and Related Parameters */ +#define TELEMETRY_0 ((uint8_t) 0x80) +#define TELEMETRY_2 ((uint8_t) 0x82) +#define TELEMETRY_26 ((uint8_t) 0x9A) +#define TELEMETRY_40 ((uint8_t) 0xA8) +#define TELEMETRY_64 ((uint8_t) 0xC0) + +#define TELEMETRY_0_LEN ((uint8_t) 12) +#define TELEMETRY_2_LEN ((uint8_t) 12) +#define TELEMETRY_26_LEN ((uint8_t) 14) +#define TELEMETRY_40_LEN ((uint8_t) 18) +#define TELEMETRY_64_LEN ((uint8_t) 132) + +/* Struct for telmetry status, ID 0*/ +typedef struct _tlm_status_t { + uint8_t nodeType; + uint8_t interfaceVersion; + uint8_t firmwareVersionMajor; + uint8_t firmwareVersionMinor; + uint16_t runtimeSeconds; + uint16_t runtimeMSeconds; +} tlm_status_t; + +/* Struct for telmetry Serial number, ID 2 */ +typedef struct _tlm_communication_status_t { + uint16_t tcCounter; + uint16_t tlmCounter; + uint8_t tcBufferOverrunFlag; + uint8_t i2ctlmReadErrorFlag; + uint8_t uarttlmProtocolErrorFlag; + uint8_t uartIncompleteMsgFlag; +} tlm_communication_status_t; + +/* Struct for telemetry power, ID 26 */ +typedef struct _tlm_power_t { + uint16_t threeVcurrent; + uint16_t sramOneCurrent; + uint16_t sramTwoCurrent; + uint16_t fiveVcurrent; + uint8_t sramOneOverCurrent; + uint8_t sramTwoOverCurrent; +} tlm_power_t; + +/* Struct for telemetry configuration, ID 40 */ +typedef struct _tlm_config_t { + uint8_t cameraOneDetectionThreshold; + uint8_t cameraTwoDetectionThreshold; + uint8_t cameraOneAutoAdjustMode; + uint16_t cameraOneExposure; + uint8_t cameraOneAGC; + uint8_t cameraOneBlueGain; + uint8_t cameraOneRedGain; + uint8_t cameraTwoAutoAdjustMode; + uint16_t cameraTwoExposure; + uint8_t cameraTwoAGC; + uint8_t cameraTwoBlueGain; + uint8_t cameraTwoRedGain; +} tlm_config_t; + +/* Struct for telemetry full image, ID 66-69 */ +typedef struct _tlm_full_image_t { + uint8_t imageBytes[MAXIMUM_BYTES]; +} tlm_full_image_t; + +/* struct for telemetry read sensor masks ID 72-73 */ +typedef struct _tlm_read_sensor_mask_t { + uint16_t MinXAreaOne; + uint16_t MaxXAreaOne; + uint16_t MinYAreaOne; + uint16_t MaxYAreaOne; + uint16_t MinXAreaTwo; + uint16_t MaxXAreaTwo; + uint16_t MinYAreaTwo; + uint16_t MaxYAreaTwo; + uint16_t MinXAreaThree; + uint16_t MaxXAreaThree; + uint16_t MinYAreaThree; + uint16_t MaxYAreaThree; + uint16_t MinXAreaFourth; + uint16_t MaxXAreaFourth; + uint16_t MinYAreaFourth; + uint16_t MaxYAreaFourth; + uint16_t MinXAreaFifth; + uint16_t MaxXAreaFifth; + uint16_t MinYAreaFifth; + uint16_t MaxYAreaFifth; +} tlm_read_sensor_mask_t; + +/*************************************************************************************************** + PRIVATE FUNCTION STUBS +***************************************************************************************************/ +static int tlmStatus(tlm_status_t *telemetry_reply); +static int tlmPower(tlm_power_t *telemetry_reply); +static int tlmConfig(tlm_config_t *telemetry_reply); +static int tlmImageFrame(tlm_image_frame_t *telemetry_reply); +static int tcCameraDetectionThreshold(uint8_t camera, uint8_t detectionThreshold); +static int tcCameraAutoAdjust(uint8_t camera, uint8_t enabler); +static int tcCameraSettings(uint8_t camera, uint16_t exposureTime, uint8_t AGC, uint8_t blue_gain, uint8_t red_gain); +static uint16_t getNumberOfFramesFromSize(uint8_t size); + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +/* + * Memory allocation and initialization of a new image of a given size + * + * @note it is important to free the allocated pointer memory after usage + * @param size defines the image size used to allocate memory, 0 = 1024x1024, 1 = 512x512, 2 = 256x256, 3 = 128x128, 4 = 64x64 + * @return pointer to a new image in memory + */ +full_image_t * initializeNewImage(uint8_t size) { + uint16_t numberOfFrames = getNumberOfFramesFromSize(size); + full_image_t *image = calloc(1, sizeof(*image) + sizeof(tlm_image_frame_t) * numberOfFrames); + printf("Image allocated memory size = %i bytes\n", sizeof(*image) + sizeof(tlm_image_frame_t) * numberOfFrames); + if (image != NULL) { + image->imageSize = size; + image->framesCount = numberOfFrames; + } + return image; +} + +/* + * Used to capture an image with CubeSense Camera + * + * @param camera defines which sensor to use to capture an image, 0 = Camera 1, 1 = Camera 2 + * @param sram defines which SRAM to use on Cubesense, 0 = SRAM1, 1 = SRAM2 + * @param location defines which SRAM slot to use within selected SRAM, 0 = top, 1 = bottom + * @return error, 0 on successful, otherwise failure + * */ +int captureImage(uint8_t camera, uint8_t sram, uint8_t location) { + int error; + tlm_telecommand_ack_t telecommand_ack = {0}; + + // Send Telecommand to Camera to Take a Photo + error = tcImageCapture(camera, sram, location); + + if (error != SUCCESS) + return error; + + // Request telecommand acknowledgment with TLM 3 + error = tlmTelecommandAcknowledge(&telecommand_ack); + + if (error != SUCCESS) + return error; + + return telecommand_ack.tc_error_flag; +} + + +/* + * Used to Download an image from CubeSense Camera + * + * @param sram defines which SRAM to use on Cubesense + * @param location defines which SRAM slot to use within selected SRAM, 0 = top, 1 = bottom + * @param image defines a pointer to where the entire photo will reside with an image ID + * + * @return error, 0 on success, otherwise failure + * */ +int downloadImage(uint8_t sram, uint8_t location, full_image_t *image) { + int imageFrameNum = 1; + int error; + uint8_t counter = 0; + uint8_t isFirstRequest = 1; + tlm_image_frame_info_t imageFrameInfo = {0}; + tlm_image_frame_t imageFrame = {0}; + + // Verify if image has been initialized + if (image == NULL) { + return E_GENERIC; + } + + printf("\n--- Initializing Image Download (TC 64) of SRAM=%i and location=%i---\n\n", sram, location); + // Send telecommand 64 to initialize a download + error = tcInitImageDownload(sram, location, image->imageSize); + if (error != SUCCESS) { + return error; + } + + // Loop for the amount of frames that are being downloaded + for (uint16_t i = 0; i < image->framesCount; i++) { + printf("\nFRAME NUMBER = %i | Attempts:", i); + // Request image frame status until image frame is loaded in the camera buffer, + // the counter is used to ensure we don't deadlock + counter = 0; + while((imageFrameNum != i) && (counter < IMAGE_FRAME_MAX_RETRY)) { + if (isFirstRequest == 1) { + isFirstRequest = 0; + } else { + vTaskDelay(IMAGE_FRAME_INTERVAL_MS); // Delay in ms between each frame info request + } + printf(" %i ", counter); + error = tlmImageFrameInfo(&imageFrameInfo); + if(error != SUCCESS){ + return error; + } + + imageFrameNum = imageFrameInfo.imageFrameNumber; + counter++; + } + + // Collect Image Frame with TLM 64 + error = tlmImageFrame(&imageFrame); + if(error != SUCCESS) { + return error; + } + + // Store Image Frame inside master struct + image->imageFrames[i] = imageFrame; + + if (i+1 < image->framesCount) { + // Advance Image Download to Continue to the next Frame + error = tcAdvanceImageDownload(i+1); + if(error != SUCCESS) { + return error; + } + } + } + + // Give the image an ID + image->image_ID = sram + (location << 1); + + return SUCCESS; +} + +/* + * Used to created a 3D vector from a detection of both sensors + * + * @note The RADSAT-SK ADCS will only have access to SRAM1 + * @param data a struct that will contain the components of 2 3D vectors + * @return 0 on success, otherwise failure + */ +int getResultsAndTriggerNewDetection(detection_results_t *data) { + int error = 0; + tlm_detection_result_and_trigger_adcs_t sun_sensor_data = {0}; + tlm_detection_result_and_trigger_adcs_t nadir_sensor_data = {0}; + interpret_detection_result_t sun_sensor_coords = {0}; + interpret_detection_result_t nadir_sensor_coords = {0}; + + printf("\n--------- SUN ---------"); + // Request results of Sun detection with TLM 22 + error = tlmSensorResultAndDetection(&sun_sensor_data, sensor1_sram1); + if (error != 0) return error; + + printf("\n-------- NADIR --------"); + // Request results of nadir detection with TLM 23 + error = tlmSensorResultAndDetection(&nadir_sensor_data, sensor2_sram2); + if (error != 0) return error; + + // If either detection result is "not scheduled", wait 1s and send another request + if (sun_sensor_data.detectionResult == 1 || nadir_sensor_data.detectionResult == 1) { + vTaskDelay(1000); + if (sun_sensor_data.detectionResult == 1) { + printf("\n--------- SUN ---------"); + error = tlmSensorResultAndDetection(&sun_sensor_data, sensor1_sram1); + if (error != 0) return error; + } + if (nadir_sensor_data.detectionResult == 1) { + printf("\n-------- NADIR --------"); + error = tlmSensorResultAndDetection(&nadir_sensor_data, sensor2_sram2); + if (error != 0) return error; + } + } + + // If detection is successful, calculate the detection results + if (sun_sensor_data.detectionResult == 7) { + sun_sensor_coords = calculateDetectionVector(sun_sensor_data.alpha, sun_sensor_data.beta); + } + if (nadir_sensor_data.detectionResult == 7) { + nadir_sensor_coords = calculateDetectionVector(nadir_sensor_data.alpha, nadir_sensor_data.beta); + } + + // Fill struct with data + data->sunSensorX = sun_sensor_coords.X_AXIS; + data->sunSensorY = sun_sensor_coords.Y_AXIS; + data->sunSensorZ = sun_sensor_coords.Z_AXIS; + data->nadirSensorX = nadir_sensor_coords.X_AXIS; + data->nadirSensorY = nadir_sensor_coords.Y_AXIS; + data->nadirSensorZ = nadir_sensor_coords.Z_AXIS; + + return SUCCESS; +} + +/* + * Used to collect the telemetry on the CubeSense Camera + * + * @param cameraTelemetry a struct that will used to house all important telemetry on board + * @return 0 on success, otherwise failure + */ +int getCameraTelemetry(CameraTelemetry *cameraTelemetry) { + int error; + tlm_status_t tlmStatusStruct = {0}; + tlm_power_t tlmPowerStruct = {0}; + tlm_config_t tlmConfigStruct = {0}; + + // Grab All telemetry and check if successful while doing so + error = tlmStatus(&tlmStatusStruct); + + if (error != SUCCESS) + return error; + + error = tlmPower(&tlmPowerStruct); + + if (error != SUCCESS) + return error; + + error = tlmConfig(&tlmConfigStruct); + + if (error != SUCCESS) + return error; + + // Assign telemetry to master telemetry struct + cameraTelemetry->upTime = tlmStatusStruct.runtimeSeconds; + cameraTelemetry->powerTelemetry.current_3V3 = tlmPowerStruct.threeVcurrent; + cameraTelemetry->powerTelemetry.current_5V = tlmPowerStruct.fiveVcurrent; + cameraTelemetry->powerTelemetry.current_SRAM_1 = tlmPowerStruct.sramOneCurrent; + cameraTelemetry->powerTelemetry.current_SRAM_2 = tlmPowerStruct.sramTwoCurrent; + cameraTelemetry->powerTelemetry.overcurrent_SRAM_1 = tlmPowerStruct.sramOneOverCurrent; + cameraTelemetry->powerTelemetry.overcurrent_SRAM_2 = tlmPowerStruct.sramTwoOverCurrent; + cameraTelemetry->cameraOneTelemetry.autoAdjustMode = tlmConfigStruct.cameraOneAutoAdjustMode; + cameraTelemetry->cameraOneTelemetry.autoGainControl = tlmConfigStruct.cameraOneAGC; + cameraTelemetry->cameraOneTelemetry.blueGain = tlmConfigStruct.cameraOneBlueGain; + cameraTelemetry->cameraOneTelemetry.detectionThreshold = tlmConfigStruct.cameraOneDetectionThreshold; + cameraTelemetry->cameraOneTelemetry.exposure = tlmConfigStruct.cameraOneExposure; + cameraTelemetry->cameraOneTelemetry.redGain = tlmConfigStruct.cameraOneRedGain; + cameraTelemetry->cameraTwoTelemetry.autoAdjustMode = tlmConfigStruct.cameraTwoAutoAdjustMode; + cameraTelemetry->cameraTwoTelemetry.autoGainControl = tlmConfigStruct.cameraTwoAGC; + cameraTelemetry->cameraTwoTelemetry.blueGain = tlmConfigStruct.cameraTwoBlueGain; + cameraTelemetry->cameraTwoTelemetry.detectionThreshold = tlmConfigStruct.cameraTwoDetectionThreshold; + cameraTelemetry->cameraTwoTelemetry.exposure = tlmConfigStruct.cameraTwoExposure; + cameraTelemetry->cameraTwoTelemetry.redGain = tlmConfigStruct.cameraTwoRedGain; + + return SUCCESS; +} + +/* + * In the case the ground station wants to adjust camera settings + * + * @param cameraTelemetry a struct that will contain the values that want to be adjusted + * */ +int setCameraConfig(CameraTelemetry *cameraTelemetry) { + int error; + + // Update Auto adjust for camera one + error = tcCameraAutoAdjust(SUN_SENSOR, cameraTelemetry->cameraOneTelemetry.autoAdjustMode); + if(error != SUCCESS) { + return error; + } + + // Update Auto adjust for camera two + error = tcCameraAutoAdjust(NADIR_SENSOR, cameraTelemetry->cameraTwoTelemetry.autoAdjustMode); + if (error != SUCCESS) { + return error; + } + + // Update detection Threshold for camera one + error = tcCameraDetectionThreshold(SUN_SENSOR, cameraTelemetry->cameraOneTelemetry.detectionThreshold); + if (error != SUCCESS) { + return error; + } + + // Update detection Threshold for camera two + error = tcCameraDetectionThreshold(NADIR_SENSOR, cameraTelemetry->cameraTwoTelemetry.detectionThreshold); + if (error != SUCCESS) { + return error; + } + + // Update camera one settings + error = tcCameraSettings(SUN_SENSOR, cameraTelemetry->cameraOneTelemetry.exposure, + cameraTelemetry->cameraOneTelemetry.autoGainControl, + cameraTelemetry->cameraOneTelemetry.blueGain, + cameraTelemetry->cameraOneTelemetry.redGain); + if(error != SUCCESS) { + return error; + } + + // Update camera Two settings + error = tcCameraSettings(NADIR_SENSOR, cameraTelemetry->cameraTwoTelemetry.exposure, + cameraTelemetry->cameraTwoTelemetry.autoGainControl, + cameraTelemetry->cameraTwoTelemetry.blueGain, + cameraTelemetry->cameraTwoTelemetry.redGain); + if(error != SUCCESS) { + return error; + } + + return SUCCESS; +} + +/* + * Filtering out unwanted images. Use the images within range of 40 to 240 on the grayscale range + * + * @post return 0 if image is in desired range 1 if it is not + * @param image where the entire photo will reside with an image ID + * @return 0 on success, otherwise failure + * */ +int SaturationFilter(full_image_t *image) { + uint16_t sumOfAverages = 0; + uint16_t allFrameAverage = 0; + + for (int i = 0; i < image->framesCount; i++) { + int sum = 0; + //average of one frame's bytes + for (int j = 0; j < FRAME_BYTES; j++) { + sum += image->imageFrames[i].image_bytes[j]; + } + sumOfAverages += sum/FRAME_BYTES; + } + + //average of all the average of all frame bytes + allFrameAverage = sumOfAverages/image->framesCount; + + // checking if the overall average is in reasonable range + if (allFrameAverage < 40 || allFrameAverage > 240) { + return 1; + } + + return SUCCESS; +} + +/* + * Send a reset telecommand to reset the given CubeSense system (TC 0) + * + * @param resetOption defines the system to reset, 1 = reset communication, 2 = reset cameras, 3 = reset MCU + * @return 0 on success, otherwise failure + */ +int executeReset(uint8_t resetOption) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_0_LEN); + sizeOfBuffer = TELECOMMAND_0_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_0; + + // Fill buffer with detection threshold + telecommandBuffer[TELECOMMAND_OFFSET_0] = resetOption; + + // Send telecommand + error = uartTransmit(UART_CAMERA_BUS, telecommandBuffer, sizeOfBuffer); // No escaping needed + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/*************************************************************************************************** + PRIVATE FUNCTIONS +***************************************************************************************************/ +/* + * Used to retrieve the status of the Camera (TLM ID 0) + * + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +static int tlmStatus(tlm_status_t *telemetry_reply) { + uint8_t *telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_0; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_8); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_0_LEN); + + if (error != 0) { + free(telemetryBuffer); + return E_GENERIC; + } + + // Fill telemetry reply, data from uart read starts at index two + telemetry_reply->nodeType = telemetryBuffer[TELEMETRY_OFFSET_0]; + telemetry_reply->interfaceVersion = telemetryBuffer[TELEMETRY_OFFSET_1]; + telemetry_reply->firmwareVersionMajor = telemetryBuffer[TELEMETRY_OFFSET_2]; + telemetry_reply->firmwareVersionMinor = telemetryBuffer[TELEMETRY_OFFSET_3]; + memcpy(&telemetry_reply->runtimeSeconds, &telemetryBuffer[TELEMETRY_OFFSET_4], sizeof(telemetry_reply->runtimeSeconds)); + memcpy(&telemetry_reply->runtimeMSeconds, &telemetryBuffer[TELEMETRY_OFFSET_6], sizeof(telemetry_reply->runtimeMSeconds)); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to retrieve the power status of the Camera (TLM ID 26) + * + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +static int tlmPower(tlm_power_t *telemetry_reply) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_26; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) + return error; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_10); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_26_LEN); + + if (error != 0){ + free(telemetryBuffer); + return error; + } + + // Fill telemetry reply, data from uart read starts at index two + memcpy(&telemetry_reply->threeVcurrent, &telemetryBuffer[TELEMETRY_OFFSET_0], sizeof(telemetry_reply->threeVcurrent)); + memcpy(&telemetry_reply->sramOneCurrent, &telemetryBuffer[TELEMETRY_OFFSET_2], sizeof(telemetry_reply->sramOneCurrent)); + memcpy(&telemetry_reply->sramTwoCurrent, &telemetryBuffer[TELEMETRY_OFFSET_4], sizeof(telemetry_reply->sramTwoCurrent)); + memcpy(&telemetry_reply->fiveVcurrent, &telemetryBuffer[TELEMETRY_OFFSET_6], sizeof(telemetry_reply->fiveVcurrent)); + telemetry_reply->sramOneOverCurrent = telemetryBuffer[TELEMETRY_OFFSET_8]; + telemetry_reply->sramTwoOverCurrent = telemetryBuffer[TELEMETRY_OFFSET_9]; + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to retrieve the configuration status of the Camera (TLM ID 40) + * + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +static int tlmConfig(tlm_config_t *telemetry_reply) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_40; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + // Error Check on uartTransmit + if (error != 0) + return error; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_14); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_40_LEN); + + // Error Check on uartRecieve, if error, free allocated buffer + if (error != 0) { + free(telemetryBuffer); + return error; + } + + // Fill telemetry reply, data from uart read starts at index two + telemetry_reply->cameraOneDetectionThreshold = telemetryBuffer[TELEMETRY_OFFSET_0]; + telemetry_reply->cameraTwoDetectionThreshold = telemetryBuffer[TELEMETRY_OFFSET_1]; + telemetry_reply->cameraOneAutoAdjustMode = telemetryBuffer[TELEMETRY_OFFSET_2]; + memcpy(&telemetry_reply->cameraOneExposure, &telemetryBuffer[TELEMETRY_OFFSET_3], sizeof(telemetry_reply->cameraOneExposure)); + telemetry_reply->cameraOneAGC = telemetryBuffer[TELEMETRY_OFFSET_5]; + telemetry_reply->cameraOneBlueGain = telemetryBuffer[TELEMETRY_OFFSET_6]; + telemetry_reply->cameraOneRedGain = telemetryBuffer[TELEMETRY_OFFSET_7]; + telemetry_reply->cameraTwoAutoAdjustMode = telemetryBuffer[TELEMETRY_OFFSET_8]; + memcpy(&telemetry_reply->cameraTwoExposure, &telemetryBuffer[TELEMETRY_OFFSET_9], sizeof(telemetry_reply->cameraTwoExposure)); + telemetry_reply->cameraTwoAGC = telemetryBuffer[TELEMETRY_OFFSET_11]; + telemetry_reply->cameraTwoBlueGain = telemetryBuffer[TELEMETRY_OFFSET_12]; + telemetry_reply->cameraTwoRedGain = telemetryBuffer[TELEMETRY_OFFSET_13]; + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to receive telemetry for image frame (TLM ID 64) + * + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tlmImageFrame(tlm_image_frame_t *telemetry_reply) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_64; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_128); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_64_LEN); + + if (error != 0) { + free(telemetryBuffer); + return E_GENERIC; + } + + // Fill telemetry reply, data from uart read starts at index two + memcpy(&telemetry_reply->image_bytes, &telemetryBuffer[TELEMETRY_OFFSET_0], sizeof(telemetry_reply->image_bytes)); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to adjust the detection threshold for the given camera + * + * @param camera defines the camera to which the threshold will be applied + * @param detectionThreshold the value for threshold + * @return error on telecommand attempt, 0 on success, otherwise failure + */ +static int tcCameraDetectionThreshold(uint8_t camera, uint8_t detectionThreshold) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_40_AND_41_LEN); + sizeOfBuffer = TELECOMMAND_40_AND_41_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + if (camera == SUN_SENSOR) { + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_40; + } else if (camera == NADIR_SENSOR) { + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_41; + } else { + return E_GENERIC; + } + + // Fill buffer with detection threshold + telecommandBuffer[TELECOMMAND_OFFSET_0] = detectionThreshold; + + // Send Telecommand + error = escapeAndTransmitTelecommand(telecommandBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Used to adjust the enable/disable the auto-adjust for the given camera + * + * @param camera defines the camera to which the auto-adjust will be applied + * @param enabler Determines to enable or disable the auto adjust + * @return error of telecommand. 0 on success, otherwise failure + */ +static int tcCameraAutoAdjust(uint8_t camera, uint8_t enabler) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_42_AND_44_LEN); + sizeOfBuffer = TELECOMMAND_42_AND_44_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + if (camera == SUN_SENSOR) { + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_42; + } else if (camera == NADIR_SENSOR) { + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_44; + } else { + return E_GENERIC; + } + + // Fill buffer with detection threshold + telecommandBuffer[TELECOMMAND_OFFSET_0] = enabler; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telecommandBuffer, sizeOfBuffer); // No escaping needed + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Used to Adjust the camera 1 settings and change the exposure, gain, blue, red control + * + * @param camera defines the camera to which the settings will be applied + * @param exposureTime changes the exposure value register + * @param AGC changes the gain control register + * @param blue_gain changes the blue gain control register + * @param red_gain changes the red gain control register + */ +static int tcCameraSettings(uint8_t camera, uint16_t exposureTime, uint8_t AGC, uint8_t blue_gain, uint8_t red_gain) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_43_AND_45_LEN); + sizeOfBuffer = TELECOMMAND_43_AND_45_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + if (camera == SUN_SENSOR) { + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_43; + } else if (camera == NADIR_SENSOR) { + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_45; + } else { + return E_GENERIC; + } + + // Fill buffer with exposureTime + memcpy(&telecommandBuffer[TELECOMMAND_OFFSET_0], &exposureTime, sizeof(exposureTime)); + + // Fill buffer with AGC + telecommandBuffer[TELECOMMAND_OFFSET_2] = AGC; + + // Fill buffer with blue gain + telecommandBuffer[TELECOMMAND_OFFSET_3] = blue_gain; + + // Fill buffer with red gain + telecommandBuffer[TELECOMMAND_OFFSET_4] = red_gain; + + // Send Telecommand + error = escapeAndTransmitTelecommand(telecommandBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Get the number of frames required for a given CubeSense image size + * + * @param size defines the resolution of the image to download, 0 = 1024x1024, 1 = 512x512, 2 = 256x256, 3 = 128x128, 4 = 64x64 + * @return number of frames corresponding to the desired size + */ +static uint16_t getNumberOfFramesFromSize(uint8_t size) { + switch(size) { + case 0: return 8192; // 1024x1024 + case 1: return 2048; // 512x512 + case 2: return 512; // 256x256 + case 3: return 128; // 128x128 + case 4: return 32; // 64x64 + default: return 32; + } +} diff --git a/radsat-sk/operation/subsystems/camera/RCamera.h b/radsat-sk/operation/subsystems/camera/RCamera.h new file mode 100644 index 00000000..e157a873 --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RCamera.h @@ -0,0 +1,84 @@ +/** + * @file RCamera.h + * @date December 23, 2021 + * @author Shiva Moghtaderi (shm153) & Atharva Kulkarni (iya789) + */ + +#ifndef RCAMERA_H_ +#define RCAMERA_H_ + +#include + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ +/*number of bytes in one frame of an image*/ +#define FRAME_BYTES 128 + +/*maximum number of bytes in one image */ +#define MAXIMUM_BYTES 1048576 + +/* Struct used to define ADCS Function*/ +typedef struct _detection_results_t { + float sunSensorX; + float sunSensorY; + float sunSensorZ; + float nadirSensorX; + float nadirSensorY; + float nadirSensorZ; +} detection_results_t; + +/* Struct that holds all power related telemetry */ +typedef struct _CameraTelemetry_PowerTelemetry { + float current_3V3; + float current_5V; + float current_SRAM_1; + float current_SRAM_2; + uint8_t overcurrent_SRAM_1; + uint8_t overcurrent_SRAM_2; +} CameraTelemetry_PowerTelemetry; + +/* Struct that holds all Camera configuration related telemetry */ +typedef struct _CameraTelemetry_ConfigurationTelemetry { + uint8_t detectionThreshold; + uint8_t autoAdjustMode; + uint16_t exposure; + uint8_t autoGainControl; + uint8_t blueGain; + uint8_t redGain; +} CameraTelemetry_ConfigurationTelemetry; + +/* Struct for Camera Telemetry Collection Function */ +typedef struct _CameraTelemetry { + uint16_t upTime; + CameraTelemetry_PowerTelemetry powerTelemetry; + CameraTelemetry_ConfigurationTelemetry cameraOneTelemetry; + CameraTelemetry_ConfigurationTelemetry cameraTwoTelemetry; +} CameraTelemetry; + +/* Struct for telemetry image frame */ +typedef struct _tlm_image_frame_t { + uint8_t image_bytes[FRAME_BYTES]; +} tlm_image_frame_t; + +/* Struct that holds full image with ID */ +typedef struct _full_image_t { + uint8_t image_ID; + uint8_t imageSize; // CubeSense's size (0 to 4) + uint16_t framesCount; + tlm_image_frame_t imageFrames[]; +} full_image_t; + +/**************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +full_image_t * initializeNewImage(uint8_t size); +int captureImage(uint8_t camera, uint8_t sram, uint8_t location); +int downloadImage(uint8_t sram, uint8_t location, full_image_t *image); +int getResultsAndTriggerNewDetection(detection_results_t *data); +int setCameraConfig(CameraTelemetry *cameraTelemetry); +int getCameraTelemetry(CameraTelemetry *cameraTelemetry); +int executeReset(uint8_t resetOption); + +#endif /* RCAMERA_H_ */ diff --git a/radsat-sk/operation/subsystems/camera/RCameraCommon.c b/radsat-sk/operation/subsystems/camera/RCameraCommon.c new file mode 100644 index 00000000..0b630174 --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RCameraCommon.c @@ -0,0 +1,142 @@ +/** + * @file RCameraCommon.c + * @date August 19, 2022 + */ + +#include +#include +//#include +#include +#include +//#include +//#include + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ +/* + * Used to dynamically allocated buffer sizes as each telecommand and telemetry + * require different sizes + * + * @note must use Free() to free the allocated memory when finished using the buffer + * @param message_size defines how many data bytes are required in the buffer + * @return dynamically allocated buffer + * */ +uint8_t * MessageBuilder(uint8_t message_size) { + + // Define the total size the buffer should be + uint8_t total_buffer_length = message_size + BASE_MESSAGE_LEN; + + // Dynamically Allocate a buffer for telecommand and telemetry + uint8_t* buffer = malloc(sizeof(*buffer) * total_buffer_length); + + // Fill buffer with default values + for(uint8_t i = 0; i < total_buffer_length; i++) { + if (i == 0) { + buffer[i] = ESCAPE_CHARACTER; + } + else if (i == 1) { + buffer[i] = START_IDENTIFIER; + } + else if (i == total_buffer_length-2) { + buffer[i] = ESCAPE_CHARACTER; + } + else if (i == total_buffer_length-1) { + buffer[i] = END_IDENTIFIER; + } + else { + buffer[i] = FILLER; + } + } + + return buffer; +} + + +/* + * Used to escape and transmit UART telecommand messages + * + * @param telecommandBuffer is a pointer to a telecommand buffer to escape and transmit + * @param messageSize defines how many data bytes are required in the buffer + * @return error of telecommand attempt. 0 on success, otherwise failure + */ +int escapeAndTransmitTelecommand(uint8_t *telecommandBuffer, uint8_t messageSize) { + uint8_t *escapedBuffer; + uint8_t escapeBufferIndex; + uint8_t escapeCharacterCount = 0; + uint8_t newMessageSize; + int error; + + // Count the number of characters that should be escaped in the data bytes + for(uint8_t i = TELECOMMAND_OFFSET_0; i < messageSize - 2; i++) { + if (telecommandBuffer[i] == ESCAPE_CHARACTER) { + escapeCharacterCount++; + } + } + + // If no escaping is needed, send the telecommand as is and let function caller free the buffer + if (escapeCharacterCount == 0) { + return uartTransmit(UART_CAMERA_BUS, telecommandBuffer, messageSize); + } + + // Calculate the new buffer size + newMessageSize = messageSize + escapeCharacterCount; + + // Build a new telecommand buffer + escapedBuffer = MessageBuilder(newMessageSize - BASE_MESSAGE_LEN); + + // Fill the new buffer adding escape characters + escapeBufferIndex = MESSAGE_ID_OFFSET; + for(uint8_t i = MESSAGE_ID_OFFSET; i < messageSize - 2; i++, escapeBufferIndex++) { + escapedBuffer[escapeBufferIndex] = telecommandBuffer[i]; + if (telecommandBuffer[i] == ESCAPE_CHARACTER) { + // Add an extra escape character + escapeBufferIndex++; + escapedBuffer[escapeBufferIndex] = telecommandBuffer[i]; + } + } + + // Send the escaped telecommand buffer + error = uartTransmit(UART_CAMERA_BUS, escapedBuffer, newMessageSize); + + // Free the new escaped buffer + free(escapedBuffer); + + return error; +} + + +/* + * Used to receive and unescape UART telemetry messages + * + * @param telemetryBuffer is a pointer to a telemetry buffer where data will be stored + * @param messageSize defines how many data bytes are required in the buffer + * @return error of telecommand attempt. 0 on success, otherwise failure + */ +int receiveAndUnescapeTelemetry(uint8_t *telemetryBuffer, uint8_t messageSize) { + int error; + uint8_t currentByte; + uint8_t isEscapeCharacter = 0; + + for(uint8_t i = 0; i < messageSize; i++) { + // Read a single byte at a time to catch escape characters + error = uartReceive(UART_CAMERA_BUS, ¤tByte, 1); + if (error != 0) { + return E_GENERIC; + } + + // Check for escaped characters in the data bytes + if (i >= TELEMETRY_OFFSET_0 && i < messageSize - 2) { + if (isEscapeCharacter == 0 && currentByte == ESCAPE_CHARACTER) { + isEscapeCharacter = 1; + i--; // Current byte is escaping the following data byte, so ignore it since it is an extra byte + } else { + // Assign the data byte to the buffer + telemetryBuffer[i] = currentByte; + isEscapeCharacter = 0; + } + } + } + + return SUCCESS; +} diff --git a/radsat-sk/operation/subsystems/camera/RCameraCommon.h b/radsat-sk/operation/subsystems/camera/RCameraCommon.h new file mode 100644 index 00000000..40893e2a --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RCameraCommon.h @@ -0,0 +1,68 @@ +/** + * @file RCameraCommon.h + * @date August 19, 2022 + */ + +#ifndef RCAMERACOMMON_H_ +#define RCAMERACOMMON_H_ + +#include + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ +#define TELECOMMAND_OFFSET_0 ((uint8_t) 3) +#define TELECOMMAND_OFFSET_1 ((uint8_t) 4) +#define TELECOMMAND_OFFSET_2 ((uint8_t) 5) +#define TELECOMMAND_OFFSET_3 ((uint8_t) 6) +#define TELECOMMAND_OFFSET_4 ((uint8_t) 7) +#define TELECOMMAND_RESPONSE_OFFSET ((uint8_t) 2) +#define TELECOMMAND_RESPONSE_LEN ((uint16_t) 1) + +#define TELEMETRY_REQUEST_LEN ((uint8_t) 1) +#define TELEMETRY_REPLY_SIZE_1 ((uint8_t) 1) +#define TELEMETRY_REPLY_SIZE_3 ((uint8_t) 3) +#define TELEMETRY_REPLY_SIZE_6 ((uint8_t) 6) +#define TELEMETRY_REPLY_SIZE_8 ((uint8_t) 8) +#define TELEMETRY_REPLY_SIZE_10 ((uint8_t) 10) +#define TELEMETRY_REPLY_SIZE_14 ((uint8_t) 14) +#define TELEMETRY_REPLY_SIZE_128 ((uint8_t) 128) + +#define TELEMETRY_OFFSET_0 ((uint8_t) 2) +#define TELEMETRY_OFFSET_1 ((uint8_t) 3) +#define TELEMETRY_OFFSET_2 ((uint8_t) 4) +#define TELEMETRY_OFFSET_3 ((uint8_t) 5) +#define TELEMETRY_OFFSET_4 ((uint8_t) 6) +#define TELEMETRY_OFFSET_5 ((uint8_t) 7) +#define TELEMETRY_OFFSET_6 ((uint8_t) 8) +#define TELEMETRY_OFFSET_7 ((uint8_t) 9) +#define TELEMETRY_OFFSET_8 ((uint8_t) 10) +#define TELEMETRY_OFFSET_9 ((uint8_t) 11) +#define TELEMETRY_OFFSET_10 ((uint8_t) 12) +#define TELEMETRY_OFFSET_11 ((uint8_t) 13) +#define TELEMETRY_OFFSET_12 ((uint8_t) 14) +#define TELEMETRY_OFFSET_13 ((uint8_t) 15) + +#define MESSAGE_ID_OFFSET ((uint8_t) 2) // For both telecommand and telemetry + +#define BASE_MESSAGE_LEN ((uint8_t) 4) +#define ESCAPE_CHARACTER ((uint16_t) 0x1F) +#define START_IDENTIFIER ((uint16_t) 0x7F) +#define FILLER ((uint16_t) 0x00) +#define END_IDENTIFIER ((uint16_t) 0xFF) + +#define SUN_SENSOR ((uint8_t) 0) +#define NADIR_SENSOR ((uint8_t) 1) +#define SRAM1 ((uint8_t) 0) +#define SRAM2 ((uint8_t) 1) +#define TOP_HALVE ((uint8_t) 0) +#define BOTTOM_HALVE ((uint8_t) 1) + +/**************************************************************************************************** + PUBLIC API +***************************************************************************************************/ +uint8_t* MessageBuilder(uint8_t response_size); +int escapeAndTransmitTelecommand(uint8_t *telecommandBuffer, uint8_t messageSize); +int receiveAndUnescapeTelemetry(uint8_t *telemetryBuffer, uint8_t messageSize); + +#endif /* RCAMERACOMMON_H_ */ diff --git a/radsat-sk/operation/subsystems/camera/RImage.c b/radsat-sk/operation/subsystems/camera/RImage.c new file mode 100644 index 00000000..8c3349e7 --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RImage.c @@ -0,0 +1,417 @@ +/** + * @file RImage.c + * @date March 26, 2022 + * @author Addi Amaya (Caa746) + */ + +#include +#include +#include +#include +#include +#include +#include + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ + +#define TELECOMMAND_21 ((uint8_t) 0x15) +#define TELECOMMAND_64 ((uint8_t) 0x40) +#define TELECOMMAND_65 ((uint8_t) 0x41) + +#define TELECOMMAND_21_LEN ((uint8_t) 4) +#define TELECOMMAND_64_LEN ((uint8_t) 4) +#define TELECOMMAND_65_LEN ((uint8_t) 3) + +#define TELEMETRY_3 ((uint8_t) 0x83) +#define TELEMETRY_20 ((uint8_t) 0x94) +#define TELEMETRY_21 ((uint8_t) 0x95) +#define TELEMETRY_65 ((uint8_t) 0xC1) + +#define TELEMETRY_3_LEN ((uint8_t) 7) +#define TELEMETRY_20_AND_21_LEN ((uint8_t) 10) +#define TELEMETRY_65_LEN ((uint8_t) 7) + +#define SIZE_OF_BITMAP ((uint8_t) 4096) + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ +/* + * Used to send image capture telecommand (TC ID 21) + * + * @param camera defines which camera to use on Cubesense, camera 1 = 0, camera 2 = 1 + * @param SRAM defines which SRAM to use on Cubesense, SRAM1 = 0, SRAM2 = 1 + * @param location defines which SRAM slot to use within selected SRAM + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tcImageCapture(uint8_t camera, uint8_t SRAM, uint8_t location) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_21_LEN); + sizeOfBuffer = TELECOMMAND_21_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_21; + + // Fill buffer with Camera selection + telecommandBuffer[TELECOMMAND_OFFSET_0] = camera; + + // Fill buffer with SRAM + telecommandBuffer[TELECOMMAND_OFFSET_1] = SRAM; + + // Fill buffer with Location in SRAM + telecommandBuffer[TELECOMMAND_OFFSET_2] = location; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telecommandBuffer, sizeOfBuffer); // No escaping needed + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Used to receive telemetry for sensor results (TLM ID 20 & 21) + * + * @param camera defines the selected sensor from which the detection result will be received + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tlmSensorResult(uint8_t camera, tlm_detection_result_and_trigger_t *telemetry_reply) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + if (camera == SUN_SENSOR) { + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_20; + } else if (camera == NADIR_SENSOR) { + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_21; + } else { + return E_GENERIC; + } + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_6); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_20_AND_21_LEN); + + if (error != 0) { + free(telemetryBuffer); + return E_GENERIC; + } + + // Fill telemetry reply + memcpy(&telemetry_reply->alpha, &telemetryBuffer[TELEMETRY_OFFSET_0], sizeof(telemetry_reply->alpha)); + memcpy(&telemetry_reply->beta, &telemetryBuffer[TELEMETRY_OFFSET_2], sizeof(telemetry_reply->beta)); + telemetry_reply->captureResult = telemetryBuffer[TELEMETRY_OFFSET_4]; + telemetry_reply->detectionResult = telemetryBuffer[TELEMETRY_OFFSET_5]; + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to send image capture telecommand (TC ID 64) + * + * @param SRAM defines which SRAM to use on Cubesense, SRAM 1 = 0, SRAM 2 = 1 + * @param location defines which SRAM slot to use within selected SRAM, 0 = top, 1 = bottom + * @param size defines the resolution of the image to download, 4 = 64x64, 1 = 512x512, 0 = 1024x1024 + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tcInitImageDownload(uint8_t SRAM, uint8_t location, uint8_t size) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_64_LEN); + sizeOfBuffer = TELECOMMAND_64_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_64; + + // Fill buffer with Camera selection + telecommandBuffer[TELECOMMAND_OFFSET_0] = SRAM; + + // Fill buffer with SRAM + telecommandBuffer[TELECOMMAND_OFFSET_1] = location; + + // Fill buffer with Location in SRAM + telecommandBuffer[TELECOMMAND_OFFSET_2] = size; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telecommandBuffer, sizeOfBuffer); // No escaping needed + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Used to receive telemetry for image frame (TLM ID 65) + * + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tlmImageFrameInfo(tlm_image_frame_info_t *telemetry_reply) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_65; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_3); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_65_LEN); + + if (error != 0) { + free(telemetryBuffer); + return E_GENERIC; + } + + // Fill telemetry reply, data from uart read starts at index two + memcpy(&telemetry_reply->imageFrameNumber, &telemetryBuffer[TELEMETRY_OFFSET_0], sizeof(telemetry_reply->imageFrameNumber)); + telemetry_reply->checksum = telemetryBuffer[TELEMETRY_OFFSET_2]; + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to send advance image capture telecommand (TC ID 65) + * + * @param NextFrameNumber number of next frame to be loaded + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tcAdvanceImageDownload(uint16_t nextFrameNumber) { + uint8_t *telecommandBuffer; + uint8_t *telecommandResponse; + uint16_t sizeOfBuffer; + uint8_t tcErrorFlag; + int error; + + // Dynamically allocate a buffer to hold the Telecommand message with header and footer implemented + telecommandBuffer = MessageBuilder(TELECOMMAND_65_LEN); + sizeOfBuffer = TELECOMMAND_65_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with Telecommand ID + telecommandBuffer[MESSAGE_ID_OFFSET] = TELECOMMAND_65; + + // Fill buffer with Next frame number + memcpy(&telecommandBuffer[TELECOMMAND_OFFSET_0], &nextFrameNumber, sizeof(nextFrameNumber)); + + // Send Telecommand + error = escapeAndTransmitTelecommand(telecommandBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telecommandBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telecommand message with header and footer implemented + telecommandResponse = MessageBuilder(TELECOMMAND_RESPONSE_LEN); + sizeOfBuffer = TELECOMMAND_RESPONSE_LEN + BASE_MESSAGE_LEN; + + // Read automatically reply to telecommand + error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); + + if (error != 0) { + free(telecommandResponse); + return E_GENERIC; + } + + // Receive the telecommand response from buffer + tcErrorFlag = telecommandResponse[TELECOMMAND_RESPONSE_OFFSET]; + + // Free the dynamically allocated buffer + free(telecommandResponse); + + if (tcErrorFlag != 0) { + return E_GENERIC; + } + + return SUCCESS; +} + +/* + * Used to receive telemetry for telecommand acknoledgment (TLM ID 3) + * + * @param telemetry_reply struct that holds all the information from the telemetry response + * @return error of telecommand attempt. 0 on success, otherwise failure + * */ +int tlmTelecommandAcknowledge(tlm_telecommand_ack_t *telemetry_reply) { + uint8_t* telemetryBuffer; + uint16_t sizeOfBuffer; + int error; + + // Ensure the input pointers are not NULL + if (telemetry_reply == 0) + return E_GENERIC; + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REQUEST_LEN); + sizeOfBuffer = TELEMETRY_REQUEST_LEN + BASE_MESSAGE_LEN; + + // Fill buffer with telemetry ID + telemetryBuffer[MESSAGE_ID_OFFSET] = TELEMETRY_3; + + // Send Telemetry Request + error = uartTransmit(UART_CAMERA_BUS, telemetryBuffer, sizeOfBuffer); + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + if (error != 0) { + return E_GENERIC; + } + + // Dynamically allocate a buffer to hold the telemetry message with header and footer implemented + telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_3); + + // Reading Automatic reply from CubeSense regarding status of Telemetry request + error = uartReceive(UART_CAMERA_BUS, telemetryBuffer, TELEMETRY_3_LEN); // No unescaping needed + + if (error != 0) { + free(telemetryBuffer); + return E_GENERIC; + } + + // Fill telemetry reply, data from uart read starts at index two + memcpy(&telemetry_reply->tc_error_flag, &telemetryBuffer[TELEMETRY_OFFSET_2], sizeof(telemetry_reply->tc_error_flag)); + + + // Free the dynamically allocated buffer + free(telemetryBuffer); + + return SUCCESS; +} + +/* + * Used to calculate the mean value of bit map + * + * @param image the downloaded array of bytes from the camera + * @return the mean value of the for the image + */ +int calculateMeanOfTheImage(uint8_t *image) { + + float sum,mean; + uint8_t n = SIZE_OF_BITMAP; + + for (uint8_t j = 0 ; j < n; ++j) { + sum = sum + image[j]; + } + + mean = sum / n; + + return mean; +} diff --git a/radsat-sk/operation/subsystems/camera/RImage.h b/radsat-sk/operation/subsystems/camera/RImage.h new file mode 100644 index 00000000..8d705e6f --- /dev/null +++ b/radsat-sk/operation/subsystems/camera/RImage.h @@ -0,0 +1,42 @@ +/** + * @file RImage.h + * @date March 26, 2022 + * @author Addi Amaya (Caa746) + */ + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ + +/* Struct for telemetry Detection result and Trigger, ID 20-25 */ +typedef struct _tlm_detection_result_and_trigger_t { + uint16_t alpha; + uint16_t beta; + uint8_t captureResult; + uint8_t detectionResult; +} tlm_detection_result_and_trigger_t; + +/* Struct for telemetry image frame info, ID 65 */ +typedef struct _tlm_image_frame_info_t { + uint16_t imageFrameNumber; + uint8_t checksum; +} tlm_image_frame_info_t; + +/* Struct for telemetry image frame info, ID 3 */ +typedef struct _tlm_telecommand_ack_t { + uint8_t last_tc_id; + uint8_t processed_flag; + uint8_t tc_error_flag; +} tlm_telecommand_ack_t; + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +int tcAdvanceImageDownload(uint16_t nextFrameNumber); +int tcImageCapture(uint8_t camera, uint8_t SRAM, uint8_t location); +int tlmTelecommandAcknowledge(tlm_telecommand_ack_t *telemetry_reply); +int tlmSensorResult(uint8_t camera, tlm_detection_result_and_trigger_t *telemetry_reply); +int tcInitImageDownload(uint8_t SRAM, uint8_t location, uint8_t size); +int tlmImageFrameInfo(tlm_image_frame_info_t *telemetry_reply); +int calculateMeanOfTheImage(uint8_t *image); diff --git a/radsat-sk/src/tasks/RAdcsCaptureTask.c b/radsat-sk/src/tasks/RAdcsCaptureTask.c index 7a7189b2..9b4f938d 100644 --- a/radsat-sk/src/tasks/RAdcsCaptureTask.c +++ b/radsat-sk/src/tasks/RAdcsCaptureTask.c @@ -6,7 +6,7 @@ #include #include - +#include #include #include @@ -19,7 +19,7 @@ #define ADCS_CAPTURES_PER_HOUR (1) /** ADCS Capture Task delay (in ms). */ -#define ADCS_CAPURE_TASK_DELAY_MS (MS_PER_HOUR / ADCS_CAPTURES_PER_HOUR) +#define ADCS_CAPTURE_TASK_DELAY_MS (MS_PER_HOUR / ADCS_CAPTURES_PER_HOUR) /*************************************************************************************************** @@ -37,6 +37,9 @@ void AdcsCaptureTask(void* parameters) { debugPrint("AdcsCaptureTask(): About to capture ADCS data.\n"); - vTaskDelay(ADCS_CAPURE_TASK_DELAY_MS); + // TODO + // To get detection results and trigger new detection, use --> "getResultsAndTriggerNewDetection(...)" + + vTaskDelay(ADCS_CAPTURE_TASK_DELAY_MS); } } diff --git a/radsat-sk/src/tasks/RImageCaptureTask.c b/radsat-sk/src/tasks/RImageCaptureTask.c index b26cb696..2ed8413d 100644 --- a/radsat-sk/src/tasks/RImageCaptureTask.c +++ b/radsat-sk/src/tasks/RImageCaptureTask.c @@ -6,7 +6,7 @@ #include #include - +#include #include #include @@ -19,7 +19,7 @@ #define IMAGE_CAPTURES_PER_WEEK (1) /** Image Capture Task delay (in ms). */ -#define IMAGE_CAPURE_TASK_DELAY_MS (MS_PER_WEEK / IMAGE_CAPTURES_PER_WEEK) +#define IMAGE_CAPTURE_TASK_DELAY_MS (MS_PER_WEEK / IMAGE_CAPTURES_PER_WEEK) /*************************************************************************************************** @@ -37,7 +37,12 @@ void ImageCaptureTask(void* parameters) { debugPrint("ImageCaptureTask(): About to capture an image.\n"); - vTaskDelay(IMAGE_CAPURE_TASK_DELAY_MS); + // TODO + // To initialize an image buffer, use --> "initializeNewImage(...)" + // To take a picture, use --------------> "captureImage(...)" + // To download a picture, use ----------> "downloadImage(...)" + + vTaskDelay(IMAGE_CAPTURE_TASK_DELAY_MS); } } From 2ee01435382d7053870e131193d99ae2aced3583 Mon Sep 17 00:00:00 2001 From: "Sam Hillis (sph264)" Date: Wed, 2 Nov 2022 21:28:04 -0600 Subject: [PATCH 02/10] Prevented CommunicationTxTask from accessing mutex before its declared --- radsat-sk/src/tasks/RCommunicationTasks.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/radsat-sk/src/tasks/RCommunicationTasks.c b/radsat-sk/src/tasks/RCommunicationTasks.c index 54925a5e..220be260 100644 --- a/radsat-sk/src/tasks/RCommunicationTasks.c +++ b/radsat-sk/src/tasks/RCommunicationTasks.c @@ -47,7 +47,7 @@ #define RX_MUTEX_WAIT_TICKS 100 #define TX_MUTEX_WAIT_TICKS 100 -xSemaphoreHandle stateMachineMutex; +xSemaphoreHandle stateMachineMutex = NULL; @@ -93,8 +93,7 @@ void CommunicationRxTask(void* parameters) { uint8_t messageTag = 0; int z = 0; - #define NUMMESSAGES 3 - uint16_t sizeToRead[NUMMESSAGES] = {15, 13, 13}; + uint16_t sizeToRead[3] = {15, 13, 13}; stateMachineMutex = xSemaphoreCreateMutex(); @@ -239,6 +238,11 @@ void CommunicationTxTask(void* parameters) { uint8_t txMessageSize = 0; // size (in bytes) of an outgoing frame uint8_t txMessage[TRANCEIVER_TX_MAX_FRAME_SIZE] = { 0 }; // output buffer for messages to be transmitted + // Making sure this task doesn't try to access the Mutex before it's set + while (stateMachineMutex == NULL) { + vTaskDelay(10); + } + while (1) { // getNextFrame enters the state machine, grab mutex From 3382d1345248fd0bf4c9cd1daf96f762bfdef437 Mon Sep 17 00:00:00 2001 From: William Moretti Date: Wed, 25 Jan 2023 11:58:32 -0500 Subject: [PATCH 03/10] Framework/implement FRAM storage (#213) * started implementation of data storage in FRAM * allocated 2 bytes for cursors in FRAM; started read implementation * finished implementation of FRAM in FileTransferService * removed unnecessary code * Update radsat-sk/operation/services/RFileTransferService.c Co-authored-by: Austin * removed shared variables and implemented a reset function for the file transfer in FRAM Co-authored-by: Austin Co-authored-by: William Moretti <=> --- radsat-sk/framework/fileio/RFram.c | 3 + radsat-sk/framework/fileio/RFram.h | 16 ++++ .../operation/services/RFileTransferService.c | 92 ++++++++++++++----- .../operation/services/RFileTransferService.h | 2 + 4 files changed, 90 insertions(+), 23 deletions(-) diff --git a/radsat-sk/framework/fileio/RFram.c b/radsat-sk/framework/fileio/RFram.c index 63d0244e..c3a220b0 100644 --- a/radsat-sk/framework/fileio/RFram.c +++ b/radsat-sk/framework/fileio/RFram.c @@ -34,6 +34,9 @@ int framInit(void) { int error = FRAM_start(); + if (error == SUCCESS) + initialized = 1; + // TODO: record errors (if present) to System Manager return error; diff --git a/radsat-sk/framework/fileio/RFram.h b/radsat-sk/framework/fileio/RFram.h index 32bb5b7b..27c08a2b 100644 --- a/radsat-sk/framework/fileio/RFram.h +++ b/radsat-sk/framework/fileio/RFram.h @@ -9,6 +9,22 @@ #include +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ + +/** FRAM address of the write cursor (2 bytes allocated). */ +#define FRAM_WRITE_CURSOR_ADDR (0x00) + +/** FRAM address of the read cursor (2 bytes allocated). */ +#define FRAM_READ_CURSOR_ADDR (0x02) + +/** FRAM start address of the data. */ +#define FRAM_DATA_START_ADDR (0x04) + +/** Size of each data block in FRAM (in bytes). */ +#define FRAM_DATA_FRAME_SIZE (TRANCEIVER_TX_MAX_FRAME_SIZE + 1) + /*************************************************************************************************** PUBLIC API diff --git a/radsat-sk/operation/services/RFileTransferService.c b/radsat-sk/operation/services/RFileTransferService.c index 40c9ce0f..317ebc3d 100644 --- a/radsat-sk/operation/services/RFileTransferService.c +++ b/radsat-sk/operation/services/RFileTransferService.c @@ -10,6 +10,7 @@ #include #include #include +#include /** @@ -33,21 +34,11 @@ /** Error code for failed message wrapping. */ #define ERROR_MESSAGE_WRAPPING (-2) -/** Struct that defines a prepared frame and its total size. */ -typedef struct _frame_t { - uint8_t data[TRANCEIVER_TX_MAX_FRAME_SIZE]; ///> The buffer holding the prepared frame +/** Struct that defines a prepared FRAM frame and its total size. */ +typedef struct _fram_frame_t { uint8_t size; ///> The size (in bytes) of the entire frame -} frame_t; - - -/** Local FIFO containing frames prepared for downlink. */ -static frame_t frames[MAX_FRAME_COUNT] = { 0 }; - -/** Cursor for writing to the frame FIFO (starts ahead of the read cursor). */ -static uint8_t frameWriteCursor = 1; - -/** Cursor for reading from the frame FIFO. */ -static uint8_t frameReadCursor = 0; + uint8_t data[TRANCEIVER_TX_MAX_FRAME_SIZE]; ///> The buffer holding the prepared frame +} fram_frame_t; /*************************************************************************************************** @@ -63,23 +54,38 @@ static uint8_t frameReadCursor = 0; * @return The size of the frame placed into the buffer; 0 on error. */ uint8_t fileTransferNextFrame(uint8_t* frame) { + // get the read cursor from FRAM + uint16_t frameReadCursor = 0; + framRead(&frameReadCursor, FRAM_READ_CURSOR_ADDR, 2); // ensure that there is a valid frame ahead - if (frames[frameReadCursor + 1].size == 0) + uint16_t nextFrameReadCursor = frameReadCursor + 1; + if (nextFrameReadCursor == MAX_FRAME_COUNT) + nextFrameReadCursor = 0; + + fram_frame_t fram_frame = {0}; + void* framDataAddr = FRAM_DATA_START_ADDR + (nextFrameReadCursor * FRAM_DATA_FRAME_SIZE); + framRead(&fram_frame, framDataAddr, FRAM_DATA_FRAME_SIZE); + + if (fram_frame.size == 0) return SUCCESS; // invalidate the current (now previous frame) - memset(&frames[frameReadCursor], 0, sizeof(frames[frameReadCursor])); + fram_frame_t emptyFrame = {0}; + framDataAddr = FRAM_DATA_START_ADDR + (frameReadCursor * FRAM_DATA_FRAME_SIZE); + framWrite(&emptyFrame, framDataAddr, FRAM_DATA_FRAME_SIZE); // increment the read cursor frameReadCursor++; if (frameReadCursor == MAX_FRAME_COUNT) frameReadCursor = 0; + // save read cursor value in FRAM + framWrite(&frameReadCursor, FRAM_READ_CURSOR_ADDR, 2); // transfer the new (now current) frame into the provided buffer - memcpy(frame, frames[frameReadCursor].data, frames[frameReadCursor].size); + memcpy(frame, fram_frame.data, fram_frame.size); - return frames[frameReadCursor].size; + return fram_frame.size; } @@ -90,13 +96,21 @@ uint8_t fileTransferNextFrame(uint8_t* frame) { * @return The size of the frame placed into the buffer; 0 on error. */ uint8_t fileTransferCurrentFrame(uint8_t* frame) { + // get the read cursor from FRAM + uint16_t frameReadCursor = 0; + framRead(&frameReadCursor, FRAM_READ_CURSOR_ADDR, 2); + + // read current frame from FRAM + fram_frame_t fram_frame = {0}; + void* framDataAddr = FRAM_DATA_START_ADDR + (frameReadCursor * FRAM_DATA_FRAME_SIZE); + framRead(&fram_frame, framDataAddr, FRAM_DATA_FRAME_SIZE); // only provide a frame if the cursor is pointing at a valid frame - if (frames[frameReadCursor].size > 0) - memcpy(frame, frames[frameReadCursor].data, frames[frameReadCursor].size); + if (fram_frame.size > 0) + memcpy(frame, fram_frame.data, fram_frame.size); // return the size of the frame - return frames[frameReadCursor].size; + return fram_frame.size; } @@ -119,6 +133,12 @@ int fileTransferAddMessage(const void* message, uint8_t size, uint16_t messageTa if (size == 0 || size > (uint8_t)PROTO_MAX_ENCODED_SIZE) return E_PARAM_OUTOFBOUNDS; + // get the write & read cursors from FRAM + uint16_t frameWriteCursor = 0; + uint16_t frameReadCursor = 0; + framRead(&frameWriteCursor, FRAM_WRITE_CURSOR_ADDR, 2); + framRead(&frameReadCursor, FRAM_READ_CURSOR_ADDR, 2); + // ensure we are not about to overwrite frames that have not been read if (frameWriteCursor == frameReadCursor) return ERROR_CURSOR; @@ -134,18 +154,44 @@ int fileTransferAddMessage(const void* message, uint8_t size, uint16_t messageTa memcpy(newMessageAddr, message, size); // wrap new message - frames[frameWriteCursor].size = messageWrap(&newMessage, frames[frameWriteCursor].data); + fram_frame_t fram_frame = {0}; + fram_frame.size = messageWrap(&newMessage, fram_frame.data); + + // write data in FRAM + void* framDataAddr = FRAM_DATA_START_ADDR + (frameWriteCursor * FRAM_DATA_FRAME_SIZE); + framWrite(&fram_frame, framDataAddr, FRAM_DATA_FRAME_SIZE); // return error if message wrapping failed - if (frames[frameWriteCursor].size == 0) + if (fram_frame.size == 0) return ERROR_MESSAGE_WRAPPING; // upon success, increment cursor frameWriteCursor++; if (frameWriteCursor == MAX_FRAME_COUNT) frameWriteCursor = 0; + // save write cursor value in FRAM + framWrite(&frameWriteCursor, FRAM_WRITE_CURSOR_ADDR, 2); // return success return SUCCESS; } + +/** + * Resets the file transfer content in FRAM to the following initial values: + * - Write Cursor = 1 + * - Read Cursor = 0 + * - All data frames are filled with 0. + */ +void fileTransferReset(void) { + // reset the cursors to their default values + uint16_t defaultCursors[2] = {1,0}; + framWrite(&defaultCursors, FRAM_WRITE_CURSOR_ADDR, 4); + + // reset all data frames to zero + fram_frame_t emptyFrame = {0}; + for(int i=0; i Date: Wed, 25 Jan 2023 12:06:41 -0500 Subject: [PATCH 04/10] Operation/end to end camera controls (#216) * Added CameraService & refactored camera code to fit use cases. Fixed uart receive issue during large image download. * improved image frame format for downlink; changed image frame index to circular buffer; added functions to set & get flags * added missing flags to prevent usage of cubesense while busy * removed testing code and unecessary comments Co-authored-by: w-moretti <=> --- radsat-sk/operation/services/RCameraService.c | 653 ++++++++++++++++++ radsat-sk/operation/services/RCameraService.h | 75 ++ .../operation/services/RTelecommandService.c | 89 +++ radsat-sk/operation/subsystems/camera/RADCS.c | 15 +- radsat-sk/operation/subsystems/camera/RADCS.h | 21 +- .../operation/subsystems/camera/RCamera.c | 169 +++-- .../operation/subsystems/camera/RCamera.h | 47 +- .../subsystems/camera/RCameraCommon.c | 37 +- .../subsystems/camera/RCameraCommon.h | 17 + .../operation/subsystems/camera/RImage.c | 5 + radsat-sk/src/tasks/RAdcsCaptureTask.c | 44 +- radsat-sk/src/tasks/RImageCaptureTask.c | 76 +- 12 files changed, 1111 insertions(+), 137 deletions(-) create mode 100644 radsat-sk/operation/services/RCameraService.c create mode 100644 radsat-sk/operation/services/RCameraService.h diff --git a/radsat-sk/operation/services/RCameraService.c b/radsat-sk/operation/services/RCameraService.c new file mode 100644 index 00000000..1f523adc --- /dev/null +++ b/radsat-sk/operation/services/RCameraService.c @@ -0,0 +1,653 @@ +/** + * @file RCameraService.c + * @date September 28, 2022 + * @author + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*************************************************************************************************** + DEFINITIONS & PRIVATE GLOBALS +***************************************************************************************************/ + +/* Number of attempts for image capture with successful detection results */ +#define VALID_IMAGE_RETRY_COUNT (10) + +/* Delay in milliseconds to allow image capture to be completed */ +#define IMAGE_CAPTURE_DELAY_MS (1000) + +/** Stack size (in bytes) allotted to the image download FreeRTOS Task. */ +#define DOWNLOAD_TASK_STACK_SIZE (configMINIMAL_STACK_SIZE + 50) + +/** Current camera settings and initialization flag. **/ +static CameraSettings cameraSettings = { 0 }; +//static uint8_t cameraSettingsInitialized = 0; + +/** Interval (in ms) for the automatic image and ADCS capture tasks. **/ +int adcsCaptureInterval = 0; +int imageCaptureInterval = 0; + +/** Size used for image download during automatic image capture **/ +/** 0 = 1024x1024, 1 = 512x512, 2 = 256x256, 3 = 128x128, 4 = 64x64 **/ +uint8_t imageDownloadSize = 4; + +/** Pointer to a local image frames prepared for downlink. */ +static full_image_t *image; +/** Flag indicating image is ready for downlink. **/ +static uint8_t imageReadyForDownlink = 0; +/** Flag indicating system ready for a new image capture. **/ +static uint8_t imageReadyForNewCapture = 1; + +/** Index of the current image frame prepared for downlink. */ +static int currentImageFrameIndex = -1; + +/** Flag indicating that the CubeSense is currently in use. Used to prevent conflicts. **/ +static uint8_t cubeSenseIsInUse = 0; + +/** FreeRTOS Task Handles. */ +static xTaskHandle imageDownloadTaskHandle; + +/** Image Download Task Priority. Periodically download image frames; medium priority task. */ +static const int imageDownloadTaskPriority = configMAX_PRIORITIES - 3; + +/* Struct for image download parameters and local variable */ +typedef struct _image_download_t { + uint8_t sram; + uint8_t size; +} image_download_t; +static image_download_t downloadParameters = {0}; + +/* Struct for ADCS burst measurement parameters and local variable */ +typedef struct _adcs_capture_settings_t { + uint8_t nbMeasurements; + int interval; +} adcs_capture_settings_t; +static adcs_capture_settings_t adcsSettings = {0}; + +/** Pointer to a local ADCS measurements results prepared for downlink. */ +static adcs_detection_results_t *adcsResults; +/** Flag indicating ADCS is ready for a new burst (1=ready, 0=not ready). **/ +static uint8_t adcsReadyForNewBurst = 1; + +/*************************************************************************************************** + PRIVATE FUNCTION STUBS +***************************************************************************************************/ + +void ImageDownloadTask(void* parameters); + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +/* + * Trigger a CubeSense reset. + * + * @param resetOption defines the system to reset, 1 = reset communication, 2 = reset cameras, 3 = reset MCU + * @return 0 on success, otherwise failure + */ +int requestReset(uint8_t resetOption) { + // Check for invalid reset option + if (resetOption < 1 || resetOption > 3) + return E_GENERIC; + + // Flag CubeSense as "in use" + cubeSenseIsInUse = 1; + + int error = executeReset(resetOption); + cubeSenseIsInUse = 0; + return error; +} + + +/* + * Set settings for both CubeSense cameras. + * + * @param sunSettings defines the Sun camera settings to set + * @param nadirSettings defines the nadir camera settings to set + * @return error, 0 for success, otherwise failure + */ +int setCamerasSettings(CameraSettings_ConfigurationSettings sunSettings, CameraSettings_ConfigurationSettings nadirSettings) { + // Flag CubeSense as "in use" + cubeSenseIsInUse = 1; + + int error; + + // TODO: Do we need this or not? Is the FULL struct sent via telecommand? + /*if (!cameraSettingsInitialized) { + printf("Getting camera settings...\n"); + error = getSettings(&cameraSettings); + if (error != SUCCESS) { + printf("Failed to get camera settings...\n"); + cubeSenseIsInUse = 0; + return E_GENERIC; + } + printf("Exposure = %i\n", cameraSettings.cameraTwoSettings.exposure); + printf("Detection Threshold = %i\n", cameraSettings.cameraTwoSettings.detectionThreshold); + cameraSettingsInitialized = 1; + }*/ + + cameraSettings.cameraOneSettings = sunSettings; + cameraSettings.cameraTwoSettings = nadirSettings; + + error = setSettings(&cameraSettings); + cubeSenseIsInUse = 0; + if (error != SUCCESS) { + printf("Failed to update camera settings...\n"); + return E_GENERIC; + } + + return SUCCESS; +} + + + +/* + * Set the interval used for automatic ADCS capture. + * + * @param interval defines the delay between each run of the ADCS capture task + */ +void setADCSCaptureInterval(int interval) { + adcsCaptureInterval = interval; +} + + +/* + * Get the interval used for automatic ADCS capture. + * + * @return interval, in milliseconds + */ +int getADCSCaptureInterval(void) { + return adcsCaptureInterval; +} + +/* + * Set the capture settings used for ADCS measurement, which + * will execute a sequential burst of measurements. + * + * @param nbMeasurements defines the number of measurements in the burst + * @param interval defines the interval (in ms) between measurements in a burst + */ +void setADCSBurstSettings(uint8_t nbMeasurements, int interval) { + adcsSettings.nbMeasurements = nbMeasurements; + adcsSettings.interval = interval; +} + + +/* + * Take a burst of measurements for ADCS and + * store the results in a static variable. + * + * @return error, 0 for success, otherwise failure + */ +int takeADCSBurstMeasurements(void) { + // Flag CubeSense as "in use" + cubeSenseIsInUse = 1; + + int error; + + // Free allocated memory of struct + free(adcsResults); + + // Allocate memory to struct to store measurements + adcsResults = initializeNewADCSResults(adcsSettings.nbMeasurements); + + // Run a first image capture & detect so the first results can be read + error = triggerNewDetectionForBothSensors(); + if (error != SUCCESS) { + printf("Failed to trigger a first capture and detect...\n"); + } + + // Wait for first captures to be done + vTaskDelay(adcsSettings.interval); + + // Iterate to get the detection results + uint8_t resultIndex = 0; + printf("Number of measurements = %d\n", adcsSettings.nbMeasurements); + for (int i = 0; i < adcsSettings.nbMeasurements; i++) { + printf("Detection measurement #%d\n", i); + // Get detection results and trigger a new detection + detection_results_t detectionResult = {0}; + error = getResultsAndTriggerNewDetection(&detectionResult); + if (error == SUCCESS) { + // Store the successful result + adcsResults->results[resultIndex] = detectionResult; + resultIndex++; + } + + // Wait before the next measurement + vTaskDelay(adcsSettings.interval); + } + // Store the number of valid measurements + adcsResults->validMeasurementsCount = resultIndex; + + // Set the ADCS readiness flag so no new burst is executed + // if enough detection results were successfully + adcsReadyForNewBurst = adcsResults->validMeasurementsCount > adcsSettings.nbMeasurements/2 ? 0 : 1; + + cubeSenseIsInUse = 0; + return SUCCESS; +} + + +/* + * Get the ADCS burst measurement results. + * + * @return struct holding the ADCS measurement results for the burst + */ +adcs_detection_results_t * getADCSBurstResults(void) { + return adcsResults; +} + + +/* + * Memory allocation and initialization of a new ADCS results structure + * + * @note it is important to free the allocated pointer memory after usage + * @param nbMeasurements defines the number of measurements desired + * @return pointer to the new ADCS results struct in memory + */ +adcs_detection_results_t * initializeNewADCSResults(uint8_t nbMeasurements) { + adcs_detection_results_t *adcs = calloc(1, sizeof(*adcs) + sizeof(detection_results_t) * nbMeasurements); + printf("ADCS Results allocated memory size = %i bytes\n", sizeof(*adcs) + sizeof(detection_results_t) * nbMeasurements); + return adcs; +} + + +/* + * Set the flag indicating that ADCS is ready for a new burst of measurements. + */ +void setADCSReadyForNewBurst(void) { + adcsReadyForNewBurst = 1; +} + + +/* + * Get the flag indicating if ADCS is ready for a new burst. + * This flag needs to be reset to 1 via a telecommand. + * + * @return ADCS readiness, 1 for ready, 0 for not ready + */ +uint8_t getADCSReadyForNewBurstState(void) { + return adcsReadyForNewBurst; +} + + +/* + * Set the interval used for automatic image capture. + * + * @param interval defines the delay between each run of the image capture task + */ +void setImageCaptureInterval(int interval) { + imageCaptureInterval = interval; +} + + +/* + * Get the interval used for automatic image capture. + * + * @return interval, in milliseconds + */ +int getImageCaptureInterval(void) { + return imageCaptureInterval; +} + + +/* + * Set the image download size used for automatic image capture. + * + * @param size defines the image download size ranging from 0 (largest) to 4 (smallest) + */ +void setImageDownloadSize(uint8_t size) { + // Check for invalid size + if (size > 4) + size = 4; + + imageDownloadSize = size; +} + + +/* + * Get the image download size used for automatic image capture. + * + * @return size, ranging from 0 to 4 (see CubeSense's manual) + */ +uint8_t getImageDownloadSize(void) { + return imageDownloadSize; +} + + +/* + * Trigger a new image capture given the specified parameters. + * + * @param camera defines which sensor to use to capture an image, 0 = Camera 1, 1 = Camera 2 + * @param sram defines which SRAM to use on CubeSense, 0 = SRAM1, 1 = SRAM2 + * @param size defines the image size used to allocate memory, 0 = 1024x1024, 1 = 512x512, 2 = 256x256, 3 = 128x128, 4 = 64x64 + * @param location defines which SRAM slot to use within selected SRAM, 0 = top, 1 = bottom + * @return error, 0 on success, otherwise failure + */ +int requestImageCapture(uint8_t camera, uint8_t sram, uint8_t location) { + // Flag CubeSense as "in use" + cubeSenseIsInUse = 1; + + int error = captureImage(camera, sram, location); + cubeSenseIsInUse = 0; + return error; +} + + +/* + * Trigger a new image capture given the specified parameters, run + * the detection algorithm on said image, and download the image into RAM. + * + * @param camera defines which sensor to use to capture an image, 0 = Camera 1, 1 = Camera 2 + * @param sram defines which SRAM to use on CubeSense, 0 = SRAM1, 1 = SRAM2 + * @param size defines the image size used to allocate memory, 0 = 1024x1024, 1 = 512x512, 2 = 256x256, 3 = 128x128, 4 = 64x64 + * @return error, 0 on success, otherwise failure + */ +int requestImageCaptureDetectAndDownload(uint8_t camera, uint8_t sram, uint8_t size) { + int error = requestImageCaptureAndDetect(camera, sram); + if (error != SUCCESS) { + return error; + } + + error = requestImageDownload(sram, size); + if (error != SUCCESS) { + return error; + } + + return SUCCESS; +} + + +/* + * Trigger a new image capture given the specified parameters + * and run the detection algorithm on said image. + * Once we have successful detection, another picture is taken in the bottom SRAM location. + * + * @param camera defines which sensor to use to capture an image, 0 = Camera 1, 1 = Camera 2 + * @param sram defines which SRAM to use on CubeSense, 0 = SRAM1, 1 = SRAM2 + * @return error, 0 on success, otherwise failure + */ +int requestImageCaptureAndDetect(uint8_t camera, uint8_t sram) { + // Flag CubeSense as "in use" + cubeSenseIsInUse = 1; + + int error; + uint8_t retryCount = 0; + uint8_t detectionStatus = 1; + + // Capture an image and run the detection algorithm to get the results + // Retry a few times if results are invalid + while (detectionStatus != 0 && retryCount < VALID_IMAGE_RETRY_COUNT) { + printf("\nImage capture attempt: %d\n", retryCount); + // Capture image + error = captureImageAndDetect(camera, sram); + if (error == SUCCESS) { + // Wait to allow the image capture to complete + vTaskDelay(IMAGE_CAPTURE_DELAY_MS); + + // Get the detection status using the nadir sensor + detectionStatus = getSingleDetectionStatus(camera == 0 ? sensor1 : sensor2); + } else { + printf("Failed during image capture...\n"); + } + + // Wait before a retry + vTaskDelay(IMAGE_CAPTURE_DELAY_MS); + + // Increase retry counter + retryCount++; + } + + // Detection results are still invalid, stop trying + if (detectionStatus != 0) { + printf("Failed to capture a valid image...\n"); + // Flag CubeSense as "not used" + cubeSenseIsInUse = 0; + return E_GENERIC; + } + + // Detection success, so take another image in the bottom location + error = requestImageCapture(camera, sram, BOTTOM_HALVE); + if (error != SUCCESS) { + printf("Failed to capture image in bottom location...\n"); + } else { + printf("Successfully captured image in bottom location.\n"); + } + vTaskDelay(IMAGE_CAPTURE_DELAY_MS); + + // Flag CubeSense as "not used" + cubeSenseIsInUse = 0; + + // Reset flag so no new capture is made + imageReadyForNewCapture = 0; + return SUCCESS; +} + + +/* + * Start the download of the image into RAM. + * + * @param sram defines which SRAM to use on CubeSense, 0 = SRAM1, 1 = SRAM2 + * @param size defines the image size used to allocate memory, 0 = 1024x1024, 1 = 512x512, 2 = 256x256, 3 = 128x128, 4 = 64x64 + * @return error, 0 on success, otherwise failure + */ +int requestImageDownload(uint8_t sram, uint8_t size) { + // Store the download parameters to be used by the image download task + downloadParameters.sram = sram; + downloadParameters.size = size; + + // Create a task to download an image from CubeSense + int error = xTaskCreate(ImageDownloadTask, + (const signed char*)"Image Download Task", + DOWNLOAD_TASK_STACK_SIZE, + NULL, + imageDownloadTaskPriority, + &imageDownloadTaskHandle); + if (error != pdPASS) { + debugPrint("requestImageDownload(): failed to create ImageDownloadTask.\n"); + return E_GENERIC; + } + + return SUCCESS; +} + + +/* + * Set the image ready for new capture flag. + */ +void setImageReadyForNewCapture(void) { + imageReadyForNewCapture = 1; +} + + +/* + * Get the image ready for new capture flag. + * + * @return ready for new capture state, 0 for not ready, 1 for ready + */ +uint8_t getImageReadyForNewCaptureState(void) { + return imageReadyForNewCapture; +} + + +/* + * Get the ready for downlink state of the stored image. + * + * @return ready for downlink state, 0 for not ready, 1 for ready + */ +uint8_t getImageReadyForDownlinkState(void) { + return imageReadyForDownlink; +} + + +/* + * Get the total number of image frames available for downlink. + * + * @return number of image frames + */ +uint16_t getImageFramesCount(void) { + // Check stored image validity + if (!imageReadyForDownlink) + return 0; + + return image->framesCount; +} + + +/* + * Get the CubeSense usage state. This should be used to prevent + * different CubeSense functions being called at the same time. + * + * @return CubeSense usage, 1 if CubeSense is in use, 0 if it's unused + */ +uint8_t getCubeSenseUsageState(void) { + return cubeSenseIsInUse; +} + + +/* + * Set the image transfer frame index to the specified value. + * + * @param index defines the value to set the image transfer frame index + */ +void setImageTransferFrameIndex(int index) { + currentImageFrameIndex = index; +} + +/** + * Provide the next image frame. Calling the function passed the number of image frames + * will loop back at the first image frame (circular buffer). + * + * @param frame pointer to a buffer that the frame will be placed into. Set by function. + * @return The size of the image frame placed into the buffer; 0 on error. + */ +uint8_t imageTransferNextFrame(image_frame_t* frame) { + // Check for null pointer + if (frame == NULL) + return 0; + + // Check stored image validity + if (!imageReadyForDownlink) + return 0; + + // Validate that there is a next frame + int nextFrameIndex = currentImageFrameIndex + 1; + if (nextFrameIndex < 0) + return 0; + + // Increment image frame index + currentImageFrameIndex++; + + // Circular indexing + if (currentImageFrameIndex >= image->framesCount) + currentImageFrameIndex = 0; + + // Get image frame size + uint8_t size = sizeof(image->imageFrames[currentImageFrameIndex].image_bytes); + + // Copy the image frame to the argument pointer + //printf("Copying image frame index %d containing %i bytes.\n", currentImageFrameIndex, size); + memcpy(frame->image_bytes, image->imageFrames[currentImageFrameIndex].image_bytes, size); + frame->frameIndex = currentImageFrameIndex; + + // Return size of the frame + return size; +} + + +/** + * Provide the current image frame. + * + * @param frame pointer to a buffer that the frame will be placed into. Set by function. + * @return The size of the image frame placed into the buffer; 0 on error. + */ +uint8_t imageTransferCurrentFrame(image_frame_t* frame) { + return imageTransferSpecificFrame(frame, currentImageFrameIndex); +} + + +/** + * Provide the image frame at the specified index. + * + * @param frame pointer to a buffer that the frame will be placed into. Set by function. + * @param index defines which image frame to copy in the buffer + * @return The size of the image frame placed into the buffer; 0 on error. + */ +uint8_t imageTransferSpecificFrame(image_frame_t* frame, int index) { + // Check for null pointer + if (frame == NULL) + return 0; + + // Check stored image validity + if (!imageReadyForDownlink) + return 0; + + // Validate image frame index + if (index < 0 || index >= image->framesCount) + return 0; + + // Get image frame size + uint8_t size = sizeof(image->imageFrames[index].image_bytes); + + // Copy the image frame to the argument pointer + printf("\nCopying image frame index %i containing %i bytes.\n", index, size); + memcpy(frame->image_bytes, image->imageFrames[index].image_bytes, size); + frame->frameIndex = index; + + // Return size of the frame + return size; +} + +/*************************************************************************************************** + FREERTOS TASKS +***************************************************************************************************/ + +/* + * Task to download an image from CubeSense into RAM. + * + * @param parameters defines the task parameters (unused) + */ +void ImageDownloadTask(void* parameters) { + // Flag CubeSense as "in use" + cubeSenseIsInUse = 1; + + // ignore the input parameter + (void)parameters; + + printf("ImageDownloadTask 1: SRAM = %i | Size = %i\n", downloadParameters.sram, downloadParameters.size); + + // Reset the image downlink ready flag since allocated memory is freed + imageReadyForDownlink = 0; + // Free the previous image allocated memory + free(image); + // Reset the image frame index + currentImageFrameIndex = -1; + + // Initialize a new block of memory for the new image + image = initializeNewImage(downloadParameters.size); + // Download all image frames and store them in RAM + int error = downloadImage(downloadParameters.sram, BOTTOM_HALVE, image); + if (error != SUCCESS) { + printf("\nImageDownloadTask(): Failed to download all image frames...\n"); + } else { + printf("\nImageDownloadTask(): Successfully downloaded image!\n"); + + // Flag the stored image as valid + imageReadyForDownlink = 1; + } + + // Flag CubeSense as "not used" + cubeSenseIsInUse = 0; + + // Let this task delete itself + vTaskDelete(imageDownloadTaskHandle); +} diff --git a/radsat-sk/operation/services/RCameraService.h b/radsat-sk/operation/services/RCameraService.h new file mode 100644 index 00000000..c1da2431 --- /dev/null +++ b/radsat-sk/operation/services/RCameraService.h @@ -0,0 +1,75 @@ +/** + * @file RCameraService.h + * @date September 28, 2022 + * @author + */ + +#ifndef RCAMERASERVICE_H_ +#define RCAMERASERVICE_H_ + +#include +#include + +/*************************************************************************************************** + DEFINITIONS +***************************************************************************************************/ + +/** Struct prepared for downlink that holds the ADCS measurement results **/ +typedef struct _adcs_detection_results_t { + uint8_t validMeasurementsCount; + detection_results_t results[]; // Variable size, corresponding to the number of measurements in a burst +} adcs_detection_results_t; + +/* Struct prepared for downlink that holds an image frame */ +typedef struct _image_frame_t { + uint16_t frameIndex; + uint8_t image_bytes[FRAME_BYTES]; +} image_frame_t; + +/*************************************************************************************************** + PUBLIC API +***************************************************************************************************/ + +/** General functions **/ +int requestReset(uint8_t resetOption); +int setCamerasSettings(CameraSettings_ConfigurationSettings sunSettings, CameraSettings_ConfigurationSettings nadirSettings); +/***********************/ + +/** ADCS Capture functions **/ +void setADCSCaptureInterval(int interval); +int getADCSCaptureInterval(void); + +void setADCSBurstSettings(uint8_t nbMeasurements, int interval); +int takeADCSBurstMeasurements(void); +adcs_detection_results_t * getADCSBurstResults(void); +adcs_detection_results_t * initializeNewADCSResults(uint8_t nbMeasurements); + +void setADCSReadyForNewBurst(void); +uint8_t getADCSReadyForNewBurstState(void); +/****************************/ + +/** Image Capture functions **/ +void setImageCaptureInterval(int interval); +int getImageCaptureInterval(void); + +void setImageDownloadSize(uint8_t size); +uint8_t getImageDownloadSize(void); + +int requestImageCapture(uint8_t camera, uint8_t sram, uint8_t location); +int requestImageCaptureDetectAndDownload(uint8_t camera, uint8_t sram, uint8_t size); +int requestImageCaptureAndDetect(uint8_t camera, uint8_t sram); +int requestImageDownload(uint8_t sram, uint8_t size); + +void setImageReadyForNewCapture(void); +uint8_t getImageReadyForNewCaptureState(void); +uint8_t getImageReadyForDownlinkState(void); +uint16_t getImageFramesCount(void); +uint8_t getCubeSenseUsageState(void); + +void setImageTransferFrameIndex(int index); +uint8_t imageTransferNextFrame(image_frame_t* frame); +uint8_t imageTransferCurrentFrame(image_frame_t* frame); +uint8_t imageTransferSpecificFrame(image_frame_t* frame, int index); +/*****************************/ + +#endif /* RCAMERASERVICE_H_ */ diff --git a/radsat-sk/operation/services/RTelecommandService.c b/radsat-sk/operation/services/RTelecommandService.c index dec7462d..7eee2035 100644 --- a/radsat-sk/operation/services/RTelecommandService.c +++ b/radsat-sk/operation/services/RTelecommandService.c @@ -5,7 +5,9 @@ */ #include +#include #include +#include /*************************************************************************************************** @@ -20,6 +22,7 @@ * @return The tag of the processed telecommand (0 on failure). */ uint8_t telecommandHandle(uint8_t* wrappedMessage, uint8_t size) { + int error; // ensure the input pointer is not NULL if (wrappedMessage == 0) @@ -75,6 +78,92 @@ uint8_t telecommandHandle(uint8_t* wrappedMessage, uint8_t size) { // TODO: implement functionality break; + +/* + // TO ADD: Reset cameras + case (?): + // TODO: Pass argument (reset option) + if (!getCubeSenseUsageState()) { + error = requestReset(resetOption); + if (error != 0) { + printf("Error resetting cameras.\n"); + } + } + break; + + // TO ADD: Change both cameras' settings + case (?): + // TODO: Pass arguments (2x CameraSettings_ConfigurationSettings struct) + if (!getCubeSenseUsageState()) { + // TODO: To replace with passed arguments + CameraSettings_ConfigurationSettings sunSettings = {0}; + CameraSettings_ConfigurationSettings nadirSettings = {0}; + error = setCamerasSettings(sunSettings, nadirSettings); + if (error != 0) { + printf("Error updating cameras settings.\n"); + } + } + break; + + // TO ADD: Change automatic image capture interval + case (?): + // TODO: Pass argument (interval in ms) + setImageCaptureInterval(interval); + break; + + // TO ADD: Change automatic image download size + case (?): + // TODO: Pass argument (0, 1, 2, 3 or 4) + setImageDownloadSize(downloadSize); + break; + + // TO ADD: Set image as ready for a new capture + case (?): + setImageReadyForNewCapture(); + break; + + // TO ADD: Change image transfer frame index + case (?): + // TODO: Pass argument (frame index) + setImageTransferFrameIndex(frameIndex); + break; + + // TO ADD: Take manual image + case (?): + if (!getCubeSenseUsageState()) { + error = requestImageCapture(NADIR_SENSOR, SRAM2, BOTTOM_HALVE); + if (error != 0) { + printf("Failed to manually capture an image...\n"); + } + } + break; + + // TO ADD: Manually start the download of an image + case (?): + // TODO: Pass arguments (image size) + if (!getCubeSenseUsageState()) { + error = requestImageDownload(SRAM2, imageSize); + if (error != 0) { + printf("Failed to start the image download...\n"); + } + } + break; + + // TO ADD: Update ADCS settings + case (?): + // TODO: Pass arguments (nb of measurements in a burst, interval between measurements) + setADCSBurstSettings(nbMeasurements, interval); + break; + + // TO ADD: Reset the adcs readiness flag so it can take another burst of measurements + case (?): + setADCSReadyForNewBurst(); + break; + + // TODO: Implement missing camera telecommands +*/ + + // unknown telecommand default: // do nothing; return failure diff --git a/radsat-sk/operation/subsystems/camera/RADCS.c b/radsat-sk/operation/subsystems/camera/RADCS.c index 30f40037..d75cd6e8 100644 --- a/radsat-sk/operation/subsystems/camera/RADCS.c +++ b/radsat-sk/operation/subsystems/camera/RADCS.c @@ -11,6 +11,7 @@ #include #include #include +#include /*************************************************************************************************** DEFINITIONS @@ -19,7 +20,7 @@ #define TELECOMMAND_20 ((uint8_t) 0x14) #define TELECOMMAND_20_LEN ((uint8_t) 3) -#define TELEMETRY_22_TO_25_LEN ((uint8_t) 10) +#define TELEMETRY_20_TO_25_LEN ((uint8_t) 10) // TODO: REMOVE. Test purposes only. @@ -48,6 +49,7 @@ void printDetectionData(tlm_detection_result_and_trigger_adcs_t *data) { printf("Beta Angle = %d\n", data->beta); printf("Capture Result = %d (%s)\n", data->captureResult, capture_results[data->captureResult]); printf("Detection Result = %d (%s)\n", data->detectionResult, detection_results[data->detectionResult]); + printf("Timestamp = %lu\n", data->timestamp); printf("----------------------\n"); } @@ -118,7 +120,7 @@ int tcImageCaptureAndDetection(uint8_t camera, uint8_t sram) { } /* - * Used to request the detection results and trigger a new detection (TLM ID 22 to 25) + * Used to request the detection results and trigger a new detection (TLM ID 20 to 25) * * @param telemetry_reply defines where the detection results will be stored * @param sensorSelection defines the selected sensor and SRAM to get the detection results from @@ -154,7 +156,7 @@ int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemet telemetryBuffer = MessageBuilder(TELEMETRY_REPLY_SIZE_6); // Reading Automatic reply from CubeSense regarding status of Telemetry request - error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_22_TO_25_LEN); + error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_20_TO_25_LEN); if (error != 0) { free(telemetryBuffer); @@ -166,6 +168,7 @@ int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemet memcpy(&telemetry_reply->beta, &telemetryBuffer[TELEMETRY_OFFSET_2], sizeof(telemetry_reply->beta)); telemetry_reply->captureResult = telemetryBuffer[TELEMETRY_OFFSET_4]; telemetry_reply->detectionResult = telemetryBuffer[TELEMETRY_OFFSET_5]; + Time_getUnixEpoch((unsigned int *)&(telemetry_reply->timestamp)); // Free the dynamically allocated buffer free(telemetryBuffer); @@ -175,6 +178,8 @@ int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemet return SUCCESS; } +// NOTE: Function below is no longer used since it adds to CPU processing and +// to the downlink message size. Raw angles (alpha & beta) will be downlink instead. /* * Used to interpret detection results into a 3D vector * @@ -182,7 +187,7 @@ int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemet * @param beta result in centidegrees after executing TLM22 or 25 * @return struct containing the components of the 3D Vector */ -interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t beta) { +/*interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t beta) { float theta; float phi; interpret_detection_result_t data = {0}; @@ -195,4 +200,4 @@ interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t b data.Z_AXIS = cos(theta); return data; -} +}*/ diff --git a/radsat-sk/operation/subsystems/camera/RADCS.h b/radsat-sk/operation/subsystems/camera/RADCS.h index a6b16ffd..40ecd214 100644 --- a/radsat-sk/operation/subsystems/camera/RADCS.h +++ b/radsat-sk/operation/subsystems/camera/RADCS.h @@ -4,37 +4,26 @@ * @author Addi Amaya (Caa746) */ +#include +#include + /*************************************************************************************************** DEFINITIONS ***************************************************************************************************/ -/* Enum of telemetry request ID for sensor result and new detection */ -typedef enum _SensorResultAndDetection { - sensor1_sram1 = 0x96, // TLM 22 - sensor2_sram2 = 0x97, // TLM 23 - sensor1_sram2 = 0x98, // TLM 24 - sensor2_sram1 = 0x99 // TLM 25 -} SensorResultAndDetection; - /* Struct for telemetry Detection result and Trigger, ID 20-25 */ typedef struct _tlm_detection_result_and_trigger_adcs_t { + uint32_t timestamp; uint16_t alpha; uint16_t beta; uint8_t captureResult; uint8_t detectionResult; } tlm_detection_result_and_trigger_adcs_t; -/* Struct to define 3D vector */ -typedef struct _interpret_detection_result_t { - float X_AXIS; - float Y_AXIS; - float Z_AXIS; -} interpret_detection_result_t; - /*************************************************************************************************** PUBLIC API ***************************************************************************************************/ int tcImageCaptureAndDetection(uint8_t camera, uint8_t sram); int tlmSensorResultAndDetection(tlm_detection_result_and_trigger_adcs_t *telemetry_reply, SensorResultAndDetection sensorSelection); -interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t beta); +//interpret_detection_result_t calculateDetectionVector(uint16_t alpha, uint16_t beta); diff --git a/radsat-sk/operation/subsystems/camera/RCamera.c b/radsat-sk/operation/subsystems/camera/RCamera.c index 85bf2d2f..81647b1e 100644 --- a/radsat-sk/operation/subsystems/camera/RCamera.c +++ b/radsat-sk/operation/subsystems/camera/RCamera.c @@ -190,6 +190,32 @@ int captureImage(uint8_t camera, uint8_t sram, uint8_t location) { return telecommand_ack.tc_error_flag; } +/* + * Used to capture an image with CubeSense Camera and trigger a detection on the captured image + * + * @param camera defines which sensor to use to capture an image, 0 = Camera 1, 1 = Camera 2 + * @param sram defines which SRAM to use on Cubesense, 0 = SRAM1, 1 = SRAM2 + * @return error, 0 on successful, otherwise failure + * */ +int captureImageAndDetect(uint8_t camera, uint8_t sram) { + int error; + tlm_telecommand_ack_t telecommand_ack = {0}; + + // Send TLM 20 to take a picture and trigger detection on it + error = tcImageCaptureAndDetection(camera, sram); + + if (error != SUCCESS) + return error; + + // Request telecommand acknowledgment with TLM 3 + error = tlmTelecommandAcknowledge(&telecommand_ack); + + if (error != SUCCESS) + return error; + + return telecommand_ack.tc_error_flag; +} + /* * Used to Download an image from CubeSense Camera @@ -217,6 +243,7 @@ int downloadImage(uint8_t sram, uint8_t location, full_image_t *image) { // Send telecommand 64 to initialize a download error = tcInitImageDownload(sram, location, image->imageSize); if (error != SUCCESS) { + printf("Error during tcInitImageDownload()...\n"); return error; } @@ -252,6 +279,8 @@ int downloadImage(uint8_t sram, uint8_t location, full_image_t *image) { image->imageFrames[i] = imageFrame; if (i+1 < image->framesCount) { + // Quickly pause the task to allow other important tasks to execute if necessary + vTaskDelay(1); // Advance Image Download to Continue to the next Frame error = tcAdvanceImageDownload(i+1); if(error != SUCCESS) { @@ -267,18 +296,34 @@ int downloadImage(uint8_t sram, uint8_t location, full_image_t *image) { } /* - * Used to created a 3D vector from a detection of both sensors + * Run the image capture & detection and return the detection status. * - * @note The RADSAT-SK ADCS will only have access to SRAM1 - * @param data a struct that will contain the components of 2 3D vectors + * @param sensorSelection defines the selected sensor to get the detection results from * @return 0 on success, otherwise failure */ +int getSingleDetectionStatus(SensorResultAndDetection sensorSelection) { + int error = 0; + tlm_detection_result_and_trigger_adcs_t sensor_data = {0}; + + // Request detection results for the selected sensor + error = tlmSensorResultAndDetection(&sensor_data, sensorSelection); + if (error != SUCCESS) return error; + + return sensor_data.detectionResult == 7 ? SUCCESS : 1; +} + +/* + * Used to created a 3D vector from a detection of both sensors, then + * trigger a new image capture for further detection. + * + * @note The RADSAT-SK ADCS will only have access to SRAM1 + * @param data a struct that will contain the sun and nadir detection angles + * @return error; 0 on success, otherwise failure + */ int getResultsAndTriggerNewDetection(detection_results_t *data) { int error = 0; tlm_detection_result_and_trigger_adcs_t sun_sensor_data = {0}; tlm_detection_result_and_trigger_adcs_t nadir_sensor_data = {0}; - interpret_detection_result_t sun_sensor_coords = {0}; - interpret_detection_result_t nadir_sensor_coords = {0}; printf("\n--------- SUN ---------"); // Request results of Sun detection with TLM 22 @@ -306,37 +351,55 @@ int getResultsAndTriggerNewDetection(detection_results_t *data) { } // If detection is successful, calculate the detection results + uint8_t success = 1; if (sun_sensor_data.detectionResult == 7) { - sun_sensor_coords = calculateDetectionVector(sun_sensor_data.alpha, sun_sensor_data.beta); + data->sunTimestamp = sun_sensor_data.timestamp; + data->sunAlphaAngle = sun_sensor_data.alpha; + data->sunBetaAngle = sun_sensor_data.beta; + success = 0; } if (nadir_sensor_data.detectionResult == 7) { - nadir_sensor_coords = calculateDetectionVector(nadir_sensor_data.alpha, nadir_sensor_data.beta); + data->nadirTimestamp = nadir_sensor_data.timestamp; + data->nadirAlphaAngle = nadir_sensor_data.alpha; + data->nadirBetaAngle = nadir_sensor_data.beta; + success = 0; } - // Fill struct with data - data->sunSensorX = sun_sensor_coords.X_AXIS; - data->sunSensorY = sun_sensor_coords.Y_AXIS; - data->sunSensorZ = sun_sensor_coords.Z_AXIS; - data->nadirSensorX = nadir_sensor_coords.X_AXIS; - data->nadirSensorY = nadir_sensor_coords.Y_AXIS; - data->nadirSensorZ = nadir_sensor_coords.Z_AXIS; + return success; +} + +/* + * Trigger a new image capture and detection for both camera sensors. + * + * @return error; 0 on success, otherwise failure + */ +int triggerNewDetectionForBothSensors(void) { + int error = 0; + + // New capture and detect with the Sun camera + error = captureImageAndDetect(SUN_SENSOR, SRAM1); + if (error != SUCCESS) return error; + + // New capture and detect with the Earth camera + error = captureImageAndDetect(NADIR_SENSOR, SRAM2); + if (error != SUCCESS) return error; return SUCCESS; } /* - * Used to collect the telemetry on the CubeSense Camera + * Used to collect the settings on the CubeSense Camera * - * @param cameraTelemetry a struct that will used to house all important telemetry on board + * @param cameraSettings a struct that will used to house all important settings on board * @return 0 on success, otherwise failure */ -int getCameraTelemetry(CameraTelemetry *cameraTelemetry) { +int getSettings(CameraSettings *cameraSettings) { int error; tlm_status_t tlmStatusStruct = {0}; tlm_power_t tlmPowerStruct = {0}; tlm_config_t tlmConfigStruct = {0}; - // Grab All telemetry and check if successful while doing so + // Grab All settings and check if successful while doing so error = tlmStatus(&tlmStatusStruct); if (error != SUCCESS) @@ -352,26 +415,26 @@ int getCameraTelemetry(CameraTelemetry *cameraTelemetry) { if (error != SUCCESS) return error; - // Assign telemetry to master telemetry struct - cameraTelemetry->upTime = tlmStatusStruct.runtimeSeconds; - cameraTelemetry->powerTelemetry.current_3V3 = tlmPowerStruct.threeVcurrent; - cameraTelemetry->powerTelemetry.current_5V = tlmPowerStruct.fiveVcurrent; - cameraTelemetry->powerTelemetry.current_SRAM_1 = tlmPowerStruct.sramOneCurrent; - cameraTelemetry->powerTelemetry.current_SRAM_2 = tlmPowerStruct.sramTwoCurrent; - cameraTelemetry->powerTelemetry.overcurrent_SRAM_1 = tlmPowerStruct.sramOneOverCurrent; - cameraTelemetry->powerTelemetry.overcurrent_SRAM_2 = tlmPowerStruct.sramTwoOverCurrent; - cameraTelemetry->cameraOneTelemetry.autoAdjustMode = tlmConfigStruct.cameraOneAutoAdjustMode; - cameraTelemetry->cameraOneTelemetry.autoGainControl = tlmConfigStruct.cameraOneAGC; - cameraTelemetry->cameraOneTelemetry.blueGain = tlmConfigStruct.cameraOneBlueGain; - cameraTelemetry->cameraOneTelemetry.detectionThreshold = tlmConfigStruct.cameraOneDetectionThreshold; - cameraTelemetry->cameraOneTelemetry.exposure = tlmConfigStruct.cameraOneExposure; - cameraTelemetry->cameraOneTelemetry.redGain = tlmConfigStruct.cameraOneRedGain; - cameraTelemetry->cameraTwoTelemetry.autoAdjustMode = tlmConfigStruct.cameraTwoAutoAdjustMode; - cameraTelemetry->cameraTwoTelemetry.autoGainControl = tlmConfigStruct.cameraTwoAGC; - cameraTelemetry->cameraTwoTelemetry.blueGain = tlmConfigStruct.cameraTwoBlueGain; - cameraTelemetry->cameraTwoTelemetry.detectionThreshold = tlmConfigStruct.cameraTwoDetectionThreshold; - cameraTelemetry->cameraTwoTelemetry.exposure = tlmConfigStruct.cameraTwoExposure; - cameraTelemetry->cameraTwoTelemetry.redGain = tlmConfigStruct.cameraTwoRedGain; + // Assign settings to master settings struct + cameraSettings->upTime = tlmStatusStruct.runtimeSeconds; + cameraSettings->powerSettings.current_3V3 = tlmPowerStruct.threeVcurrent; + cameraSettings->powerSettings.current_5V = tlmPowerStruct.fiveVcurrent; + cameraSettings->powerSettings.current_SRAM_1 = tlmPowerStruct.sramOneCurrent; + cameraSettings->powerSettings.current_SRAM_2 = tlmPowerStruct.sramTwoCurrent; + cameraSettings->powerSettings.overcurrent_SRAM_1 = tlmPowerStruct.sramOneOverCurrent; + cameraSettings->powerSettings.overcurrent_SRAM_2 = tlmPowerStruct.sramTwoOverCurrent; + cameraSettings->cameraOneSettings.autoAdjustMode = tlmConfigStruct.cameraOneAutoAdjustMode; + cameraSettings->cameraOneSettings.autoGainControl = tlmConfigStruct.cameraOneAGC; + cameraSettings->cameraOneSettings.blueGain = tlmConfigStruct.cameraOneBlueGain; + cameraSettings->cameraOneSettings.detectionThreshold = tlmConfigStruct.cameraOneDetectionThreshold; + cameraSettings->cameraOneSettings.exposure = tlmConfigStruct.cameraOneExposure; + cameraSettings->cameraOneSettings.redGain = tlmConfigStruct.cameraOneRedGain; + cameraSettings->cameraTwoSettings.autoAdjustMode = tlmConfigStruct.cameraTwoAutoAdjustMode; + cameraSettings->cameraTwoSettings.autoGainControl = tlmConfigStruct.cameraTwoAGC; + cameraSettings->cameraTwoSettings.blueGain = tlmConfigStruct.cameraTwoBlueGain; + cameraSettings->cameraTwoSettings.detectionThreshold = tlmConfigStruct.cameraTwoDetectionThreshold; + cameraSettings->cameraTwoSettings.exposure = tlmConfigStruct.cameraTwoExposure; + cameraSettings->cameraTwoSettings.redGain = tlmConfigStruct.cameraTwoRedGain; return SUCCESS; } @@ -379,49 +442,49 @@ int getCameraTelemetry(CameraTelemetry *cameraTelemetry) { /* * In the case the ground station wants to adjust camera settings * - * @param cameraTelemetry a struct that will contain the values that want to be adjusted + * @param cameraSettings a struct that will contain the values that want to be adjusted * */ -int setCameraConfig(CameraTelemetry *cameraTelemetry) { +int setSettings(CameraSettings *cameraSettings) { int error; // Update Auto adjust for camera one - error = tcCameraAutoAdjust(SUN_SENSOR, cameraTelemetry->cameraOneTelemetry.autoAdjustMode); + error = tcCameraAutoAdjust(SUN_SENSOR, cameraSettings->cameraOneSettings.autoAdjustMode); if(error != SUCCESS) { return error; } // Update Auto adjust for camera two - error = tcCameraAutoAdjust(NADIR_SENSOR, cameraTelemetry->cameraTwoTelemetry.autoAdjustMode); + error = tcCameraAutoAdjust(NADIR_SENSOR, cameraSettings->cameraTwoSettings.autoAdjustMode); if (error != SUCCESS) { return error; } // Update detection Threshold for camera one - error = tcCameraDetectionThreshold(SUN_SENSOR, cameraTelemetry->cameraOneTelemetry.detectionThreshold); + error = tcCameraDetectionThreshold(SUN_SENSOR, cameraSettings->cameraOneSettings.detectionThreshold); if (error != SUCCESS) { return error; } // Update detection Threshold for camera two - error = tcCameraDetectionThreshold(NADIR_SENSOR, cameraTelemetry->cameraTwoTelemetry.detectionThreshold); + error = tcCameraDetectionThreshold(NADIR_SENSOR, cameraSettings->cameraTwoSettings.detectionThreshold); if (error != SUCCESS) { return error; } // Update camera one settings - error = tcCameraSettings(SUN_SENSOR, cameraTelemetry->cameraOneTelemetry.exposure, - cameraTelemetry->cameraOneTelemetry.autoGainControl, - cameraTelemetry->cameraOneTelemetry.blueGain, - cameraTelemetry->cameraOneTelemetry.redGain); + error = tcCameraSettings(SUN_SENSOR, cameraSettings->cameraOneSettings.exposure, + cameraSettings->cameraOneSettings.autoGainControl, + cameraSettings->cameraOneSettings.blueGain, + cameraSettings->cameraOneSettings.redGain); if(error != SUCCESS) { return error; } // Update camera Two settings - error = tcCameraSettings(NADIR_SENSOR, cameraTelemetry->cameraTwoTelemetry.exposure, - cameraTelemetry->cameraTwoTelemetry.autoGainControl, - cameraTelemetry->cameraTwoTelemetry.blueGain, - cameraTelemetry->cameraTwoTelemetry.redGain); + error = tcCameraSettings(NADIR_SENSOR, cameraSettings->cameraTwoSettings.exposure, + cameraSettings->cameraTwoSettings.autoGainControl, + cameraSettings->cameraTwoSettings.blueGain, + cameraSettings->cameraTwoSettings.redGain); if(error != SUCCESS) { return error; } @@ -726,6 +789,7 @@ int tlmImageFrame(tlm_image_frame_t *telemetry_reply) { free(telemetryBuffer); if (error != 0) { + printf("tlmImageFrame(): Error during uartTransmit()... (error=%d)\n", error); return E_GENERIC; } @@ -736,6 +800,7 @@ int tlmImageFrame(tlm_image_frame_t *telemetry_reply) { error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_64_LEN); if (error != 0) { + printf("tlmImageFrame(): Error during receiveAndUnescapeTelemetry()... (error=%d)\n", error); free(telemetryBuffer); return E_GENERIC; } diff --git a/radsat-sk/operation/subsystems/camera/RCamera.h b/radsat-sk/operation/subsystems/camera/RCamera.h index e157a873..99ac5d06 100644 --- a/radsat-sk/operation/subsystems/camera/RCamera.h +++ b/radsat-sk/operation/subsystems/camera/RCamera.h @@ -7,7 +7,9 @@ #ifndef RCAMERA_H_ #define RCAMERA_H_ +#include #include +#include /*************************************************************************************************** DEFINITIONS @@ -18,43 +20,43 @@ /*maximum number of bytes in one image */ #define MAXIMUM_BYTES 1048576 -/* Struct used to define ADCS Function*/ +/* Struct used to hold the successful image detection angles */ typedef struct _detection_results_t { - float sunSensorX; - float sunSensorY; - float sunSensorZ; - float nadirSensorX; - float nadirSensorY; - float nadirSensorZ; + uint32_t sunTimestamp; + uint16_t sunAlphaAngle; + uint16_t sunBetaAngle; + uint32_t nadirTimestamp; + uint16_t nadirAlphaAngle; + uint16_t nadirBetaAngle; } detection_results_t; -/* Struct that holds all power related telemetry */ -typedef struct _CameraTelemetry_PowerTelemetry { +/* Struct that holds all power related settings */ +typedef struct _CameraSettings_PowerSettings { float current_3V3; float current_5V; float current_SRAM_1; float current_SRAM_2; uint8_t overcurrent_SRAM_1; uint8_t overcurrent_SRAM_2; -} CameraTelemetry_PowerTelemetry; +} CameraSettings_PowerSettings; -/* Struct that holds all Camera configuration related telemetry */ -typedef struct _CameraTelemetry_ConfigurationTelemetry { +/* Struct that holds all Camera configuration related settings */ +typedef struct _CameraSettings_ConfigurationSettings { uint8_t detectionThreshold; uint8_t autoAdjustMode; uint16_t exposure; uint8_t autoGainControl; uint8_t blueGain; uint8_t redGain; -} CameraTelemetry_ConfigurationTelemetry; +} CameraSettings_ConfigurationSettings; -/* Struct for Camera Telemetry Collection Function */ -typedef struct _CameraTelemetry { +/* Struct for Camera Settings Collection Function */ +typedef struct _CameraSettings { uint16_t upTime; - CameraTelemetry_PowerTelemetry powerTelemetry; - CameraTelemetry_ConfigurationTelemetry cameraOneTelemetry; - CameraTelemetry_ConfigurationTelemetry cameraTwoTelemetry; -} CameraTelemetry; + CameraSettings_PowerSettings powerSettings; + CameraSettings_ConfigurationSettings cameraOneSettings; + CameraSettings_ConfigurationSettings cameraTwoSettings; +} CameraSettings; /* Struct for telemetry image frame */ typedef struct _tlm_image_frame_t { @@ -75,10 +77,13 @@ typedef struct _full_image_t { full_image_t * initializeNewImage(uint8_t size); int captureImage(uint8_t camera, uint8_t sram, uint8_t location); +int captureImageAndDetect(uint8_t camera, uint8_t sram); int downloadImage(uint8_t sram, uint8_t location, full_image_t *image); +int getSingleDetectionStatus(SensorResultAndDetection sensorSelection); int getResultsAndTriggerNewDetection(detection_results_t *data); -int setCameraConfig(CameraTelemetry *cameraTelemetry); -int getCameraTelemetry(CameraTelemetry *cameraTelemetry); +int triggerNewDetectionForBothSensors(void); +int setSettings(CameraSettings *cameraSettings); +int getSettings(CameraSettings *cameraSettings); int executeReset(uint8_t resetOption); #endif /* RCAMERA_H_ */ diff --git a/radsat-sk/operation/subsystems/camera/RCameraCommon.c b/radsat-sk/operation/subsystems/camera/RCameraCommon.c index 0b630174..f93d7157 100644 --- a/radsat-sk/operation/subsystems/camera/RCameraCommon.c +++ b/radsat-sk/operation/subsystems/camera/RCameraCommon.c @@ -8,7 +8,7 @@ //#include #include #include -//#include +#include //#include /*************************************************************************************************** @@ -114,26 +114,23 @@ int escapeAndTransmitTelecommand(uint8_t *telecommandBuffer, uint8_t messageSize * @return error of telecommand attempt. 0 on success, otherwise failure */ int receiveAndUnescapeTelemetry(uint8_t *telemetryBuffer, uint8_t messageSize) { - int error; - uint8_t currentByte; - uint8_t isEscapeCharacter = 0; - - for(uint8_t i = 0; i < messageSize; i++) { - // Read a single byte at a time to catch escape characters - error = uartReceive(UART_CAMERA_BUS, ¤tByte, 1); - if (error != 0) { - return E_GENERIC; - } + // Receive the message via UART + int error = uartReceive(UART_CAMERA_BUS, telemetryBuffer, messageSize); + if (error != 0) { + printf("receiveAndUnescapeTelemetry(): Error during first uartReceive()... (error=%d)\n", error); + return E_GENERIC; + } - // Check for escaped characters in the data bytes - if (i >= TELEMETRY_OFFSET_0 && i < messageSize - 2) { - if (isEscapeCharacter == 0 && currentByte == ESCAPE_CHARACTER) { - isEscapeCharacter = 1; - i--; // Current byte is escaping the following data byte, so ignore it since it is an extra byte - } else { - // Assign the data byte to the buffer - telemetryBuffer[i] = currentByte; - isEscapeCharacter = 0; + // Check for escaped characters in the data bytes + for(uint8_t i = TELEMETRY_OFFSET_0; i < messageSize - 2; i++) { + if (telemetryBuffer[i] == ESCAPE_CHARACTER) { + // Left shift array to remove the duplicated escape character + memmove(&telemetryBuffer[i+1], &telemetryBuffer[i+2], (messageSize-i-1)*sizeof(uint8_t)); + // Read one more byte from UART and store at the end of the buffer + error = uartReceive(UART_CAMERA_BUS, &telemetryBuffer[messageSize-1], 1); + if (error != 0) { + printf("receiveAndUnescapeTelemetry(): Error during second uartReceive()... (error=%d)\n", error); + return E_GENERIC; } } } diff --git a/radsat-sk/operation/subsystems/camera/RCameraCommon.h b/radsat-sk/operation/subsystems/camera/RCameraCommon.h index 40893e2a..a72cf786 100644 --- a/radsat-sk/operation/subsystems/camera/RCameraCommon.h +++ b/radsat-sk/operation/subsystems/camera/RCameraCommon.h @@ -58,6 +58,23 @@ #define TOP_HALVE ((uint8_t) 0) #define BOTTOM_HALVE ((uint8_t) 1) +/* Enum of telemetry request ID for sensor result and new detection */ +typedef enum _SensorResultAndDetection { + sensor1 = 0x94, // TLM 20 (no new detection) + sensor2 = 0x95, // TLM 21 (no new detection) + sensor1_sram1 = 0x96, // TLM 22 + sensor2_sram2 = 0x97, // TLM 23 + sensor1_sram2 = 0x98, // TLM 24 + sensor2_sram1 = 0x99 // TLM 25 +} SensorResultAndDetection; + +/* Struct to define 3D vector */ +typedef struct _interpret_detection_result_t { + float X_AXIS; + float Y_AXIS; + float Z_AXIS; +} interpret_detection_result_t; + /**************************************************************************************************** PUBLIC API ***************************************************************************************************/ diff --git a/radsat-sk/operation/subsystems/camera/RImage.c b/radsat-sk/operation/subsystems/camera/RImage.c index 8c3349e7..026322e4 100644 --- a/radsat-sk/operation/subsystems/camera/RImage.c +++ b/radsat-sk/operation/subsystems/camera/RImage.c @@ -204,6 +204,7 @@ int tcInitImageDownload(uint8_t SRAM, uint8_t location, uint8_t size) { free(telecommandBuffer); if (error != 0) { + printf("tcInitImageDownload(): Error during uartTransmit()...\n"); return E_GENERIC; } @@ -215,6 +216,7 @@ int tcInitImageDownload(uint8_t SRAM, uint8_t location, uint8_t size) { error = uartReceive(UART_CAMERA_BUS, telecommandResponse, sizeOfBuffer); if (error != 0) { + printf("tcInitImageDownload(): Error during uartReceive()..."); free(telecommandResponse); return E_GENERIC; } @@ -226,6 +228,7 @@ int tcInitImageDownload(uint8_t SRAM, uint8_t location, uint8_t size) { free(telecommandResponse); if (tcErrorFlag != 0) { + printf("tcInitImageDownload(): Bad tcErrorFlag...\n"); return E_GENERIC; } @@ -261,6 +264,7 @@ int tlmImageFrameInfo(tlm_image_frame_info_t *telemetry_reply) { free(telemetryBuffer); if (error != 0) { + printf("tlmImageFrameInfo(): Error during uartTransmit()... (error=%d)\n", error); return E_GENERIC; } @@ -271,6 +275,7 @@ int tlmImageFrameInfo(tlm_image_frame_info_t *telemetry_reply) { error = receiveAndUnescapeTelemetry(telemetryBuffer, TELEMETRY_65_LEN); if (error != 0) { + printf("tlmImageFrameInfo(): Error during receiveAndUnescapeTelemetry()... (error=%d)\n", error); free(telemetryBuffer); return E_GENERIC; } diff --git a/radsat-sk/src/tasks/RAdcsCaptureTask.c b/radsat-sk/src/tasks/RAdcsCaptureTask.c index 9b4f938d..1e979666 100644 --- a/radsat-sk/src/tasks/RAdcsCaptureTask.c +++ b/radsat-sk/src/tasks/RAdcsCaptureTask.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -16,10 +17,13 @@ ***************************************************************************************************/ /** How many ADCS readings to capture per hour. */ -#define ADCS_CAPTURES_PER_HOUR (1) +#define ADCS_CAPTURES_PER_HOUR (1) -/** ADCS Capture Task delay (in ms). */ -#define ADCS_CAPTURE_TASK_DELAY_MS (MS_PER_HOUR / ADCS_CAPTURES_PER_HOUR) +/** ADCS Capture Task normal delay (in ms). */ +#define ADCS_CAPTURE_TASK_NORMAL_DELAY_MS (MS_PER_HOUR / ADCS_CAPTURES_PER_HOUR) + +/** ADCS Capture Task short delay (in ms). */ +#define ADCS_CAPTURE_TASK_SHORT_DELAY_MS (10000) /*************************************************************************************************** @@ -27,19 +31,43 @@ ***************************************************************************************************/ void AdcsCaptureTask(void* parameters) { + int error; // ignore the input parameter (void)parameters; + // Set the default automatic ADCS capture interval + setADCSCaptureInterval(ADCS_CAPTURE_TASK_NORMAL_DELAY_MS); + + // Initialize the ADCS capture settings (5 measurements, 5 seconds between measurements) + setADCSBurstSettings(5, 5000); + while (1) { - // TODO: implement ADCS capture + // TODO: Uncomment function call when Brian's branch will be ready/merged + // Check if satellite is currently in downlink/uplink mode (1) or not (0) + uint8_t commIsActive = 0; //communicationPassModeActive(); + + // Check if CubeSense is already in use (1) or not (0) + uint8_t cubeSenseIsInUse = getCubeSenseUsageState(); - debugPrint("AdcsCaptureTask(): About to capture ADCS data.\n"); + // Check if ready for a new ADCS burst measurements (1) or not (0) + uint8_t adcsReadyForNewBurst = getADCSReadyForNewBurstState(); - // TODO - // To get detection results and trigger new detection, use --> "getResultsAndTriggerNewDetection(...)" + if (!commIsActive && !cubeSenseIsInUse && adcsReadyForNewBurst) { + printf("Starting ADCS burst measurements\n"); + error = takeADCSBurstMeasurements(); + if (error != 0) { + printf("Failed to capture ADCS measurements...\n"); + } + } - vTaskDelay(ADCS_CAPTURE_TASK_DELAY_MS); + if (cubeSenseIsInUse) { + // CubeSense was in use, wait only for a small delay to retry task + vTaskDelay(ADCS_CAPTURE_TASK_SHORT_DELAY_MS); + } else { + // Normal operations with normal delay between task execution + vTaskDelay(getADCSCaptureInterval()); + } } } diff --git a/radsat-sk/src/tasks/RImageCaptureTask.c b/radsat-sk/src/tasks/RImageCaptureTask.c index 2ed8413d..76c58221 100644 --- a/radsat-sk/src/tasks/RImageCaptureTask.c +++ b/radsat-sk/src/tasks/RImageCaptureTask.c @@ -5,21 +5,25 @@ */ #include +#include #include -#include #include #include +#include /*************************************************************************************************** DEFINITIONS & PRIVATE GLOBALS ***************************************************************************************************/ -/** How many images to capture per week. */ -#define IMAGE_CAPTURES_PER_WEEK (1) +/** How many images to capture per day. */ +#define IMAGE_CAPTURES_PER_DAY (1) -/** Image Capture Task delay (in ms). */ -#define IMAGE_CAPTURE_TASK_DELAY_MS (MS_PER_WEEK / IMAGE_CAPTURES_PER_WEEK) +/** Image Capture Task normal delay (in ms). */ +#define IMAGE_CAPTURE_TASK_NORMAL_DELAY_MS (MS_PER_DAY / IMAGE_CAPTURES_PER_DAY) + +/** Image Capture Task short delay (in ms), used when task couldn't execute because CubeSense is in use. */ +#define IMAGE_CAPTURE_TASK_SHORT_DELAY_MS (10000) /*************************************************************************************************** @@ -27,22 +31,64 @@ ***************************************************************************************************/ void ImageCaptureTask(void* parameters) { + int error; // ignore the input parameter (void)parameters; - while (1) { - - // TODO: implement image capture + // Set the default automatic image capture interval + setImageCaptureInterval(IMAGE_CAPTURE_TASK_NORMAL_DELAY_MS); - debugPrint("ImageCaptureTask(): About to capture an image.\n"); - - // TODO - // To initialize an image buffer, use --> "initializeNewImage(...)" - // To take a picture, use --------------> "captureImage(...)" - // To download a picture, use ----------> "downloadImage(...)" + while (1) { - vTaskDelay(IMAGE_CAPTURE_TASK_DELAY_MS); + // TODO: Uncomment function call when Brian's branch will be ready/merged + // Check if satellite is currently in downlink/uplink mode (1) or not (0) + uint8_t commIsActive = 0; //communicationPassModeActive(); + + // Check if CubeSense is already in use (1) or not (0) + uint8_t cubeSenseIsInUse = getCubeSenseUsageState(); + + if (!commIsActive && !cubeSenseIsInUse) { + // Check if ready for a new image capture + if (getImageReadyForNewCaptureState()) { + printf("READY for new image capture\n"); + + // Request a new capture using the Earth sensor and SRAM2 + error = requestImageCaptureAndDetect(NADIR_SENSOR, SRAM2); + if (error != SUCCESS) { + printf("Error capturing an image.\n"); + } else { + // Successful image capture, proceed to + // start a task to download the image + uint8_t imageSize = getImageDownloadSize(); + printf("Image download size = %i\n", imageSize); + error = requestImageDownload(SRAM2, imageSize); + if (error != SUCCESS) { + printf("Error starting image download.\n"); + } + } + } else { + printf("NOT READY for new image capture\n"); + + // No request for a new capture and image not yet ready + // for downlink, so retry to download the image + if (!getImageReadyForDownlinkState()) { + uint8_t imageSize = getImageDownloadSize(); + error = requestImageDownload(SRAM2, imageSize); + if (error != SUCCESS) { + printf("Error starting image download.\n"); + } + } + } + } + + if (cubeSenseIsInUse) { + // CubeSense was in use, wait only for a small delay to retry task + vTaskDelay(IMAGE_CAPTURE_TASK_SHORT_DELAY_MS); + } else { + // Normal operations with normal delay between task execution + vTaskDelay(getImageCaptureInterval()); + } } } From 55fac25b13b690195535ad4fb939f09601daf12a Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 29 Jan 2023 09:28:44 -0600 Subject: [PATCH 05/10] Test/test suite (#220) * Refactorying and adding files and code for test suite * Include testing and moved test suite to testing * Changed to remove testing file from compiling for debug and release. Setup UI as well. * Fix test UI, added unfinished dosimeter test, fixed project file bugs, added launch config. * Added debugReadIntMinMax so that user input dosn't block the watchdog * Added CR and DEL definitions * Initial implementation of general test exicution with GUI * Fixes and extend tests to dosimeter * Fixed mainTestMenuTask return non-null value and ending function with vTaskDelete * Event more dumb fixes * Fixed old issues * more old issues fixed * Final touched before pull * Remove Demo files --------- Co-authored-by: brian --- radsat-sk/.cproject | 57 ++++++- .../operation/services/RFileTransferService.c | 4 + radsat-sk/operation/subsystems/RBattery.c | 6 +- radsat-sk/operation/subsystems/RDosimeter.c | 98 +++++++----- radsat-sk/operation/subsystems/RDosimeter.h | 4 + radsat-sk/operation/subsystems/RTransceiver.c | 2 +- radsat-sk/operation/utility/RDebug.c | 54 ++++++- radsat-sk/operation/utility/RDebug.h | 1 + radsat-sk/radsat-sk-debug.launch | 2 +- radsat-sk/radsat-sk-test.launch | 58 +++++++ radsat-sk/src/main.c | 38 +++-- .../src/tasks/RDosimeterCollectionTask.c | 2 +- radsat-sk/src/tasks/RSatelliteWatchdogTask.c | 6 + radsat-sk/testing/RTestSuite.c | 146 ++++++++++++++++++ radsat-sk/testing/{unit => }/RTestSuite.h | 8 +- radsat-sk/testing/RTestUtils.c | 45 ++++++ radsat-sk/testing/RTestUtils.h | 31 ++++ radsat-sk/testing/unit/RTestDosimeter.c | 68 +++++++- radsat-sk/testing/unit/RTestDosimeter.h | 8 +- radsat-sk/testing/unit/RTestPdb.c | 71 +++++++++ radsat-sk/testing/unit/RTestPdb.h | 21 +++ radsat-sk/testing/unit/RTestSuite.c | 26 ---- 22 files changed, 652 insertions(+), 104 deletions(-) create mode 100644 radsat-sk/radsat-sk-test.launch create mode 100644 radsat-sk/testing/RTestSuite.c rename radsat-sk/testing/{unit => }/RTestSuite.h (73%) create mode 100644 radsat-sk/testing/RTestUtils.c create mode 100644 radsat-sk/testing/RTestUtils.h create mode 100644 radsat-sk/testing/unit/RTestPdb.c create mode 100644 radsat-sk/testing/unit/RTestPdb.h delete mode 100644 radsat-sk/testing/unit/RTestSuite.c diff --git a/radsat-sk/.cproject b/radsat-sk/.cproject index bedd8f2b..d76e1093 100644 --- a/radsat-sk/.cproject +++ b/radsat-sk/.cproject @@ -90,6 +90,8 @@ + + - @@ -399,10 +398,10 @@ + - - +