import FixePosition from '../../entities/FixePosition' import ClimbSinkSegment from '../../entities/ClimbSinkSegment' export default class ClimbSinkParser { public CLIMB_HEIGHT_THRESHOLD = 100 // 100 meters will be consider a climb to highlight on the map public CLIMB_TIME_THRESHOLD = 300000 // 5 minutes timeframe will be consider a climb to highlight on the map public SINK_HEIGHT_THRESHOLD = 100 // 100 meters will be consider a sink to highlight on the map public SINK_TIME_THRESHOLD = 120000 // 2 minutes timeframe will be consider a sink to highlight on the map public CLIMB_OR_SINK_STOP_THRESHOLD = 15000 // 10 seconds will be consider a climb or sink to highlight on the map public climbSinkFixesIndexesToAdd: Array = [] public climbSinkSegments: Array = [] public isClimbOrSinkType: 'NONE' | 'CLIMB' | 'SINK' = 'NONE' public isClimbOrSinkAchieved: boolean = false public climbOrSinkStartedAt: number = 0 public climbOrSinkEndedAt: number = 0 public climbOrSinkHeightStart: number = 0 public climbOrSinkHeightEnd: number = 0 public climbOrSinkHeightTotal: number = 0 public climbOrSinkStopedAt: number = 0 constructor({ climbHeight = 100, sinkHeight = 100, sinkTime = 120000, climbTime = 300000, stopTime = 15000 } = {}) { this.CLIMB_HEIGHT_THRESHOLD = climbHeight this.SINK_HEIGHT_THRESHOLD = sinkHeight this.SINK_TIME_THRESHOLD = sinkTime this.CLIMB_TIME_THRESHOLD = climbTime this.CLIMB_OR_SINK_STOP_THRESHOLD = stopTime } private getIsClimbOrSink(fixePosition: FixePosition) { if(fixePosition.climbSinkRate === 0) return 'NONE' if(fixePosition.climbSinkRate > 0) return 'CLIMB' return 'SINK' } public processClimbSinkSegment(fixePosition: FixePosition, index: number) { fixePosition.isClimbingOrSinkType = this.getIsClimbOrSink(fixePosition) if(fixePosition.climbSinkRate !== 0) { if(this.isClimbOrSinkType === 'NONE') { this.climbOrSinkStartedAt = fixePosition.timestamp this.climbOrSinkHeightStart = fixePosition.gpsAltitude this.climbOrSinkHeightTotal += fixePosition.altitudeDiff this.isClimbOrSinkType = fixePosition.climbSinkRate > 0 ? 'CLIMB' : 'SINK' } else { const addSink = Boolean(this.isClimbOrSinkType === 'SINK' && fixePosition.gpsAltitude < this.climbOrSinkHeightEnd) const addClimb = Boolean(this.isClimbOrSinkType === 'CLIMB' && fixePosition.gpsAltitude > this.climbOrSinkHeightEnd) this.climbSinkFixesIndexesToAdd.push(index) if(addSink || addClimb) { this.climbOrSinkEndedAt = fixePosition.timestamp this.climbOrSinkHeightEnd = fixePosition.gpsAltitude this.climbOrSinkHeightTotal = this.climbOrSinkHeightEnd - this.climbOrSinkHeightStart } const CLIMB_OR_SINK_TIME_THRESHOLD = this.isClimbOrSinkType === 'SINK' ? this.SINK_TIME_THRESHOLD : this.CLIMB_TIME_THRESHOLD const CLIMB_OR_SINK_HEIGHT_THRESHOLD = this.isClimbOrSinkType === 'SINK' ? this.SINK_HEIGHT_THRESHOLD : this.CLIMB_HEIGHT_THRESHOLD const climbOrSinkingDurationTs = this.climbOrSinkEndedAt - this.climbOrSinkStartedAt const timediffFromLastClimbOrSink = (fixePosition.timestamp - this.climbOrSinkEndedAt) this.isClimbOrSinkAchieved = Boolean( Math.abs(this.climbOrSinkHeightTotal) >= CLIMB_OR_SINK_HEIGHT_THRESHOLD && climbOrSinkingDurationTs <= CLIMB_OR_SINK_TIME_THRESHOLD ) if(!this.isClimbOrSinkAchieved && timediffFromLastClimbOrSink > this.CLIMB_OR_SINK_STOP_THRESHOLD) { this.isClimbOrSinkType = 'NONE' this.climbOrSinkHeightTotal = 0 this.climbSinkFixesIndexesToAdd = [] this.isClimbOrSinkAchieved = false return } if (this.isClimbOrSinkAchieved && timediffFromLastClimbOrSink > this.CLIMB_OR_SINK_STOP_THRESHOLD ) { this.climbSinkSegments.push({ isClimbOrSinkType: this.isClimbOrSinkType, climbOrSinkHeightStart: this.climbOrSinkHeightStart, climbOrSinkHeightEnd: this.climbOrSinkHeightEnd, climbOrSinkStartedAt: this.climbOrSinkStartedAt, climbOrSinkEndedAt: this.climbOrSinkEndedAt, climbOrSinkHeightTotal: this.climbOrSinkHeightTotal, climbOrSinkingDurationTs, fixesIndexes: [ ...this.climbSinkFixesIndexesToAdd ] }) this.isClimbOrSinkType = 'NONE' this.climbOrSinkHeightTotal = 0 this.climbSinkFixesIndexesToAdd = [] } } } } }