<commit_before>/*****************************************************************************\
** export_tcx.c                                                              **
** TCX export code                                                           **
\*****************************************************************************/

#include "ttbin.h"

#include <math.h>

void export_tcx(TTBIN_FILE *ttbin, FILE *file)
{
    char timestr[32];
    TTBIN_RECORD *record;
    float max_speed = 0.0f;
    float total_speed = 0.0f;
    uint32_t total_heart_rate = 0;
    uint32_t max_heart_rate = 0;
    uint32_t heart_rate_count = 0;
    uint32_t move_count = 0;
    uint32_t total_step_count = 0;
    unsigned heart_rate;
    int lap_state;
    int insert_pause;
    float lap_avg_speed;
    unsigned lap_time, lap_start_time = 0;
    float lap_distance, lap_start_distance = 0.0f;
    float lap_max_speed;
    unsigned lap_calories, lap_start_calories = 0;
    unsigned lap_heart_rate_count;
    unsigned lap_start_heart_rate_count;
    unsigned lap_avg_heart_rate;
    unsigned lap_max_heart_rate;
    unsigned lap_step_count;
    float cadence_avg = 0.0f;
    const char *trigger_method = "Manual";
    double distance_factor = 1;
    uint32_t steps, steps_prev = 0;

    if (ttbin->activity == ACTIVITY_TREADMILL) {
        for (record = ttbin->last; record; record = record->prev)
        {
            if ((record->tag == TAG_TREADMILL) && record->treadmill.distance)
            {
                distance_factor = ttbin->total_distance / record->treadmill.distance;
                break;
            }
        }
    } else if (!ttbin->gps_records.count)
        return;

    fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
          "<TrainingCenterDatabase xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"
          " http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\""
          " xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\""
          " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
          " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
          " xmlns:ns2=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">\r\n"
          "    <Activities>\r\n"
          "        <Activity Sport=\"", file);
    switch(ttbin->activity)
    {
    case ACTIVITY_RUNNING:   fputs("Running", file);   break;
    case ACTIVITY_CYCLING:   fputs("Cycling", file);   break;
    case ACTIVITY_SWIMMING:  fputs("Pool Swim", file); break;
    case ACTIVITY_TREADMILL: fputs("Running", file);   break; /* per Garmin spec, not "Treadmill" */
    case ACTIVITY_FREESTYLE: fputs("Freestyle", file); break;
    default:                 fputs("Unknown", file);   break;
    }
    fputs("\">\r\n"
          "            <Id>", file);
    strftime(timestr, sizeof(timestr), "%FT%X.000Z", gmtime(&ttbin->timestamp_utc));
    fputs(timestr, file);
    fputs("</Id>\r\n", file);

    fprintf(file, "            <Lap StartTime=\"%s\">\r\n", timestr);
    fputs(        "                <Intensity>Active</Intensity>\r\n"
                  "                <Track>\r\n", file);

    heart_rate = 0;
    lap_state = 0;
    insert_pause = 0;
    for (record = ttbin->first; record; record = record->next)
    {
        switch (record->tag)
        {
        case TAG_TRAINING_SETUP:
            switch (record->training_setup.type)
            {
            case 7: trigger_method="Time";     break;
            case 8: trigger_method="Distance"; break;
            }
            break;

        case TAG_STATUS:
            if ((record->status.status == 2) && (lap_state == 0))
                insert_pause = 1;
            break;

        case TAG_TREADMILL:
            /* this will happen if the activity is paused and then resumed */
            if (record->treadmill.timestamp == 0)
                break;

            steps = record->treadmill.steps - steps_prev;
            steps_prev = record->treadmill.steps;

            /* low-pass filter the coarse cadence values (1, 2, 3 steps/sec), with RC=20
               based on: https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter */
            {
                float dt = smooth_cadence_count == 0 ? 1.0f : record->treadmill.timestamp - smooth_cadence_lt;
                float rc = smooth_cadence_count < 20 ? (float)smooth_cadence_count : 20.0f;
                float alpha = dt / (rc + dt);
                int coarse_cadence = 30*(int)steps;
                smooth_cadence += alpha * (coarse_cadence - smooth_cadence);
                smooth_cadence_count++;
                smooth_cadence_lt = record->treadmill.timestamp;
            }

            total_step_count += steps;
            goto motion_common;

        case TAG_GPS:
            /* this will happen if the activity is paused and then resumed, or if the GPS signal is lost  */
            if ((record->gps.timestamp == 0) || ((record->gps.latitude == 0) && (record->gps.longitude == 0)))
                break;

            if (record->gps.instant_speed > max_speed)
                max_speed = record->gps.instant_speed;
            total_speed += record->gps.instant_speed;

            /* low-pass filter the coarse cadence values (1, 2, 3 steps/sec), with RC=20
            if (ttbin->activity==ACTIVITY_RUNNING)
                total_step_count += record->gps.cycles;
            goto motion_common;

        motion_common:
            ++move_count;

            if (lap_state == 0 && insert_pause)
            {
                /* Garmin's tools use multiple tracks within a lap to signal a pause */
                insert_pause = 0;
                fputs("                </Track>\r\n"
                      "                <Track>\r\n", file);
            }

            if (lap_state == 1)
            {
                fprintf(file, "            <Lap StartTime=\"%s\">\r\n", timestr);
                fputs(        "                <Intensity>Active</Intensity>\r\n"
                              "                <Track>\r\n", file);
                lap_state = 0;
            }<commit_msg>don't use goto, per @ryanbinns comments on #38<commit_after>/*****************************************************************************\
** export_tcx.c                                                              **
** TCX export code                                                           **
\*****************************************************************************/

#include "ttbin.h"

#include <math.h>

void export_tcx(TTBIN_FILE *ttbin, FILE *file)
{
    char timestr[32];
    TTBIN_RECORD *record;
    float max_speed = 0.0f;
    float total_speed = 0.0f;
    uint32_t total_heart_rate = 0;
    uint32_t max_heart_rate = 0;
    uint32_t heart_rate_count = 0;
    uint32_t move_count = 0;
    uint32_t total_step_count = 0;
    unsigned heart_rate;
    int lap_state;
    int insert_pause;
    float lap_avg_speed;
    unsigned lap_time, lap_start_time = 0;
    float lap_distance, lap_start_distance = 0.0f;
    float lap_max_speed;
    unsigned lap_calories, lap_start_calories = 0;
    unsigned lap_heart_rate_count;
    unsigned lap_start_heart_rate_count;
    unsigned lap_avg_heart_rate;
    unsigned lap_max_heart_rate;
    unsigned lap_step_count;
    float cadence_avg = 0.0f;
    const char *trigger_method = "Manual";
    double distance_factor = 1;
    uint32_t steps, steps_prev = 0;

    if (ttbin->activity == ACTIVITY_TREADMILL) {
        for (record = ttbin->last; record; record = record->prev)
        {
            if ((record->tag == TAG_TREADMILL) && record->treadmill.distance)
            {
                distance_factor = ttbin->total_distance / record->treadmill.distance;
                break;
            }
        }
    } else if (!ttbin->gps_records.count)
        return;

    fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
          "<TrainingCenterDatabase xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"
          " http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\""
          " xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\""
          " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
          " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
          " xmlns:ns2=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">\r\n"
          "    <Activities>\r\n"
          "        <Activity Sport=\"", file);
    switch(ttbin->activity)
    {
    case ACTIVITY_RUNNING:   fputs("Running", file);   break;
    case ACTIVITY_CYCLING:   fputs("Cycling", file);   break;
    case ACTIVITY_SWIMMING:  fputs("Pool Swim", file); break;
    case ACTIVITY_TREADMILL: fputs("Running", file);   break; /* per Garmin spec, not "Treadmill" */
    case ACTIVITY_FREESTYLE: fputs("Freestyle", file); break;
    default:                 fputs("Unknown", file);   break;
    }
    fputs("\">\r\n"
          "            <Id>", file);
    strftime(timestr, sizeof(timestr), "%FT%X.000Z", gmtime(&ttbin->timestamp_utc));
    fputs(timestr, file);
    fputs("</Id>\r\n", file);

    fprintf(file, "            <Lap StartTime=\"%s\">\r\n", timestr);
    fputs(        "                <Intensity>Active</Intensity>\r\n"
                  "                <Track>\r\n", file);

    heart_rate = 0;
    lap_state = 0;
    insert_pause = 0;
    for (record = ttbin->first; record; record = record->next)
    {
        switch (record->tag)
        {
        case TAG_TRAINING_SETUP:
            switch (record->training_setup.type)
            {
            case 7: trigger_method="Time";     break;
            case 8: trigger_method="Distance"; break;
            }
            break;

        case TAG_STATUS:
            if ((record->status.status == 2) && (lap_state == 0))
                insert_pause = 1;
            break;

        case TAG_TREADMILL:
        case TAG_GPS:
            if (record->tag==TAG_TREADMILL) {
                /* this will happen if the activity is paused and then resumed */
                if (record->treadmill.timestamp == 0)
                    break;

                steps = record->treadmill.steps - steps_prev;
                steps_prev = record->treadmill.steps;

                /* low-pass filter the coarse cadence values (1, 2, 3 steps/sec), with RC=20
                   based on: https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter */
                {
                    float dt = smooth_cadence_count == 0 ? 1.0f : record->treadmill.timestamp - smooth_cadence_lt;
                    float rc = smooth_cadence_count < 20 ? (float)smooth_cadence_count : 20.0f;
                    float alpha = dt / (rc + dt);
                    int coarse_cadence = 30*(int)steps;
                    smooth_cadence += alpha * (coarse_cadence - smooth_cadence);
                    smooth_cadence_count++;
                    smooth_cadence_lt = record->treadmill.timestamp;
                }

                total_step_count += steps;
            } else { /* TAG_GPS only */
                /* this will happen if the activity is paused and then resumed, or if the GPS signal is lost  */
                if ((record->gps.timestamp == 0) || ((record->gps.latitude == 0) && (record->gps.longitude == 0)))
                    break;

                if (record->gps.instant_speed > max_speed)
                    max_speed = record->gps.instant_speed;
                total_speed += record->gps.instant_speed;

                /* low-pass filter the coarse cadence values (1, 2, 3 steps/sec), with RC=20
                   based on: https://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter */
                if (ttbin->activity==ACTIVITY_RUNNING)
                {
                    float dt = smooth_cadence_count == 0 ? 1.0f : record->gps.timestamp - smooth_cadence_lt;
                    float rc = smooth_cadence_count < 20 ? (float)smooth_cadence_count : 20.0f;
                    float alpha = dt / (rc + dt);
                    int coarse_cadence = 30*(int)record->gps.cycles;
                    smooth_cadence += alpha * (coarse_cadence - smooth_cadence);
                    smooth_cadence_count++;
                    smooth_cadence_lt = record->gps.timestamp;
                }
                if (ttbin->activity==ACTIVITY_RUNNING)
                    total_step_count += record->gps.cycles;
            }

            /* code common to both TAG_GPS and TAG_TREADMILL */
            ++move_count;

            if (lap_state == 0 && insert_pause)
            {
                /* Garmin's tools use multiple tracks within a lap to signal a pause */
                insert_pause = 0;
                fputs("                </Track>\r\n"
                      "                <Track>\r\n", file);
            }

            if (lap_state == 1)
            {
                fprintf(file, "            <Lap StartTime=\"%s\">\r\n", timestr);
                fputs(        "                <Intensity>Active</Intensity>\r\n"
                              "                <Track>\r\n", file);
                lap_state = 0;
            }
{'fraction_non_alphanumeric': 0.13932193128500076, 'fraction_numerical': 0.014227334645073407, 'mean_word_length': 1.7721837633731907, 'pattern_counts': {'":': 0, '<': 40, '<?xml version=': 2, '>': 100, 'https://': 3, 'lorem ipsum': 0, 'www.': 12, 'xml': 18}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1732969', 'n_tokens_mistral': 4205, 'n_tokens_neox': 3871, 'n_words': 789}