Packages

A high-performance Elixir library for decoding Garmin .fit files using a C++ NIF.

Current section

Files

Jump to
fit_decoder c_src fit_sdk cpp examples encode encode.cpp
Raw

c_src/fit_sdk/cpp/examples/encode/encode.cpp

////////////////////////////////////////////////////////////////////////////////
// The following FIT Protocol software provided may be used with FIT protocol
// devices only and remains the copyrighted property of Garmin Canada Inc.
// The software is being provided on an "as-is" basis and as an accommodation,
// and therefore all warranties, representations, or guarantees of any kind
// (whether express, implied or statutory) including, without limitation,
// warranties of merchantability, non-infringement, or fitness for a particular
// purpose, are specifically disclaimed.
//
// Copyright 2008-2016 Garmin Canada Inc.
////////////////////////////////////////////////////////////////////////////////
#include <fstream>
#include <cstdlib>
#include <cmath>
#include <time.h>
#include "fit_encode.hpp"
#include "fit_mesg_broadcaster.hpp"
#include "fit_file_id_mesg.hpp"
#include "fit_date_time.hpp"
// 2 * PI (3.14159265)
#define TWOPI 6.2831853
// Number of semicircles per meter at the equator
#define SC_PER_M 107.173
int EncodeActivityFile()
{
try
{
// Open the file
std::fstream file;
file.open("ExampleActivity.fit", std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
if (!file.is_open())
{
printf("Error opening file ExampleActivity.fit\n");
return -1;
}
// Create a FIT Encode object
fit::Encode encode(fit::ProtocolVersion::V20);
// Write the FIT header to the output stream
encode.Open(file);
// The starting timestamp for the activity
fit::DateTime startTime(std::time(0));
// Every FIT file MUST contain a File ID message
fit::FileIdMesg fileIdMesg;
fileIdMesg.SetType(FIT_FILE_ACTIVITY);
fileIdMesg.SetManufacturer(FIT_MANUFACTURER_DEVELOPMENT);
fileIdMesg.SetProduct(1);
fileIdMesg.SetTimeCreated(startTime.GetTimeStamp());
// You should create a serial number unique to your platform
srand((unsigned int)time(NULL));
fileIdMesg.SetSerialNumber(rand() % 10000 + 1);
encode.Write(fileIdMesg);
// Create the Developer Id message for the developer data fields.
fit::DeveloperDataIdMesg developerIdMesg;
// It is a BEST PRACTICE to use the same Guid for all FIT files created by your platform
// 00010203-0405-0607-0809-0A0B0C0D0E0F
FIT_UINT8 appId[] = { 0x03,0x02,0x01,0x00,0x05,0x04,0x07,0x06,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };
for (FIT_UINT8 i = 0; i < 16; i++)
{
developerIdMesg.SetApplicationId(i, appId[i]);
}
developerIdMesg.SetDeveloperDataIndex(0);
developerIdMesg.SetApplicationVersion(110);
encode.Write(developerIdMesg);
// Create the Developer Data Field Descriptions
fit::FieldDescriptionMesg doughnutsFieldDescMesg;
doughnutsFieldDescMesg.SetDeveloperDataIndex(0);
doughnutsFieldDescMesg.SetFieldDefinitionNumber(0);
doughnutsFieldDescMesg.SetFitBaseTypeId(FIT_BASE_TYPE_FLOAT32);
doughnutsFieldDescMesg.SetFieldName(0, L"Doughnuts Earned");
doughnutsFieldDescMesg.SetUnits(0, L"Doughnuts");
doughnutsFieldDescMesg.SetNativeMesgNum(FIT_MESG_NUM_SESSION);
encode.Write(doughnutsFieldDescMesg);
fit::FieldDescriptionMesg hrFieldDescMesg;
hrFieldDescMesg.SetDeveloperDataIndex(0);
hrFieldDescMesg.SetFieldDefinitionNumber(1);
hrFieldDescMesg.SetFitBaseTypeId(FIT_BASE_TYPE_UINT8);
hrFieldDescMesg.SetFieldName(0, L"Heart Rate");
hrFieldDescMesg.SetUnits(0, L"bpm");
hrFieldDescMesg.SetNativeFieldNum(fit::RecordMesg::FieldDefNum::HeartRate);
hrFieldDescMesg.SetNativeMesgNum(FIT_MESG_NUM_RECORD);
encode.Write(hrFieldDescMesg);
// Timer Events are a BEST PRACTICE for FIT ACTIVITY files
fit::EventMesg eventMesgStart;
eventMesgStart.SetTimestamp(startTime.GetTimeStamp());
eventMesgStart.SetEvent(FIT_EVENT_TIMER);
eventMesgStart.SetEventType(FIT_EVENT_TYPE_START);
encode.Write(eventMesgStart);
// Every FIT ACTIVITY file MUST contain Record messages
fit::DateTime timestamp(startTime);
// Create one hour (3600 seconds) of Record data
for (FIT_UINT16 i = 0; i < 3600; i++)
{
// Create a new Record message and set the timestamp
fit::RecordMesg recordMesg;
recordMesg.SetTimestamp(timestamp.GetTimeStamp());
// Fake Record Data of Various Signal Patterns
recordMesg.SetDistance(i); // Ramp
recordMesg.SetSpeed(1); // Flatline
recordMesg.SetHeartRate((FIT_UINT8)((sin(TWOPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine
recordMesg.SetCadence((FIT_UINT8)(i % 255)); // Sawtooth
recordMesg.SetPower((FIT_UINT16)((i % 255) < 127 ? 150 : 250)); // Square
recordMesg.SetAltitude((float)std::abs(((float)(i % 255)) - 127.0f)); // Triangle
recordMesg.SetPositionLat(0);
recordMesg.SetPositionLong((FIT_SINT32)(i * SC_PER_M));
// Add a Developer Field to the Record Message
fit::DeveloperField developerHrField(hrFieldDescMesg, developerIdMesg);
developerHrField.AddValue((FIT_UINT8)((sin(TWOPI * (0.01 * i + 10)) + 1.0) * 127.0)); // Sine
recordMesg.AddDeveloperField(developerHrField);
// Write the Rercord message to the output stream
encode.Write(recordMesg);
// Increment the timestamp by one second
timestamp.add(fit::DateTime((FIT_DATE_TIME)1));
}
// Timer Events are a BEST PRACTICE for FIT ACTIVITY files
fit::EventMesg eventMesgStop;
eventMesgStop.SetTimestamp(timestamp.GetTimeStamp());
eventMesgStop.SetEvent(FIT_EVENT_TIMER);
eventMesgStop.SetEventType(FIT_EVENT_TYPE_STOP);
encode.Write(eventMesgStop);
// Every FIT ACTIVITY file MUST contain at least one Lap message
fit::LapMesg lapMesg;
lapMesg.SetTimestamp(timestamp.GetTimeStamp());
lapMesg.SetStartTime(startTime.GetTimeStamp());
lapMesg.SetTotalElapsedTime((FIT_FLOAT32)(timestamp.GetTimeStamp() - startTime.GetTimeStamp()));
lapMesg.SetTotalTimerTime((FIT_FLOAT32)(timestamp.GetTimeStamp() - startTime.GetTimeStamp()));
encode.Write(lapMesg);
// Every FIT ACTIVITY file MUST contain at least one Session message
fit::SessionMesg sessionMesg;
sessionMesg.SetTimestamp(timestamp.GetTimeStamp());
sessionMesg.SetStartTime(startTime.GetTimeStamp());
sessionMesg.SetTotalElapsedTime((FIT_FLOAT32)(timestamp.GetTimeStamp() - startTime.GetTimeStamp()));
sessionMesg.SetTotalTimerTime((FIT_FLOAT32)(timestamp.GetTimeStamp() - startTime.GetTimeStamp()));
sessionMesg.SetSport(FIT_SPORT_STAND_UP_PADDLEBOARDING);
sessionMesg.SetSubSport(FIT_SUB_SPORT_GENERIC);
sessionMesg.SetFirstLapIndex(0);
sessionMesg.SetNumLaps(1);
// Add a Developer Field to the Session message
fit::DeveloperField doughnutsEarnedDevField(doughnutsFieldDescMesg, developerIdMesg);
doughnutsEarnedDevField.AddValue(sessionMesg.GetTotalElapsedTime() / 1200.0f);
sessionMesg.AddDeveloperField(doughnutsEarnedDevField);
encode.Write(sessionMesg);
// Every FIT ACTIVITY file MUST contain EXACTLY one Activity message
fit::ActivityMesg activityMesg;
activityMesg.SetTimestamp(timestamp.GetTimeStamp());
activityMesg.SetNumSessions(1);
int timezoneOffset = -7 * 3600;
activityMesg.SetLocalTimestamp((FIT_LOCAL_DATE_TIME)((int)timestamp.GetTimeStamp() + timezoneOffset));
encode.Write(activityMesg);
// Update the data size in the header and calculate the CRC
if (!encode.Close())
{
printf("Error closing encode.\n");
return -1;
}
// Close the file
file.close();
printf("Encoded FIT file ExampleActivity.fit.\n");
return 0;
}
catch (...)
{
throw std::exception("Exception encoding activity file");
}
}
int EncodeSettingsFile()
{
try
{
fit::Encode encode(fit::ProtocolVersion::V10);
std::fstream file;
file.open("ExampleSettings.fit", std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
if (!file.is_open())
{
printf("Error opening file ExampleSettings.fit\n");
return -1;
}
fit::FileIdMesg fileIdMesg; // Every FIT file requires a File ID message
fileIdMesg.SetType(FIT_FILE_SETTINGS);
fileIdMesg.SetManufacturer(FIT_MANUFACTURER_DEVELOPMENT);
fileIdMesg.SetProduct(1);
fileIdMesg.SetSerialNumber(12345);
fit::UserProfileMesg userProfileMesg;
userProfileMesg.SetGender(FIT_GENDER_FEMALE);
userProfileMesg.SetWeight((FIT_FLOAT32)63.1);
userProfileMesg.SetAge(99);
std::wstring wstring_name(L"TestUser");
userProfileMesg.SetFriendlyName(wstring_name);
encode.Open(file);
encode.Write(fileIdMesg);
encode.Write(userProfileMesg);
if (!encode.Close())
{
printf("Error closing encode.\n");
return -1;
}
file.close();
printf("Encoded FIT file ExampleSettings.fit.\n");
return 0;
}
catch (...)
{
throw std::exception("Exception encoding settings file.");
}
}
int EncodeMonitoringFile()
{
try
{
fit::Encode encode(fit::ProtocolVersion::V10);
std::fstream file;
time_t current_time_unix = time(0);
fit::DateTime initTime(current_time_unix);
file.open("ExampleMonitoringFile.fit", std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);
if (!file.is_open())
{
printf("Error opening file ExampleMonitoringFile.fit\n");
return -1;
}
encode.Open(file);
fit::FileIdMesg fileIdMesg; // Every FIT file requires a File ID message
fileIdMesg.SetType(FIT_FILE_MONITORING_B);
fileIdMesg.SetManufacturer(FIT_MANUFACTURER_DEVELOPMENT);
fileIdMesg.SetProduct(1);
fileIdMesg.SetSerialNumber(12345);
encode.Write(fileIdMesg);
fit::DeviceInfoMesg deviceInfoMesg;
deviceInfoMesg.SetTimestamp(initTime.GetTimeStamp()); // Convert to FIT time and write timestamp.
deviceInfoMesg.SetBatteryStatus(FIT_BATTERY_STATUS_GOOD);
encode.Write(deviceInfoMesg);
fit::MonitoringMesg monitoringMesg;
// By default, each time a new message is written the Local Message Type 0 will be redefined to match the new message.
// In this case,to avoid having a definition message each time there is a DeviceInfoMesg, we can manually set the Local Message Type of the MonitoringMessage to '1'.
// By doing this we avoid an additional 7 definition messages in our FIT file.
monitoringMesg.SetLocalNum(1);
monitoringMesg.SetTimestamp(initTime.GetTimeStamp()); // Initialise Timestamp to now
monitoringMesg.SetCycles(0); // Initialise Cycles to 0
for (int i = 0; i < 4; i++) // This loop represents 1/6 of a day
{
for (int j = 0; j < 4; j++) // Each one of these loops represent 1 hour
{
fit::DateTime walkingTime(current_time_unix);
monitoringMesg.SetTimestamp(walkingTime.GetTimeStamp());
monitoringMesg.SetActivityType(FIT_ACTIVITY_TYPE_WALKING); // By setting this to WALKING, the Cycles field will be interpretted as Steps
monitoringMesg.SetCycles(monitoringMesg.GetCycles() + (rand() % 1000 + 1)); // Cycles are accumulated (i.e. must be increasing)
encode.Write(monitoringMesg);
current_time_unix += (time_t)(3600); //Add an hour to our contrieved timestamp
}
fit::DateTime statusTime(current_time_unix);
deviceInfoMesg.SetTimestamp(statusTime.GetTimeStamp());
deviceInfoMesg.SetBatteryStatus(FIT_BATTERY_STATUS_GOOD);
encode.Write(deviceInfoMesg);
}
if (!encode.Close())
{
printf("Error closing encode.\n");
return -1;
}
file.close();
printf("Encoded FIT file ExampleMonitoringFile.fit.\n");
return 0;
}
catch (...)
{
throw std::exception("Exception encoding monitoring file.");
}
}
int main()
{
printf("FIT Encode Example Application\n");
int returnValue = 0;
try
{
returnValue += EncodeSettingsFile();
returnValue += EncodeMonitoringFile();
returnValue += EncodeActivityFile();
}
catch (const std::exception &e)
{
printf("Exception occurred while encoding example files: %s", e.what());
return -1;
}
return returnValue;
}