@sports-alliance/sports-lib
Version:
A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc
855 lines (854 loc) • 47.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuuntoSampleMapper = exports.SuuntoIntensityZonesMapper = exports.SuuntoSettingsMapper = exports.EventImporterSuuntoJSON = void 0;
const event_1 = require("../../../event");
const activity_1 = require("../../../../activities/activity");
const creator_1 = require("../../../../creators/creator");
const lap_1 = require("../../../../laps/lap");
const data_altitude_1 = require("../../../../data/data.altitude");
const data_cadence_1 = require("../../../../data/data.cadence");
const data_heart_rate_1 = require("../../../../data/data.heart-rate");
const data_speed_1 = require("../../../../data/data.speed");
const data_vertical_speed_1 = require("../../../../data/data.vertical-speed");
const data_temperature_1 = require("../../../../data/data.temperature");
const data_sea_level_pressure_1 = require("../../../../data/data.sea-level-pressure");
const data_latitude_degrees_1 = require("../../../../data/data.latitude-degrees");
const data_longitude_degrees_1 = require("../../../../data/data.longitude-degrees");
const data_power_1 = require("../../../../data/data.power");
const data_altitude_gps_1 = require("../../../../data/data.altitude-gps");
const data_absolute_pressure_1 = require("../../../../data/data.absolute-pressure");
const data_ehpe_1 = require("../../../../data/data.ehpe");
const data_evpe_1 = require("../../../../data/data.evpe");
const data_number_of_satellites_1 = require("../../../../data/data.number-of-satellites");
const data_satellite_5_best_snr_1 = require("../../../../data/data.satellite-5-best-snr");
const intensity_zones_1 = require("../../../../intensity-zones/intensity-zones");
const data_ibi_1 = require("../../../../data/ibi/data.ibi");
const importer_suunto_activity_ids_1 = require("./importer.suunto.activity.ids");
const importer_suunto_device_names_1 = require("./importer.suunto.device.names");
const data_duration_1 = require("../../../../data/data.duration");
const data_elapsed_time_1 = require("../../../../data/data.elapsed-time");
const data_altitude_max_1 = require("../../../../data/data.altitude-max");
const data_distance_1 = require("../../../../data/data.distance");
const data_ascent_time_1 = require("../../../../data/data.ascent-time");
const data_descent_time_1 = require("../../../../data/data.descent-time");
const data_descent_1 = require("../../../../data/data.descent");
const data_ascent_1 = require("../../../../data/data.ascent");
const data_epoc_1 = require("../../../../data/data.epoc");
const data_energy_1 = require("../../../../data/data.energy");
const data_feeling_1 = require("../../../../data/data.feeling");
const data_recovery_time_1 = require("../../../../data/data.recovery-time");
const data_vo2_max_1 = require("../../../../data/data.vo2-max");
const data_pause_1 = require("../../../../data/data.pause");
const data_heart_rate_avg_1 = require("../../../../data/data.heart-rate-avg");
const data_heart_rate_max_1 = require("../../../../data/data.heart-rate-max");
const data_heart_rate_min_1 = require("../../../../data/data.heart-rate-min");
const data_cadence_avg_1 = require("../../../../data/data.cadence-avg");
const data_cadence_max_1 = require("../../../../data/data.cadence-max");
const data_cadence_min_1 = require("../../../../data/data.cadence-min");
const data_power_avg_1 = require("../../../../data/data.power-avg");
const data_power_max_1 = require("../../../../data/data.power-max");
const data_power_min_1 = require("../../../../data/data.power-min");
const data_speed_avg_1 = require("../../../../data/data.speed-avg");
const data_speed_max_1 = require("../../../../data/data.speed-max");
const data_speed_min_1 = require("../../../../data/data.speed-min");
const data_temperature_avg_1 = require("../../../../data/data.temperature-avg");
const data_temperature_max_1 = require("../../../../data/data.temperature-max");
const data_temperature_min_1 = require("../../../../data/data.temperature-min");
const data_vertical_speed_avg_1 = require("../../../../data/data.vertical-speed-avg");
const data_vertical_speed_max_1 = require("../../../../data/data.vertical-speed-max");
const data_vertical_speed_min_1 = require("../../../../data/data.vertical-speed-min");
const data_altitude_avg_1 = require("../../../../data/data.altitude-avg");
const data_altitude_min_1 = require("../../../../data/data.altitude-min");
const data_fused_location_1 = require("../../../../data/data.fused-location");
const activity_types_1 = require("../../../../activities/activity.types");
const lap_types_1 = require("../../../../laps/lap.types");
const data_pace_avg_1 = require("../../../../data/data.pace-avg");
const data_pace_max_1 = require("../../../../data/data.pace-max");
const data_pace_min_1 = require("../../../../data/data.pace-min");
const data_timer_time_1 = require("../../../../data/data.timer-time");
const data_fused_altitude_1 = require("../../../../data/data.fused-altitude");
const data_battery_charge_1 = require("../../../../data/data.battery-charge");
const data_battery_current_1 = require("../../../../data/data.battery-current");
const data_battery_voltage_1 = require("../../../../data/data.battery-voltage");
const helpers_1 = require("../../../utilities/helpers");
const event_utilities_1 = require("../../../utilities/event.utilities");
const data_alti_baro_profile_1 = require("../../../../data/data.alti-baro-profile");
const data_auto_lap_distance_1 = require("../../../../data/data.auto-lap-distance");
const data_auto_lap_duration_1 = require("../../../../data/data.auto-lap-duration");
const data_auto_pause_used_1 = require("../../../../data/data.auto-pause-used");
const data_bike_pod_used_1 = require("../../../../data/data.bike-pod-used");
const data_enabled_navigation_systems_1 = require("../../../../data/data.enabled-navigation-systems");
const data_foot_pod_used_1 = require("../../../../data/data.foot-pod-used");
const data_heart_rate_used_1 = require("../../../../data/data.heart-rate-used");
const data_power_pod_used_1 = require("../../../../data/data.power-pod-used");
const data_store_1 = require("../../../../data/data.store");
const ibi_stream_1 = require("../../../../streams/ibi-stream");
const data_steps_1 = require("../../../../data/data.steps");
const data_pool_length_1 = require("../../../../data/data.pool-length");
const data_device_location_1 = require("../../../../data/data.device-location");
const data_aerobic_training_effect_1 = require("../../../../data/data-aerobic-training-effect");
const data_heart_rate_zone_one_duration_1 = require("../../../../data/data.heart-rate-zone-one-duration");
const data_heart_rate_zone_two_duration_1 = require("../../../../data/data.heart-rate-zone-two-duration");
const data_heart_rate_zone_three_duration_1 = require("../../../../data/data.heart-rate-zone-three-duration");
const data_heart_rate_zone_four_duration_1 = require("../../../../data/data.heart-rate-zone-four-duration");
const data_heart_rate_zone_five_duration_1 = require("../../../../data/data.heart-rate-zone-five-duration");
const data_power_zone_one_duration_1 = require("../../../../data/data.power-zone-one-duration");
const data_power_zone_two_duration_1 = require("../../../../data/data.power-zone-two-duration");
const data_power_zone_three_duration_1 = require("../../../../data/data.power-zone-three-duration");
const data_power_zone_four_duration_1 = require("../../../../data/data.power-zone-four-duration");
const data_power_zone_five_duration_1 = require("../../../../data/data.power-zone-five-duration");
const data_speed_zone_one_duration_1 = require("../../../../data/data.speed-zone-one-duration");
const data_speed_zone_two_duration_1 = require("../../../../data/data.speed-zone-two-duration");
const data_speed_zone_three_duration_1 = require("../../../../data/data.speed-zone-three-duration");
const data_speed_zone_four_duration_1 = require("../../../../data/data.speed-zone-four-duration");
const data_speed_zone_five_duration_1 = require("../../../../data/data.speed-zone-five-duration");
const data_ground_contact_time_1 = require("../../../../data/data.ground-contact-time");
const data_ground_contact_time_avg_1 = require("../../../../data/data.ground-contact-time-avg");
const data_ground_contact_time_max_1 = require("../../../../data/data.ground-contact-time-max");
const data_ground_contact_time_min_1 = require("../../../../data/data.ground-contact-time-min");
const data_vertical_oscillation_1 = require("../../../../data/data.vertical-oscillation");
const data_vertical_oscillation_avg_1 = require("../../../../data/data.vertical-oscillation-avg");
const data_vertical_oscillation_max_1 = require("../../../../data/data.vertical-oscillation-max");
const data_vertical_oscillation_min_1 = require("../../../../data/data.vertical-oscillation-min");
const data_fitness_age_1 = require("../../../../data/data.fitness-age");
const data_max_hr_setting_1 = require("../../../../data/data.max-hr-setting");
const data_stamina_1 = require("../../../../data/data.stamina");
const data_depth_1 = require("../../../../data/data.depth");
const data_depth_max_1 = require("../../../../data/data.depth-max");
const file_type_enum_1 = require("../../file-type.enum");
const activity_parsing_options_1 = require("../../../../activities/activity-parsing-options");
class EventImporterSuuntoJSON {
static getFromJSONString(jsonString, options = activity_parsing_options_1.ActivityParsingOptions.DEFAULT) {
return new Promise((resolve, _reject) => {
const eventJSONObject = JSON.parse(jsonString);
// debugger;
// Create a creator and pass it to all activities (later)
const creator = new creator_1.Creator(importer_suunto_device_names_1.ImporterSuuntoDeviceNames[eventJSONObject.DeviceLog.Device.Name] || // Try to get a listed name
eventJSONObject.DeviceLog.Device.Name // If not fallback to typed
);
creator.serialNumber = eventJSONObject.DeviceLog.Device.SerialNumber;
creator.hwInfo = eventJSONObject.DeviceLog.Device.Info.HW;
creator.swInfo = eventJSONObject.DeviceLog.Device.Info.SW;
// Go over the samples and get the ones with activity start times
const activityStartEventSamples = eventJSONObject.DeviceLog.Samples.filter((sample) => {
return sample.Events && sample.Events[0] && sample.Events[0].Activity;
});
// Check if there is a Fused Altitude event
const fusedAltitudeEventSamples = eventJSONObject.DeviceLog.Samples.filter((sample) => {
return sample.Events && sample.Events[0] && sample.Events[0].Altitude;
});
// Get the lap start events
const lapEventSamples = eventJSONObject.DeviceLog.Samples.filter((sample) => {
return (sample.Events &&
sample.Events[0] &&
sample.Events[0].Lap &&
sample.Events[0].Lap.Type !== 'Start' &&
sample.Events[0].Lap.Type !== 'Stop');
});
// Get the stop event
const stopEventSample = eventJSONObject.DeviceLog.Samples.find((sample) => {
return sample.Events && sample.Events[0] && sample.Events[0].Lap && sample.Events[0].Lap.Type === 'Stop';
});
// Add the stop event to the laps since it's also a lap stop event
if (stopEventSample) {
lapEventSamples.push(stopEventSample);
}
// Get the activity windows
const activityWindows = eventJSONObject.DeviceLog.Windows
? eventJSONObject.DeviceLog.Windows.filter((windowObj) => {
return windowObj.Window.Type === 'Activity';
}).map((activityWindow) => activityWindow.Window)
: [];
// Get the lap windows
const lapWindows = eventJSONObject.DeviceLog.Windows
? eventJSONObject.DeviceLog.Windows.filter((windowObj) => {
return windowObj.Window.Type === 'Lap' || windowObj.Window.Type === 'Autolap';
}).map((lapWindow) => lapWindow.Window)
: [];
// Create the activities
const activities = activityStartEventSamples.map((activityStartEventSample, index) => {
// If no stop event, use the last sample time as end date
const lastSampleTime = eventJSONObject.DeviceLog.Samples[eventJSONObject.DeviceLog.Samples.length - 1].TimeISO8601;
const fallbackEndTime = stopEventSample ? stopEventSample.TimeISO8601 : lastSampleTime;
const activity = new activity_1.Activity(new Date(activityStartEventSample.TimeISO8601), activityStartEventSamples.length - 1 === index
? new Date(fallbackEndTime)
: new Date(activityStartEventSamples[index + 1].TimeISO8601), activity_types_1.ActivityTypes[(importer_suunto_activity_ids_1.ImporterSuuntoActivityIds[activityStartEventSample.Events[0].Activity.ActivityType])], creator, options);
// Set the end date to the stop event time if the activity is the last or the only one else set it on the next itery time
// Create the stats these are a 1:1 ref arrays
if (activityWindows[index]) {
this.getStats(activityWindows[index]).forEach(stat => {
activity.addStat(stat);
});
}
// Add the pause from end date minurs start date and removing the duration as widows do not contain the pause time
if (!activity.getDuration()) {
const elapsedSeconds = (activity.endDate.getTime() - activity.startDate.getTime()) / 1000;
activity.setDuration(new data_duration_1.DataDuration(elapsedSeconds));
activity.setTimer(new data_timer_time_1.DataTimerTime(elapsedSeconds));
activity.addStat(new data_elapsed_time_1.DataElapsedTime(elapsedSeconds));
}
else if (!activity.getStat(data_elapsed_time_1.DataElapsedTime.type)) {
const elapsedSeconds = (activity.endDate.getTime() - activity.startDate.getTime()) / 1000;
activity.addStat(new data_elapsed_time_1.DataElapsedTime(elapsedSeconds));
}
if (!activity.getTimer()) {
activity.setTimer(new data_timer_time_1.DataTimerTime(activity.getDuration().getValue()));
}
activity.setPause(new data_pause_1.DataPause((activity.endDate.getTime() - activity.startDate.getTime()) / 1000 - activity.getDuration().getValue()));
// Set the zones for the activity @todo fix
this.setIntensityZones(activity, eventJSONObject.DeviceLog.Header);
// Add the fused altitude event
if (fusedAltitudeEventSamples.length) {
activity.addStat(new data_fused_altitude_1.DataFusedAltitude(true));
}
return activity;
});
// If nothing found
if (!activities.length) {
activities.push(new activity_1.Activity(new Date(eventJSONObject.DeviceLog.Header.DateTime), new Date(new Date(eventJSONObject.DeviceLog.Header.DateTime).getTime() +
eventJSONObject.DeviceLog.Header.Duration * 1000), activity_types_1.ActivityTypes.unknown, creator, options));
}
// set the start dates of all lap types to the start of the first activity
const lapStartDatesByType = lapEventSamples.reduce((lapStartDatesByTypeObject, lapEventSample, index) => {
// If its a stop event then set the start date to the previous
if (lapEventSample.Events[0].Lap.Type === 'Stop' && lapEventSamples.length > 1) {
lapStartDatesByTypeObject[lapEventSample.Events[0].Lap.Type] = new Date(lapEventSamples[index - 1].TimeISO8601);
return lapStartDatesByTypeObject;
}
lapStartDatesByTypeObject[lapEventSample.Events[0].Lap.Type] = activities[0].startDate;
return lapStartDatesByTypeObject;
}, {});
const laps = lapEventSamples.reduce((lapArray, lapEventSample, lapIndex) => {
// if there is only one lap then skip it's the whole activity
if (lapEventSamples.length === 1) {
return lapArray;
}
// Set the end date
const lapEndDate = new Date(lapEventSample.TimeISO8601);
// Set the start date.
// Set it for the next run
const lap = new lap_1.Lap(lapStartDatesByType[lapEventSample.Events[0].Lap.Type], lapEndDate, lapIndex + 1, lap_types_1.LapTypes[lapEventSample.Events[0].Lap.Type]);
lapStartDatesByType[lapEventSample.Events[0].Lap.Type] = lapEndDate;
if (lapWindows[lapIndex]) {
this.getStats(lapWindows[lapIndex]).forEach(stat => {
lap.addStat(stat);
});
}
// Add the pause from end date minurs start date and removing the duration as widows do not contain the pause time
if (!lap.getDuration()) {
const elapsedSeconds = (lap.endDate.getTime() - lap.startDate.getTime()) / 1000;
lap.setDuration(new data_duration_1.DataDuration(elapsedSeconds));
lap.setTimer(new data_timer_time_1.DataTimerTime(elapsedSeconds));
lap.addStat(new data_elapsed_time_1.DataElapsedTime(elapsedSeconds));
}
else if (!lap.getStat(data_elapsed_time_1.DataElapsedTime.type)) {
const elapsedSeconds = (lap.endDate.getTime() - lap.startDate.getTime()) / 1000;
lap.addStat(new data_elapsed_time_1.DataElapsedTime(elapsedSeconds));
}
if (!lap.getTimer()) {
lap.setTimer(new data_timer_time_1.DataTimerTime(lap.getDuration().getValue()));
}
lap.setPause(new data_pause_1.DataPause((lap.endDate.getTime() - lap.startDate.getTime()) / 1000 - lap.getDuration().getValue()));
lapArray.push(lap);
return lapArray;
}, []);
// Add the laps to the belonging activity. If a lap starts or stops at the activity date delta then it belong to the acitvity
// @todo move laps to event so we don't have cross border laps to acivities and decouple them
activities.forEach((activity) => {
laps
.filter((lap) => {
// If the lap start belongs to the activity
if (lap.startDate <= activity.endDate && lap.startDate >= activity.startDate) {
return true;
}
// if the lap end belongs also...
if (lap.endDate >= activity.startDate && lap.endDate <= activity.endDate) {
return true;
}
return false;
})
.forEach((activityLap) => {
activity.addLap(activityLap);
});
});
// Add the samples that belong to the activity and the ibi data.
activities.forEach((activity) => {
// Get the samples that belong to this activity
const activitySamples = eventJSONObject.DeviceLog.Samples.filter((sample) => new Date(sample.TimeISO8601) >= activity.startDate && new Date(sample.TimeISO8601) <= activity.endDate);
// debugger;
// Check if there is fused Location
activity.addStat(new data_fused_location_1.DataFusedLocation(false));
activity.addStat(new data_fused_location_1.DataFusedLocation(activitySamples.filter((sample) => this.hasFusedLocData(sample)).length > 0));
// Should filter on type and create the samples
this.setStreamsForActivity(activity, activitySamples);
});
// Add the ibiData
if (eventJSONObject.DeviceLog['R-R'] && eventJSONObject.DeviceLog['R-R'].Data) {
// prepare the data array per activity removing the offset
activities.forEach((activity) => {
let timeSum = 0;
const ibiData = eventJSONObject.DeviceLog['R-R'].Data.filter((ibi) => {
timeSum += ibi;
const ibiDataDate = new Date(activities[0].startDate.getTime() + timeSum);
return ibiDataDate >= activity.startDate && ibiDataDate <= activity.endDate;
});
// Override HR from RR only if we get enough plausible samples after filtering.
const hrSamplesFromIBI = this.getHRSamplesFromIBIData(activity, ibiData);
if (hrSamplesFromIBI.length > this.MIN_VALID_RR_SAMPLES_FOR_HR_OVERRIDE) {
const existingHRStream = activity.getAllStreams().find(stream => stream.type === data_heart_rate_1.DataHeartRate.type);
if (existingHRStream) {
activity.removeStream(existingHRStream);
}
this.setStreamsForActivity(activity, hrSamplesFromIBI);
}
activity.addStream(new ibi_stream_1.IBIStream(ibiData));
});
}
// Create an event
// @todo check if start and end date can derive from the json
const event = new event_1.Event('', activities[0].startDate, activities[activities.length - 1].endDate, file_type_enum_1.FileType.SUUNTO);
activities.forEach(activity => event.addActivity(activity));
// Get the settings and add it to all activities as it's logical
if (eventJSONObject.DeviceLog.Header.Settings) {
this.getSettings(eventJSONObject.DeviceLog.Header.Settings).forEach(stat => {
event.getActivities().forEach(activity => activity.addStat(stat));
});
}
if (activities.length === 1) {
const stats = this.getStats(eventJSONObject.DeviceLog.Header).filter(stat => stat instanceof data_steps_1.DataSteps ||
stat instanceof data_vo2_max_1.DataVO2Max ||
stat instanceof data_device_location_1.DataDeviceLocation ||
stat instanceof data_pool_length_1.DataPoolLength);
stats.forEach(stat => activities[0].addStat(stat));
}
// Generate stats
event_utilities_1.EventUtilities.generateStatsForAll(event);
// Populate the event stats from the Header Object
this.getStats(eventJSONObject.DeviceLog.Header).forEach(stat => {
event.addStat(stat);
});
resolve(event);
});
}
static hasFusedLocData(sample) {
return !!sample.Inertial || !!sample.GpsRef;
}
static setIntensityZones(activity, object) {
exports.SuuntoIntensityZonesMapper.forEach(intensityZonesMap => {
if (!object[intensityZonesMap.sampleField]) {
return;
}
const zones = new intensity_zones_1.IntensityZones(intensityZonesMap.dataType);
zones.zone1Duration = object[intensityZonesMap.sampleField].Zone1Duration;
zones.zone2Duration = object[intensityZonesMap.sampleField].Zone2Duration;
zones.zone2LowerLimit = intensityZonesMap.convertSampleValue(object[intensityZonesMap.sampleField].Zone2LowerLimit);
zones.zone3Duration = object[intensityZonesMap.sampleField].Zone3Duration;
zones.zone3LowerLimit = intensityZonesMap.convertSampleValue(object[intensityZonesMap.sampleField].Zone3LowerLimit);
zones.zone4Duration = object[intensityZonesMap.sampleField].Zone4Duration;
zones.zone4LowerLimit = intensityZonesMap.convertSampleValue(object[intensityZonesMap.sampleField].Zone4LowerLimit);
zones.zone5Duration = object[intensityZonesMap.sampleField].Zone5Duration;
zones.zone5LowerLimit = intensityZonesMap.convertSampleValue(object[intensityZonesMap.sampleField].Zone5LowerLimit);
activity.intensityZones.push(zones);
switch (intensityZonesMap.dataType) {
case data_heart_rate_1.DataHeartRate.type:
activity.addStat(new data_heart_rate_zone_one_duration_1.DataHeartRateZoneOneDuration(zones.zone1Duration));
activity.addStat(new data_heart_rate_zone_two_duration_1.DataHeartRateZoneTwoDuration(zones.zone2Duration));
activity.addStat(new data_heart_rate_zone_three_duration_1.DataHeartRateZoneThreeDuration(zones.zone3Duration));
activity.addStat(new data_heart_rate_zone_four_duration_1.DataHeartRateZoneFourDuration(zones.zone4Duration));
activity.addStat(new data_heart_rate_zone_five_duration_1.DataHeartRateZoneFiveDuration(zones.zone5Duration));
break;
case data_power_1.DataPower.type:
activity.addStat(new data_power_zone_one_duration_1.DataPowerZoneOneDuration(zones.zone1Duration));
activity.addStat(new data_power_zone_two_duration_1.DataPowerZoneTwoDuration(zones.zone2Duration));
activity.addStat(new data_power_zone_three_duration_1.DataPowerZoneThreeDuration(zones.zone3Duration));
activity.addStat(new data_power_zone_four_duration_1.DataPowerZoneFourDuration(zones.zone4Duration));
activity.addStat(new data_power_zone_five_duration_1.DataPowerZoneFiveDuration(zones.zone5Duration));
break;
case data_speed_1.DataSpeed.type:
activity.addStat(new data_speed_zone_one_duration_1.DataSpeedZoneOneDuration(zones.zone1Duration));
activity.addStat(new data_speed_zone_two_duration_1.DataSpeedZoneTwoDuration(zones.zone2Duration));
activity.addStat(new data_speed_zone_three_duration_1.DataSpeedZoneThreeDuration(zones.zone3Duration));
activity.addStat(new data_speed_zone_four_duration_1.DataSpeedZoneFourDuration(zones.zone4Duration));
activity.addStat(new data_speed_zone_five_duration_1.DataSpeedZoneFiveDuration(zones.zone5Duration));
break;
}
});
}
static getHRSamplesFromIBIData(activity, ibiData) {
// activity.ibiData = new IBIData(ibiData);
// @todo optimize
// Create a second IBIData so we can have filtering on those with keeping the original
const samples = [];
new data_ibi_1.IBIData(ibiData)
.lowLimitBPMFilter()
.highLimitBPMFilter()
.movingMedianFilter()
.lowPassFilter()
.getAsBPM()
.forEach((value, key, _map) => {
samples.push({
TimeISO8601: new Date(activity.startDate.getTime() + key).toISOString(),
HR: value / 60
});
});
return samples;
}
static setStreamsForActivity(activity, samples) {
// console.log(`Setting streams for activity with ${samples.length} samples`);
exports.SuuntoSampleMapper.forEach(sampleMapping => {
const subjectSamples = samples.filter(sample => (0, helpers_1.isNumberOrString)(sample[sampleMapping.sampleField]));
// if (sampleMapping.sampleField === 'GroundContactTime') {
// console.log(`GCT Samples found: ${subjectSamples.length}`);
// }
if (subjectSamples.length) {
activity.addStream(activity.createStream(sampleMapping.dataType));
subjectSamples.forEach(subjectSample => {
activity.addDataToStream(sampleMapping.dataType, new Date(subjectSample.TimeISO8601), sampleMapping.convertSampleValue(subjectSample[sampleMapping.sampleField]));
});
}
});
}
static getSettings(settings) {
const stats = [];
exports.SuuntoSettingsMapper.forEach(settingsMapping => {
if (settingsMapping.getValue(settings) !== null && settingsMapping.getValue(settings) !== undefined) {
stats.push(data_store_1.DynamicDataLoader.getDataInstanceFromDataType(settingsMapping.dataType, settingsMapping.getValue(settings)));
}
});
return stats;
}
// @todo convert this to a mapping as well
static getStats(object) {
const stats = [];
if ((0, helpers_1.isNumber)(object.Distance)) {
stats.push(new data_distance_1.DataDistance(object.Distance));
}
if ((0, helpers_1.isNumberOrString)(object.AscentTime)) {
stats.push(new data_ascent_time_1.DataAscentTime(object.AscentTime));
}
if ((0, helpers_1.isNumberOrString)(object.DescentTime)) {
stats.push(new data_descent_time_1.DataDescentTime(object.DescentTime));
}
if ((0, helpers_1.isNumberOrString)(object.Ascent)) {
stats.push(new data_ascent_1.DataAscent(object.Ascent));
}
if ((0, helpers_1.isNumberOrString)(object.Descent)) {
stats.push(new data_descent_1.DataDescent(object.Descent));
}
if ((0, helpers_1.isNumberOrString)(object.StepCount)) {
stats.push(new data_steps_1.DataSteps(object.StepCount));
}
if ((0, helpers_1.isNumberOrString)(object.MAXVO2)) {
stats.push(new data_vo2_max_1.DataVO2Max(object.MAXVO2));
}
if ((0, helpers_1.isNumberOrString)(object.PoolLength)) {
stats.push(new data_pool_length_1.DataPoolLength(object.PoolLength));
}
if ((0, helpers_1.isNumberOrString)(object.DeviceLocation)) {
stats.push(new data_device_location_1.DataDeviceLocation(object.DeviceLocation));
}
if ((0, helpers_1.isNumberOrString)(object.EPOC)) {
stats.push(new data_epoc_1.DataEPOC(object.EPOC));
}
if ((0, helpers_1.isNumberOrString)(object.Energy)) {
stats.push(new data_energy_1.DataEnergy((object.Energy * 0.239) / 1000));
}
if ((0, helpers_1.isNumberOrString)(object.Feeling)) {
stats.push(new data_feeling_1.DataFeeling(object.Feeling));
}
if ((0, helpers_1.isNumberOrString)(object.PeakTrainingEffect)) {
stats.push(new data_aerobic_training_effect_1.DataAerobicTrainingEffect(object.PeakTrainingEffect));
}
if ((0, helpers_1.isNumberOrString)(object.RecoveryTime)) {
stats.push(new data_recovery_time_1.DataRecoveryTime(object.RecoveryTime));
}
if ((0, helpers_1.isNumberOrString)(object.MAXVO2)) {
stats.push(new data_vo2_max_1.DataVO2Max(object.MAXVO2));
}
const totalDuration = (0, helpers_1.isNumberOrString)(object.Duration) ? Number(object.Duration) : null;
const pauseDuration = (0, helpers_1.isNumberOrString)(object.PauseDuration) ? Number(object.PauseDuration) : 0;
const activeDuration = totalDuration !== null && Number.isFinite(totalDuration) ? Math.max(totalDuration - pauseDuration, 0) : null;
stats.push(new data_pause_1.DataPause(pauseDuration));
if (totalDuration !== null && Number.isFinite(totalDuration)) {
stats.push(new data_elapsed_time_1.DataElapsedTime(totalDuration));
}
if (activeDuration !== null) {
stats.push(new data_duration_1.DataDuration(activeDuration));
stats.push(new data_timer_time_1.DataTimerTime(activeDuration));
}
// double case
if (Array.isArray(object.Altitude)) {
if ((0, helpers_1.isNumber)(object.Altitude[0].Avg)) {
stats.push(new data_altitude_avg_1.DataAltitudeAvg(object.Altitude[0].Avg));
}
if ((0, helpers_1.isNumber)(object.Altitude[0].Max)) {
stats.push(new data_altitude_max_1.DataAltitudeMax(object.Altitude[0].Max));
}
if ((0, helpers_1.isNumber)(object.Altitude[0].Min)) {
stats.push(new data_altitude_min_1.DataAltitudeMin(object.Altitude[0].Min));
}
}
else if (object.Altitude) {
if ((0, helpers_1.isNumber)(object.Altitude.Max)) {
stats.push(new data_altitude_max_1.DataAltitudeMax(object.Altitude.Max));
}
if ((0, helpers_1.isNumber)(object.Altitude.Min)) {
stats.push(new data_altitude_min_1.DataAltitudeMin(object.Altitude.Min));
}
}
if (Array.isArray(object.HR)) {
if ((0, helpers_1.isNumber)(object.HR[0].Avg)) {
stats.push(new data_heart_rate_avg_1.DataHeartRateAvg(object.HR[0].Avg * 60));
}
if ((0, helpers_1.isNumber)(object.HR[0].Max)) {
stats.push(new data_heart_rate_max_1.DataHeartRateMax(object.HR[0].Max * 60));
}
if ((0, helpers_1.isNumber)(object.HR[0].Min)) {
stats.push(new data_heart_rate_min_1.DataHeartRateMin(object.HR[0].Min * 60));
}
}
if (Array.isArray(object.Cadence)) {
if ((0, helpers_1.isNumber)(object.Cadence[0].Avg)) {
stats.push(new data_cadence_avg_1.DataCadenceAvg(object.Cadence[0].Avg * 60));
}
if ((0, helpers_1.isNumber)(object.Cadence[0].Max)) {
stats.push(new data_cadence_max_1.DataCadenceMax(object.Cadence[0].Max * 60));
}
if ((0, helpers_1.isNumber)(object.Cadence[0].Min)) {
stats.push(new data_cadence_min_1.DataCadenceMin(object.Cadence[0].Min * 60));
}
}
if (Array.isArray(object.Power)) {
if ((0, helpers_1.isNumber)(object.Power[0].Avg)) {
stats.push(new data_power_avg_1.DataPowerAvg(object.Power[0].Avg));
}
if ((0, helpers_1.isNumber)(object.Power[0].Max)) {
stats.push(new data_power_max_1.DataPowerMax(object.Power[0].Max));
}
if ((0, helpers_1.isNumber)(object.Power[0].Min)) {
stats.push(new data_power_min_1.DataPowerMin(object.Power[0].Min));
}
}
if (Array.isArray(object.Speed)) {
if ((0, helpers_1.isNumber)(object.Speed[0].Avg)) {
stats.push(new data_speed_avg_1.DataSpeedAvg(object.Speed[0].Avg));
stats.push(new data_pace_avg_1.DataPaceAvg((0, helpers_1.convertSpeedToPace)(object.Speed[0].Avg)));
}
if ((0, helpers_1.isNumber)(object.Speed[0].Max)) {
stats.push(new data_speed_max_1.DataSpeedMax(object.Speed[0].Max));
stats.push(new data_pace_max_1.DataPaceMax((0, helpers_1.convertSpeedToPace)(object.Speed[0].Max)));
}
if ((0, helpers_1.isNumber)(object.Speed[0].Min)) {
stats.push(new data_speed_min_1.DataSpeedMin(object.Speed[0].Min));
stats.push(new data_pace_min_1.DataPaceMin((0, helpers_1.convertSpeedToPace)(object.Speed[0].Min)));
}
}
if (Array.isArray(object.Temperature)) {
if ((0, helpers_1.isNumber)(object.Temperature[0].Avg)) {
stats.push(new data_temperature_avg_1.DataTemperatureAvg(object.Temperature[0].Avg - 273.15));
}
if ((0, helpers_1.isNumber)(object.Temperature[0].Max)) {
stats.push(new data_temperature_max_1.DataTemperatureMax(object.Temperature[0].Max - 273.15));
}
if (object.Temperature[0].Min !== null) {
stats.push(new data_temperature_min_1.DataTemperatureMin(object.Temperature[0].Min - 273.15));
}
}
if (Object.prototype.hasOwnProperty.call(object, 'VerticalSpeed')) {
// Double action here
if (Array.isArray(object.VerticalSpeed)) {
if ((0, helpers_1.isNumber)(object.VerticalSpeed[0].Avg)) {
stats.push(new data_vertical_speed_avg_1.DataVerticalSpeedAvg(object.VerticalSpeed[0].Avg));
}
if ((0, helpers_1.isNumber)(object.VerticalSpeed[0].Max)) {
stats.push(new data_vertical_speed_max_1.DataVerticalSpeedMax(object.VerticalSpeed[0].Max));
}
if ((0, helpers_1.isNumber)(object.VerticalSpeed[0].Min)) {
stats.push(new data_vertical_speed_min_1.DataVerticalSpeedMin(object.VerticalSpeed[0].Min));
}
}
else {
if ((0, helpers_1.isNumber)(object.VerticalSpeed)) {
stats.push(new data_vertical_speed_avg_1.DataVerticalSpeedAvg(object.VerticalSpeed));
}
}
}
// Ground Contact Time (Running Dynamics)
if (Object.prototype.hasOwnProperty.call(object, 'GroundContactTime')) {
if (Array.isArray(object.GroundContactTime)) {
if ((0, helpers_1.isNumber)(object.GroundContactTime[0].Avg)) {
stats.push(new data_ground_contact_time_avg_1.DataGroundContactTimeAvg(object.GroundContactTime[0].Avg * 1000)); // Convert s to ms
}
if ((0, helpers_1.isNumber)(object.GroundContactTime[0].Max)) {
stats.push(new data_ground_contact_time_max_1.DataGroundContactTimeMax(object.GroundContactTime[0].Max * 1000));
}
if ((0, helpers_1.isNumber)(object.GroundContactTime[0].Min)) {
stats.push(new data_ground_contact_time_min_1.DataGroundContactTimeMin(object.GroundContactTime[0].Min * 1000));
}
}
}
// Vertical Oscillation (Running Dynamics)
if (Object.prototype.hasOwnProperty.call(object, 'VerticalOscillation')) {
if (Array.isArray(object.VerticalOscillation)) {
if ((0, helpers_1.isNumber)(object.VerticalOscillation[0].Avg)) {
stats.push(new data_vertical_oscillation_avg_1.DataVerticalOscillationAvg(object.VerticalOscillation[0].Avg * 1000)); // Convert m to mm
}
if ((0, helpers_1.isNumber)(object.VerticalOscillation[0].Max)) {
stats.push(new data_vertical_oscillation_max_1.DataVerticalOscillationMax(object.VerticalOscillation[0].Max * 1000));
}
if ((0, helpers_1.isNumber)(object.VerticalOscillation[0].Min)) {
stats.push(new data_vertical_oscillation_min_1.DataVerticalOscillationMin(object.VerticalOscillation[0].Min * 1000));
}
}
}
// Fitness Age
if ((0, helpers_1.isNumber)(object.FitnessAge)) {
stats.push(new data_fitness_age_1.DataFitnessAge(object.FitnessAge));
}
// Personal MaxHR
if (object.Personal && (0, helpers_1.isNumber)(object.Personal.MaxHR)) {
stats.push(new data_max_hr_setting_1.DataMaxHRSetting(object.Personal.MaxHR * 60)); // Convert from Hz to bpm
}
if (Array.isArray(object.Endurance)) {
const endurance = object.Endurance[0];
if ((0, helpers_1.isNumber)(endurance === null || endurance === void 0 ? void 0 : endurance.Cur)) {
stats.push(new data_stamina_1.DataStamina(endurance.Cur));
}
if ((0, helpers_1.isNumber)(endurance === null || endurance === void 0 ? void 0 : endurance.Min)) {
stats.push(new data_stamina_1.DataStaminaMin(endurance.Min));
}
}
else if (object.Endurance) {
if ((0, helpers_1.isNumber)(object.Endurance.Cur)) {
stats.push(new data_stamina_1.DataStamina(object.Endurance.Cur));
}
if ((0, helpers_1.isNumber)(object.Endurance.Min)) {
stats.push(new data_stamina_1.DataStaminaMin(object.Endurance.Min));
}
}
// Depth (Diving)
if (Object.prototype.hasOwnProperty.call(object, 'Depth')) {
if (Array.isArray(object.Depth)) {
if ((0, helpers_1.isNumber)(object.Depth[0].Max) && object.Depth[0].Max > 0) {
stats.push(new data_depth_max_1.DataDepthMax(object.Depth[0].Max));
}
}
else if ((0, helpers_1.isNumber)(object.Depth.Max) && object.Depth.Max > 0) {
stats.push(new data_depth_max_1.DataDepthMax(object.Depth.Max));
}
}
return stats;
}
}
exports.EventImporterSuuntoJSON = EventImporterSuuntoJSON;
EventImporterSuuntoJSON.MIN_VALID_RR_SAMPLES_FOR_HR_OVERRIDE = 10;
exports.SuuntoSettingsMapper = [
{
dataType: data_alti_baro_profile_1.DataAltiBaroProfile.type,
getValue: (settings) => {
return settings['AltiBaroProfile'];
}
},
{
dataType: data_auto_lap_distance_1.DataAutoLapDistance.type,
getValue: (settings) => {
if (!settings['AutoLap']) {
return null;
}
return settings['AutoLap']['Distance'];
}
},
{
dataType: data_auto_lap_duration_1.DataAutoLapDuration.type,
getValue: (settings) => {
if (!settings['AutoLap']) {
return null;
}
return settings['AutoLap']['Duration'];
}
},
{
dataType: data_auto_pause_used_1.DataAutoPauseUsed.type,
getValue: (settings) => {
if (!settings['AutoPause']) {
return null;
}
return settings['AutoPause']['Enabled'];
}
},
{
dataType: data_bike_pod_used_1.DataBikePodUsed.type,
getValue: (settings) => {
return settings['BikePodUsed'];
}
},
{
dataType: data_enabled_navigation_systems_1.DataEnabledNavigationSystems.type,
getValue: (settings) => {
return settings['EnabledNavigationSystems'];
}
},
{
dataType: data_foot_pod_used_1.DataFootPodUsed.type,
getValue: (settings) => {
return settings['FootPodUsed'];
}
},
// {
// dataType: DataFusedAltitude.type,
// getValue: (settings: any) => {
// return settings['FusedAltiUsed'];
// },
// },
{
dataType: data_heart_rate_used_1.DataHeartRateUsed.type,
getValue: (settings) => {
return settings['HrUsed'];
}
},
{
dataType: data_power_pod_used_1.DataPowerPodUsed.type,
getValue: (settings) => {
return settings['PowerPodUsed'];
}
}
];
exports.SuuntoIntensityZonesMapper = [
{
dataType: data_heart_rate_1.DataHeartRate.type,
sampleField: 'HrZones',
convertSampleValue: (value) => Number(value * 60)
},
{
dataType: data_power_1.DataPower.type,
sampleField: 'PowerZones',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_speed_1.DataSpeed.type,
sampleField: 'SpeedZones',
convertSampleValue: (value) => Number(value)
}
];
exports.SuuntoSampleMapper = [
{
dataType: data_latitude_degrees_1.DataLatitudeDegrees.type,
sampleField: 'Latitude',
convertSampleValue: (value) => Number(value * (180 / Math.PI))
},
{
dataType: data_longitude_degrees_1.DataLongitudeDegrees.type,
sampleField: 'Longitude',
convertSampleValue: (value) => Number(value * (180 / Math.PI))
},
{
dataType: data_heart_rate_1.DataHeartRate.type,
sampleField: 'HR',
convertSampleValue: (value) => Number(value * 60)
},
{
dataType: data_distance_1.DataDistance.type,
sampleField: 'Distance',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_absolute_pressure_1.DataAbsolutePressure.type,
sampleField: 'AbsPressure',
convertSampleValue: (value) => Number(value / 100)
},
{
dataType: data_sea_level_pressure_1.DataSeaLevelPressure.type,
sampleField: 'SeaLevelPressure',
convertSampleValue: (value) => Number(value / 100)
},
{
dataType: data_altitude_gps_1.DataGPSAltitude.type,
sampleField: 'GPSAltitude',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_altitude_1.DataAltitude.type,
sampleField: 'Altitude',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_cadence_1.DataCadence.type,
sampleField: 'Cadence',
convertSampleValue: (value) => Number(value * 60)
},
{
dataType: data_power_1.DataPower.type,
sampleField: 'Power',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_speed_1.DataSpeed.type,
sampleField: 'Speed',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_temperature_1.DataTemperature.type,
sampleField: 'Temperature',
convertSampleValue: (value) => Number(value - 273.15)
},
{
dataType: data_vertical_speed_1.DataVerticalSpeed.type,
sampleField: 'VerticalSpeed',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_ehpe_1.DataEHPE.type,
sampleField: 'EHPE',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_evpe_1.DataEVPE.type,
sampleField: 'EVPE',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_number_of_satellites_1.DataNumberOfSatellites.type,
sampleField: 'NumberOfSatellites',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_satellite_5_best_snr_1.DataSatellite5BestSNR.type,
sampleField: 'Satellite5BestSNR',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_battery_charge_1.DataBatteryCharge.type,
sampleField: 'BatteryCharge',
convertSampleValue: (value) => Number(value * 100)
},
{
dataType: data_battery_current_1.DataBatteryCurrent.type,
sampleField: 'BatteryCurrent',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_battery_voltage_1.DataBatteryVoltage.type,
sampleField: 'BatteryVoltage',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_stamina_1.DataStamina.type,
sampleField: 'Endurance',
convertSampleValue: (value) => Number(value)
},
{
dataType: data_vertical_oscillation_1.DataVerticalOscillation.type,
sampleField: 'VerticalOscillation',
convertSampleValue: (value) => Number(value * 1000) // Convert m to mm
},
{
dataType: data_ground_contact_time_1.DataGroundContactTime.type,
sampleField: 'GroundContactTime',
convertSampleValue: (value) => Number(value * 1000) // Convert s to ms
},
{
dataType: data_depth_1.DataDepth.type,
sampleField: 'Depth',
convertSampleValue: (value) => Number(value)
}
];