UNPKG

@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

948 lines 216 kB
"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_fe