@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
3,752 lines • 216 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActivityUtilities = void 0;
const data_heart_rate_1 = require("../../data/data.heart-rate");
const data_max_hr_setting_1 = require("../../data/data.max-hr-setting");
const data_cadence_1 = require("../../data/data.cadence");
const data_speed_1 = require("../../data/data.speed");
const data_weight_1 = require("../../data/data.weight");
const data_gender_1 = require("../../data/data.gender");
const data_vertical_speed_1 = require("../../data/data.vertical-speed");
const data_temperature_1 = require("../../data/data.temperature");
const data_absolute_pressure_1 = require("../../data/data.absolute-pressure");
const data_evpe_1 = require("../../data/data.evpe");
const data_evpe_min_1 = require("../../data/data.evpe-min");
const data_evpe_max_1 = require("../../data/data.evpe-max");
const data_evpe_avg_1 = require("../../data/data.evpe-avg");
const data_ehpe_1 = require("../../data/data.ehpe");
const data_ehpe_min_1 = require("../../data/data.ehpe-min");
const data_ehpe_max_1 = require("../../data/data.ehpe-max");
const data_ehpe_avg_1 = require("../../data/data.ehpe-avg");
const data_satellite_5_best_snr_1 = require("../../data/data.satellite-5-best-snr");
const data_satellite_5_best_snr_min_1 = require("../../data/data.satellite-5-best-snr-min");
const data_satellite_5_best_snr_max_1 = require("../../data/data.satellite-5-best-snr-max");
const data_satellite_5_best_snr_avg_1 = require("../../data/data.satellite-5-best-snr-avg");
const data_number_of_satellites_1 = require("../../data/data.number-of-satellites");
const data_number_of_satellites_min_1 = require("../../data/data.number-of-satellites-min");
const data_number_of_satellites_max_1 = require("../../data/data.number-of-satellites-max");
const data_number_of_satellites_avg_1 = require("../../data/data.number-of-satellites-avg");
const data_altitude_1 = require("../../data/data.altitude");
const data_power_1 = require("../../data/data.power");
const data_altitude_max_1 = require("../../data/data.altitude-max");
const data_altitude_min_1 = require("../../data/data.altitude-min");
const data_altitude_avg_1 = require("../../data/data.altitude-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_heart_rate_avg_1 = require("../../data/data.heart-rate-avg");
const data_cadence_max_1 = require("../../data/data.cadence-max");
const data_cadence_min_1 = require("../../data/data.cadence-min");
const data_cadence_avg_1 = require("../../data/data.cadence-avg");
const data_speed_max_1 = require("../../data/data.speed-max");
const data_speed_min_1 = require("../../data/data.speed-min");
const data_speed_avg_1 = require("../../data/data.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_vertical_speed_avg_1 = require("../../data/data.vertical-speed-avg");
const data_power_max_1 = require("../../data/data.power-max");
const data_power_min_1 = require("../../data/data.power-min");
const data_power_avg_1 = require("../../data/data.power-avg");
const data_temperature_max_1 = require("../../data/data.temperature-max");
const data_temperature_min_1 = require("../../data/data.temperature-min");
const data_temperature_avg_1 = require("../../data/data.temperature-avg");
const data_absolute_pressure_min_1 = require("../../data/data.absolute-pressure-min");
const data_absolute_pressure_max_1 = require("../../data/data.absolute-pressure-max");
const data_absolute_pressure_avg_1 = require("../../data/data.absolute-pressure-avg");
const data_distance_1 = require("../../data/data.distance");
const data_duration_1 = require("../../data/data.duration");
const data_pause_1 = require("../../data/data.pause");
const data_ascent_1 = require("../../data/data.ascent");
const data_descent_1 = require("../../data/data.descent");
const geolib_adapter_1 = require("../../geodesy/adapters/geolib.adapter");
const data_pace_max_1 = require("../../data/data.pace-max");
const data_pace_1 = require("../../data/data.pace");
const data_pace_min_1 = require("../../data/data.pace-min");
const data_pace_avg_1 = require("../../data/data.pace-avg");
const data_effort_pace_1 = require("../../data/data.effort-pace");
const data_effort_pace_avg_1 = require("../../data/data.effort-pace-avg");
const data_effort_pace_min_1 = require("../../data/data.effort-pace-min");
const data_effort_pace_max_1 = require("../../data/data.effort-pace-max");
const data_battery_charge_1 = require("../../data/data.battery-charge");
const data_battery_consumption_1 = require("../../data/data.battery-consumption");
const data_battery_life_estimation_1 = require("../../data/data.battery-life-estimation");
const data_latitude_degrees_1 = require("../../data/data.latitude-degrees");
const stream_1 = require("../../streams/stream");
const helpers_1 = require("./helpers");
const data_longitude_degrees_1 = require("../../data/data.longitude-degrees");
const stream_derivation_registry_1 = require("../../streams/stream.derivation.registry");
const data_energy_1 = require("../../data/data.energy");
const data_start_altitude_1 = require("../../data/data.start-altitude");
const data_end_altitude_1 = require("../../data/data.end-altitude");
const data_swim_pace_max_1 = require("../../data/data.swim-pace-max");
const data_swim_pace_1 = require("../../data/data.swim-pace");
const data_swim_pace_min_1 = require("../../data/data.swim-pace-min");
const data_swim_pace_avg_1 = require("../../data/data.swim-pace-avg");
const data_feeling_1 = require("../../data/data.feeling");
const data_power_watts_per_kg_1 = require("../../data/data.power-watts-per-kg");
const data_critical_power_1 = require("../../data/data.critical-power");
const data_w_prime_1 = require("../../data/data.w-prime");
const data_ftp_1 = require("../../data/data.ftp");
const data_power_left_1 = require("../../data/data.power-left");
const data_power_balance_left_1 = require("../../data/data.power-balance-left");
const data_power_balance_right_1 = require("../../data/data.power-balance-right");
const data_power_right_1 = require("../../data/data.power-right");
const data_air_power_min_1 = require("../../data/data.air-power-min");
const data_air_power_1 = require("../../data/data.air-power");
const data_air_power_max_1 = require("../../data/data.air-power-max");
const data_air_power_avg_1 = require("../../data/data.air-power-avg");
const data_leg_stiffness_1 = require("../../data/data.leg-stiffness");
const data_leg_stiffness_min_1 = require("../../data/data.leg-stiffness-min");
const data_leg_stiffness_max_1 = require("../../data/data.leg-stiffness-max");
const data_leg_stiffness_avg_1 = require("../../data/data.leg-stiffness-avg");
const data_rpe_1 = require("../../data/data.rpe");
const data_gnss_distance_1 = require("../../data/data.gnss-distance");
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_power_zone_six_duration_1 = require("../../data/data.power-zone-six-duration");
const data_power_zone_seven_duration_1 = require("../../data/data.power-zone-seven-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_ground_contact_time_balance_left_1 = require("../../data/data-ground-contact-time-balance-left");
const data_ground_contact_time_balance_right_1 = require("../../data/data-ground-contact-time-balance-right");
const data_running_dynamics_balance_1 = require("../../data/data.running-dynamics-balance");
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_vertical_ratio_1 = require("../../data/data.vertical-ratio");
const data_vertical_ratio_min_1 = require("../../data/data.vertical-ratio-min");
const data_vertical_ratio_max_1 = require("../../data/data.vertical-ratio-max");
const data_vertical_ratio_avg_1 = require("../../data/data.vertical-ratio-avg");
const data_stamina_1 = require("../../data/data.stamina");
const data_stance_time_balance_left_1 = require("../../data/data-stance-time-balance-left");
const data_stance_time_balance_right_1 = require("../../data/data-stance-time-balance-right");
const data_store_1 = require("../../data/data.store");
const data_start_position_1 = require("../../data/data.start-position");
const data_end_position_1 = require("../../data/data.end-position");
const data_grade_adjusted_speed_avg_1 = require("../../data/data.grade-adjusted-speed-avg");
const data_grade_adjusted_pace_avg_1 = require("../../data/data.grade-adjusted-pace-avg");
const data_grade_adjusted_speed_1 = require("../../data/data.grade-adjusted-speed");
const data_grade_adjusted_pace_1 = require("../../data/data.grade-adjusted-pace");
const data_grade_adjusted_speed_max_1 = require("../../data/data.grade-adjusted-speed-max");
const data_grade_adjusted_speed_min_1 = require("../../data/data.grade-adjusted-speed-min");
const data_grade_adjusted_pace_max_1 = require("../../data/data.grade-adjusted-pace-max");
const data_grade_adjusted_pace_min_1 = require("../../data/data.grade-adjusted-pace-min");
const data_grade_1 = require("../../data/data.grade");
const data_grade_min_1 = require("../../data/data.grade-min");
const data_grade_max_1 = require("../../data/data.grade-max");
const data_grade_avg_1 = require("../../data/data.grade-avg");
const activity_types_1 = require("../../activities/activity.types");
const data_moving_time_1 = require("../../data/data.moving-time");
const data_timer_time_1 = require("../../data/data.timer-time");
const data_elapsed_time_1 = require("../../data/data.elapsed-time");
const data_altitude_smooth_1 = require("../../data/data.altitude-smooth");
const data_grade_smooth_1 = require("../../data/data.grade-smooth");
const data_swolf_25m_1 = require("../../data/data.swolf-25m");
const data_swolf_50m_1 = require("../../data/data.swolf-50m");
const low_pass_filter_1 = require("./grade-calculator/low-pass-filter");
const data_power_intensity_factor_1 = require("../../data/data.power-intensity-factor");
const data_power_normalized_1 = require("../../data/data.power-normalized");
const data_training_stress_score_1 = require("../../data/data.training-stress-score");
const data_training_stress_score_method_1 = require("../../data/data.training-stress-score-method");
const data_power_work_1 = require("../../data/data.power-work");
const grade_calculator_1 = require("./grade-calculator/grade-calculator");
const data_vo2_max_1 = require("../../data/data.vo2-max");
const data_total_grit_1 = require("../../data/data.total-grit");
const data_total_flow_1 = require("../../data/data.total-flow");
const data_jump_count_1 = require("../../data/data.jump-count");
const data_jump_event_1 = require("../../data/data.jump-event");
const data_jump_stats_1 = require("../../data/data.jump-stats");
const data_power_curve_1 = require("../../data/data.power-curve");
const data_durability_evidence_1 = require("../../data/data.durability-evidence");
const tss_calculator_1 = require("./tss/tss-calculator");
const activity_durability_1 = require("./activity-durability");
// @ts-ignore
const kalmanjs_1 = __importDefault(require("kalmanjs"));
/* Configure filtering values */
// Altitude stream
const ALTITUDE_SPIKES_FILTER_WIN = 3;
// Fix abnormal streams
const SPEED_STREAM_STD_DEV_THRESHOLD_DEFAULT = 25 / 3.6; // Kph to mps
const SPEED_STREAM_STD_DEV_THRESHOLD_MAP = new Map([
[activity_types_1.ActivityTypeGroups.RunningGroup, 15 / 3.6], // kph to m/s
[activity_types_1.ActivityTypeGroups.CyclingGroup, 27 / 3.6], // kph to m/s
[activity_types_1.ActivityTypeGroups.MountainBikingGroup, 27 / 3.6], // kph to m/s
[activity_types_1.ActivityTypeGroups.SwimmingGroup, 5 / 3.6] // kph to m/s
]);
class ActivityUtilities {
/**
* Provide average from laps a given stat type
*/
static getDataTypeAvgFromLaps(activity, statType, filterOver) {
const data = activity
.getLaps()
.map(lap => { var _a; return (_a = lap.getStat(statType)) === null || _a === void 0 ? void 0 : _a.getValue(); })
.filter(d => Number.isFinite(d) && (Number.isFinite(filterOver) ? d > filterOver : true));
if (data.length > 0) {
return this.getAverage(data);
}
return null;
}
/**
* Provide average of a given stream type
*/
static getDataTypeAvg(activity, streamType, startDate, endDate, filterOver) {
const data = (activity
.getSquashedStreamData(streamType, startDate, endDate)
.filter(streamData => streamData !== Infinity &&
streamData !== -Infinity &&
(Number.isFinite(filterOver) ? streamData > filterOver : true)));
return this.getAverage(data);
}
static round(value, decimals = 0) {
const decimalsFactor = Math.pow(10, decimals);
return Math.round(value * decimalsFactor) / decimalsFactor;
}
static addLeftRightBalanceStatsFromStreams(activity, LeftDataClass, RightDataClass) {
const leftStat = activity.getStat(LeftDataClass.type);
const rightStat = activity.getStat(RightDataClass.type);
const leftStatValue = leftStat === null || leftStat === void 0 ? void 0 : leftStat.getValue();
const rightStatValue = rightStat === null || rightStat === void 0 ? void 0 : rightStat.getValue();
if (leftStat && rightStat) {
return;
}
if (leftStat && !rightStat && typeof leftStatValue === 'number') {
activity.addStat(new RightDataClass(this.round(100 - leftStatValue, 2)));
return;
}
if (rightStat && !leftStat && typeof rightStatValue === 'number') {
activity.addStat(new LeftDataClass(this.round(100 - rightStatValue, 2)));
return;
}
if (activity.hasStreamData(LeftDataClass.type)) {
const avgLeftBalance = this.round(this.getDataTypeAvg(activity, LeftDataClass.type), 2);
activity.addStat(new LeftDataClass(avgLeftBalance));
activity.addStat(new RightDataClass(this.round(100 - avgLeftBalance, 2)));
return;
}
if (activity.hasStreamData(RightDataClass.type)) {
const avgRightBalance = this.round(this.getDataTypeAvg(activity, RightDataClass.type), 2);
activity.addStat(new RightDataClass(avgRightBalance));
activity.addStat(new LeftDataClass(this.round(100 - avgRightBalance, 2)));
}
}
static getAverage(data) {
return this.getSum(data) / data.length;
}
static getSum(data) {
return data.reduce((sumbuff, value) => {
sumbuff += value;
return sumbuff;
}, 0);
}
static getDataTypeMax(activity, streamType, startDate, endDate) {
return this.getActivityDataTypeMinOrMax(activity, streamType, true, startDate, endDate);
}
static getDataTypeMin(activity, streamType, startDate, endDate, filterOver) {
return this.getActivityDataTypeMinOrMax(activity, streamType, false, startDate, endDate, filterOver);
}
static getDataTypeMinToMaxDifference(activity, streamType, startDate, endDate) {
return (this.getDataTypeMax(activity, streamType, startDate, endDate) -
this.getDataTypeMin(activity, streamType, startDate, endDate));
}
static getDataTypeFirst(activity, streamType, startDate, endDate) {
const data = activity.getSquashedStreamData(streamType, startDate, endDate);
return data[0];
}
static getDataTypeLast(activity, streamType, startDate, endDate) {
const data = activity.getSquashedStreamData(streamType, startDate, endDate);
return data[data.length - 1];
}
static cropDistance(startDistance, endDistance, activity) {
// Short to do the search just in case
let startDistanceDate; // Does not sound right
let endDistanceDate;
// debugger;
activity.getStreamData(data_distance_1.DataDistance.type).forEach((distanceFromData, index) => {
// Find the index with greater dinstnce and convert it to time
if (startDistance && !startDistanceDate && distanceFromData && distanceFromData >= startDistance) {
startDistanceDate = new Date(activity.startDate.getTime() + index * 1000);
return;
}
// Same for end
if (endDistance && !endDistanceDate && distanceFromData && distanceFromData >= endDistance) {
endDistanceDate = new Date(activity.startDate.getTime() + index * 1000);
return;
}
});
if (!startDistanceDate && !endDistanceDate) {
return activity;
}
activity = this.cropTime(activity, startDistanceDate, endDistanceDate);
// Remove because it is invalid, you cannot just offset the distance as a stream I think
const distanceStream = activity.getAllStreams().find(s => data_distance_1.DataDistance.type === s.type);
if (distanceStream) {
activity.removeStream(distanceStream);
}
const gnssDistanceStream = activity.getAllStreams().find(s => data_gnss_distance_1.DataGNSSDistance.type === s.type);
if (gnssDistanceStream) {
activity.removeStream(gnssDistanceStream);
}
return activity;
}
/**
* Crops left,right on time.
* Start and end date need to be relative to the activity start / end time
* @param activity
* @param startDate
* @param endDate
*/
static cropTime(activity, startDate, endDate) {
activity.getAllStreams().forEach(stream => {
// Get the data for the range specified
const trimmedStreamData = activity.getStreamData(stream.type, startDate, endDate);
activity.removeStream(stream);
activity.addStream(new stream_1.Stream(stream.type, trimmedStreamData));
});
activity.startDate = startDate || activity.startDate;
activity.endDate = endDate || activity.endDate;
// debugger
return activity;
}
static getStreamDataTypesBasedOnDataType(streamToBaseOn, streams) {
return streamToBaseOn.getData().reduce((accu, streamDataItem, index) => {
if (!(0, helpers_1.isNumberOrString)(streamDataItem)) {
return accu;
}
const dataItem = {
[streamToBaseOn.type]: streamDataItem
};
streams.forEach(stream => {
dataItem[stream.type] = stream.getData()[index];
});
accu.push(dataItem);
return accu;
}, []);
}
static getStreamDataTypesBasedOnTime(startDate, endDate, streams) {
const streamDataBasedOnTime = {};
for (let i = 0; i < this.getDataLength(startDate, endDate); i++) {
// Perhaps this can be optimized with a search function
streams.forEach((stream) => {
if ((0, helpers_1.isNumber)(stream.getData()[i])) {
streamDataBasedOnTime[startDate.getTime() + i * 1000] =
streamDataBasedOnTime[startDate.getTime() + i * 1000] || {};
streamDataBasedOnTime[startDate.getTime() + i * 1000][stream.type] = stream.getData()[i];
}
});
}
return streamDataBasedOnTime;
}
static getDataLength(startDate, endDate) {
return Math.ceil((+endDate - +startDate) / 1000) + 1;
}
static createJumpAggregate() {
return {
min: Infinity,
max: -Infinity,
sum: 0,
count: 0
};
}
static getJumpEvents(activity) {
const maybeGetAllEvents = activity.getAllEvents;
if (typeof maybeGetAllEvents !== 'function') {
return [];
}
return maybeGetAllEvents
.call(activity)
.filter((event) => {
if (!event || typeof event.getType !== 'function') {
return false;
}
if (event instanceof data_jump_event_1.DataJumpEvent) {
return true;
}
const typedEvent = event;
return typedEvent.getType() === data_jump_event_1.DataJumpEvent.type && typedEvent.jumpData !== undefined;
})
.map(event => event);
}
static getFiniteStatValue(activity, statType) {
const stat = activity.getStat(statType);
if (!stat) {
return null;
}
const value = stat.getValue();
return typeof value === 'number' && Number.isFinite(value) ? value : null;
}
static getJumpAggregatesFromEvents(jumpEvents) {
const aggregates = new Map(this.jumpStatFamilies.map(family => [family.key, this.createJumpAggregate()]));
jumpEvents.forEach(jumpEvent => {
this.jumpStatFamilies.forEach(family => {
const value = family.getEventValue(jumpEvent);
if (typeof value === 'number' && Number.isFinite(value)) {
const aggregate = aggregates.get(family.key);
aggregate.min = Math.min(aggregate.min, value);
aggregate.max = Math.max(aggregate.max, value);
aggregate.sum += value;
aggregate.count += 1;
}
});
});
return aggregates;
}
static addMissingJumpStatsFromEvents(activity) {
const jumpEvents = this.getJumpEvents(activity);
if (!jumpEvents.length) {
return;
}
const aggregates = this.getJumpAggregatesFromEvents(jumpEvents);
this.jumpStatFamilies.forEach(family => {
const aggregate = aggregates.get(family.key);
if (aggregate.count <= 0) {
return;
}
if (!activity.getStat(family.minType)) {
activity.addStat(family.createMin(aggregate.min));
}
if (!activity.getStat(family.maxType)) {
activity.addStat(family.createMax(aggregate.max));
}
if (!activity.getStat(family.avgType)) {
activity.addStat(family.createAvg(aggregate.sum / aggregate.count));
}
});
}
static getSummaryJumpStatsForActivities(activities) {
const summaryAggregates = new Map(this.jumpStatFamilies.map(family => [family.key, this.createJumpAggregate()]));
activities.forEach(activity => {
const jumpEvents = this.getJumpEvents(activity);
if (jumpEvents.length > 0) {
const eventAggregates = this.getJumpAggregatesFromEvents(jumpEvents);
this.jumpStatFamilies.forEach(family => {
const eventAggregate = eventAggregates.get(family.key);
if (eventAggregate.count <= 0) {
return;
}
const summaryAggregate = summaryAggregates.get(family.key);
summaryAggregate.min = Math.min(summaryAggregate.min, eventAggregate.min);
summaryAggregate.max = Math.max(summaryAggregate.max, eventAggregate.max);
summaryAggregate.sum += eventAggregate.sum;
summaryAggregate.count += eventAggregate.count;
});
return;
}
const jumpCount = this.getFiniteStatValue(activity, data_jump_count_1.DataJumpCount.type);
this.jumpStatFamilies.forEach(family => {
const summaryAggregate = summaryAggregates.get(family.key);
const minValue = this.getFiniteStatValue(activity, family.minType);
if (minValue !== null) {
summaryAggregate.min = Math.min(summaryAggregate.min, minValue);
}
const maxValue = this.getFiniteStatValue(activity, family.maxType);
if (maxValue !== null) {
summaryAggregate.max = Math.max(summaryAggregate.max, maxValue);
}
const avgValue = this.getFiniteStatValue(activity, family.avgType);
if (avgValue !== null) {
const weight = jumpCount !== null ? Math.max(0, jumpCount) : 1;
summaryAggregate.sum += avgValue * weight;
summaryAggregate.count += weight;
}
});
});
const stats = [];
this.jumpStatFamilies.forEach(family => {
const aggregate = summaryAggregates.get(family.key);
if (aggregate.min !== Infinity) {
stats.push(family.createMin(aggregate.min));
}
if (aggregate.max !== -Infinity) {
stats.push(family.createMax(aggregate.max));
}
if (aggregate.count > 0) {
stats.push(family.createAvg(aggregate.sum / aggregate.count));
}
});
return stats;
}
static generateMissingStreamsAndStatsForActivity(activity) {
var _a;
this.generateMissingStreams(activity);
this.fixAbnormalStreamData(activity);
this.generateMissingStatsForActivity(activity);
this.generateMissingSpeedDerivedStatsForActivity(activity);
const existingDurability = (_a = activity.getStat(data_durability_evidence_1.DataDurabilityEvidence.type)) === null || _a === void 0 ? void 0 : _a.getValue();
const canonicalExistingDurability = (0, data_durability_evidence_1.normalizeDurabilityEvidenceValue)(existingDurability);
const hasSourceData = canonicalExistingDurability ? (0, activity_durability_1.hasActivityDurabilitySourceData)(activity) : false;
const canReuseExistingDurability = !!canonicalExistingDurability &&
(!hasSourceData ||
canonicalExistingDurability.sourceFingerprint === (0, activity_durability_1.calculateActivityDurabilitySourceFingerprint)(activity));
const existingIsCanonical = !!canonicalExistingDurability &&
JSON.stringify(existingDurability) === JSON.stringify(canonicalExistingDurability);
if (!canReuseExistingDurability) {
activity.removeStat(data_durability_evidence_1.DataDurabilityEvidence.type);
const durability = (0, activity_durability_1.analyzeActivityDurability)(activity, { includeTimeline: false }).summary;
if (durability) {
activity.addStat(new data_durability_evidence_1.DataDurabilityEvidence(durability));
}
}
else if (!existingIsCanonical) {
activity.removeStat(data_durability_evidence_1.DataDurabilityEvidence.type);
activity.addStat(new data_durability_evidence_1.DataDurabilityEvidence(canonicalExistingDurability));
}
this.generateMissingUnitStatsForActivity(activity); // Perhaps this needs to happen on user level so needs to go out of here
}
static fixAbnormalStreamData(activity) {
var _a, _b, _c;
// Check if fix abnormal speed option has been enable and if we have stream data and position data (e.g. do not fix for swim pool activities)
if (((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.streams) === null || _b === void 0 ? void 0 : _b.fixAbnormal) === null || _c === void 0 ? void 0 : _c.speed) &&
activity.hasStreamData(data_speed_1.DataSpeed.type) &&
activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type) &&
activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type)) {
// Check for speed data dispersion using standard deviation
const speedStdDev = (0, helpers_1.standardDeviation)(activity.getSquashedStreamData(data_speed_1.DataSpeed.type));
// Get speed standard deviation threshold at which we will attempt to fix the stream
const stdDevThreshold = SPEED_STREAM_STD_DEV_THRESHOLD_MAP.get(activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type)) ||
SPEED_STREAM_STD_DEV_THRESHOLD_DEFAULT;
if (speedStdDev > stdDevThreshold) {
// Fix/Predict speed stream through Kalman filtering
this.shapeStream(data_speed_1.DataSpeed.type, activity, squashedSpeedData => {
// Grade stream
const SPEED_KALMAN_SMOOTHING = {
R: 0.01, // Speed model calculation is something stable
Q: speedStdDev * 2 // We intend to get a measurement error which can be under and over std dev (explaining the double factor)
};
// Apply kalman filter
const kf = new kalmanjs_1.default(SPEED_KALMAN_SMOOTHING);
return squashedSpeedData.map(v => (v === null ? null : kf.filter(v)));
});
}
}
}
static generateMissingStreams(activity) {
// Compute missing streams
this.generateMissingStreamsForActivity(activity);
// Always include derived base streams (like Pace), but conditionally include unit variants
const includeUnitVariants = !activity.parseOptions || activity.parseOptions.generateUnitStreams;
activity.addStreams(this.createUnitStreamsFromStreams(activity.getAllStreams(), activity.type, undefined, {
includeDerivedTypes: true, // Always include derived base types (Pace etc)
includeUnitVariants
}));
}
static getSummaryStatsForActivities(activities) {
const stats = [];
// If only one
if (activities.length === 1) {
return activities[0].getStatsAsArray().filter(stat => stat.getType() !== data_durability_evidence_1.DataDurabilityEvidence.type);
}
let duration = 0;
let ascent = 0;
let descent = 0;
let energy = 0;
let distance = 0;
let pauseTime = 0;
let averageHeartRate = 0;
let averagePower = 0;
let averagePowerWattsPerKg = 0;
let hasAveragePowerWattsPerKg = false;
let averagePowerNormalized = 0;
let hasAveragePowerNormalized = false;
let averageCadence = 0;
let averageSpeed = 0;
let averageGradeAdjustedSpeed = 0;
let averagePace = 0;
let averageEffortPace = 0;
let averageGradeAdjustedPace = 0;
let averageSwimPace = 0;
let averageTemperature = 0;
let averageAbsolutePressure = 0;
let averageEVPE = 0;
let hasAverageEVPE = false;
let averageEHPE = 0;
let hasAverageEHPE = false;
let averageSatellite5BestSNR = 0;
let hasAverageSatellite5BestSNR = false;
let averageNumberOfSatellites = 0;
let hasAverageNumberOfSatellites = false;
let averageGrade = 0;
let hasAverageGrade = false;
let averageAirPower = 0;
let averageVerticalSpeed = 0;
let averageAltitude = 0;
let averageLegStiffness = 0;
let hasAverageLegStiffness = false;
let averageVerticalRatio = 0;
let hasAverageVerticalRatio = false;
let averageVerticalOscillation = 0;
let hasAverageVerticalOscillation = false;
let averageFeeling = 0;
let averageRPE = 0;
// Sum Duration
activities.forEach(activity => {
duration += activity.getDuration().getValue();
});
stats.push(new data_duration_1.DataDuration(duration));
// Sum pause time
activities.forEach(activity => {
pauseTime += activity.getPause().getValue();
});
stats.push(new data_pause_1.DataPause(pauseTime));
// Sum Distance
activities.forEach(activity => {
distance += activity.getDistance().getValue();
});
stats.push(new data_distance_1.DataDistance(distance));
// Sum ascent
activities.forEach(activity => {
const activityAscent = activity.getStat(data_ascent_1.DataAscent.type);
if (activityAscent) {
ascent += activityAscent.getValue();
}
});
stats.push(new data_ascent_1.DataAscent(ascent));
// Sum descent
activities.forEach(activity => {
const activityDescent = activity.getStat(data_descent_1.DataDescent.type);
if (activityDescent) {
descent += activityDescent.getValue();
}
});
stats.push(new data_descent_1.DataDescent(descent));
// Sum energy
activities.forEach(activity => {
const activityEnergy = activity.getStat(data_energy_1.DataEnergy.type);
if (activityEnergy) {
energy += activityEnergy.getValue();
}
});
stats.push(new data_energy_1.DataEnergy(energy));
// Avg Avg HR
activities.forEach(activity => {
const activityAvgHeartRate = activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type);
if (activityAvgHeartRate) {
// The below will fallback for 0
averageHeartRate = averageHeartRate
? (averageHeartRate + activityAvgHeartRate.getValue()) / 2
: activityAvgHeartRate.getValue();
}
});
if (averageHeartRate) {
stats.push(new data_heart_rate_avg_1.DataHeartRateAvg(averageHeartRate));
}
// Avg Avg HR
activities.forEach(activity => {
const activityAvgHeartRate = activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type);
if (activityAvgHeartRate) {
// The below will fallback for 0
averageHeartRate = averageHeartRate
? (averageHeartRate + activityAvgHeartRate.getValue()) / 2
: activityAvgHeartRate.getValue();
}
});
if (averageHeartRate) {
stats.push(new data_heart_rate_avg_1.DataHeartRateAvg(averageHeartRate));
}
// Avg Avg Power
activities.forEach(activity => {
const activityAvgPower = activity.getStat(data_power_avg_1.DataPowerAvg.type);
if (activityAvgPower) {
// The below will fallback for 0
averagePower = averagePower
? (averagePower + activityAvgPower.getValue()) / 2
: activityAvgPower.getValue();
}
});
if (averagePower) {
stats.push(new data_power_avg_1.DataPowerAvg(averagePower));
}
// Avg Avg Power-to-Weight ratio (W/kg)
activities.forEach(activity => {
const activityPowerWattsPerKg = this.resolvePowerWattsPerKg(activity);
if (activityPowerWattsPerKg !== null) {
averagePowerWattsPerKg = hasAveragePowerWattsPerKg
? (averagePowerWattsPerKg + activityPowerWattsPerKg) / 2
: activityPowerWattsPerKg;
hasAveragePowerWattsPerKg = true;
}
});
if (hasAveragePowerWattsPerKg) {
stats.push(new data_power_watts_per_kg_1.DataPowerWattsPerKg(averagePowerWattsPerKg));
}
// Avg Normalized Power
activities.forEach(activity => {
const activityPowerNormalized = activity.getStat(data_power_normalized_1.DataPowerNormalized.type);
if (activityPowerNormalized) {
averagePowerNormalized = hasAveragePowerNormalized
? (averagePowerNormalized + activityPowerNormalized.getValue()) / 2
: activityPowerNormalized.getValue();
hasAveragePowerNormalized = true;
}
});
if (hasAveragePowerNormalized) {
stats.push(new data_power_normalized_1.DataPowerNormalized(averagePowerNormalized));
}
// Avg Avg Cadence
activities.forEach(activity => {
const activityAvgCadence = activity.getStat(data_cadence_avg_1.DataCadenceAvg.type);
if (activityAvgCadence) {
// The below will fallback for 0
averageCadence = averageCadence
? (averageCadence + activityAvgCadence.getValue()) / 2
: activityAvgCadence.getValue();
}
});
if (averageCadence) {
stats.push(new data_cadence_avg_1.DataCadenceAvg(averageCadence));
}
// Avg Avg Speed
activities.forEach(activity => {
const activityAvgSpeed = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (activityAvgSpeed) {
// The below will fallback for 0
averageSpeed = averageSpeed
? (averageSpeed + activityAvgSpeed.getValue()) / 2
: activityAvgSpeed.getValue();
}
});
if (averageSpeed) {
stats.push(new data_speed_avg_1.DataSpeedAvg(averageSpeed));
}
// Avg Avg Gap Speed
activities.forEach(activity => {
const activityAvgGradeAdjustedSpeed = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (activityAvgGradeAdjustedSpeed) {
// The below will fallback for 0
averageGradeAdjustedSpeed = averageGradeAdjustedSpeed
? (averageGradeAdjustedSpeed + activityAvgGradeAdjustedSpeed.getValue()) / 2
: activityAvgGradeAdjustedSpeed.getValue();
}
});
if (averageGradeAdjustedSpeed) {
stats.push(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg(averageGradeAdjustedSpeed));
}
// Avg Avg Pace
activities.forEach(activity => {
const activityAvgPace = activity.getStat(data_pace_avg_1.DataPaceAvg.type);
if (activityAvgPace) {
// The below will fallback for 0
averagePace = averagePace
? (averagePace + activityAvgPace.getValue()) / 2
: activityAvgPace.getValue();
}
});
if (averagePace) {
stats.push(new data_pace_avg_1.DataPaceAvg(averagePace));
}
// Avg Avg Effort Pace
activities.forEach(activity => {
const activityAvgEffortPace = activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvg.type);
if (activityAvgEffortPace) {
// The below will fallback for 0
averageEffortPace = averageEffortPace
? (averageEffortPace + activityAvgEffortPace.getValue()) / 2
: activityAvgEffortPace.getValue();
}
});
if (averageEffortPace) {
stats.push(new data_effort_pace_avg_1.DataEffortPaceAvg(averageEffortPace));
}
// Avg Avg GAP Pace
activities.forEach(activity => {
const activityAvgGradeAdjustedPace = activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type);
if (activityAvgGradeAdjustedPace) {
// The below will fallback for 0
averageGradeAdjustedPace = averageGradeAdjustedPace
? (averageGradeAdjustedPace + activityAvgGradeAdjustedPace.getValue()) / 2
: activityAvgGradeAdjustedPace.getValue();
}
});
if (averageGradeAdjustedPace) {
stats.push(new data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg(averageGradeAdjustedPace));
}
// Avg Avg SwimPace
activities.forEach(activity => {
const activityAvgSwimPace = activity.getStat(data_swim_pace_avg_1.DataSwimPaceAvg.type);
if (activityAvgSwimPace) {
// The below will fallback for 0
averageSwimPace = averageSwimPace
? (averageSwimPace + activityAvgSwimPace.getValue()) / 2
: activityAvgSwimPace.getValue();
}
});
if (averageSwimPace) {
stats.push(new data_swim_pace_avg_1.DataSwimPaceAvg(averageSwimPace));
}
// Avg Avg Temperature
activities.forEach(activity => {
const activityAvgTemperature = activity.getStat(data_temperature_avg_1.DataTemperatureAvg.type);
if (activityAvgTemperature) {
// The below will fallback for 0
averageTemperature = averageTemperature
? (averageTemperature + activityAvgTemperature.getValue()) / 2
: activityAvgTemperature.getValue();
}
});
if (averageTemperature) {
stats.push(new data_temperature_avg_1.DataTemperatureAvg(averageTemperature));
}
// Avg Avg Absolute Pressure
activities.forEach(activity => {
const activityAvgAbsolutePressure = activity.getStat(data_absolute_pressure_avg_1.DataAbsolutePressureAvg.type);
if (activityAvgAbsolutePressure) {
averageAbsolutePressure = averageAbsolutePressure
? (averageAbsolutePressure + activityAvgAbsolutePressure.getValue()) / 2
: activityAvgAbsolutePressure.getValue();
}
});
if (averageAbsolutePressure) {
stats.push(new data_absolute_pressure_avg_1.DataAbsolutePressureAvg(averageAbsolutePressure));
}
// Avg Avg EVPE
activities.forEach(activity => {
const activityAvgEVPE = activity.getStat(data_evpe_avg_1.DataEVPEAvg.type);
if (activityAvgEVPE) {
averageEVPE = hasAverageEVPE
? (averageEVPE + activityAvgEVPE.getValue()) / 2
: activityAvgEVPE.getValue();
hasAverageEVPE = true;
}
});
if (hasAverageEVPE) {
stats.push(new data_evpe_avg_1.DataEVPEAvg(averageEVPE));
}
// Avg Avg EHPE
activities.forEach(activity => {
const activityAvgEHPE = activity.getStat(data_ehpe_avg_1.DataEHPEAvg.type);
if (activityAvgEHPE) {
averageEHPE = hasAverageEHPE
? (averageEHPE + activityAvgEHPE.getValue()) / 2
: activityAvgEHPE.getValue();
hasAverageEHPE = true;
}
});
if (hasAverageEHPE) {
stats.push(new data_ehpe_avg_1.DataEHPEAvg(averageEHPE));
}
// Avg Avg Satellite 5 Best SNR
activities.forEach(activity => {
const activityAvgSatellite5BestSNR = activity.getStat(data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg.type);
if (activityAvgSatellite5BestSNR) {
averageSatellite5BestSNR = hasAverageSatellite5BestSNR
? (averageSatellite5BestSNR + activityAvgSatellite5BestSNR.getValue()) / 2
: activityAvgSatellite5BestSNR.getValue();
hasAverageSatellite5BestSNR = true;
}
});
if (hasAverageSatellite5BestSNR) {
stats.push(new data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg(averageSatellite5BestSNR));
}
// Avg Avg Number of Satellites
activities.forEach(activity => {
const activityAvgNumberOfSatellites = activity.getStat(data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg.type);
if (activityAvgNumberOfSatellites) {
averageNumberOfSatellites = hasAverageNumberOfSatellites
? (averageNumberOfSatellites + activityAvgNumberOfSatellites.getValue()) / 2
: activityAvgNumberOfSatellites.getValue();
hasAverageNumberOfSatellites = true;
}
});
if (hasAverageNumberOfSatellites) {
stats.push(new data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg(averageNumberOfSatellites));
}
// Avg Grade
activities.forEach(activity => {
const activityAvgGrade = activity.getStat(data_grade_avg_1.DataGradeAvg.type);
if (activityAvgGrade) {
averageGrade = hasAverageGrade
? (averageGrade + activityAvgGrade.getValue()) / 2
: activityAvgGrade.getValue();
hasAverageGrade = true;
}
});
if (hasAverageGrade) {
stats.push(new data_grade_avg_1.DataGradeAvg(averageGrade));
}
// Avg Avg Air Power
activities.forEach(activity => {
const activityAvgAirPower = activity.getStat(data_air_power_avg_1.DataAirPowerAvg.type);
if (activityAvgAirPower) {
averageAirPower = averageAirPower
? (averageAirPower + activityAvgAirPower.getValue()) / 2
: activityAvgAirPower.getValue();
}
});
if (averageAirPower) {
stats.push(new data_air_power_avg_1.DataAirPowerAvg(averageAirPower));
}
// Avg Avg Vertical Speed
activities.forEach(activity => {
const activityAvgVerticalSpeed = activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type);
if (activityAvgVerticalSpeed) {
averageVerticalSpeed = averageVerticalSpeed
? (averageVerticalSpeed + activityAvgVerticalSpeed.getValue()) / 2
: activityAvgVerticalSpeed.getValue();
}
});
if (averageVerticalSpeed) {
stats.push(new data_vertical_speed_avg_1.DataVerticalSpeedAvg(averageVerticalSpeed));
}
// Avg Avg Altitude
activities.forEach(activity => {
const activityAvgAltitude = activity.getStat(data_altitude_avg_1.DataAltitudeAvg.type);
if (activityAvgAltitude) {
averageAltitude = averageAltitude
? (averageAltitude + activityAvgAltitude.getValue()) / 2
: activityAvgAltitude.getValue();
}
});
if (averageAltitude) {
stats.push(new data_altitude_avg_1.DataAltitudeAvg(averageAltitude));
}
// Avg Avg Leg Stiffness
activities.forEach(activity => {
const activityAvgLegStiffness = activity.getStat(data_leg_stiffness_avg_1.DataLegStiffnessAvg.type);
if (activityAvgLegStiffness) {
averageLegStiffness = hasAverageLegStiffness
? (averageLegStiffness + activityAvgLegStiffness.getValue()) / 2
: activityAvgLegStiffness.getValue();
hasAverageLegStiffness = true;
}
});
if (hasAverageLegStiffness) {
stats.push(new data_leg_stiffness_avg_1.DataLegStiffnessAvg(averageLegStiffness));
}
// Avg Avg Vertical Ratio
activities.forEach(activity => {
const activityAvgVerticalRatio = activity.getStat(data_vertical_ratio_avg_1.DataVerticalRatioAvg.type);
if (activityAvgVerticalRatio) {
averageVerticalRatio = hasAverageVerticalRatio
? (averageVerticalRatio + activityAvgVerticalRatio.getValue()) / 2
: activityAvgVerticalRatio.getValue();
hasAverageVerticalRatio = true;
}
});
if (hasAverageVerticalRatio) {
stats.push(new data_vertical_ratio_avg_1.DataVerticalRatioAvg(averageVerticalRatio));
}
// Avg Feeling
activities.forEach(activity => {
const activityAvgFeeling = activity.getStat(data_feeling_1.DataFeeling.type);
if (activityAvgFeeling) {
// The below will fallback for 0
averageFeeling = averageFeeling
? Math.ceil((averageFeeling + activityAvgFeeling.getValue()) / 2)
: activityAvgFeeling.getValue();
}
});
if (averageFeeling) {
stats.push(new data_feeling_1.DataFeeling(averageFeeling));
}
// Avg RPE
activities.forEach(activity => {
const activityAvgRPE = activity.getStat(data_rpe_1.DataRPE.type);
if (activityAvgRPE) {
// The below will fallback for 0
averageRPE = averageRPE
? Math.ceil((averageRPE + activityAvgRPE.getValue()) / 2)
: activityAvgRPE.getValue();
}
});
if (averageRPE) {
stats.push(new data_rpe_1.DataRPE(averageRPE));
}
// Max VO2 Max
let maxVO2Max = 0;
activities.forEach(activity => {
const activityVO2Max = activity.getStat(data_vo2_max_1.DataVO2Max.type);
if (activityVO2Max) {
maxVO2Max = Math.max(maxVO2Max, activityVO2Max.getValue());
}
});
if (maxVO2Max) {
stats.push(new data_vo2_max_1.DataVO2Max(maxVO2Max));
}
// Sum Moving Time
let movingTime = 0;
activities.forEach(activity => {
const activityMovingTime = activity.getStat(data_moving_time_1.DataMovingTime.type);
if (activityMovingTime) {
movingTime += activityMovingTime.getValue();
}
});
if (movingTime) {
stats.push(new data_moving_time_1.DataMovingTime(movingTime));
}
// Sum Total Grit
let totalGrit = 0;
activities.forEach(activity => {
const activityTotalGrit = activity.getStat(data_total_grit_1.DataTotalGrit.type);
if (activityTotalGrit) {
totalGrit += activityTotalGrit.getValue();
}
});
if (totalGrit) {
stats.push(new data_total_grit_1.DataTotalGrit(totalGrit));
}
// Sum Total Flow
let totalFlow = 0;
activities.forEach(activity => {
const activityTotalFlow = activity.getStat(data_total_flow_1.DataTotalFlow.type);
if (activityTotalFlow) {
totalFlow += activityTotalFlow.getValue();
}
});
if (totalFlow) {
stats.push(new data_total_flow_1.DataTotalFlow(totalFlow));
}
// Sum Jump Count
let jumpCount = 0;
activities.forEach(activity => {
const activityJumpCount = activity.getStat(data_jump_count_1.DataJumpCount.type);
if (activityJumpCount) {
jumpCount += activityJumpCount.getValue();
}
});
if (jumpCount) {
stats.push(new data_jump_count_1.DataJumpCount(jumpCount));
}
this.getSummaryJumpStatsForActivities(activities).forEach(stat => stats.push(stat));
// Max Heart Rate
let maxHeartRate = 0;
activities.forEach(activity => {
const activityMaxHeartRate = activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type);
if (activityMaxHeartRate) {
maxHeartRate = Math.max(maxHeartRate, activityMaxHeartRate.getValue());
}
});
if (maxHeartRate) {
stats.push(new data_heart_rate_max_1.DataHeartRateMax(maxHeartRate));
}
// Min Heart Rate
let minHeartRate = null;
activities.forEach(activity => {
const activityMinHeartRate = activity.getStat(data_heart_rate_min_1.DataHeartRateMin.type);
if (activityMinHeartRate) {
const val = activityMinHeartRate.getValue();
minHeartRate = minHeartRate === null ? val : Math.min(minHeartRate, val);
}
});
if (minHeartRate !== null) {
stats.push(new data_heart_rate_min_1.DataHeartRateMin(minHeartRate));
}
// Max Power
let maxPower = 0;
activities.forEach(activity => {
const activityMaxPower = activity.getStat(data_power_max_1.DataPowerMax.type);
if (activityMaxPower) {
maxPower = Math.max(maxPower, activityMaxPower.getValue());
}
});
if (maxPower) {
stats.push(new data_power_max_1.DataPowerMax(maxPower));
}
// Min Power
let minPower = null;
activities.forEach(activity => {
const activityMinPower = activity.getStat(data_power_min_1.DataPowerMin.type);
if (activityMinPower) {
const val = activityMinPower.getValue();
minPower = minPower === null ? val : Math.min(minPower, val);
}
});
if (minPower !== null) {
stats.push(new data_power_min_1.DataPowerMin(minPower));
}
// Max Speed
let maxSpeed = 0;
activities.forEach(activity => {
const activityMaxSpeed = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (activityMaxSpeed) {
maxSpeed = Math.max(maxSpeed, activityMaxSpeed.getValue());
}
});
if (maxSpeed) {
stats.push(new data_speed_max_1.DataSpeedMax(maxSpeed));
}
// Min Speed
let minSpeed = null;
activities.forEach(activity => {
const activityMinSpeed = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (activityMinSpeed) {
const val = activityMinSpeed.getValue();
minSpeed = minSpeed === null ? val : Math.min(minSpeed, val);
}
});
if (minSpeed !== null) {
stats.push(new data_speed_min_1.DataSpeedMin(minSpeed));
}
// Max Cadence
let maxCadence = 0;
activities.forEach(activity => {
const activityMaxCadence = activity.getStat(data_cadence_max_1.DataCadenceMax.type);
if (activityMaxCadence) {
maxCadence = Math.max(maxCadence, activityMaxCadence.getValue());
}
});
if (maxCadence) {
stats.push(new data_cadence_max_1.DataCadenceMax(maxCadence));
}
// Min Cadence
let minCadence = null;
activities.forEach(activity => {
const activityMinCadence = activity.getStat(data_cadence_min_1.DataCadenceMin.type);
if (activityMinCadence) {
const val = activityMinCadence.getValue();
minCadence = minCadence === null ? val : Math.min(minCadence, val);
}
});
if (minCadence !== null) {
stats.push(new data_cadence_min_1.DataCadenceMin(minCadence));
}
// Max Altitude
let maxAltitude = -Infinity;
activities.forEach(activity => {
const activityMaxAltitude = activity.getStat(data_altitude_max_1.DataAltitudeMax.type);
if (activityMaxAltitude) {
maxAltitude = Math.max(maxAltitude, activityMaxAltitude.getValue());
}
});
if (maxAltitude !== -Infinity) {
stats.push(new data_altitude_max_1.DataAltitudeMax(maxAltitude));
}
// Min Altitude
let minAltitude = Infinity;
activities.forEach(activity => {
const activityMinAltitude = activity.getStat(data_altitude_min_1.DataAltitudeMin.type);
if (activityMinAltitude) {
minAltitude = Math.min(minAltitude, activityMinAltitude.getValue());
}
});
if (minAltitude !== Infinity) {
stats.push(new data_altitude_min_1.DataAltitudeMin(minAltitude));
}
// Max Temperature
let maxTemperature = -Infinity;
activities.forEach(activity => {
const activityMaxTemperature = activity.getStat(data_temperature_max_1.DataTemperatureMax.type);
if (activityMaxTemperature) {
maxTemperature = Math.max(maxTemperature, activityMaxTemperature.getValue());
}
});
if (maxTemperature !== -Infinity) {
stats.push(new data_temperature_max_1.DataTemperatureMax(maxTemperature));
}
// Min Temperature
let minTemperature = Infinity;
activities.forEach(activity => {
const activityMinTemperature = activity.getStat(data_temperature_min_1.DataTemperatureMin.type);
if (activityMinTemperature) {
minTemperature = Math.min(minTemperature, activityMinTemperature.getValue());
}
});
if (minTemperature !== Infinity) {
stats.push(new data_temperature_min_1.DataTemperatureMin(minTemperature));
}
// Max Air Power
let maxAirPower = -Infinity;
activities.forEach(activity => {
const activityMaxAirPower = activity.getStat(data_air_power_max_1.DataAirPowerMax.type);
if (activityMaxAirPower) {
maxAirPower = Math.max(maxAirPower, activityMaxAirPower.getValue());
}
});
if (maxAirPower !== -Infinity) {
stats.push(new data_air_power_max_1.DataAirPowerMax(maxAirPower));
}
// Min Air Power
let minAirPower = Infinity;
activities.forEach(activity => {
const activityMinAirPower = activity.getStat(data_air_power_min_1.DataAirPowerMin.type);
if (activityMinAirPower) {
minAirPower = Math.min(minAirPower, activityMinAirPower.getValue());
}
});
if (minAirPower !== Infinity) {
stats.push(new data_air_power_min_1.DataAirPowerMin(minAirPower));
}
// Max Vertical Speed
let maxVerticalSpeed = -Infinity;
activities.forEach(activity => {
const activityMaxVerticalSpeed = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (activityMaxVerticalSpeed) {
maxVerticalSpeed = Math.max(maxVerticalSpeed, activityMaxVerticalSpeed.getValue());
}
});
if (maxVerticalSpeed !== -Infinity) {
stats.push(new data_vertical_speed_max_1.DataVerticalSpeedMax(maxVerticalSpeed));
}
// Min Vertical Speed
let minVerticalSpeed = Infinity;
activities.forEach(activity => {
const activityMinVerticalSpeed = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (activityMinVerticalSpeed) {
minVerticalSpeed = Math.min(minVerticalSpeed, activityMinVerticalSpeed.getValue());
}
});
if (minVerticalSpeed !== Infinity) {
stats.push(new data_vertical_speed_min_1.DataVerticalSpeedMin(minVerticalSpeed));
}
// Max Ground Contact Time
let maxGroundContactTime = -Infinity;
activities.forEach(activity => {
const activityMaxGroundContactTime = activity.getStat(data_ground_contact_time_max_1.DataGroundContactTimeMax.type);
if (activityMaxGroundContactTime) {
maxGroundContactTime = Math.max(maxGroundContactTime, activityMaxGroundContactTime.getValue());
}
});
if (maxGroundContactTime !== -Infinity) {
stats.push(new data_ground_contact_time_max_1.DataGroundContactTimeMax(maxGroundContactTime));
}
// Min Ground Contact Time
let minGroundContactTime = Infinity;
activities.forEach(activity => {
const activityMinGroundContactTime = activity.getStat(data_ground_contact_time_min_1.DataGroundContactTimeMin.type);
if (activityMinGroundContactTime) {
minGroundContactTime = Math.min(minGroundContactTime, activityMinGroundContactTime.getValue());
}
});
if (minGroundContactTime !== Infinity) {
stats.push(new data_ground_contact_time_min_1.DataGroundContactTimeMin(minGroundContactTime));
}
// Max Vertical Oscillation
let maxVerticalOscillation = -Infinity;
activities.forEach(activity => {
const activityMaxVerticalOscillation = activity.getStat(data_vertical_oscillation_max_1.DataVerticalOscillationMax.type);
if (activityMaxVerticalOscillation) {
maxVerticalOscillation = Math.max(maxVerticalOscillation, activityMaxVerticalOscillation.getValue());
}
});
if (maxVerticalOscillation !== -Infinity) {
stats.push(new data_vertical_oscillation_max_1.DataVerticalOscillationMax(maxVerticalOscillation));
}
// Min Vertical Oscillation
let minVerticalOscillation = Infinity;
activities.forEach(activity => {
const activityMinVerticalOscillation = activity.getStat(data_vertical_oscillation_min_1.DataVerticalOscillationMin.type);
if (activityMinVerticalOscillation) {
minVerticalOscillation = Math.min(minVerticalOscillation, activityMinVerticalOscillation.getValue());
}
});
if (minVerticalOscillation !== Infinity) {
stats.push(new data_vertical_oscillation_min_1.DataVerticalOscillationMin(minVerticalOscillation));
}
// Max Leg Stiffness
let maxLegStiffness = -Infinity;
activities.forEach(activity => {
const activityMaxLegStiffness = activity.getStat(data_leg_stiffness_max_1.DataLegStiffnessMax.type);
if (activityMaxLegStiffness) {
maxLegStiffness = Math.max(maxLegStiffness, activityMaxLegStiffness.getValue());
}
});
if (maxLegStiffness !== -Infinity) {
stats.push(new data_leg_stiffness_max_1.DataLegStiffnessMax(maxLegStiffness));
}
// Min Leg Stiffness
let minLegStiffness = Infinity;
activities.forEach(activity => {
const activityMinLegStiffness = activity.getStat(data_leg_stiffness_min_1.DataLegStiffnessMin.type);
if (activityMinLegStiffness) {
minLegStiffness = Math.min(minLegStiffness, activityMinLegStiffness.getValue());
}
});
if (minLegStiffness !== Infinity) {
stats.push(new data_leg_stiffness_min_1.DataLegStiffnessMin(minLegStiffness));
}
// Max Vertical Ratio
let maxVerticalRatio = -Infinity;
activities.forEach(activity => {
const activityMaxVerticalRatio = activity.getStat(data_vertical_ratio_max_1.DataVerticalRatioMax.type);
if (activityMaxVerticalRatio) {
maxVerticalRatio = Math.max(maxVerticalRatio, activityMaxVerticalRatio.getValue());
}
});
if (maxVerticalRatio !== -Infinity) {
stats.push(new data_vertical_ratio_max_1.DataVerticalRatioMax(maxVerticalRatio));
}
// Min Vertical Ratio
let minVerticalRatio = Infinity;
activities.forEach(activity => {
const activityMinVerticalRatio = activity.getStat(data_vertical_ratio_min_1.DataVerticalRatioMin.type);
if (activityMinVerticalRatio) {
minVerticalRatio = Math.min(minVerticalRatio, activityMinVerticalRatio.getValue());
}
});
if (minVerticalRatio !== Infinity) {
stats.push(new data_vertical_ratio_min_1.DataVerticalRatioMin(minVerticalRatio));
}
// Max Pace
let maxPace = -Infinity;
activities.forEach(activity => {
const activityMaxPace = activity.getStat(data_pace_max_1.DataPaceMax.type);
if (activityMaxPace) {
maxPace = Math.max(maxPace, activityMaxPace.getValue());
}
});
if (maxPace !== -Infinity) {
stats.push(new data_pace_max_1.DataPaceMax(maxPace));
}
// Min Pace
let minPace = Infinity;
activities.forEach(activity => {
const activityMinPace = activity.getStat(data_pace_min_1.DataPaceMin.type);
if (activityMinPace) {
const value = Number(activityMinPace.getValue());
if (Number.isFinite(value)) {
minPace = Math.min(minPace, value);
}
}
});
if (minPace !== Infinity) {
stats.push(new data_pace_min_1.DataPaceMin(minPace));
}
// Max Effort Pace
let maxEffortPace = -Infinity;
activities.forEach(activity => {
const activityMaxEffortPace = activity.getStat(data_effort_pace_max_1.DataEffortPaceMax.type);
if (activityMaxEffortPace) {
maxEffortPace = Math.max(maxEffortPace, activityMaxEffortPace.getValue());
}
});
if (maxEffortPace !== -Infinity) {
stats.push(new data_effort_pace_max_1.DataEffortPaceMax(maxEffortPace));
}
// Min Effort Pace
let minEffortPace = Infinity;
activities.forEach(activity => {
const activityMinEffortPace = activity.getStat(data_effort_pace_min_1.DataEffortPaceMin.type);
if (activityMinEffortPace) {
const value = Number(activityMinEffortPace.getValue());
if (Number.isFinite(value)) {
minEffortPace = Math.min(minEffortPace, value);
}
}
});
if (minEffortPace !== Infinity) {
stats.push(new data_effort_pace_min_1.DataEffortPaceMin(minEffortPace));
}
// Max Grade Adjusted Pace
let maxGradeAdjustedPace = -Infinity;
activities.forEach(activity => {
const activityMaxGradeAdjustedPace = activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type);
if (activityMaxGradeAdjustedPace) {
maxGradeAdjustedPace = Math.max(maxGradeAdjustedPace, activityMaxGradeAdjustedPace.getValue());
}
});
if (maxGradeAdjustedPace !== -Infinity) {
stats.push(new data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax(maxGradeAdjustedPace));
}
// Min Grade Adjusted Pace
let minGradeAdjustedPace = Infinity;
activities.forEach(activity => {
const activityMinGradeAdjustedPace = activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type);
if (activityMinGradeAdjustedPace) {
const value = Number(activityMinGradeAdjustedPace.getValue());
if (Number.isFinite(value)) {
minGradeAdjustedPace = Math.min(minGradeAdjustedPace, value);
}
}
});
if (minGradeAdjustedPace !== Infinity) {
stats.push(new data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin(minGradeAdjustedPace));
}
// Max Swim Pace
let maxSwimPace = -Infinity;
activities.forEach(activity => {
const activityMaxSwimPace = activity.getStat(data_swim_pace_max_1.DataSwimPaceMax.type);
if (activityMaxSwimPace) {
maxSwimPace = Math.max(maxSwimPace, activityMaxSwimPace.getValue());
}
});
if (maxSwimPace !== -Infinity) {
stats.push(new data_swim_pace_max_1.DataSwimPaceMax(maxSwimPace));
}
// Min Swim Pace
let minSwimPace = Infinity;
activities.forEach(activity => {
const activityMinSwimPace = activity.getStat(data_swim_pace_min_1.DataSwimPaceMin.type);
if (activityMinSwimPace) {
const value = Number(activityMinSwimPace.getValue());
if (Number.isFinite(value)) {
minSwimPace = Math.min(minSwimPace, value);
}
}
});
if (minSwimPace !== Infinity) {
stats.push(new data_swim_pace_min_1.DataSwimPaceMin(minSwimPace));
}
// Max Grade Adjusted Speed
let maxGradeAdjustedSpeed = -Infinity;
activities.forEach(activity => {
const activityMaxGradeAdjustedSpeed = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (activityMaxGradeAdjustedSpeed) {
maxGradeAdjustedSpeed = Math.max(maxGradeAdjustedSpeed, activityMaxGradeAdjustedSpeed.getValue());
}
});
if (maxGradeAdjustedSpeed !== -Infinity) {
stats.push(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax(maxGradeAdjustedSpeed));
}
// Min Grade Adjusted Speed
let minGradeAdjustedSpeed = Infinity;
activities.forEach(activity => {
const activityMinGradeAdjustedSpeed = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (activityMinGradeAdjustedSpeed) {
minGradeAdjustedSpeed = Math.min(minGradeAdjustedSpeed, activityMinGradeAdjustedSpeed.getValue());
}
});
if (minGradeAdjustedSpeed !== Infinity) {
stats.push(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin(minGradeAdjustedSpeed));
}
// Max Grade
let maxGrade = -Infinity;
activities.forEach(activity => {
const activityMaxGrade = activity.getStat(data_grade_max_1.DataGradeMax.type);
if (activityMaxGrade) {
maxGrade = Math.max(maxGrade, activityMaxGrade.getValue());
}
});
if (maxGrade !== -Infinity) {
stats.push(new data_grade_max_1.DataGradeMax(maxGrade));
}
// Min Grade
let minGrade = Infinity;
activities.forEach(activity => {
const activityMinGrade = activity.getStat(data_grade_min_1.DataGradeMin.type);
if (activityMinGrade) {
minGrade = Math.min(minGrade, activityMinGrade.getValue());
}
});
if (minGrade !== Infinity) {
stats.push(new data_grade_min_1.DataGradeMin(minGrade));
}
// Max Absolute Pressure
let maxAbsolutePressure = -Infinity;
activities.forEach(activity => {
const activityMaxAbsolutePressure = activity.getStat(data_absolute_pressure_max_1.DataAbsolutePressureMax.type);
if (activityMaxAbsolutePressure) {
maxAbsolutePressure = Math.max(maxAbsolutePressure, activityMaxAbsolutePressure.getValue());
}
});
if (maxAbsolutePressure !== -Infinity) {
stats.push(new data_absolute_pressure_max_1.DataAbsolutePressureMax(maxAbsolutePressure));
}
// Min Absolute Pressure
let minAbsolutePressure = Infinity;
activities.forEach(activity => {
const activityMinAbsolutePressure = activity.getStat(data_absolute_pressure_min_1.DataAbsolutePressureMin.type);
if (activityMinAbsolutePressure) {
minAbsolutePressure = Math.min(minAbsolutePressure, activityMinAbsolutePressure.getValue());
}
});
if (minAbsolutePressure !== Infinity) {
stats.push(new data_absolute_pressure_min_1.DataAbsolutePressureMin(minAbsolutePressure));
}
// Max EVPE
let maxEVPE = -Infinity;
activities.forEach(activity => {
const activityMaxEVPE = activity.getStat(data_evpe_max_1.DataEVPEMax.type);
if (activityMaxEVPE) {
maxEVPE = Math.max(maxEVPE, activityMaxEVPE.getValue());
}
});
if (maxEVPE !== -Infinity) {
stats.push(new data_evpe_max_1.DataEVPEMax(maxEVPE));
}
// Min EVPE
let minEVPE = Infinity;
activities.forEach(activity => {
const activityMinEVPE = activity.getStat(data_evpe_min_1.DataEVPEMin.type);
if (activityMinEVPE) {
minEVPE = Math.min(minEVPE, activityMinEVPE.getValue());
}
});
if (minEVPE !== Infinity) {
stats.push(new data_evpe_min_1.DataEVPEMin(minEVPE));
}
// Max EHPE
let maxEHPE = -Infinity;
activities.forEach(activity => {
const activityMaxEHPE = activity.getStat(data_ehpe_max_1.DataEHPEMax.type);
if (activityMaxEHPE) {
maxEHPE = Math.max(maxEHPE, activityMaxEHPE.getValue());
}
});
if (maxEHPE !== -Infinity) {
stats.push(new data_ehpe_max_1.DataEHPEMax(maxEHPE));
}
// Min EHPE
let minEHPE = Infinity;
activities.forEach(activity => {
const activityMinEHPE = activity.getStat(data_ehpe_min_1.DataEHPEMin.type);
if (activityMinEHPE) {
minEHPE = Math.min(minEHPE, activityMinEHPE.getValue());
}
});
if (minEHPE !== Infinity) {
stats.push(new data_ehpe_min_1.DataEHPEMin(minEHPE));
}
// Max Satellite 5 Best SNR
let maxSatellite5BestSNR = -Infinity;
activities.forEach(activity => {
const activityMaxSatellite5BestSNR = activity.getStat(data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax.type);
if (activityMaxSatellite5BestSNR) {
maxSatellite5BestSNR = Math.max(maxSatellite5BestSNR, activityMaxSatellite5BestSNR.getValue());
}
});
if (maxSatellite5BestSNR !== -Infinity) {
stats.push(new data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax(maxSatellite5BestSNR));
}
// Min Satellite 5 Best SNR
let minSatellite5BestSNR = Infinity;
activities.forEach(activity => {
const activityMinSatellite5BestSNR = activity.getStat(data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin.type);
if (activityMinSatellite5BestSNR) {
minSatellite5BestSNR = Math.min(minSatellite5BestSNR, activityMinSatellite5BestSNR.getValue());
}
});
if (minSatellite5BestSNR !== Infinity) {
stats.push(new data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin(minSatellite5BestSNR));
}
// Max Number of Satellites
let maxNumberOfSatellites = -Infinity;
activities.forEach(activity => {
const activityMaxNumberOfSatellites = activity.getStat(data_number_of_satellites_max_1.DataNumberOfSatellitesMax.type);
if (activityMaxNumberOfSatellites) {
maxNumberOfSatellites = Math.max(maxNumberOfSatellites, activityMaxNumberOfSatellites.getValue());
}
});
if (maxNumberOfSatellites !== -Infinity) {
stats.push(new data_number_of_satellites_max_1.DataNumberOfSatellitesMax(maxNumberOfSatellites));
}
// Min Number of Satellites
let minNumberOfSatellites = Infinity;
activities.forEach(activity => {
const activityMinNumberOfSatellites = activity.getStat(data_number_of_satellites_min_1.DataNumberOfSatellitesMin.type);
if (activityMinNumberOfSatellites) {
minNumberOfSatellites = Math.min(minNumberOfSatellites, activityMinNumberOfSatellites.getValue());
}
});
if (minNumberOfSatellites !== Infinity) {
stats.push(new data_number_of_satellites_min_1.DataNumberOfSatellitesMin(minNumberOfSatellites));
}
// Avg Ground Contact Time
let averageGroundContactTime = 0;
activities.forEach(activity => {
const activityGCT = activity.getStat(data_ground_contact_time_avg_1.DataGroundContactTimeAvg.type);
if (activityGCT) {
averageGroundContactTime = averageGroundContactTime
? (averageGroundContactTime + activityGCT.getValue()) / 2
: activityGCT.getValue();
}
});
if (averageGroundContactTime) {
stats.push(new data_ground_contact_time_avg_1.DataGroundContactTimeAvg(averageGroundContactTime));
}
// Avg Vertical Oscillation
activities.forEach(activity => {
const activityVerticalOscillation = activity.getStat(data_vertical_oscillation_avg_1.DataVerticalOscillationAvg.type);
if (activityVerticalOscillation) {
averageVerticalOscillation = hasAverageVerticalOscillation
? (averageVerticalOscillation + activityVerticalOscillation.getValue()) / 2
: activityVerticalOscillation.getValue();
hasAverageVerticalOscillation = true;
}
});
if (hasAverageVerticalOscillation) {
stats.push(new data_vertical_oscillation_avg_1.DataVerticalOscillationAvg(averageVerticalOscillation));
}
stats.push(...this.getIntensityZonesStatsAggregated(activities));
// Add start and end position
// This expects the to be sorted
const activitiesWithStartPosition = activities.filter(activity => activity.getStat(data_start_position_1.DataStartPosition.type));
const activitiesWithEndPosition = activities.filter(activity => activity.getStat(data_end_position_1.DataEndPosition.type));
if (activitiesWithStartPosition && activitiesWithStartPosition.length) {
const startPositionStat = activitiesWithStartPosition[0].getStat(data_start_position_1.DataStartPosition.type);
stats.push(new data_start_position_1.DataStartPosition(startPositionStat.getValue()));
}
if (activitiesWithEndPosition && activitiesWithEndPosition.length) {
const endPositionStat = (activitiesWithEndPosition[activitiesWithEndPosition.length - 1].getStat(data_end_position_1.DataEndPosition.type));
stats.push(new data_end_position_1.DataEndPosition(endPositionStat.getValue()));
}
// debugger;
return stats;
}
/**
* Calculate Mean Max Power for specified durations
* @param activity The activity to analyze
* @param durations Array of durations in seconds. If not provided, a "Best-in-Class" high-granularity set is used.
*/
static calculateMeanMaxPower(activity, durations) {
if (!activity.hasStreamData(data_power_1.DataPower.type)) {
return new data_power_curve_1.DataPowerCurve([]);
}
// Default "Best-in-Class" granularity if no durations provided
if (!durations || durations.length === 0) {
durations = [
// High Resolution Sprint (1s - 10s)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
// Sprint Transition (12s - 50s)
12, 15, 18, 20, 25, 30, 35, 40, 45, 50,
// Anaerobic Capacity (1m - 4m)
60, 75, 90, 120, 150, 180, 210, 240,
// VO2 Max (5m - 10m)
300, 360, 420, 480, 540, 600,
// Threshold & Endurance (15m - 5h)
900, 1200, 1500, 1800, 2400, 3600, 5400, 7200, 10800, 14400, 18000
];
}
const powerData = activity.getStreamData(data_power_1.DataPower.type);
const curvePoints = [];
// Get user weight for W/kg calculation
const weightStat = activity.getStat(data_weight_1.DataWeight.type);
const weight = weightStat ? weightStat.getValue() : null;
// Sort durations to ensure consistent processing order, though not strictly necessary
const sortedDurations = [...durations].sort((a, b) => a - b);
for (const duration of sortedDurations) {
if (duration <= 0 || duration > powerData.length) {
continue;
}
let maxAvgPower = 0;
let currentSum = 0;
// Helper to treat null as 0
const getValue = (val) => (typeof val === 'number' ? val : 0);
// Initial window
for (let i = 0; i < duration; i++) {
currentSum += getValue(powerData[i]);
}
maxAvgPower = currentSum / duration;
// Slide window
for (let i = duration; i < powerData.length; i++) {
currentSum = currentSum - getValue(powerData[i - duration]) + getValue(powerData[i]);
const currentAvg = currentSum / duration;
if (currentAvg > maxAvgPower) {
maxAvgPower = currentAvg;
}
}
const point = {
duration: new data_duration_1.DataDuration(duration),
power: new data_power_1.DataPower(maxAvgPower)
};
if (weight && weight > 0) {
// Calculate W/kg, round to 2 decimal places for cleanliness
const wKg = Math.round((maxAvgPower / weight) * 100) / 100;
point.wattsPerKg = new data_power_watts_per_kg_1.DataPowerWattsPerKg(wKg);
}
curvePoints.push(point);
}
return new data_power_curve_1.DataPowerCurve(curvePoints);
}
static calculateFTP(activity) {
let twentyMinutePower;
const isValidPower = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0;
const curveStat = activity.getStat(data_power_curve_1.DataPowerCurve.type);
if (curveStat && curveStat.getValue()) {
const points = curveStat.getValue();
const point = points.find(p => p.duration.getValue() === this.FTP_DURATION_SECONDS);
if (point) {
twentyMinutePower = point.power.getValue();
}
}
if (!isValidPower(twentyMinutePower) && activity.hasStreamData(data_power_1.DataPower.type)) {
const curve = this.calculateMeanMaxPower(activity, [this.FTP_DURATION_SECONDS]);
const points = curve.getValue();
if (points.length > 0) {
twentyMinutePower = points[0].power.getValue();
}
}
if (!isValidPower(twentyMinutePower)) {
return null;
}
return new data_ftp_1.DataFTP(this.round(twentyMinutePower * this.FTP_FACTOR));
}
static calculatePowerIntensityFactor(activity) {
const normalizedPower = this.resolveNormalizedPower(activity);
const ftp = this.resolveFunctionalThresholdPower(activity);
if (normalizedPower === null || ftp === null) {
return null;
}
return new data_power_intensity_factor_1.DataPowerIntensityFactor(this.round(normalizedPower / ftp, this.INTENSITY_FACTOR_DECIMALS));
}
static toPositiveFiniteNumber(value) {
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null;
}
static resolvePowerWattsPerKg(activity) {
const existingPowerWattsPerKg = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_power_watts_per_kg_1.DataPowerWattsPerKg.type));
if (existingPowerWattsPerKg !== null) {
return existingPowerWattsPerKg;
}
const averagePower = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_power_avg_1.DataPowerAvg.type));
const weight = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_weight_1.DataWeight.type));
if (averagePower === null || weight === null) {
return null;
}
return this.round(averagePower / weight, 2);
}
static getDurationSecondsWithoutPauses(activity) {
const timerTime = this.getFiniteStatValue(activity, data_timer_time_1.DataTimerTime.type);
if (timerTime !== null && timerTime > 0) {
return timerTime;
}
const movingTime = this.getFiniteStatValue(activity, data_moving_time_1.DataMovingTime.type);
if (movingTime !== null && movingTime > 0) {
return movingTime;
}
const duration = this.getFiniteStatValue(activity, data_duration_1.DataDuration.type);
if (duration !== null && duration > 0) {
const pause = this.getFiniteStatValue(activity, data_pause_1.DataPause.type);
if (pause !== null && pause >= 0 && duration - pause > 0) {
return duration - pause;
}
return duration;
}
const fallbackDurationSeconds = (activity.endDate.getTime() - activity.startDate.getTime()) / 1000;
if (Number.isFinite(fallbackDurationSeconds) && fallbackDurationSeconds > 0) {
return fallbackDurationSeconds;
}
return 0;
}
static getTssOverrideValue(activity, key) {
var _a, _b, _c;
return this.toPositiveFiniteNumber((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.overrides) === null || _c === void 0 ? void 0 : _c[key]);
}
static getZoneFiveLowerLimit(activity, zoneType) {
const zone = activity.intensityZones.find(intensityZone => intensityZone.type === zoneType);
return this.toPositiveFiniteNumber(zone === null || zone === void 0 ? void 0 : zone.zone5LowerLimit);
}
static getStreamSamplesByDuration(activity, streamType) {
if (!activity.hasStreamData(streamType)) {
return [];
}
return activity
.getStreamDataByDuration(streamType, true, true)
.map(sample => ({ duration: sample.time / 1000, value: sample.value }))
.filter(sample => Number.isFinite(sample.duration) && Number.isFinite(sample.value));
}
static resolveNormalizedPower(activity) {
const existingNormalizedPower = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_power_normalized_1.DataPowerNormalized.type));
if (existingNormalizedPower !== null) {
return existingNormalizedPower;
}
if (!activity.hasStreamData(data_power_1.DataPower.type)) {
return null;
}
const powerDurationStream = activity.getStreamDataByDuration(data_power_1.DataPower.type, true, true);
const timeStream = powerDurationStream.map(item => item.time / 1000);
const powerStream = powerDurationStream.map(item => item.value);
if (powerStream.length < 2 || timeStream.length < 2) {
return null;
}
const computedNormalizedPower = this.toPositiveFiniteNumber(this.computeNormalizedPower(powerStream, timeStream));
if (computedNormalizedPower === null) {
return null;
}
activity.addStat(new data_power_normalized_1.DataPowerNormalized(computedNormalizedPower));
return computedNormalizedPower;
}
static resolveFunctionalThresholdPower(activity) {
const override = this.getTssOverrideValue(activity, 'functionalThresholdPower');
if (override !== null) {
return override;
}
const existingFtp = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_ftp_1.DataFTP.type));
if (existingFtp !== null) {
return existingFtp;
}
const derivedFtp = this.calculateFTP(activity);
if (!derivedFtp) {
return null;
}
if (!activity.getStat(data_ftp_1.DataFTP.type)) {
activity.addStat(derivedFtp);
}
return this.toPositiveFiniteNumber(derivedFtp.getValue());
}
static persistFunctionalThresholdPowerOverride(activity) {
const override = this.getTssOverrideValue(activity, 'functionalThresholdPower');
if (override === null) {
return;
}
const currentFtp = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_ftp_1.DataFTP.type));
if (currentFtp !== override) {
activity.addStat(new data_ftp_1.DataFTP(override));
}
}
static resolveLactateThresholdHr(activity) {
const override = this.getTssOverrideValue(activity, 'lactateThresholdHR');
if (override !== null) {
return override;
}
return this.getZoneFiveLowerLimit(activity, data_heart_rate_1.DataHeartRate.type);
}
static resolveMaxHeartRate(activity) {
const override = this.getTssOverrideValue(activity, 'maxHeartRate');
if (override !== null) {
return override;
}
const setting = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_max_hr_setting_1.DataMaxHRSetting.type));
if (setting !== null) {
return setting;
}
return this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_heart_rate_max_1.DataHeartRateMax.type));
}
static resolveRestingHeartRate(activity) {
return this.getTssOverrideValue(activity, 'restingHeartRate');
}
static resolveGender(activity) {
var _a;
const value = (_a = activity.getStat(data_gender_1.DataGender.type)) === null || _a === void 0 ? void 0 : _a.getValue();
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
}
static resolveFunctionalThresholdPace(activity) {
const override = this.getTssOverrideValue(activity, 'functionalThresholdPace');
if (override !== null) {
return override;
}
return this.getZoneFiveLowerLimit(activity, data_speed_1.DataSpeed.type);
}
static resolveSwimSpeed(activity) {
const averageSpeed = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_speed_avg_1.DataSpeedAvg.type));
if (averageSpeed !== null) {
return averageSpeed;
}
const speedSamples = this.getStreamSamplesByDuration(activity, data_speed_1.DataSpeed.type);
if (!speedSamples.length) {
return null;
}
const averageFromStream = speedSamples.reduce((sum, sample) => sum + sample.value, 0) / speedSamples.length;
return this.toPositiveFiniteNumber(averageFromStream);
}
static resolveThresholdSwimSpeed(activity, swimSpeed) {
var _a, _b, _c;
const thresholdOverride = this.getTssOverrideValue(activity, 'thresholdSwimSpeed');
if (thresholdOverride !== null) {
return thresholdOverride;
}
const legacyOverride = this.getTssOverrideValue(activity, 'refSwimSpeed');
if (legacyOverride !== null) {
return legacyOverride;
}
const fromSwimPaceZones = this.getZoneFiveLowerLimit(activity, data_swim_pace_1.DataSwimPace.type);
if (fromSwimPaceZones !== null) {
const convertedSpeed = this.toPositiveFiniteNumber((0, helpers_1.convertSwimPaceToSpeed)(fromSwimPaceZones));
if (convertedSpeed !== null) {
return convertedSpeed;
}
}
const fromSpeedZones = this.getZoneFiveLowerLimit(activity, data_speed_1.DataSpeed.type);
if (fromSpeedZones !== null) {
return fromSpeedZones;
}
const enableHeuristicFallbacks = (_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.enableHeuristicFallbacks) !== null && _c !== void 0 ? _c : true;
if (enableHeuristicFallbacks) {
return swimSpeed;
}
return null;
}
static resolveMetScore(activity, durationSeconds) {
const override = this.getTssOverrideValue(activity, 'metScore');
if (override !== null) {
return override;
}
const energy = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_energy_1.DataEnergy.type));
const weight = this.toPositiveFiniteNumber(this.getFiniteStatValue(activity, data_weight_1.DataWeight.type));
if (energy === null || weight === null || durationSeconds <= 0) {
return null;
}
const estimatedMetScore = tss_calculator_1.TssCalculator.estimateMetScore(energy, weight, durationSeconds);
return this.toPositiveFiniteNumber(estimatedMetScore);
}
static resolveMetThreshold(activity) {
var _a;
return (_a = this.getTssOverrideValue(activity, 'thresholdMet')) !== null && _a !== void 0 ? _a : 10;
}
static calculatePowerTss(activity) {
const ftp = this.resolveFunctionalThresholdPower(activity);
if (ftp === null) {
return null;
}
const normalizedPower = this.resolveNormalizedPower(activity);
if (normalizedPower === null) {
return null;
}
return tss_calculator_1.TssCalculator.calculatePowerTss({
totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
functionalThresholdPower: ftp,
normalizedPower,
samples: []
});
}
static calculateHrTss(activity) {
const maxHeartRate = this.resolveMaxHeartRate(activity);
if (maxHeartRate === null) {
return null;
}
const lactateThresholdHR = this.resolveLactateThresholdHr(activity);
const restingHeartRate = this.resolveRestingHeartRate(activity);
const gender = this.resolveGender(activity);
const samples = this.getStreamSamplesByDuration(activity, data_heart_rate_1.DataHeartRate.type).map(sample => ({
duration: sample.duration,
hr: sample.value
}));
if (!samples.length) {
return null;
}
return tss_calculator_1.TssCalculator.calculateHrTss({
totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
lactateThresholdHR: lactateThresholdHR !== null && lactateThresholdHR !== void 0 ? lactateThresholdHR : undefined,
maxHeartRate,
restingHeartRate: restingHeartRate !== null && restingHeartRate !== void 0 ? restingHeartRate : undefined,
gender,
samples
});
}
static calculatePaceTss(activity) {
var _a, _b, _c;
const functionalThresholdPace = this.resolveFunctionalThresholdPace(activity);
if (functionalThresholdPace === null) {
return null;
}
const speedSamples = this.getStreamSamplesByDuration(activity, data_speed_1.DataSpeed.type);
if (!speedSamples.length) {
return null;
}
const enableHeuristicFallbacks = (_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.enableHeuristicFallbacks) !== null && _c !== void 0 ? _c : true;
const gradeSmoothByDuration = new Map(this.getStreamSamplesByDuration(activity, data_grade_smooth_1.DataGradeSmooth.type).map(sample => [
sample.duration,
sample.value / 100
]));
const gradeByDuration = new Map(this.getStreamSamplesByDuration(activity, data_grade_1.DataGrade.type).map(sample => [sample.duration, sample.value / 100]));
const verticalSamples = this.getStreamSamplesByDuration(activity, data_vertical_speed_1.DataVerticalSpeed.type);
const verticalByDuration = new Map(verticalSamples.map(sample => [sample.duration, sample.value]));
const samples = speedSamples
.map(sample => {
const speed = sample.value;
const verticalSpeed = verticalByDuration.get(sample.duration);
let grade = gradeSmoothByDuration.get(sample.duration);
if (grade === undefined) {
grade = gradeByDuration.get(sample.duration);
}
if (grade === undefined && verticalSpeed !== undefined && speed > 0) {
grade = verticalSpeed / speed;
}
if (grade === undefined) {
if (!enableHeuristicFallbacks) {
return null;
}
grade = 0;
}
return {
duration: sample.duration,
speed,
grade,
verticalSpeed: verticalSpeed === undefined ? null : verticalSpeed
};
})
.filter((sample) => sample !== null);
if (!samples.length || samples.length !== speedSamples.length) {
return null;
}
return tss_calculator_1.TssCalculator.calculatePaceTss({
totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
functionalThresholdPace,
samples
});
}
static calculateSwimPaceTss(activity) {
const swimSpeed = this.resolveSwimSpeed(activity);
if (swimSpeed === null) {
return null;
}
const thresholdSwimSpeed = this.resolveThresholdSwimSpeed(activity, swimSpeed);
if (thresholdSwimSpeed === null) {
return null;
}
return tss_calculator_1.TssCalculator.calculateSwimTss({
totalDurationWithoutPauses: this.getDurationSecondsWithoutPauses(activity),
swimSpeed,
thresholdSwimSpeed,
samples: []
});
}
static calculateMetTss(activity) {
const durationSeconds = this.getDurationSecondsWithoutPauses(activity);
if (durationSeconds <= 0) {
return null;
}
const metScore = this.resolveMetScore(activity, durationSeconds);
if (metScore === null) {
return null;
}
return tss_calculator_1.TssCalculator.calculateMetTss({
totalDurationWithoutPauses: durationSeconds,
metScore,
thresholdMet: this.resolveMetThreshold(activity),
samples: []
});
}
static supportsPaceTss(activity) {
const activityGroup = activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type);
if (activityGroup === activity_types_1.ActivityTypeGroups.RunningGroup || activityGroup === activity_types_1.ActivityTypeGroups.TrailRunningGroup) {
return true;
}
return activity.type === activity_types_1.ActivityTypes.TrackAndField;
}
static calculateTrainingStressScoreByPriority(activity) {
const powerTss = this.calculatePowerTss(activity);
if (powerTss) {
return powerTss;
}
const hrTss = this.calculateHrTss(activity);
if (hrTss) {
return hrTss;
}
const activityGroup = activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type);
const paceLikeTss = activityGroup === activity_types_1.ActivityTypeGroups.SwimmingGroup
? this.calculateSwimPaceTss(activity)
: this.supportsPaceTss(activity)
? this.calculatePaceTss(activity)
: null;
if (paceLikeTss) {
return paceLikeTss;
}
return this.calculateMetTss(activity);
}
static updatePowerOutputsFromPowerTss(activity, powerTss) {
if (Number.isFinite(powerTss.normalizedPower)) {
activity.addStat(new data_power_normalized_1.DataPowerNormalized(powerTss.normalizedPower));
}
if (Number.isFinite(powerTss.intensityFactor)) {
activity.addStat(new data_power_intensity_factor_1.DataPowerIntensityFactor(this.round(powerTss.intensityFactor, this.INTENSITY_FACTOR_DECIMALS)));
}
}
static setTrainingStressScoreMethod(activity, method) {
activity.addStat(new data_training_stress_score_method_1.DataTrainingStressScoreMethod(method));
}
static generateTrainingStressScore(activity) {
var _a, _b, _c;
const existingTss = this.getFiniteStatValue(activity, data_training_stress_score_1.DataTrainingStressScore.type);
const preserveImportedTss = (_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.tss) === null || _b === void 0 ? void 0 : _b.preserveImportedTss) !== null && _c !== void 0 ? _c : true;
if (existingTss !== null && preserveImportedTss) {
if (!activity.getStat(data_training_stress_score_method_1.DataTrainingStressScoreMethod.type)) {
this.setTrainingStressScoreMethod(activity, data_training_stress_score_method_1.TrainingStressScoreMethod.IMPORTED);
}
return;
}
const result = this.calculateTrainingStressScoreByPriority(activity);
if (!result) {
return;
}
activity.addStat(new data_training_stress_score_1.DataTrainingStressScore(this.round(result.trainingStressScore, this.TRAINING_STRESS_SCORE_DECIMALS)));
this.setTrainingStressScoreMethod(activity, result.calculationMethod);
if (result.calculationMethod === data_training_stress_score_method_1.TrainingStressScoreMethod.POWER) {
this.persistFunctionalThresholdPowerOverride(activity);
this.updatePowerOutputsFromPowerTss(activity, result);
}
}
/**
* Calculate Critical Power (CP) and W' (Anaerobic Work Capacity)
* using the Monod & Scherrer 2-parameter model (Power vs 1/Time).
* @param activity
*/
static calculateCriticalPowerAndWPrime(activity) {
const curveStat = activity.getStat(data_power_curve_1.DataPowerCurve.type);
if (!curveStat || !curveStat.getValue()) {
return null;
}
const points = curveStat.getValue();
// Filter for durations between 3 minutes (180s) and 20 minutes (1200s)
// This range is standard to avoid anaerobic dominance (<3m) and aerobic drift/fatigue (>20m)
const dataset = points.filter(p => {
const d = p.duration.getValue();
return d >= 180 && d <= 1200;
});
if (dataset.length < 2) {
return null; // Not enough data points
}
// Linear Regression: y = mx + c
// y = Power
// x = 1 / time
// m = W' (Slope)
// c = CP (Intercept)
let sumX = 0;
let sumY = 0;
let sumXY = 0;
let sumXX = 0;
const n = dataset.length;
dataset.forEach(p => {
const t = p.duration.getValue();
const P = p.power.getValue();
const x = 1 / t;
const y = P;
sumX += x;
sumY += y;
sumXY += x * y;
sumXX += x * x;
});
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
// Validate results - CP and W' must be positive
if (intercept <= 0 || slope <= 0) {
return null;
}
return {
cp: new data_critical_power_1.DataCriticalPower(Math.round(intercept)),
wPrime: new data_w_prime_1.DataWPrime(Math.round(slope)) // W' is in Joules
};
}
static getIntensityZonesStatsAggregated(statClassInstances) {
return [
data_heart_rate_zone_one_duration_1.DataHeartRateZoneOneDuration.type,
data_heart_rate_zone_two_duration_1.DataHeartRateZoneTwoDuration.type,
data_heart_rate_zone_three_duration_1.DataHeartRateZoneThreeDuration.type,
data_heart_rate_zone_four_duration_1.DataHeartRateZoneFourDuration.type,
data_heart_rate_zone_five_duration_1.DataHeartRateZoneFiveDuration.type,
data_power_zone_one_duration_1.DataPowerZoneOneDuration.type,
data_power_zone_two_duration_1.DataPowerZoneTwoDuration.type,
data_power_zone_three_duration_1.DataPowerZoneThreeDuration.type,
data_power_zone_four_duration_1.DataPowerZoneFourDuration.type,
data_power_zone_five_duration_1.DataPowerZoneFiveDuration.type,
data_power_zone_six_duration_1.DataPowerZoneSixDuration.type,
data_power_zone_seven_duration_1.DataPowerZoneSevenDuration.type,
data_speed_zone_one_duration_1.DataSpeedZoneOneDuration.type,
data_speed_zone_two_duration_1.DataSpeedZoneTwoDuration.type,
data_speed_zone_three_duration_1.DataSpeedZoneThreeDuration.type,
data_speed_zone_four_duration_1.DataSpeedZoneFourDuration.type,
data_speed_zone_five_duration_1.DataSpeedZoneFiveDuration.type
].reduce((statsArray, zone) => {
const zoneDuration = statClassInstances.reduce((duration, statClassInstance) => {
const durationStat = statClassInstance.getStat(zone);
if (durationStat) {
duration = duration || 0;
duration += durationStat.getValue();
}
return duration;
}, null);
if ((0, helpers_1.isNumber)(zoneDuration)) {
statsArray.push(data_store_1.DynamicDataLoader.getDataInstanceFromDataType(zone, zoneDuration));
}
return statsArray;
}, []);
}
static getActivityDataTypeGain(activity, streamType, starDate, endDate, minDiff) {
return this.getActivityDataTypeGainOrLoss(activity, streamType, true, starDate, endDate, minDiff);
}
static getActivityDataTypeLoss(activity, streamType, starDate, endDate, minDiff) {
return this.getActivityDataTypeGainOrLoss(activity, streamType, false, starDate, endDate, minDiff);
}
static getGainOrLoss(data, gain, minDiff = 2) {
let gainOrLoss = 0;
if (!(data === null || data === void 0 ? void 0 : data.length)) {
return null;
}
data.reduce((previousValue, nextValue) => {
// For gain
if (gain) {
// Increase the gain if eligible first check to be greater plus diff [200, 300, 400, 100, 101, 102]
if (previousValue + minDiff <= nextValue) {
gainOrLoss += nextValue - previousValue;
return nextValue;
}
// if not eligible check if smaller without the diff and if yes do not register it and send it back as the last to check against
if (previousValue < nextValue) {
return previousValue;
}
return nextValue;
}
// For Loss
if (previousValue - minDiff >= nextValue) {
gainOrLoss += previousValue - nextValue;
return nextValue;
}
// if not eligible check if smaller without the diff and if yes do not register it and send it back as the last to check against
if (previousValue > nextValue) {
return previousValue;
}
return nextValue;
}, data[0]);
return gainOrLoss;
}
static getMax(data) {
return data.reduce((previousValue, currentValue) => Math.max(previousValue, currentValue), -Infinity);
}
static getMin(data) {
return data.reduce((previousValue, currentValue) => Math.min(previousValue, currentValue), Infinity);
}
static calculateTotalDistanceForActivity(activity, startDate, endDate) {
return this.geoLibAdapter.getDistance(activity.getPositionData(startDate, endDate).filter(position => position !== null));
}
/**
* Returns streams that derive from speed based on the activity type
* @param speedStream
* @param activityType
*/
static createByActivityTypeSpeedBasedStreams(speedStream, activityType) {
return activity_types_1.ActivityTypesHelper.speedDerivedDataTypesToUseForActivityType(activityType).reduce((array, dataType) => {
switch (dataType) {
case data_pace_1.DataPace.type:
return array.concat([
new stream_1.Stream(data_pace_1.DataPace.type, speedStream.getData().map(dataValue => {
if (!(0, helpers_1.isNumber)(dataValue)) {
return null;
}
return (0, helpers_1.convertSpeedToPace)(dataValue);
}))
]);
case data_swim_pace_1.DataSwimPace.type:
return array.concat([
new stream_1.Stream(data_swim_pace_1.DataSwimPace.type, speedStream.getData().map(dataValue => {
if (!(0, helpers_1.isNumber)(dataValue)) {
return null;
}
return (0, helpers_1.convertSpeedToSwimPace)(dataValue);
}))
]);
case data_speed_1.DataSpeed.type:
return array.concat(speedStream);
default:
return array;
}
}, []);
}
/**
* Returns streams that derive from grade adjusted speed based on the activity type
* @param gradeAdjustedSpeedStream
* @param activityType
*/
static createByActivityTypeAltiDistanceSpeedBasedStreams(gradeAdjustedSpeedStream, activityType) {
return activity_types_1.ActivityTypesHelper.altiDistanceSpeedDerivedDataTypesToUseForActivityType(activityType).reduce((array, dataType) => {
switch (dataType) {
case data_grade_adjusted_pace_1.DataGradeAdjustedPace.type:
return array.concat([
new stream_1.Stream(data_grade_adjusted_pace_1.DataGradeAdjustedPace.type, gradeAdjustedSpeedStream.getData().map(dataValue => {
if (!(0, helpers_1.isNumber)(dataValue)) {
return null;
}
return (0, helpers_1.convertSpeedToPace)(dataValue);
}))
]);
case data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type:
return array.concat(gradeAdjustedSpeedStream);
default:
return array;
}
}, []);
}
/**
* @todo unit test (get the pun?)
* This creates streams that are deriving as unit based streams
* For example it will create pace from speed, swim pace from speed but also speed in km/h as a unitstream
* @param streams
* @param activityType
* @param unitStreamTypes DynamicDataLoader.allUnitDerivedDataTypes this acts like a whitelist for the unit derived units ONLY!
* @param options
*/
static createUnitStreamsFromStreams(streams, activityType, unitStreamTypes, options = {
includeDerivedTypes: true,
includeUnitVariants: true
}) {
// @todo perhaps check input to be unitStreamTypesStrictly
// TODO(sports-lib): make unit-stream generation canonical when both base and pre-existing unit streams
// are present (or when multiple base streams map to the same unit variant), so downstream apps never
// need dedupe heuristics for semantically-equivalent stream families.
const unitStreamTypesToCreate = unitStreamTypes || [
...data_store_1.DynamicDataLoader.allUnitDerivedDataTypes,
...data_store_1.DynamicDataLoader.speedDerivedDataTypes
];
const existingStreamTypes = new Set(streams.map(stream => stream.type));
const generatedStreamsByType = new Map();
let baseUnitStreams = [];
// Iterate over all possible base types that can have unit variants
// This allows us to dynamically include ALL base streams (like Distance, Power, etc.) that need unit conversion
Object.keys(data_store_1.DynamicDataLoader.dataTypeUnitGroups).forEach(baseDataType => {
const stream = streams.find(s => s.type === baseDataType);
if (!stream) {
return;
}
// Special handling for derived types (Pace from Speed, etc.)
if (baseDataType === data_speed_1.DataSpeed.type && options.includeDerivedTypes) {
baseUnitStreams = baseUnitStreams.concat(this.createByActivityTypeSpeedBasedStreams(stream, activityType));
return;
}
if (baseDataType === data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type && options.includeDerivedTypes) {
baseUnitStreams = baseUnitStreams.concat(this.createByActivityTypeAltiDistanceSpeedBasedStreams(stream, activityType));
return;
}
if (baseDataType === data_vertical_speed_1.DataVerticalSpeed.type) {
// Vertical speed handling
if (activity_types_1.ActivityTypesHelper.verticalSpeedDerivedDataTypesToUseForActivityType(activityType).length) {
baseUnitStreams.push(stream);
}
return;
}
// For everything else (like Distance), just add the base stream so it can be used for unit generation
baseUnitStreams.push(stream);
});
const startWith = baseUnitStreams.filter(baseUnitStream => unitStreamTypesToCreate.indexOf(baseUnitStream.type) !== -1);
startWith.forEach(stream => {
if (existingStreamTypes.has(stream.type) || generatedStreamsByType.has(stream.type)) {
return;
}
generatedStreamsByType.set(stream.type, stream);
});
if (options.includeUnitVariants === false) {
return Array.from(generatedStreamsByType.values());
}
Object.keys(data_store_1.DynamicDataLoader.dataTypeUnitGroups).forEach(baseDataType => {
const baseStream = baseUnitStreams.find(stream => stream.type === baseDataType);
if (!baseStream) {
return;
}
Object.keys(data_store_1.DynamicDataLoader.dataTypeUnitGroups[baseDataType])
.filter(unitBasedDataType => unitStreamTypesToCreate.indexOf(unitBasedDataType) !== -1) // @todo perhaps dont filter
.forEach(unitBasedDataType => {
if (existingStreamTypes.has(unitBasedDataType) || generatedStreamsByType.has(unitBasedDataType)) {
return;
}
generatedStreamsByType.set(unitBasedDataType, new stream_1.Stream(unitBasedDataType, baseStream.getData().map(dataValue => {
if (!(0, helpers_1.isNumber)(dataValue)) {
return null;
}
return data_store_1.DynamicDataLoader.dataTypeUnitGroups[baseDataType][unitBasedDataType](dataValue);
})));
});
});
return Array.from(generatedStreamsByType.values());
}
/**
* Generates missing streams for an activity such as distance etc if they are missing
* This will always create a steam even if the distance is 0
* @param activity
*/
static generateMissingStreamsForActivity(activity) {
var _a, _b, _c, _d, _e, _f;
// Create derived primitive streams which will be needed for others streams & stats computations
this.createDerivedStreams(activity);
// First add any missing data to the streams via interpolating and extrapolating
this.addMissingDataToStreams(activity);
if (activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type) &&
activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type) &&
(!activity.hasStreamData(data_distance_1.DataDistance.type) || !activity.hasStreamData(data_gnss_distance_1.DataGNSSDistance.type))) {
const streamData = activity.createStream(data_distance_1.DataDistance.type).getData(); // Creating does not add it to activity just presets the resolution to 1s
let distance = 0;
streamData[0] = distance; // Force first distance sample to be equal to 0 instead of null
activity
.getPositionData()
.reduce((prevPosition, position, index) => {
if (!position) {
return prevPosition;
}
if (prevPosition && position) {
distance += this.geoLibAdapter.getDistance([prevPosition, position]);
}
streamData[index] = this.round(distance, 2);
return position;
});
if (!activity.hasStreamData(data_distance_1.DataDistance.type)) {
activity.addStream(new stream_1.Stream(data_distance_1.DataDistance.type, streamData));
}
if (!activity.hasStreamData(data_gnss_distance_1.DataGNSSDistance.type)) {
activity.addStream(new stream_1.Stream(data_gnss_distance_1.DataGNSSDistance.type, streamData));
}
if (!activity.hasStreamData(data_speed_1.DataSpeed.type)) {
const speedStreamData = activity.createStream(data_speed_1.DataSpeed.type).getData();
const distanceStream = activity.getStreamDataByDuration(data_distance_1.DataDistance.type);
let previousDistanceItem;
distanceStream.forEach((distanceItem, index) => {
// Use the first distance item value if previous distance is unknown
if (!previousDistanceItem) {
previousDistanceItem = distanceItem;
}
// If know distance then compute speed from last known distance item
if (Number.isFinite(distanceItem.value)) {
const deltaTime = (distanceItem.time - previousDistanceItem.time) / 1000;
const deltaDistance = (distanceItem === null || distanceItem === void 0 ? void 0 : distanceItem.value) ? distanceItem.value - ((previousDistanceItem === null || previousDistanceItem === void 0 ? void 0 : previousDistanceItem.value) || 0) : 0;
speedStreamData[index] = this.round(deltaTime > 0 ? deltaDistance / deltaTime : 0, 3);
// Keep tracking of last know distance item
previousDistanceItem = distanceItem;
}
else {
speedStreamData[index] = null;
}
});
activity.addStream(new stream_1.Stream(data_speed_1.DataSpeed.type, speedStreamData));
}
}
// Check if we can get a grade stream
if (((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.streams) === null || _b === void 0 ? void 0 : _b.smooth) === null || _c === void 0 ? void 0 : _c.grade) &&
!activity.hasStreamData(data_grade_1.DataGrade.type) &&
this.canGenerateDerivedStream(activity, data_grade_1.DataGrade.type)) {
const distanceData = activity.getStreamData(data_distance_1.DataDistance.type);
const altitudeData = activity.getStreamData(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type);
// Create the grade stream from time, distance and altitude non-squashed streams
const timeData = activity.generateTimeStream([data_distance_1.DataDistance.type]);
const gradeStreamData = grade_calculator_1.GradeCalculator.computeGradeStream(timeData.getData(), distanceData, altitudeData);
// Append new grade stream to activity
activity.addStream(new stream_1.Stream(data_grade_1.DataGrade.type, gradeStreamData));
if ((_f = (_e = (_d = activity.parseOptions) === null || _d === void 0 ? void 0 : _d.streams) === null || _e === void 0 ? void 0 : _e.smooth) === null || _f === void 0 ? void 0 : _f.gradeSmooth) {
// Duplicate and create an altitude smooth stream (we want to keep original altitude stream available)
// Activity stats and grade adjusted speed will be computed on the smoothed altitude stream
this.cloneStream(activity, data_grade_1.DataGrade.type, data_grade_smooth_1.DataGradeSmooth.type);
// Smooth grade computed stream
this.shapeStream(data_grade_smooth_1.DataGradeSmooth.type, activity, squashedGradeData => {
// Grade stream
const GRADE_KALMAN_SMOOTHING = {
R: 0.01, // Grade model is stable
Q: 0.5 // Grade measurement error which can be expected
};
// Predict proper grade values
const kf = new kalmanjs_1.default(GRADE_KALMAN_SMOOTHING);
return squashedGradeData.map(v => (v === null ? null : kf.filter(v)));
});
}
}
// Get a grade adjusted speed (applies to running and cycling-like activity groups)
const activityGroup = activity_types_1.ActivityTypesHelper.getActivityGroupForActivityType(activity.type);
if ((activityGroup === activity_types_1.ActivityTypeGroups.RunningGroup ||
activityGroup === activity_types_1.ActivityTypeGroups.TrailRunningGroup ||
activityGroup === activity_types_1.ActivityTypeGroups.CyclingGroup ||
activityGroup === activity_types_1.ActivityTypeGroups.MountainBikingGroup) &&
!activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type) &&
this.canGenerateDerivedStream(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
const speedStreamData = activity.getStreamData(data_speed_1.DataSpeed.type);
const gradeStreamData = activity.getStreamData(data_grade_smooth_1.DataGradeSmooth.type);
const gradeAdjustedSpeedData = speedStreamData.map((value, index) => value === null ? null : this.round(grade_calculator_1.GradeCalculator.estimateAdjustedSpeed(value, gradeStreamData[index] || 0), 2));
// Ensure first grade adjusted pace dont start with 0 (it's common) meaning infinity
if (!gradeAdjustedSpeedData[0]) {
const firstKnownValue = gradeAdjustedSpeedData.find(v => v > 0);
gradeAdjustedSpeedData[0] = firstKnownValue ? firstKnownValue : gradeAdjustedSpeedData[0];
}
activity.addStream(new stream_1.Stream(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type, gradeAdjustedSpeedData));
}
if (!activity.hasStreamData(data_power_right_1.DataPowerRight.type) && this.canGenerateDerivedStream(activity, data_power_right_1.DataPowerRight.type)) {
const rightPowerStream = activity.createStream(data_power_right_1.DataPowerRight.type);
const powerStreamData = activity.getStreamData(data_power_1.DataPower.type);
const rightBalanceStreamData = activity.getStreamData(data_power_balance_right_1.DataPowerBalanceRight.type);
rightPowerStream.setData(rightBalanceStreamData.reduce((accu, streamData, index) => {
const powerStreamDataItem = powerStreamData[index];
if (streamData === null || !powerStreamData || powerStreamDataItem === null) {
return accu;
}
accu[index] = (streamData / 100) * powerStreamDataItem;
return accu;
}, []));
activity.addStream(rightPowerStream);
}
if (!activity.hasStreamData(data_power_left_1.DataPowerLeft.type) && this.canGenerateDerivedStream(activity, data_power_left_1.DataPowerLeft.type)) {
const leftPowerStream = activity.createStream(data_power_left_1.DataPowerLeft.type);
const powerStreamData = activity.getStreamData(data_power_1.DataPower.type);
const leftBalanceStreamData = activity.getStreamData(data_power_balance_left_1.DataPowerBalanceLeft.type);
leftPowerStream.setData(leftBalanceStreamData.reduce((accu, streamData, index) => {
const powerStreamDataItem = powerStreamData[index];
if (streamData === null || !powerStreamData || powerStreamDataItem === null) {
return accu;
}
accu[index] = (streamData / 100) * powerStreamDataItem;
return accu;
}, []));
activity.addStream(leftPowerStream);
}
// If left stance time stream available, then add the right balance stream too
if (!activity.hasStreamData(data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type) &&
this.canGenerateDerivedStream(activity, data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type)) {
const rightStanceBalanceTimeStream = activity.createStream(data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type);
const leftStanceBalanceTimeStream = activity.getStreamData(data_stance_time_balance_left_1.DataStanceTimeBalanceLeft.type);
const rightStanceBalanceTimeData = leftStanceBalanceTimeStream.map(leftBalance => {
return Number.isFinite(leftBalance) ? 100 - leftBalance : null;
});
rightStanceBalanceTimeStream.setData(rightStanceBalanceTimeData);
activity.addStream(rightStanceBalanceTimeStream);
}
return activity;
}
/**
* Provides squashed stream data through callback for data manipulation.
* Then rebuild the stream based on duration including the missing values (null, Infinity, ...) like the source stream
* @param streamType
* @param activity
* @param shapeStreamData
*/
static shapeStream(streamType, activity, shapeStreamData) {
let streamDataByDuration = activity.getStreamDataByDuration(streamType, true, true);
// Shape data along function param
const streamData = shapeStreamData(streamDataByDuration.map(item => item.value));
// Update streamDataByDuration with shaped data
streamDataByDuration = streamDataByDuration.map((item, index) => {
item.value = streamData[index];
return item;
});
// Rebuild/replace stream with new shaped value
activity.removeStream(streamType);
activity.addStream(activity.createStream(streamType));
const activityStartTime = activity.startDate.getTime();
streamDataByDuration.forEach(item => {
activity.addDataToStream(streamType, new Date(activityStartTime + item.time), item.value);
});
}
static cloneStream(activity, sourceStreamType, targetStreamType) {
const sourceStream = activity.getStream(sourceStreamType);
const targetStream = activity.createStream(targetStreamType);
targetStream.setData(Array.from(sourceStream.getData())); // Shallow copy data to new stream
activity.addStream(targetStream);
}
/**
* Create derived primitive streams which will be needed for others streams & stats computations
* @param activity
*/
static createDerivedStreams(activity) {
var _a, _b, _c;
if (((_c = (_b = (_a = activity.parseOptions) === null || _a === void 0 ? void 0 : _a.streams) === null || _b === void 0 ? void 0 : _b.smooth) === null || _c === void 0 ? void 0 : _c.altitudeSmooth) &&
this.canGenerateDerivedStream(activity, data_altitude_smooth_1.DataAltitudeSmooth.type) &&
!activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type)) {
// Duplicate and create an altitude smooth stream (we want to keep original altitude stream available)
// Activity stats will be computed on the smoothed altitude stream
this.cloneStream(activity, data_altitude_1.DataAltitude.type, data_altitude_smooth_1.DataAltitudeSmooth.type);
// Remove spiky data altitudes
this.shapeStream(data_altitude_smooth_1.DataAltitudeSmooth.type, activity, squashedAltData => {
squashedAltData = (0, helpers_1.medianFilter)(squashedAltData, ALTITUDE_SPIKES_FILTER_WIN); // Remove data spikes
squashedAltData = low_pass_filter_1.LowPassFilter.smooth(squashedAltData); // Remove too high altitude frequencies
return squashedAltData;
});
}
return activity;
}
static canGenerateDerivedStream(activity, streamType) {
return (0, stream_derivation_registry_1.canDeriveStreamType)(streamType, requiredType => activity.hasStreamData(requiredType));
}
/**
* Back and forth fills an activity's stream data so they can be more "tree" like
* It does this for:
*
* [DataAltitude.type,
* DataHeartRate.type,
* DataCadence.type,
* DataDistance.type]
*
* Example
*
* Distance[0, 10, 30, 40, 50,null,60] #null here is legit eg missing record
* Altitude[100, 101, null, 103, null, null, 106]
* Should be
* Altitude[100,101,101,103,103,103,106]
*
* @param activity
*/
static addMissingDataToStreams(activity) {
/**
* This tries to align data with Strava.
* Strava fills HR alti cadence with the last value.
* For Power and temperature it doesn't but keeps nulls.
* However, if you keep nulls for paused portions then strava doens't give back null
* that typically indicates a sensor disconnect I suppose.
*/
const streamTypesToBackAndForthFill = [
data_altitude_1.DataAltitude.type,
data_heart_rate_1.DataHeartRate.type,
data_cadence_1.DataCadence.type,
data_distance_1.DataDistance.type
// DataSpeed.type, @todo should we be backfilling speed?
];
const timeStream = activity.generateTimeStream();
/**
* We do a second pass here and we add missing data on crossing time indexes
* for example:
* Time[0,1,2,3,4,5,7]
* Distance[0, 10, 30, 40, 50,null,60] #null here is legit eg missing record
* Altitude[100, 101, null, 103, null, null, 106]
* Should be
* Altitude[100,101,101,103,103,103,106]
*/
activity
.getAllStreams()
.filter(stream => streamTypesToBackAndForthFill.indexOf(stream.type) !== -1)
.forEach(stream => {
// Find the first sample value
let currentValue = stream.getData(true, true)[0];
// The time stream will always have more length than each stream when not back/forthfilled
const timeStreamData = timeStream.getData();
stream.setData(timeStreamData.reduce((data, time, timeIndex) => {
// If there is no timeslot put whatever was
if (!(0, helpers_1.isNumber)(time)) {
data.push(stream.getData()[timeIndex]);
return data;
}
// We have a time slot here on ... (for the first run, old is the very first next)
// If it's a number set the current , else leave it to old to forth fill
if ((0, helpers_1.isNumber)(stream.getData()[time])) {
currentValue = stream.getData()[time];
}
// Fill the current or old...
data.push(currentValue);
return data;
}, []));
});
/**
* @todo
* Linear fill distance where:
* a) There is not distance but it's not paused
* b) There is no corespoding lat/long but there is distace (aka distance = not trusted)
* About B I am not sure. That is because if there is for example an internal accelerometer
* that reports better this can help with pace and other things. Even for GAP
*/
// Fix activity having broken start lat/lng
// Case: "fixtures/others/broken-start-latlng.fit"
if (activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type)) {
this.shapeStream(data_longitude_degrees_1.DataLongitudeDegrees.type, activity, (squashedData) => {
const firstKnownCoord = squashedData.find(l => l != 0);
if (firstKnownCoord != null) {
let index = 0;
while (squashedData[index] === 0) {
squashedData[index] = firstKnownCoord;
index++;
}
}
return squashedData;
});
}
if (activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type)) {
this.shapeStream(data_latitude_degrees_1.DataLatitudeDegrees.type, activity, (squashedData) => {
const firstKnownCoord = squashedData.find(l => l != 0);
if (firstKnownCoord != null) {
let index = 0;
while (squashedData[index] === 0) {
squashedData[index] = firstKnownCoord;
index++;
}
}
return squashedData;
});
}
}
/**
*
* @param secondsPer100m
* @param avgStrokesPerMin
* @param poolLength
*/
static computeSwimSwolf(secondsPer100m, avgStrokesPerMin, poolLength) {
const minutesPer100m = secondsPer100m / 60;
const avgStrokePer100m = avgStrokesPerMin * minutesPer100m;
const strokesPerMeter = avgStrokePer100m / 100;
const secondsPerMeter = secondsPer100m / 100;
return this.round((secondsPerMeter + strokesPerMeter) * poolLength, 1);
}
/**
* Andrew Coggan weighted power compute method
* 1) starting at the 30s mark, calculate a rolling 30 s average (of the preceding time points, obviously).
* 2) raise all the values obtained in step #1 to the 4th power.
* 3) take the average of all of the values obtained in step #2.
* 4) take the 4th root of the value obtained in step #3.
* (And when you get tired of exporting every file to, e.g., Excel to perform such calculations, help develop a program
* like WKO+ to do the work for you <g>.)
*/
static computeNormalizedPower(powerArray, timeArray) {
const WEIGHTED_WATTS_TIME_BUFFER = 30; // Seconds
const poweredWeightedWatts = [];
let accumulatedTimeInBuffer = 0; // seconds
let wattsInBuffer = [];
for (const [index, current] of timeArray.entries()) {
if (index === 0) {
continue;
}
wattsInBuffer.push(powerArray[index]);
if (accumulatedTimeInBuffer >= WEIGHTED_WATTS_TIME_BUFFER) {
const meanWatts = this.getAverage(wattsInBuffer);
if (Number.isFinite(meanWatts)) {
poweredWeightedWatts.push(Math.pow(meanWatts, 4));
}
// Reset
accumulatedTimeInBuffer = 0;
wattsInBuffer = [];
}
accumulatedTimeInBuffer += current - timeArray[index - 1];
}
return Math.sqrt(Math.sqrt(this.getAverage(poweredWeightedWatts)));
}
static getActivityDataTypeGainOrLoss(activity, streamType, gain, startDate, endDate, minDiff) {
return this.getGainOrLoss(activity.getSquashedStreamData(streamType, startDate, endDate), gain, minDiff);
}
static getActivityDataTypeMinOrMax(activity, streamType, max, startDate, endDate, filterOver) {
const data = activity
.getSquashedStreamData(streamType, startDate, endDate)
.filter(streamData => streamData !== Infinity &&
streamData !== -Infinity &&
(Number.isFinite(filterOver) ? streamData > filterOver : true));
if (max) {
return this.getMax(data);
}
return this.getMin(data);
}
/**
* Generates the stats for an activity
* @todo move to factory with next version
* @param activity
*/
static generateMissingStatsForActivity(activity) {
// If there is no distance or distance for some reason is 0
const activityDistanceStat = activity.getStat(data_distance_1.DataDistance.type);
if (!activityDistanceStat || activityDistanceStat.getValue() === 0) {
let distance = 0;
if (activity.hasStreamData(data_distance_1.DataDistance.type)) {
const distanceData = activity.getSquashedStreamData(data_distance_1.DataDistance.type);
distance = distanceData[distanceData.length - 1] - distanceData[0] || 0;
}
else if (activity.hasStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type) &&
activity.hasStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type)) {
distance = this.calculateTotalDistanceForActivity(activity, activity.startDate, activity.endDate);
}
activity.addStat(new data_distance_1.DataDistance(distance));
}
if (!activity.getStat(data_gnss_distance_1.DataGNSSDistance.type) && activity.hasStreamData(data_gnss_distance_1.DataGNSSDistance.type)) {
activity.addStat(new data_gnss_distance_1.DataGNSSDistance(activity.getSquashedStreamData(data_gnss_distance_1.DataGNSSDistance.type)[activity.getSquashedStreamData(data_gnss_distance_1.DataGNSSDistance.type).length - 1]));
}
// Ascent (altitude gain)
if (!activity_types_1.ActivityTypesHelper.shouldExcludeAscent(activity.type) &&
!activity.getStat(data_ascent_1.DataAscent.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
const gain = this.getActivityDataTypeGain(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type);
if (gain !== null) {
activity.addStat(new data_ascent_1.DataAscent(gain));
}
}
// Descent (altitude loss)
if (!activity_types_1.ActivityTypesHelper.shouldExcludeDescent(activity.type) &&
!activity.getStat(data_descent_1.DataDescent.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
const loss = this.getActivityDataTypeLoss(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type);
if (loss !== null) {
activity.addStat(new data_descent_1.DataDescent(loss));
}
}
// Altitude Max
if (!activity.getStat(data_altitude_max_1.DataAltitudeMax.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
activity.addStat(new data_altitude_max_1.DataAltitudeMax(this.getDataTypeMax(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
}
// Altitude Min
if (!activity.getStat(data_altitude_min_1.DataAltitudeMin.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
activity.addStat(new data_altitude_min_1.DataAltitudeMin(this.getDataTypeMin(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
}
// Altitude Avg
if (!activity.getStat(data_altitude_avg_1.DataAltitudeAvg.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type))) {
activity.addStat(new data_altitude_avg_1.DataAltitudeAvg(this.getDataTypeAvg(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
}
// Altitude start
if (!activity.getStat(data_start_altitude_1.DataStartAltitude.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type)) &&
this.getDataTypeFirst(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)) {
activity.addStat(new data_start_altitude_1.DataStartAltitude(this.getDataTypeFirst(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
}
// Altitude end
if (!activity.getStat(data_end_altitude_1.DataEndAltitude.type) &&
(activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) || activity.hasStreamData(data_altitude_1.DataAltitude.type)) &&
this.getDataTypeLast(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)) {
activity.addStat(new data_end_altitude_1.DataEndAltitude(this.getDataTypeLast(activity, activity.hasStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type) ? data_altitude_smooth_1.DataAltitudeSmooth.type : data_altitude_1.DataAltitude.type)));
}
// Heart Rate Max
if (!activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type) && activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)) {
activity.addStat(new data_heart_rate_max_1.DataHeartRateMax(this.getDataTypeMax(activity, data_heart_rate_1.DataHeartRate.type)));
}
// Heart Rate Min
if (!activity.getStat(data_heart_rate_min_1.DataHeartRateMin.type) && activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)) {
activity.addStat(new data_heart_rate_min_1.DataHeartRateMin(this.getDataTypeMin(activity, data_heart_rate_1.DataHeartRate.type)));
}
// Heart Rate Avg
if (!activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type) && activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)) {
activity.addStat(new data_heart_rate_avg_1.DataHeartRateAvg(this.round(this.getDataTypeAvg(activity, data_heart_rate_1.DataHeartRate.type))));
}
// Cadence Max
if (!activity.getStat(data_cadence_max_1.DataCadenceMax.type) && activity.hasStreamData(data_cadence_1.DataCadence.type)) {
activity.addStat(new data_cadence_max_1.DataCadenceMax(this.getDataTypeMax(activity, data_cadence_1.DataCadence.type)));
}
// Cadence Min
if (!activity.getStat(data_cadence_min_1.DataCadenceMin.type) && activity.hasStreamData(data_cadence_1.DataCadence.type)) {
// Get min cadence except 0. A 0 cadence is not meaningful.
const minCadenceOver = 0;
activity.addStat(new data_cadence_min_1.DataCadenceMin(this.getDataTypeMin(activity, data_cadence_1.DataCadence.type, undefined, undefined, minCadenceOver)));
}
// Cadence Avg
if (!activity.getStat(data_cadence_avg_1.DataCadenceAvg.type) && activity.hasStreamData(data_cadence_1.DataCadence.type)) {
// Get avg cadence except 0 values. Platforms like garmin/strava don't include 0 cadences in their averages.
const avgCadenceOver = 0;
const avgCadence = this.getDataTypeAvg(activity, data_cadence_1.DataCadence.type, undefined, undefined, avgCadenceOver);
activity.addStat(new data_cadence_avg_1.DataCadenceAvg(this.round(avgCadence)));
}
// Speed Max
if (!activity.getStat(data_speed_max_1.DataSpeedMax.type) && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
activity.addStat(new data_speed_max_1.DataSpeedMax(this.getDataTypeMax(activity, data_speed_1.DataSpeed.type)));
}
// Speed Min
if (!activity.getStat(data_speed_min_1.DataSpeedMin.type) && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
activity.addStat(new data_speed_min_1.DataSpeedMin(this.getDataTypeMin(activity, data_speed_1.DataSpeed.type)));
}
// Speed Avg
if (!activity.getStat(data_speed_avg_1.DataSpeedAvg.type) && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
activity.addStat(new data_speed_avg_1.DataSpeedAvg(this.getDataTypeAvg(activity, data_speed_1.DataSpeed.type)));
}
// Effort Pace Max
if (!activity.getStat(data_effort_pace_max_1.DataEffortPaceMax.type) && activity.hasStreamData(data_effort_pace_1.DataEffortPace.type)) {
activity.addStat(new data_effort_pace_max_1.DataEffortPaceMax(this.getDataTypeMax(activity, data_effort_pace_1.DataEffortPace.type)));
}
// Effort Pace Min
if (!activity.getStat(data_effort_pace_min_1.DataEffortPaceMin.type) && activity.hasStreamData(data_effort_pace_1.DataEffortPace.type)) {
activity.addStat(new data_effort_pace_min_1.DataEffortPaceMin(this.getDataTypeMin(activity, data_effort_pace_1.DataEffortPace.type)));
}
// Effort Pace Avg
if (!activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvg.type) && activity.hasStreamData(data_effort_pace_1.DataEffortPace.type)) {
activity.addStat(new data_effort_pace_avg_1.DataEffortPaceAvg(this.getDataTypeAvg(activity, data_effort_pace_1.DataEffortPace.type)));
}
// Grade Adjusted Speed Max
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type) && activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax(this.getDataTypeMax(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)));
}
// Grade Adjusted Speed Min
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type) && activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin(this.getDataTypeMin(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)));
}
// Grade Adjusted Speed Avg
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type) && activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg(this.getDataTypeAvg(activity, data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)));
}
// Grade Max/Min/Avg (prefer smoothed grade when available)
const gradeStreamType = activity.hasStreamData(data_grade_smooth_1.DataGradeSmooth.type)
? data_grade_smooth_1.DataGradeSmooth.type
: activity.hasStreamData(data_grade_1.DataGrade.type)
? data_grade_1.DataGrade.type
: null;
if (gradeStreamType) {
if (!activity.getStat(data_grade_max_1.DataGradeMax.type)) {
activity.addStat(new data_grade_max_1.DataGradeMax(this.getDataTypeMax(activity, gradeStreamType)));
}
if (!activity.getStat(data_grade_min_1.DataGradeMin.type)) {
activity.addStat(new data_grade_min_1.DataGradeMin(this.getDataTypeMin(activity, gradeStreamType)));
}
if (!activity.getStat(data_grade_avg_1.DataGradeAvg.type)) {
activity.addStat(new data_grade_avg_1.DataGradeAvg(this.getDataTypeAvg(activity, gradeStreamType)));
}
}
// Vertical Speed Max
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type) && activity.hasStreamData(data_vertical_speed_1.DataVerticalSpeed.type)) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMax(this.getDataTypeMax(activity, data_vertical_speed_1.DataVerticalSpeed.type)));
}
// Vertical Speed Min
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type) && activity.hasStreamData(data_vertical_speed_1.DataVerticalSpeed.type)) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMin(this.getDataTypeMin(activity, data_vertical_speed_1.DataVerticalSpeed.type)));
}
// Vertical Speed Avg
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type) && activity.hasStreamData(data_vertical_speed_1.DataVerticalSpeed.type)) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvg(this.getDataTypeAvg(activity, data_vertical_speed_1.DataVerticalSpeed.type)));
}
// Power Max
if (!activity.getStat(data_power_max_1.DataPowerMax.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
activity.addStat(new data_power_max_1.DataPowerMax(this.getDataTypeMax(activity, data_power_1.DataPower.type)));
}
// Power Min
if (!activity.getStat(data_power_min_1.DataPowerMin.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
activity.addStat(new data_power_min_1.DataPowerMin(this.getDataTypeMin(activity, data_power_1.DataPower.type)));
}
// Power AVG
if (!activity.getStat(data_power_avg_1.DataPowerAvg.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
activity.addStat(new data_power_avg_1.DataPowerAvg(this.getDataTypeAvg(activity, data_power_1.DataPower.type)));
}
// Power-to-Weight Ratio (W/kg) from average power and body weight
if (!activity.getStat(data_power_watts_per_kg_1.DataPowerWattsPerKg.type)) {
const powerWattsPerKg = this.resolvePowerWattsPerKg(activity);
if (powerWattsPerKg !== null) {
activity.addStat(new data_power_watts_per_kg_1.DataPowerWattsPerKg(powerWattsPerKg));
}
}
// Power Normalized
if (!activity.getStat(data_power_normalized_1.DataPowerNormalized.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
const powerDurationStream = activity.getStreamDataByDuration(data_power_1.DataPower.type, true, true);
const timeStream = powerDurationStream.map(item => item.time / 1000);
const powerStream = powerDurationStream.map(item => item.value);
const normalizedPower = this.computeNormalizedPower(powerStream, timeStream);
activity.addStat(new data_power_normalized_1.DataPowerNormalized(normalizedPower));
}
// Power Curve
if (!activity.getStat(data_power_curve_1.DataPowerCurve.type) && activity.hasStreamData(data_power_1.DataPower.type)) {
const powerCurve = this.calculateMeanMaxPower(activity);
if (powerCurve) {
activity.addStat(powerCurve);
activity.powerCurve = powerCurve;
}
}
// FTP (0.95 * best 20 minute power)
if (!activity.getStat(data_ftp_1.DataFTP.type)) {
const ftp = this.calculateFTP(activity);
if (ftp) {
activity.addStat(ftp);
}
}
// Intensity Factor (IF = Normalized Power / FTP)
if (!activity.getStat(data_power_intensity_factor_1.DataPowerIntensityFactor.type)) {
const intensityFactor = this.calculatePowerIntensityFactor(activity);
if (intensityFactor) {
activity.addStat(intensityFactor);
}
}
// Training Stress Score (priority: POWER -> HR -> PACE/SWIM_PACE -> MET)
this.generateTrainingStressScore(activity);
// Critical Power & W'
// calculateCriticalPowerAndWPrime requires DataPowerCurve to be present (which we just added if missing)
if ((!activity.getStat(data_critical_power_1.DataCriticalPower.type) || !activity.getStat(data_w_prime_1.DataWPrime.type)) &&
activity.getStat(data_power_curve_1.DataPowerCurve.type)) {
const cpWPrime = this.calculateCriticalPowerAndWPrime(activity);
if (cpWPrime) {
activity.addStat(cpWPrime.cp);
activity.addStat(cpWPrime.wPrime);
}
}
// Air AirPower Max
if (!activity.getStat(data_air_power_max_1.DataAirPowerMax.type) && activity.hasStreamData(data_air_power_1.DataAirPower.type)) {
activity.addStat(new data_air_power_max_1.DataAirPowerMax(this.getDataTypeMax(activity, data_air_power_1.DataAirPower.type)));
}
// Air AirPower Min
if (!activity.getStat(data_air_power_min_1.DataAirPowerMin.type) && activity.hasStreamData(data_air_power_1.DataAirPower.type)) {
activity.addStat(new data_air_power_min_1.DataAirPowerMin(this.getDataTypeMin(activity, data_air_power_1.DataAirPower.type)));
}
// Air AirPower AVG
if (!activity.getStat(data_air_power_avg_1.DataAirPowerAvg.type) && activity.hasStreamData(data_air_power_1.DataAirPower.type)) {
activity.addStat(new data_air_power_avg_1.DataAirPowerAvg(this.getDataTypeAvg(activity, data_air_power_1.DataAirPower.type)));
}
// Absolute Pressure Max
if (!activity.getStat(data_absolute_pressure_max_1.DataAbsolutePressureMax.type) && activity.hasStreamData(data_absolute_pressure_1.DataAbsolutePressure.type)) {
activity.addStat(new data_absolute_pressure_max_1.DataAbsolutePressureMax(this.getDataTypeMax(activity, data_absolute_pressure_1.DataAbsolutePressure.type)));
}
// Absolute Pressure Min
if (!activity.getStat(data_absolute_pressure_min_1.DataAbsolutePressureMin.type) && activity.hasStreamData(data_absolute_pressure_1.DataAbsolutePressure.type)) {
activity.addStat(new data_absolute_pressure_min_1.DataAbsolutePressureMin(this.getDataTypeMin(activity, data_absolute_pressure_1.DataAbsolutePressure.type)));
}
// Absolute Pressure Avg
if (!activity.getStat(data_absolute_pressure_avg_1.DataAbsolutePressureAvg.type) && activity.hasStreamData(data_absolute_pressure_1.DataAbsolutePressure.type)) {
activity.addStat(new data_absolute_pressure_avg_1.DataAbsolutePressureAvg(this.getDataTypeAvg(activity, data_absolute_pressure_1.DataAbsolutePressure.type)));
}
// EVPE Max
if (!activity.getStat(data_evpe_max_1.DataEVPEMax.type) && activity.hasStreamData(data_evpe_1.DataEVPE.type)) {
activity.addStat(new data_evpe_max_1.DataEVPEMax(this.getDataTypeMax(activity, data_evpe_1.DataEVPE.type)));
}
// EVPE Min
if (!activity.getStat(data_evpe_min_1.DataEVPEMin.type) && activity.hasStreamData(data_evpe_1.DataEVPE.type)) {
activity.addStat(new data_evpe_min_1.DataEVPEMin(this.getDataTypeMin(activity, data_evpe_1.DataEVPE.type)));
}
// EVPE Avg
if (!activity.getStat(data_evpe_avg_1.DataEVPEAvg.type) && activity.hasStreamData(data_evpe_1.DataEVPE.type)) {
activity.addStat(new data_evpe_avg_1.DataEVPEAvg(this.getDataTypeAvg(activity, data_evpe_1.DataEVPE.type)));
}
// EHPE Max
if (!activity.getStat(data_ehpe_max_1.DataEHPEMax.type) && activity.hasStreamData(data_ehpe_1.DataEHPE.type)) {
activity.addStat(new data_ehpe_max_1.DataEHPEMax(this.getDataTypeMax(activity, data_ehpe_1.DataEHPE.type)));
}
// EHPE Min
if (!activity.getStat(data_ehpe_min_1.DataEHPEMin.type) && activity.hasStreamData(data_ehpe_1.DataEHPE.type)) {
activity.addStat(new data_ehpe_min_1.DataEHPEMin(this.getDataTypeMin(activity, data_ehpe_1.DataEHPE.type)));
}
// EHPE Avg
if (!activity.getStat(data_ehpe_avg_1.DataEHPEAvg.type) && activity.hasStreamData(data_ehpe_1.DataEHPE.type)) {
activity.addStat(new data_ehpe_avg_1.DataEHPEAvg(this.getDataTypeAvg(activity, data_ehpe_1.DataEHPE.type)));
}
// Satellite 5 Best SNR Max
if (!activity.getStat(data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax.type) && activity.hasStreamData(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)) {
activity.addStat(new data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax(this.getDataTypeMax(activity, data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)));
}
// Satellite 5 Best SNR Min
if (!activity.getStat(data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin.type) && activity.hasStreamData(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)) {
activity.addStat(new data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin(this.getDataTypeMin(activity, data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)));
}
// Satellite 5 Best SNR Avg
if (!activity.getStat(data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg.type) && activity.hasStreamData(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)) {
activity.addStat(new data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg(this.getDataTypeAvg(activity, data_satellite_5_best_snr_1.DataSatellite5BestSNR.type)));
}
// Number of Satellites Max
if (!activity.getStat(data_number_of_satellites_max_1.DataNumberOfSatellitesMax.type) && activity.hasStreamData(data_number_of_satellites_1.DataNumberOfSatellites.type)) {
activity.addStat(new data_number_of_satellites_max_1.DataNumberOfSatellitesMax(this.getDataTypeMax(activity, data_number_of_satellites_1.DataNumberOfSatellites.type)));
}
// Number of Satellites Min
if (!activity.getStat(data_number_of_satellites_min_1.DataNumberOfSatellitesMin.type) && activity.hasStreamData(data_number_of_satellites_1.DataNumberOfSatellites.type)) {
activity.addStat(new data_number_of_satellites_min_1.DataNumberOfSatellitesMin(this.getDataTypeMin(activity, data_number_of_satellites_1.DataNumberOfSatellites.type)));
}
// Number of Satellites Avg
if (!activity.getStat(data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg.type) && activity.hasStreamData(data_number_of_satellites_1.DataNumberOfSatellites.type)) {
activity.addStat(new data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg(this.getDataTypeAvg(activity, data_number_of_satellites_1.DataNumberOfSatellites.type)));
}
// Temperature Max
if (!activity.getStat(data_temperature_max_1.DataTemperatureMax.type) && activity.hasStreamData(data_temperature_1.DataTemperature.type)) {
activity.addStat(new data_temperature_max_1.DataTemperatureMax(this.getDataTypeMax(activity, data_temperature_1.DataTemperature.type)));
}
// Temperature Min
if (!activity.getStat(data_temperature_min_1.DataTemperatureMin.type) && activity.hasStreamData(data_temperature_1.DataTemperature.type)) {
activity.addStat(new data_temperature_min_1.DataTemperatureMin(this.getDataTypeMin(activity, data_temperature_1.DataTemperature.type)));
}
// Temperature Avg
if (!activity.getStat(data_temperature_avg_1.DataTemperatureAvg.type) && activity.hasStreamData(data_temperature_1.DataTemperature.type)) {
activity.addStat(new data_temperature_avg_1.DataTemperatureAvg(this.getDataTypeAvg(activity, data_temperature_1.DataTemperature.type)));
}
// Battery Consumption Avg
if (!activity.getStat(data_battery_consumption_1.DataBatteryConsumption.type) && activity.hasStreamData(data_battery_charge_1.DataBatteryCharge.type)) {
activity.addStat(new data_battery_consumption_1.DataBatteryConsumption(this.getDataTypeMinToMaxDifference(activity, data_battery_charge_1.DataBatteryCharge.type)));
}
// Battery Life Estimation based on Consumption
if (!activity.getStat(data_battery_life_estimation_1.DataBatteryLifeEstimation.type)) {
const consumption = activity.getStat(data_battery_consumption_1.DataBatteryConsumption.type);
if (consumption && consumption.getValue()) {
activity.addStat(new data_battery_life_estimation_1.DataBatteryLifeEstimation(Number(((+activity.endDate - +activity.startDate) / 1000) * 100) / Number(consumption.getValue())));
}
}
// Start and end position
if ((!activity.getStat(data_start_position_1.DataStartPosition.type) || !activity.getStat(data_end_position_1.DataEndPosition.type)) &&
activity.hasPositionData()) {
const activityPositionData = activity.getPositionData().filter(data => data !== null);
const startPosition = activityPositionData[0];
const endPosition = activityPositionData[activityPositionData.length - 1];
if (startPosition && !activity.getStat(data_start_position_1.DataStartPosition.type)) {
activity.addStat(new data_start_position_1.DataStartPosition(startPosition));
}
if (endPosition && !activity.getStat(data_end_position_1.DataEndPosition.type)) {
activity.addStat(new data_end_position_1.DataEndPosition(endPosition));
}
}
this.addLeftRightBalanceStatsFromStreams(activity, data_power_balance_left_1.DataPowerBalanceLeft, data_power_balance_right_1.DataPowerBalanceRight);
this.addLeftRightBalanceStatsFromStreams(activity, data_ground_contact_time_balance_left_1.DataGroundContactTimeBalanceLeft, data_ground_contact_time_balance_right_1.DataGroundContactTimeBalanceRight);
this.addLeftRightBalanceStatsFromStreams(activity, data_running_dynamics_balance_1.DataVerticalOscillationBalanceLeft, data_running_dynamics_balance_1.DataVerticalOscillationBalanceRight);
this.addLeftRightBalanceStatsFromStreams(activity, data_running_dynamics_balance_1.DataLegSpringStiffnessBalanceLeft, data_running_dynamics_balance_1.DataLegSpringStiffnessBalanceRight);
this.addLeftRightBalanceStatsFromStreams(activity, data_running_dynamics_balance_1.DataImpactLoadingRateBalanceLeft, data_running_dynamics_balance_1.DataImpactLoadingRateBalanceRight);
// Backward compatibility for Stance Time Balance
this.addLeftRightBalanceStatsFromStreams(activity, data_stance_time_balance_left_1.DataStanceTimeBalanceLeft, data_stance_time_balance_right_1.DataStanceTimeBalanceRight);
// Ground Contact Time
if (!activity.getStat(data_ground_contact_time_max_1.DataGroundContactTimeMax.type) && activity.hasStreamData(data_ground_contact_time_1.DataGroundContactTime.type)) {
activity.addStat(new data_ground_contact_time_max_1.DataGroundContactTimeMax(this.getDataTypeMax(activity, data_ground_contact_time_1.DataGroundContactTime.type)));
}
if (!activity.getStat(data_ground_contact_time_min_1.DataGroundContactTimeMin.type) && activity.hasStreamData(data_ground_contact_time_1.DataGroundContactTime.type)) {
activity.addStat(new data_ground_contact_time_min_1.DataGroundContactTimeMin(this.getDataTypeMin(activity, data_ground_contact_time_1.DataGroundContactTime.type)));
}
if (!activity.getStat(data_ground_contact_time_avg_1.DataGroundContactTimeAvg.type) && activity.hasStreamData(data_ground_contact_time_1.DataGroundContactTime.type)) {
activity.addStat(new data_ground_contact_time_avg_1.DataGroundContactTimeAvg(this.getDataTypeAvg(activity, data_ground_contact_time_1.DataGroundContactTime.type)));
}
// Leg Stiffness
if (!activity.getStat(data_leg_stiffness_max_1.DataLegStiffnessMax.type) && activity.hasStreamData(data_leg_stiffness_1.DataLegStiffness.type)) {
activity.addStat(new data_leg_stiffness_max_1.DataLegStiffnessMax(this.getDataTypeMax(activity, data_leg_stiffness_1.DataLegStiffness.type)));
}
if (!activity.getStat(data_leg_stiffness_min_1.DataLegStiffnessMin.type) && activity.hasStreamData(data_leg_stiffness_1.DataLegStiffness.type)) {
activity.addStat(new data_leg_stiffness_min_1.DataLegStiffnessMin(this.getDataTypeMin(activity, data_leg_stiffness_1.DataLegStiffness.type)));
}
if (!activity.getStat(data_leg_stiffness_avg_1.DataLegStiffnessAvg.type) && activity.hasStreamData(data_leg_stiffness_1.DataLegStiffness.type)) {
activity.addStat(new data_leg_stiffness_avg_1.DataLegStiffnessAvg(this.getDataTypeAvg(activity, data_leg_stiffness_1.DataLegStiffness.type)));
}
// Vertical Oscillation
if (!activity.getStat(data_vertical_oscillation_max_1.DataVerticalOscillationMax.type) && activity.hasStreamData(data_vertical_oscillation_1.DataVerticalOscillation.type)) {
activity.addStat(new data_vertical_oscillation_max_1.DataVerticalOscillationMax(this.getDataTypeMax(activity, data_vertical_oscillation_1.DataVerticalOscillation.type)));
}
if (!activity.getStat(data_vertical_oscillation_min_1.DataVerticalOscillationMin.type) && activity.hasStreamData(data_vertical_oscillation_1.DataVerticalOscillation.type)) {
activity.addStat(new data_vertical_oscillation_min_1.DataVerticalOscillationMin(this.getDataTypeMin(activity, data_vertical_oscillation_1.DataVerticalOscillation.type)));
}
if (!activity.getStat(data_vertical_oscillation_avg_1.DataVerticalOscillationAvg.type) && activity.hasStreamData(data_vertical_oscillation_1.DataVerticalOscillation.type)) {
activity.addStat(new data_vertical_oscillation_avg_1.DataVerticalOscillationAvg(this.getDataTypeAvg(activity, data_vertical_oscillation_1.DataVerticalOscillation.type)));
}
// Vertical Ratio
if (!activity.getStat(data_vertical_ratio_max_1.DataVerticalRatioMax.type) && activity.hasStreamData(data_vertical_ratio_1.DataVerticalRatio.type)) {
activity.addStat(new data_vertical_ratio_max_1.DataVerticalRatioMax(this.getDataTypeMax(activity, data_vertical_ratio_1.DataVerticalRatio.type)));
}
if (!activity.getStat(data_vertical_ratio_min_1.DataVerticalRatioMin.type) && activity.hasStreamData(data_vertical_ratio_1.DataVerticalRatio.type)) {
activity.addStat(new data_vertical_ratio_min_1.DataVerticalRatioMin(this.getDataTypeMin(activity, data_vertical_ratio_1.DataVerticalRatio.type)));
}
if (!activity.getStat(data_vertical_ratio_avg_1.DataVerticalRatioAvg.type) && activity.hasStreamData(data_vertical_ratio_1.DataVerticalRatio.type)) {
activity.addStat(new data_vertical_ratio_avg_1.DataVerticalRatioAvg(this.getDataTypeAvg(activity, data_vertical_ratio_1.DataVerticalRatio.type)));
}
// Stamina
if (!activity.getStat(data_stamina_1.DataStaminaMax.type) && activity.hasStreamData(data_stamina_1.DataStamina.type)) {
activity.addStat(new data_stamina_1.DataStaminaMax(this.getDataTypeMax(activity, data_stamina_1.DataStamina.type)));
}
if (!activity.getStat(data_stamina_1.DataStaminaMin.type) && activity.hasStreamData(data_stamina_1.DataStamina.type)) {
activity.addStat(new data_stamina_1.DataStaminaMin(this.getDataTypeMin(activity, data_stamina_1.DataStamina.type)));
}
if (!activity.getStat(data_stamina_1.DataStaminaAvg.type) && activity.hasStreamData(data_stamina_1.DataStamina.type)) {
activity.addStat(new data_stamina_1.DataStaminaAvg(this.getDataTypeAvg(activity, data_stamina_1.DataStamina.type)));
}
// Potential Stamina
if (!activity.getStat(data_stamina_1.DataPotentialStaminaMax.type) && activity.hasStreamData(data_stamina_1.DataPotentialStamina.type)) {
activity.addStat(new data_stamina_1.DataPotentialStaminaMax(this.getDataTypeMax(activity, data_stamina_1.DataPotentialStamina.type)));
}
if (!activity.getStat(data_stamina_1.DataPotentialStaminaMin.type) && activity.hasStreamData(data_stamina_1.DataPotentialStamina.type)) {
activity.addStat(new data_stamina_1.DataPotentialStaminaMin(this.getDataTypeMin(activity, data_stamina_1.DataPotentialStamina.type)));
}
if (!activity.getStat(data_stamina_1.DataPotentialStaminaAvg.type) && activity.hasStreamData(data_stamina_1.DataPotentialStamina.type)) {
activity.addStat(new data_stamina_1.DataPotentialStaminaAvg(this.getDataTypeAvg(activity, data_stamina_1.DataPotentialStamina.type)));
}
this.addMissingJumpStatsFromEvents(activity);
}
static generateMissingSpeedDerivedStatsForActivity(activity) {
const getFiniteStatValue = (type) => {
const stat = activity.getStat(type);
if (!stat) {
return null;
}
const value = Number(stat.getValue());
return Number.isFinite(value) ? value : null;
};
const speedMax = getFiniteStatValue(data_speed_max_1.DataSpeedMax.type);
const speedMin = getFiniteStatValue(data_speed_min_1.DataSpeedMin.type);
const speedAvg = getFiniteStatValue(data_speed_avg_1.DataSpeedAvg.type);
// Pace is inverse of speed:
// min pace <- max speed, max pace <- min speed.
if (speedMin !== null && !activity.getStat(data_pace_max_1.DataPaceMax.type)) {
activity.addStat(new data_pace_max_1.DataPaceMax((0, helpers_1.convertSpeedToPace)(speedMin)));
}
if (speedMax !== null && !activity.getStat(data_pace_min_1.DataPaceMin.type)) {
activity.addStat(new data_pace_min_1.DataPaceMin((0, helpers_1.convertSpeedToPace)(speedMax)));
}
if (speedAvg !== null && !activity.getStat(data_pace_avg_1.DataPaceAvg.type)) {
activity.addStat(new data_pace_avg_1.DataPaceAvg((0, helpers_1.convertSpeedToPace)(speedAvg)));
}
const gradeAdjustedSpeedMax = getFiniteStatValue(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
const gradeAdjustedSpeedMin = getFiniteStatValue(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
const gradeAdjustedSpeedAvg = getFiniteStatValue(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
// GAP pace follows the same inverse rule as pace.
if (gradeAdjustedSpeedMin !== null && !activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type)) {
const targetAdjustedSpeed = speedMin !== null ? Math.min(speedMin, gradeAdjustedSpeedMin) : gradeAdjustedSpeedMin;
activity.addStat(new data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax((0, helpers_1.convertSpeedToPace)(targetAdjustedSpeed)));
}
if (gradeAdjustedSpeedMax !== null && !activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type)) {
const targetAdjustedSpeed = speedMax !== null ? Math.max(speedMax, gradeAdjustedSpeedMax) : gradeAdjustedSpeedMax;
activity.addStat(new data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin((0, helpers_1.convertSpeedToPace)(targetAdjustedSpeed)));
}
if (gradeAdjustedSpeedAvg !== null && !activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type)) {
const targetAdjustedSpeed = speedAvg !== null ? Math.max(speedAvg, gradeAdjustedSpeedAvg) : gradeAdjustedSpeedAvg;
activity.addStat(new data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg((0, helpers_1.convertSpeedToPace)(targetAdjustedSpeed)));
}
// Swim pace is also inverse of speed.
if (speedMin !== null && !activity.getStat(data_swim_pace_max_1.DataSwimPaceMax.type)) {
activity.addStat(new data_swim_pace_max_1.DataSwimPaceMax((0, helpers_1.convertSpeedToSwimPace)(speedMin)));
}
if (speedMax !== null && !activity.getStat(data_swim_pace_min_1.DataSwimPaceMin.type)) {
activity.addStat(new data_swim_pace_min_1.DataSwimPaceMin((0, helpers_1.convertSpeedToSwimPace)(speedMax)));
}
if (speedAvg !== null && !activity.getStat(data_swim_pace_avg_1.DataSwimPaceAvg.type)) {
activity.addStat(new data_swim_pace_avg_1.DataSwimPaceAvg((0, helpers_1.convertSpeedToSwimPace)(speedAvg)));
}
}
// @todo move to factory
static generateMissingUnitStatsForActivity(activity) {
var _a, _b, _c;
// Pace
if (!activity.getStat(data_pace_max_1.DataPaceMaxMinutesPerMile.type)) {
const paceMax = activity.getStat(data_pace_max_1.DataPaceMax.type);
if (paceMax) {
activity.addStat(new data_pace_max_1.DataPaceMaxMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(paceMax.getValue())));
}
}
if (!activity.getStat(data_pace_min_1.DataPaceMinMinutesPerMile.type)) {
const paceMin = activity.getStat(data_pace_min_1.DataPaceMin.type);
if (paceMin) {
activity.addStat(new data_pace_min_1.DataPaceMinMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(paceMin.getValue())));
}
}
if (!activity.getStat(data_pace_avg_1.DataPaceAvgMinutesPerMile.type)) {
const paceAvg = activity.getStat(data_pace_avg_1.DataPaceAvg.type);
if (paceAvg) {
activity.addStat(new data_pace_avg_1.DataPaceAvgMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(paceAvg.getValue())));
}
}
// Effort Pace
if (!activity.getStat(data_effort_pace_max_1.DataEffortPaceMaxMinutesPerMile.type)) {
const effortPaceMax = activity.getStat(data_effort_pace_max_1.DataEffortPaceMax.type);
if (effortPaceMax) {
activity.addStat(new data_effort_pace_max_1.DataEffortPaceMaxMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(effortPaceMax.getValue())));
}
}
if (!activity.getStat(data_effort_pace_min_1.DataEffortPaceMinMinutesPerMile.type)) {
const effortPaceMin = activity.getStat(data_effort_pace_min_1.DataEffortPaceMin.type);
if (effortPaceMin) {
activity.addStat(new data_effort_pace_min_1.DataEffortPaceMinMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(effortPaceMin.getValue())));
}
}
if (!activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvgMinutesPerMile.type)) {
const effortPaceAvg = activity.getStat(data_effort_pace_avg_1.DataEffortPaceAvg.type);
if (effortPaceAvg) {
activity.addStat(new data_effort_pace_avg_1.DataEffortPaceAvgMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(effortPaceAvg.getValue())));
}
}
// Grade Adjusted Pace
if (!activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMaxMinutesPerMile.type)) {
const gradeAdjustedPaceMax = activity.getStat(data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type);
if (gradeAdjustedPaceMax) {
activity.addStat(new data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMaxMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(gradeAdjustedPaceMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMinMinutesPerMile.type)) {
const gradeAdjustedPaceMin = activity.getStat(data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type);
if (gradeAdjustedPaceMin) {
activity.addStat(new data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMinMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(gradeAdjustedPaceMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvgMinutesPerMile.type)) {
const gradeAdjustedPaceAvg = activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type);
if (gradeAdjustedPaceAvg) {
activity.addStat(new data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvgMinutesPerMile((0, helpers_1.convertPaceToPaceInMinutesPerMile)(gradeAdjustedPaceAvg.getValue())));
}
}
// Swim Pace
if (!activity.getStat(data_swim_pace_max_1.DataSwimPaceMaxMinutesPer100Yard.type)) {
const swimPaceMax = activity.getStat(data_swim_pace_max_1.DataSwimPaceMax.type);
if (swimPaceMax) {
activity.addStat(new data_swim_pace_max_1.DataSwimPaceMaxMinutesPer100Yard((0, helpers_1.convertSwimPaceToSwimPacePer100Yard)(swimPaceMax.getValue())));
}
}
if (!activity.getStat(data_swim_pace_min_1.DataSwimPaceMinMinutesPer100Yard.type)) {
const swimPaceMin = activity.getStat(data_swim_pace_min_1.DataSwimPaceMin.type);
if (swimPaceMin) {
activity.addStat(new data_swim_pace_min_1.DataSwimPaceMinMinutesPer100Yard((0, helpers_1.convertSwimPaceToSwimPacePer100Yard)(swimPaceMin.getValue())));
}
}
if (!activity.getStat(data_swim_pace_avg_1.DataSwimPaceAvgMinutesPer100Yard.type)) {
const swimPaceAvg = activity.getStat(data_pace_avg_1.DataPaceAvg.type);
if (swimPaceAvg) {
activity.addStat(new data_swim_pace_avg_1.DataSwimPaceAvgMinutesPer100Yard((0, helpers_1.convertSwimPaceToSwimPacePer100Yard)(swimPaceAvg.getValue())));
}
}
// Speed
if (!activity.getStat(data_speed_max_1.DataSpeedMaxKilometersPerHour.type)) {
const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (speedMax) {
activity.addStat(new data_speed_max_1.DataSpeedMaxKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMax.getValue())));
}
}
if (!activity.getStat(data_speed_max_1.DataSpeedMaxMilesPerHour.type)) {
const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (speedMax) {
activity.addStat(new data_speed_max_1.DataSpeedMaxMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMax.getValue())));
}
}
if (!activity.getStat(data_speed_max_1.DataSpeedMaxFeetPerSecond.type)) {
const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (speedMax) {
activity.addStat(new data_speed_max_1.DataSpeedMaxFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMax.getValue())));
}
}
if (!activity.getStat(data_speed_max_1.DataSpeedMaxFeetPerMinute.type)) {
const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (speedMax) {
activity.addStat(new data_speed_max_1.DataSpeedMaxFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMax.getValue())));
}
}
if (!activity.getStat(data_speed_max_1.DataSpeedMaxMetersPerMinute.type)) {
const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (speedMax) {
activity.addStat(new data_speed_max_1.DataSpeedMaxMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMax.getValue())));
}
}
if (!activity.getStat(data_speed_max_1.DataSpeedMaxKnots.type)) {
const speedMax = activity.getStat(data_speed_max_1.DataSpeedMax.type);
if (speedMax) {
activity.addStat(new data_speed_max_1.DataSpeedMaxKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMax.getValue())));
}
}
if (!activity.getStat(data_speed_min_1.DataSpeedMinKilometersPerHour.type)) {
const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (speedMin) {
activity.addStat(new data_speed_min_1.DataSpeedMinKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMin.getValue())));
}
}
if (!activity.getStat(data_speed_min_1.DataSpeedMinMilesPerHour.type)) {
const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (speedMin) {
activity.addStat(new data_speed_min_1.DataSpeedMinMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMin.getValue())));
}
}
if (!activity.getStat(data_speed_min_1.DataSpeedMinFeetPerSecond.type)) {
const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (speedMin) {
activity.addStat(new data_speed_min_1.DataSpeedMinFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMin.getValue())));
}
}
if (!activity.getStat(data_speed_min_1.DataSpeedMinFeetPerMinute.type)) {
const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (speedMin) {
activity.addStat(new data_speed_min_1.DataSpeedMinFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMin.getValue())));
}
}
if (!activity.getStat(data_speed_min_1.DataSpeedMinMetersPerMinute.type)) {
const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (speedMin) {
activity.addStat(new data_speed_min_1.DataSpeedMinMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMin.getValue())));
}
}
if (!activity.getStat(data_speed_min_1.DataSpeedMinKnots.type)) {
const speedMin = activity.getStat(data_speed_min_1.DataSpeedMin.type);
if (speedMin) {
activity.addStat(new data_speed_min_1.DataSpeedMinKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMin.getValue())));
}
}
if (!activity.getStat(data_speed_avg_1.DataSpeedAvgKilometersPerHour.type)) {
const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_speed_avg_1.DataSpeedAvgKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_speed_avg_1.DataSpeedAvgMilesPerHour.type)) {
const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_speed_avg_1.DataSpeedAvgMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_speed_avg_1.DataSpeedAvgFeetPerSecond.type)) {
const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_speed_avg_1.DataSpeedAvgFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_speed_avg_1.DataSpeedAvgFeetPerMinute.type)) {
const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_speed_avg_1.DataSpeedAvgFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_speed_avg_1.DataSpeedAvgMetersPerMinute.type)) {
const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_speed_avg_1.DataSpeedAvgMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_speed_avg_1.DataSpeedAvgKnots.type)) {
const speedAvg = activity.getStat(data_speed_avg_1.DataSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_speed_avg_1.DataSpeedAvgKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedAvg.getValue())));
}
}
// Grade Adjusted Speed
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKilometersPerHour.type)) {
const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (speedMax) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMilesPerHour.type)) {
const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (speedMax) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerSecond.type)) {
const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (speedMax) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerMinute.type)) {
const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (speedMax) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMetersPerMinute.type)) {
const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (speedMax) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKnots.type)) {
const speedMax = activity.getStat(data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type);
if (speedMax) {
activity.addStat(new data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMaxKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMax.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKilometersPerHour.type)) {
const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (speedMin) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMilesPerHour.type)) {
const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (speedMin) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerSecond.type)) {
const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (speedMin) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerMinute.type)) {
const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (speedMin) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMetersPerMinute.type)) {
const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (speedMin) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKnots.type)) {
const speedMin = activity.getStat(data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type);
if (speedMin) {
activity.addStat(new data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMinKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedMin.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKilometersPerHour.type)) {
const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKilometersPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMilesPerHour.type)) {
const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerSecond.type)) {
const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerMinute.type)) {
const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMetersPerMinute.type)) {
const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(speedAvg.getValue())));
}
}
if (!activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKnots.type)) {
const speedAvg = activity.getStat(data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvg.type);
if (speedAvg) {
activity.addStat(new data_grade_adjusted_speed_avg_1.DataGradeAdjustedSpeedAvgKnots((0, helpers_1.convertSpeedToSpeedInKnots)(speedAvg.getValue())));
}
}
// Vertical speed
const verticalSpeedAvg = activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type);
if (verticalSpeedAvg) {
const verticalSpeedAvgValue = verticalSpeedAvg.getValue();
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeed.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeed(verticalSpeedAvgValue));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedFeetPerSecond.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedAvgValue)));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedMetersPerMinute.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedAvgValue)));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedFeetPerMinute.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedAvgValue)));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedMetersPerHour.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedAvgValue)));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedFeetPerHour.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedAvgValue)));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedKilometerPerHour.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedAvgValue)));
}
if (!activity.getStat(data_vertical_speed_1.DataVerticalSpeedMilesPerHour.type)) {
activity.addStat(new data_vertical_speed_1.DataVerticalSpeedMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedAvgValue)));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerSecond.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerMinute.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerMinute.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerHour.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerHour.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgKilometerPerHour.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvgMilesPerHour.type)) {
if (verticalSpeedAvg) {
activity.addStat(new data_vertical_speed_avg_1.DataVerticalSpeedAvgMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedAvg.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerSecond.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerMinute.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerMinute.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerHour.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerHour.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxKilometerPerHour.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMaxMilesPerHour.type)) {
const verticalSpeedMax = activity.getStat(data_vertical_speed_max_1.DataVerticalSpeedMax.type);
if (verticalSpeedMax) {
activity.addStat(new data_vertical_speed_max_1.DataVerticalSpeedMaxMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedMax.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerSecond.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerSecond((0, helpers_1.convertSpeedToSpeedInFeetPerSecond)(verticalSpeedMin.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerMinute.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerMinute((0, helpers_1.convertSpeedToSpeedInMetersPerMinute)(verticalSpeedMin.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerMinute.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerMinute((0, helpers_1.convertSpeedToSpeedInFeetPerMinute)(verticalSpeedMin.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerHour.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinMetersPerHour((0, helpers_1.convertSpeedToSpeedInMetersPerHour)(verticalSpeedMin.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerHour.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinFeetPerHour((0, helpers_1.convertSpeedToSpeedInFeetPerHour)(verticalSpeedMin.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinKilometerPerHour.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinKilometerPerHour((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(verticalSpeedMin.getValue())));
}
}
if (!activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMinMilesPerHour.type)) {
const verticalSpeedMin = activity.getStat(data_vertical_speed_min_1.DataVerticalSpeedMin.type);
if (verticalSpeedMin) {
activity.addStat(new data_vertical_speed_min_1.DataVerticalSpeedMinMilesPerHour((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(verticalSpeedMin.getValue())));
}
}
// Test SWOLF existence for swimming activities
if ((activity.type === activity_types_1.ActivityTypes.Swimming || activity.type === activity_types_1.ActivityTypes.OpenWaterSwimming) &&
(!activity.getStat(data_swolf_25m_1.DataSWOLF25m.type) || !activity.getStat(data_swolf_50m_1.DataSWOLF50m.type)) &&
((_a = activity.getStat(data_speed_avg_1.DataSpeedAvg.type)) === null || _a === void 0 ? void 0 : _a.getValue()) &&
((_b = activity.getStat(data_cadence_avg_1.DataCadenceAvg.type)) === null || _b === void 0 ? void 0 : _b.getValue())) {
const avgPace100m = (0, helpers_1.convertSpeedToSwimPace)(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue());
const avgCadence = activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue();
if (!activity.getStat(data_swolf_25m_1.DataSWOLF25m.type)) {
const swolf25m = ActivityUtilities.computeSwimSwolf(avgPace100m, avgCadence, 25);
activity.addStat(new data_swolf_25m_1.DataSWOLF25m(swolf25m));
}
if (!activity.getStat(data_swolf_50m_1.DataSWOLF50m.type)) {
const swolf50m = ActivityUtilities.computeSwimSwolf(avgPace100m, avgCadence, 50);
activity.addStat(new data_swolf_50m_1.DataSWOLF50m(swolf50m));
}
}
if (!activity.getStat(data_duration_1.DataDuration.type)) {
activity.addStat(new data_duration_1.DataDuration((activity.endDate.getTime() - activity.startDate.getTime()) / 1000));
}
// If timer time not set, then assign elapsed time by default (e.g. GPX file dont support timer time)
if (!activity.getStat(data_timer_time_1.DataTimerTime.type)) {
activity.addStat(new data_timer_time_1.DataTimerTime(this.round(activity.getDuration().getValue(), 2)));
}
// If missing moving time
// Or moving time equals duration, then try to build real moving from laps if available
if (!activity.getStat(data_moving_time_1.DataMovingTime.type)) {
let movingTime = 0;
// First try to compute moving time from laps
const laps = activity.getLaps();
if (laps && laps.length > 0) {
activity.getLaps().forEach(lap => {
const stat = lap.getStat(data_moving_time_1.DataMovingTime.type);
if (stat) {
movingTime += stat.getValue();
}
});
}
// Get timer time...
const timerTime = (_c = activity.getStat(data_timer_time_1.DataTimerTime.type)) === null || _c === void 0 ? void 0 : _c.getValue();
// ... and compare with moving time and determine if moving time is like "moving time"
const isMovingTimeAlike = movingTime > 0 && movingTime < timerTime;
// If moving time from laps is not valid
if (!isMovingTimeAlike && activity.hasStreamData(data_speed_1.DataSpeed.type)) {
// ...then re-compute moving time but using global records.
movingTime = 0;
const speedByDurationStream = activity.getStreamDataByDuration(data_speed_1.DataSpeed.type, true, true);
const speedThreshold = activity_types_1.ActivityTypesMoving.getSpeedThreshold(activity.type);
speedByDurationStream.forEach((speedItem, index) => {
var _a;
if (speedItem.value !== null && speedItem.value > speedThreshold) {
movingTime += (speedByDurationStream[index].time - (((_a = speedByDurationStream[index - 1]) === null || _a === void 0 ? void 0 : _a.time) || 0)) / 1000;
}
});
}
// In case moving time would be invalid, set it to timer time "at max"
if (!movingTime || movingTime > timerTime) {
movingTime = timerTime;
}
activity.addStat(new data_moving_time_1.DataMovingTime(movingTime));
}
// Add Power Work if missing when avg power and moving time are available
if (!activity.getStat(data_power_work_1.DataPowerWork.type) &&
activity.getStat(data_power_avg_1.DataPowerAvg.type) &&
activity.getStat(data_moving_time_1.DataMovingTime.type)) {
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const avgPower = activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue();
const powerWork = Math.round((avgPower * movingTime) / 1000);
activity.addStat(new data_power_work_1.DataPowerWork(powerWork));
}
// If there is no pause defined, derive it from explicit paused time semantics: elapsed - timer.
if (!activity.getStat(data_pause_1.DataPause.type)) {
const elapsedTimeStat = activity.getStat(data_elapsed_time_1.DataElapsedTime.type);
const timerTimeStat = activity.getStat(data_timer_time_1.DataTimerTime.type);
let pauseTime = 0;
if (elapsedTimeStat &&
(0, helpers_1.isNumber)(elapsedTimeStat.getValue()) &&
timerTimeStat &&
(0, helpers_1.isNumber)(timerTimeStat.getValue())) {
pauseTime = elapsedTimeStat.getValue() - timerTimeStat.getValue();
}
else {
const movingTimeStat = activity.getStat(data_moving_time_1.DataMovingTime.type);
pauseTime =
movingTimeStat && movingTimeStat.getValue()
? activity.getDuration().getValue() - movingTimeStat.getValue()
: 0;
}
activity.addStat(new data_pause_1.DataPause(this.round(Math.max(pauseTime, 0), 2)));
}
}
}
exports.ActivityUtilities = ActivityUtilities;
ActivityUtilities.geoLibAdapter = new geolib_adapter_1.GeoLibAdapter();
ActivityUtilities.FTP_DURATION_SECONDS = 1200;
ActivityUtilities.FTP_FACTOR = 0.95;
ActivityUtilities.INTENSITY_FACTOR_DECIMALS = 3;
ActivityUtilities.TRAINING_STRESS_SCORE_DECIMALS = 1;
ActivityUtilities.jumpStatFamilies = [
{
key: 'hang_time',
minType: data_jump_stats_1.DataJumpHangTimeMin.type,
maxType: data_jump_stats_1.DataJumpHangTimeMax.type,
avgType: data_jump_stats_1.DataJumpHangTimeAvg.type,
createMin: (value) => new data_jump_stats_1.DataJumpHangTimeMin(value),
createMax: (value) => new data_jump_stats_1.DataJumpHangTimeMax(value),
createAvg: (value) => new data_jump_stats_1.DataJumpHangTimeAvg(value),
getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.hang_time) === null || _b === void 0 ? void 0 : _b.getValue(); }
},
{
key: 'distance',
minType: data_jump_stats_1.DataJumpDistanceMin.type,
maxType: data_jump_stats_1.DataJumpDistanceMax.type,
avgType: data_jump_stats_1.DataJumpDistanceAvg.type,
createMin: (value) => new data_jump_stats_1.DataJumpDistanceMin(value),
createMax: (value) => new data_jump_stats_1.DataJumpDistanceMax(value),
createAvg: (value) => new data_jump_stats_1.DataJumpDistanceAvg(value),
getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.distance) === null || _b === void 0 ? void 0 : _b.getValue(); }
},
{
key: 'speed',
minType: data_jump_stats_1.DataJumpSpeedMin.type,
maxType: data_jump_stats_1.DataJumpSpeedMax.type,
avgType: data_jump_stats_1.DataJumpSpeedAvg.type,
createMin: (value) => new data_jump_stats_1.DataJumpSpeedMin(value),
createMax: (value) => new data_jump_stats_1.DataJumpSpeedMax(value),
createAvg: (value) => new data_jump_stats_1.DataJumpSpeedAvg(value),
getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.speed) === null || _b === void 0 ? void 0 : _b.getValue(); }
},
{
key: 'rotations',
minType: data_jump_stats_1.DataJumpRotationsMin.type,
maxType: data_jump_stats_1.DataJumpRotationsMax.type,
avgType: data_jump_stats_1.DataJumpRotationsAvg.type,
createMin: (value) => new data_jump_stats_1.DataJumpRotationsMin(value),
createMax: (value) => new data_jump_stats_1.DataJumpRotationsMax(value),
createAvg: (value) => new data_jump_stats_1.DataJumpRotationsAvg(value),
getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.rotations) === null || _b === void 0 ? void 0 : _b.getValue(); }
},
{
key: 'score',
minType: data_jump_stats_1.DataJumpScoreMin.type,
maxType: data_jump_stats_1.DataJumpScoreMax.type,
avgType: data_jump_stats_1.DataJumpScoreAvg.type,
createMin: (value) => new data_jump_stats_1.DataJumpScoreMin(value),
createMax: (value) => new data_jump_stats_1.DataJumpScoreMax(value),
createAvg: (value) => new data_jump_stats_1.DataJumpScoreAvg(value),
getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.score) === null || _b === void 0 ? void 0 : _b.getValue(); }
},
{
key: 'height',
minType: data_jump_stats_1.DataJumpHeightMin.type,
maxType: data_jump_stats_1.DataJumpHeightMax.type,
avgType: data_jump_stats_1.DataJumpHeightAvg.type,
createMin: (value) => new data_jump_stats_1.DataJumpHeightMin(value),
createMax: (value) => new data_jump_stats_1.DataJumpHeightMax(value),
createAvg: (value) => new data_jump_stats_1.DataJumpHeightAvg(value),
getEventValue: (jumpEvent) => { var _a, _b; return (_b = (_a = jumpEvent.jumpData) === null || _a === void 0 ? void 0 : _a.height) === null || _b === void 0 ? void 0 : _b.getValue(); }
}
];