RecordData - Terrapin-Rocket-Team/SRAD-Avionics GitHub Wiki
Home/Documentation/RecordData
The RecordData
Library (RecordData.h
)
The RecordData
library is a custom library for recording data to the SD card. It is used by the flight computer to log data from the sensors to the SD card. The library is designed to be easy to use and flexible, allowing for easy logging of data from any sensor.
This page will split into two describing both how to use it and how it works.
There is an important "gotcha" detailed at the end of "How to Use". Do not skip it. You have been warned.
Before starting, make sure you've initialized both the SD card and the PSRAM. This is done in the setup()
function of the main file. PSRAM
is an object while the SD card is not. The library relies on there being a PSRAM pointer named ram
defined in main in order to function properly. Here's an example of how to do initialize both the SD card and PSRAM:
#include "RecordData.h"
PSRAM *ram;
void setup(){
ram = new PSRAM(); // init after the SD card for better data logging.
// The SD card MUST be initialized first to allow proper data logging.
if (setupSDCard())
// The SD card is initialized.
else
// The SD card failed to initialize.
// The PSRAM must be initialized before the sensors to allow for proper data logging.
if (ram->init())
// The PSRAM is initialized.
else
// The PSRAM failed to initialize.
}
There are two files that you can record to: the FlightData
file and the Log
file. The FlightData
file is intended to be used to store output from State
during it's updates, providing a record of the state of the rocket at each time step. The Log
file is intended to be used to store any other data that you want to log. The RecordData
library provides two functions for writing to these files: recordFlightData()
and recordLogData()
.
The FlightData
file is named [FlightNumber]_FlightData.csv
and the Log
file is named [FlightNumber]_Log.txt
. Both files are stored in the root directory of the SD card. FlightNumber
is automatically found based on the existing files. The first unused integer is used.
recordFlightData()
takes a null-terminated char[]
as an argument and writes it to the FlightData
file. That's it. The function will automatically append a newline character to the end of the string.
Here's an example of how to use the library to record flight data:
recordFlightData(state.getDataString());
That's it! Very easy.
Recording to the Log file is slightly more complicated.
recordLogData()
has two versions, with and without a provided timestamp. Without the timestamp, the library will call millis()
to get the current time.
It also takes two enums that change its behavior. The first is LogType
with the values
LOG
ERROR
WARNING
INFO
The second is the Dest
enum with the values
BOTH
TO_USB
TO_FILE
Dest
is used to determine where to send the log message. It has a default value of BOTH
.
Here's an example of how to use the library to record log data:
recordLogData(INFO, "This is a test of the log data recording system.");
//Output: "CUR_TIME_IN_SECONDS - [INFO] This is a test of the log data recording system." to both the SD card and USB.
recordLogData(ERROR, "This is a test of the log data recording system.", TO_FILE);
//Output: "CUR_TIME_IN_SECONDS - [ERROR] This is a test of the log data recording system." only to the SD card.
recordLogData(5.6754, WARNING, "This is a test of the log data recording system.", TO_USB);
//Output: "5.68 - [WARNING] This is a test of the log data recording system." only to the USB.
You can currently not concatenate multiple messages together in the method call because we are using raw char[]
s. If you want to do this, you will need to concatenate the strings before passing them to the method. For example:
char logData[100];
strcpy(logData, sensors[i]->getTypeString());
strcat(logData, " [");
strcat(logData, sensors[i]->getName());
strcat(logData, "] initialized.");
recordLogData(INFO, logData);
You can also not currently log data without the time and severity prefix. This is expected to be added in the future.
There is one more important piece of functionality that you need to consider. The library has two modes of functioning: GROUND
and FLIGHT
.
The distinction between these two are very important. In GROUND
mode (the starting mode), all data sent to it (both flight and log data) is immediately sent to the SD card for storage, bypassing the PSRAM entirely. In FLIGHT
mode, the data is stored in the PSRAM until the mode is set back to GROUND
. If the mode is never set back to GROUND
, the data will be lost.
Again, because it's important:
If you are in FLIGHT
mode, you must set the mode back to GROUND
before the flight computer is powered off, or all data since FLIGHT
mode started will be lost!
To set the mode, use the setRecordMode()
function. It takes a Mode
enum with the values
FLIGHT
GROUND
The reason this exists is to prevent long data writes to the SD card from blocking the rest of the data collection from proceeding. The PSRAM is much faster than the SD card, so it can store data much faster. This is why we use it for the majority of the data collection. Testing concluded that this was not an issue while the rocket was on the ground (with a data collection rate of 10hz).
Ok now, with that out of the way, here is
There are three important classes here. RecordData
is the wrapper that uses psram
and sdCard
to store data. It is currently only designed to be used on Arduino systems.
RecordData.cpp
itself is quite straightforward. It has a recordFlightData()
and recordLogData()
function that take a char[]
and write it to the appropriate file. It also has a setRecordMode()
function that changes the mode of the library.
PSRAM.cpp
is more interesting. It is a custom class that writes to either end of the PSRAM module depending on which file it's writing to. Flight data goes to the front of the space, and Log data to the end. There is also an init()
function that initializes the PSRAM.
PSRAM stands for Pseudo Static Random Access Memory. It is a type of volatile memory that is much more performant than an SD card. It is a small chip on the bottom of the Teensy that we hand-soldered on. We use it during flight to store data temporarily because of its performance. Despite the SD card performing well on the ground, we did not want to risk losing data due to long write times during flight.
PSRAM.cpp
copies data character-by-character to the PSRAM. It stores the starting and ending location of the entire block of PSRAM, as well as a cursor to the end of the FlightData and the start of the Log data. When writing, it increments the FlightData cursor and decrements the Log data cursor, as appropriate. In this way, the two files grow towards each other.
When the data is ready to be dumped to the SD card, there are two methods used. The FlightData, being stored at the beginning and in-order, is simply fed straight to the SD card. The Log data, on the other hand, is stored in reverse order at the end of the PSRAM. To write this data, it is copied into a 2048 byte buffer (a multiple of 512, which is the block size of the SD card), reversed as it's buffered, and then written to the SD card. This is done in a loop until all the data is written.
There is also a getFreeSpace()
methd that will let a user know how much space is left. Because of the large amount of space on PSRAM, we do not currently include checks for running out of space. This is expected to be added in the future.
sdCard.cpp
is a simple class to interface with the SD card. It decides on what to name the data files and writes to them. sdCard.h
has a dependency on SdFat.h
, which is the third-party library that actually interfaces with the SD card over SPI.