package de.saring.exerciseviewer.data;
import java.time.LocalDateTime;
import java.util.stream.Stream;
/**
* This class represents an recorded exercise. There is all the data of an
* Polar S710 stored in this class right now, maybe there will be more data
* in future to be compatible with other watches too.
*
* @author Stefan Saring
* @version 1.0
*/
public final class EVExercise {
/**
* File type of an exercise (see enums).
*/
private ExerciseFileType fileType;
/**
* Name of the HRM device (optional).
*/
private String deviceName;
/**
* Timestamp of exercise.
*/
private LocalDateTime dateTime;
/**
* Exercise type (label).
*/
private String type;
/**
* Record mode (what was recorded in exercise).
*/
private RecordingMode recordingMode;
/**
* Duration of exercise in tenths of a second.
*/
private int duration;
/**
* Recording interval in seconds (e.g. 5s, 15s, 60s or DYNAMIC_RECORDING_INTERVAL).
*/
private short recordingInterval;
/**
* Average heart rate of exercise.
*/
private short heartRateAVG;
/**
* Maximim heart rate of exercise.
*/
private short heartRateMax;
/**
* The speed data of exercise (if recorded).
*/
private ExerciseSpeed speed;
/**
* The cadence data of exercise (if recorded).
*/
private ExerciseCadence cadence;
/**
* The altitude data of exercise (if recorded).
*/
private ExerciseAltitude altitude;
/**
* The temperature data of exercise.
*/
private ExerciseTemperature temperature;
/**
* Energy "wasted" for exercise (in kCal).
*/
private int energy;
/**
* Cumulative "wasted" energy of all exercises (in kCal).
*/
private int energyTotal;
/**
* Cumulative workout time (in minutes).
*/
private int sumExerciseTime;
/**
* Cumulative ride time (in minutes).
*/
private int sumRideTime;
/**
* Odometer (cumulative ride distance) in km.
*/
private int odometer;
/**
* Array of heartrate limit data (can be more then one).
*/
private HeartRateLimit[] heartRateLimits;
/**
* Array containing the data of all laps of exercise.
*/
private Lap[] lapList;
/**
* Array containing the data of all recorded samples (for each interval) of exercise.
*/
private ExerciseSample[] sampleList;
/**
* This is the list of possible file types of an exercise.
*/
public enum ExerciseFileType {
S710RAW, S610RAW, S510RAW, HRM, HAC4TUR, RS200SDRAW, F6RAW, SSCSV, GARMIN_FIT, GARMIN_TCX, PED, TIMEX_PWX, GPX
}
/**
* Constant for dynamic exercise sample recording interval (e.g. in Garmin TCX files).
*/
public static final short DYNAMIC_RECORDING_INTERVAL = -1;
public ExerciseFileType getFileType() {
return fileType;
}
public void setFileType(ExerciseFileType fileType) {
this.fileType = fileType;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public RecordingMode getRecordingMode() {
return recordingMode;
}
public void setRecordingMode(RecordingMode recordingMode) {
this.recordingMode = recordingMode;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public short getRecordingInterval() {
return recordingInterval;
}
public void setRecordingInterval(short recordingInterval) {
this.recordingInterval = recordingInterval;
}
public short getHeartRateAVG() {
return heartRateAVG;
}
public void setHeartRateAVG(short heartRateAVG) {
this.heartRateAVG = heartRateAVG;
}
public short getHeartRateMax() {
return heartRateMax;
}
public void setHeartRateMax(short heartRateMax) {
this.heartRateMax = heartRateMax;
}
public ExerciseSpeed getSpeed() {
return speed;
}
public void setSpeed(ExerciseSpeed speed) {
this.speed = speed;
}
public ExerciseCadence getCadence() {
return cadence;
}
public void setCadence(ExerciseCadence cadence) {
this.cadence = cadence;
}
public ExerciseAltitude getAltitude() {
return altitude;
}
public void setAltitude(ExerciseAltitude altitude) {
this.altitude = altitude;
}
public ExerciseTemperature getTemperature() {
return temperature;
}
public void setTemperature(ExerciseTemperature temperature) {
this.temperature = temperature;
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
}
public int getEnergyTotal() {
return energyTotal;
}
public void setEnergyTotal(int energyTotal) {
this.energyTotal = energyTotal;
}
public int getSumExerciseTime() {
return sumExerciseTime;
}
public void setSumExerciseTime(int sumExerciseTime) {
this.sumExerciseTime = sumExerciseTime;
}
public int getSumRideTime() {
return sumRideTime;
}
public void setSumRideTime(int sumRideTime) {
this.sumRideTime = sumRideTime;
}
public int getOdometer() {
return odometer;
}
public void setOdometer(int odometer) {
this.odometer = odometer;
}
public HeartRateLimit[] getHeartRateLimits() {
return heartRateLimits;
}
public void setHeartRateLimits(HeartRateLimit[] heartRateLimits) {
this.heartRateLimits = heartRateLimits;
}
public Lap[] getLapList() {
return lapList;
}
public void setLapList(Lap[] lapList) {
this.lapList = lapList;
}
public ExerciseSample[] getSampleList() {
return sampleList;
}
public void setSampleList(ExerciseSample[] sampleList) {
this.sampleList = sampleList;
}
/**
* In most file formats (e.g. S710Raw, HRM) there are no distance values for each
* recorded sample. So they need to be calculated from the sample time and speed.
* This calculation is sometimes not total precise, the distance of last sample is
* smaller/larger then the exercise distance. So all the sample distances needs to
* get recalculated in relation to the exercise distance.
*/
public void repairSamples() {
// is all the required speed data available ?
if ((this.speed == null) || (this.speed.getDistance() == 0) ||
(this.sampleList == null) || (this.sampleList.length == 0)) {
return;
}
// it's possible that there are not recorded samples for the whole exercise time
// (e.g. connection problems) => in this case we can't repair the sample distances
if (this.sampleList.length < (duration / 10 / recordingInterval)) {
return;
}
// calculate relation of exercise distance to last sample distance
ExerciseSample lastSample = this.sampleList[this.sampleList.length - 1];
double fRelation = lastSample.getDistance() / (double) this.speed.getDistance();
// process all samples and recalculate the sample distance in relation to exercise distance
for (ExerciseSample sample : this.sampleList) {
sample.setDistance((int) Math.round(sample.getDistance() / fRelation));
}
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder();
sBuilder.append(EVExercise.class.getName()).append(":\n");
sBuilder.append(" [fileType=").append(this.fileType).append("\n");
sBuilder.append(" deviceName=").append(this.deviceName).append("]\n");
sBuilder.append(" dateTime=").append(this.dateTime).append("\n");
sBuilder.append(" type=").append(this.type).append("\n");
sBuilder.append(" duration=").append(this.duration).append("\n");
sBuilder.append(" recordingInterval=").append(this.recordingInterval).append("\n");
sBuilder.append(" heartRateAVG=").append(this.heartRateAVG).append("\n");
sBuilder.append(" heartRateMax=").append(this.heartRateMax).append("\n");
sBuilder.append(" energy=").append(this.energy).append("\n");
sBuilder.append(" energyTotal=").append(this.energyTotal).append("\n");
sBuilder.append(" sumExerciseTime=").append(this.sumExerciseTime).append("\n");
sBuilder.append(" sumRideTime=").append(this.sumRideTime).append("\n");
sBuilder.append(" odometer=").append(this.odometer).append("]\n");
if (this.recordingMode != null) sBuilder.append(this.recordingMode);
if (this.speed != null) sBuilder.append(this.speed);
if (this.cadence != null) sBuilder.append(this.cadence);
if (this.altitude != null) sBuilder.append(this.altitude);
if (this.temperature != null) sBuilder.append(this.temperature);
if (this.heartRateLimits != null) {
Stream.of(this.heartRateLimits).forEach(sBuilder::append);
}
if (this.lapList != null) {
Stream.of(this.lapList).forEach(sBuilder::append);
}
if (this.sampleList != null) {
Stream.of(this.sampleList).forEach(sBuilder::append);
}
return sBuilder.toString();
}
}