mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 08:08:42 +00:00
Enable import of XDATA series from CSV files (#3179)
* Enable import of XDATA series from CSV files Since XDATA series are preserved across merges this allows to add arbitrary XDATA series to any activity. Sample WEATHER file is included for testing. Sample swim file is also included, once imported set Sport=Swim and use Fix Lap Swim with pool length=25 to rebuild standard series and laps Fixes #2010 item 4. * FixLapSwim - Alternative way to store pauses in XData Some devices s.t. Moov Now generates a CSV with REST field after each length. FixLapSwim now can use this alternative way to express pauses and to mark end of laps. This is intended to be used together with XDATA csv import. A Moov Now csv file modified to comply with our format is added as an example to test folder.
This commit is contained in:
committed by
GitHub
parent
8b8ddccb5b
commit
0b0f99889e
@@ -129,6 +129,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors, QList<Ri
|
||||
XDataSeries *trainSeries=NULL;
|
||||
XDataSeries *rrSeries=NULL;
|
||||
XDataSeries *ibikeSeries=NULL;
|
||||
XDataSeries *xdataSeries=NULL;
|
||||
|
||||
/* Joule 1.0
|
||||
Version,Date/Time,Km,Minutes,RPE,Tags,"Weight, kg","Work, kJ",FTP,"Sample Rate, s",Device Type,Firmware Version,Last Updated,Category 1,Category 2
|
||||
@@ -163,6 +164,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors, QList<Ri
|
||||
QRegExp rowproCSV("Date,Comment,Password,ID,Version,RowfileId,Rowfile_Id", Qt::CaseInsensitive);
|
||||
QRegExp wahooMACSV("GroundContactTime,MotionCount,MotionPowerZ,Cadence,MotionPowerX,WorkoutActive,Timestamp,Smoothness,MotionPowerY,_ID,VerticalOscillation,", Qt::CaseInsensitive);
|
||||
QRegExp rp3CSV ("\"id\",\"workout_interval_id\",\"ref\",\"stroke_number\",\"power\",\"avg_power\",\"stroke_rate\",\"time\",\"stroke_length\",\"distance\",\"distance_per_stroke\",\"estimated_500m_time\",\"energy_per_stroke\",\"energy_sum\",\"pulse\",\"work_per_pulse\",\"peak_force\",\"peak_force_pos\",\"rel_peak_force_pos\",\"drive_time\",\"recover_time\",\"k\",\"curve_data\",\"stroke_number_in_interval\",\"avg_calculated_power\"", Qt::CaseSensitive);
|
||||
QRegExp xdataCSV ("XDATA: (\\w+)", Qt::CaseSensitive);
|
||||
|
||||
|
||||
// X-trainer format
|
||||
@@ -422,6 +424,19 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors, QList<Ri
|
||||
|
||||
rideFile->addXData("ROW", rowSeries);
|
||||
|
||||
} else if (xdataCSV.indexIn(line) != -1) {
|
||||
|
||||
csvType = xdata;
|
||||
rideFile->setDeviceType("GoldenCheetah XData");
|
||||
rideFile->setFileFormat("GoldenCheetah XData CSV (csv)");
|
||||
unitsHeader = -1;
|
||||
recInterval = 1; // may be variable
|
||||
|
||||
// add XDATA
|
||||
xdataSeries = new XDataSeries();
|
||||
xdataSeries->name = xdataCSV.cap(1);
|
||||
++lineno;
|
||||
continue;
|
||||
|
||||
} else if (line == "secs,km,power,hr,cad,alt") {
|
||||
// OpenData CSV
|
||||
@@ -1137,6 +1152,57 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors, QList<Ri
|
||||
rowSeries->datapoints.append(p);
|
||||
}
|
||||
|
||||
} else if (csvType == xdata) {
|
||||
|
||||
// GoldenCheetah XDATA format with N series and M rows
|
||||
// XDATA: Name
|
||||
// SECS, KM, serie1[:units1], ..., seriesN[:unitsN]
|
||||
// secs1, km1, value11, ..., value1N
|
||||
// ...
|
||||
// secsM, kmM, valueM1, ..., valueMN
|
||||
|
||||
if (lineno == 2) {
|
||||
|
||||
QStringList hds = line.split(",");
|
||||
if (hds.count()>2 && hds[0]=="SECS" && hds[1]=="KM") {
|
||||
|
||||
QRegExp hd("(\\w+)(?::(\\w+))?");
|
||||
for (int i=2; i<hds.count() && i<XDATA_MAXVALUES+2; i++) {
|
||||
if (hd.indexIn(hds[i]) == -1) break;
|
||||
xdataSeries->valuename << hd.cap(1);
|
||||
xdataSeries->unitname << hd.cap(2);
|
||||
}
|
||||
rideFile->addXData(xdataSeries->name, xdataSeries);
|
||||
|
||||
} else {
|
||||
|
||||
delete xdataSeries;
|
||||
xdataSeries = NULL;
|
||||
|
||||
}
|
||||
} else if (xdataSeries != NULL) {
|
||||
|
||||
QStringList els = line.split(",", QString::KeepEmptyParts);
|
||||
if (els.count() != xdataSeries->valuename.count()+2) continue;
|
||||
// add ALL data series to XDATA
|
||||
XDataPoint *p = new XDataPoint();
|
||||
p->secs = els[0].toDouble();
|
||||
p->km = els[1].toDouble();
|
||||
for(int i=2; i<els.count(); i++) p->number[i-2] = els[i].toDouble();
|
||||
xdataSeries->datapoints.append(p);
|
||||
|
||||
// only time and distance as standard series
|
||||
rideFile->appendPoint(p->secs, // time in seconds
|
||||
0, 0, // cad, hr
|
||||
p->km, // distance (km)
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
-255, // temp
|
||||
0, 0, 0, 0, 0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
0, 0, 0, 0, 0, 0.0, 0);
|
||||
}
|
||||
|
||||
} else if (csvType == opendata) {
|
||||
|
||||
// secs,km,power,hr,cad,alt
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "RideFile.h"
|
||||
|
||||
struct CsvFileReader : public RideFileReader {
|
||||
enum csvtypes { generic, gc, powertap, joule, ergomo, motoactv, ibike, xtrain, moxy, freemotion, peripedal, cpexport, bsx, rowpro, wprime, wahooMA, rp3, opendata };
|
||||
enum csvtypes { generic, gc, powertap, joule, ergomo, motoactv, ibike, xtrain, moxy, freemotion, peripedal, cpexport, bsx, rowpro, wprime, wahooMA, rp3, opendata, xdata };
|
||||
typedef enum csvtypes CsvType;
|
||||
|
||||
virtual RideFile *openRideFile(QFile &file, QStringList &errors, QList<RideFile*>* = 0) const;
|
||||
|
||||
@@ -136,7 +136,7 @@ FixLapSwim::postProcess(RideFile *ride, DataProcessorConfig *config=0, QString o
|
||||
XDataSeries *series = ride->xdata("SWIM");
|
||||
if (!series || series->datapoints.isEmpty()) return false;
|
||||
|
||||
int typeIdx = -1, durationIdx = -1, strokesIdx = -1;
|
||||
int typeIdx = -1, durationIdx = -1, strokesIdx = -1, restIdx = -1;
|
||||
for (int a=0; a<series->valuename.count(); a++) {
|
||||
if (series->valuename.at(a) == "TYPE")
|
||||
typeIdx = a;
|
||||
@@ -144,6 +144,8 @@ FixLapSwim::postProcess(RideFile *ride, DataProcessorConfig *config=0, QString o
|
||||
durationIdx = a;
|
||||
else if (series->valuename.at(a) == "STROKES")
|
||||
strokesIdx = a;
|
||||
else if (series->valuename.at(a) == "REST")
|
||||
restIdx = a;
|
||||
}
|
||||
// Stroke Type or Duration are mandatory, Strokes only to compute cadence
|
||||
if (typeIdx == -1 || durationIdx == -1) return false;
|
||||
@@ -219,13 +221,38 @@ FixLapSwim::postProcess(RideFile *ride, DataProcessorConfig *config=0, QString o
|
||||
last_time += length_duration;
|
||||
last_distance += length_distance;
|
||||
if (length_distance == 0.0) interval++; // pauses mark laps
|
||||
}
|
||||
}
|
||||
// Alternative way to mark pauses: Rest seconds after each length
|
||||
if (restIdx>0 && p->number[restIdx]>0) {
|
||||
QVector<struct RideFilePoint> newRows;
|
||||
interval++; // pauses mark laps
|
||||
for (int i=0; i<p->number[restIdx] && i<100*GarminHWM.toInt(); i++) {
|
||||
double hr = hrdata.value(last_time + i, 0.0); // recover HR data
|
||||
newRows << RideFilePoint(
|
||||
last_time + i, 0.0, hr,
|
||||
last_distance,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
RideFile::NA,RideFile::NA,
|
||||
0.0, 0.0,0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0,0.0, 0.0,
|
||||
0.0, 0.0,0.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0,
|
||||
interval);
|
||||
}
|
||||
ride->command->appendPoints(newRows);
|
||||
last_time += p->number[restIdx];
|
||||
interval++; // pauses mark laps
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update Rec. Interval, Pool Length, set data present and commit
|
||||
ride->setRecIntSecs(1.0);
|
||||
ride->setTag("Pool Length", QString("%1").arg(pl));
|
||||
ride->setDataPresent(ride->km, true);
|
||||
ride->setDataPresent(ride->kph, true);
|
||||
ride->setDataPresent(ride->cad, strokesIdx>0);
|
||||
ride->command->endLUW();
|
||||
|
||||
12
test/rides/2019_10_10_09_56_00.csv
Normal file
12
test/rides/2019_10_10_09_56_00.csv
Normal file
@@ -0,0 +1,12 @@
|
||||
XDATA: WEATHER,,,,,
|
||||
SECS,KM,WINDSPEED:kmh,WINDHEADING,TEMPERATURE:celsius,HUMIDITY:%
|
||||
1,0.00405,7.9992,90,5,93
|
||||
901,6.78945,7.9992,70,5,91
|
||||
1801,9.83493,7.9992,70,5,91
|
||||
2701,17.0663,7.9992,70,5,91
|
||||
3601,22.8818,7.9992,70,5,91
|
||||
4501,27.7603,7.9992,70,5,91
|
||||
5401,34.0975,7.9992,70,5,91
|
||||
6301,42.4375,7.9992,70,5,91
|
||||
7201,51.6657,10.0008,220,12,57
|
||||
8093,59.3946,5.0004,0,7,93
|
||||
|
48
test/swims/2019_09_23_06_33_00.csv
Normal file
48
test/swims/2019_09_23_06_33_00.csv
Normal file
@@ -0,0 +1,48 @@
|
||||
XDATA: SWIM
|
||||
SECS,KM,TYPE,DURATION,TURN_TIME,SWIM_TIME,STROKES,REST
|
||||
1 , 0, 1,39.78,8.78,31.00,16,0.00
|
||||
2 , 0, 1,44.02,7.13,36.89,17,0.00
|
||||
3 , 0, 1,41.54,8.37,33.17,15,0.00
|
||||
4 , 0, 1,40.92,10.54,30.38,13,0.00
|
||||
5 , 0, 3,49.29,10.23,39.06,13,0.00
|
||||
6 , 0, 2,57.04,4.03,53.01,29,0.00
|
||||
7 , 0, 2,54.87,0.00,41.54,22,0.00
|
||||
8 , 0, 2,54.25,12.40,41.85,23,28.52
|
||||
9 , 0, 1,22.32,0.00,22.32,11,43.40
|
||||
10 , 0, 1,34.41,0.00,34.41,15,19.84
|
||||
11 , 0, 1,30.38,0.00,30.38,13,31.62
|
||||
12 , 0, 1,31.31,0.00,31.31,13,31.93
|
||||
13 , 0, 1,32.86,0.00,32.86,14,33.17
|
||||
14 , 0, 1,31.00,0.00,31.00,13,19.84
|
||||
15 , 0, 2,41.54,0.00,41.54,23,22.94
|
||||
16 , 0, 2,44.95,0.00,44.95,25,21.08
|
||||
17 , 0, 2,55.65,10.70,44.95,24,0.00
|
||||
18 , 0, 1,50.53,11.78,38.75,16,0.00
|
||||
19 , 0, 1,60.76,0.00,46.19,16,30.69
|
||||
20 , 0, 1,42.78,0.00,42.78,15,27.59
|
||||
21 , 0, 1,34.41,0.00,34.41,13,24.49
|
||||
22 , 0, 1,33.79,0.00,33.79,13,29.45
|
||||
23 , 0, 1,33.48,0.00,33.48,13,21.08
|
||||
24 , 0, 1,33.79,0.00,33.79,13,20.15
|
||||
25 , 0, 2,56.42,9.92,46.50,24,0.00
|
||||
26 , 0, 2,57.04,9.92,47.12,23,19.53
|
||||
27 , 0, 1,34.72,0.00,34.72,14,19.53
|
||||
28 , 0, 1,39.68,0.00,39.68,15,44.95
|
||||
29 , 0, 1,39.99,0.00,39.99,16,17.98
|
||||
30 , 0, 1,37.51,0.00,37.51,14,24.18
|
||||
31 , 0, 1,36.58,0.00,36.58,14,25.73
|
||||
32 , 0, 1,35.03,0.00,35.03,13,34.41
|
||||
33 , 0, 1,32.55,0.00,32.55,14,16.74
|
||||
34 , 0, 2,46.50,0.00,46.50,25,0.00
|
||||
35 , 0, 2,61.07,0.00,45.57,24,19.84
|
||||
36 , 0, 1,32.55,0.00,32.55,13,33.79
|
||||
37 , 0, 1,35.34,0.00,35.34,14,21.70
|
||||
38 , 0, 1,31.62,0.00,31.62,11,27.59
|
||||
39 , 0, 1,47.12,10.85,36.27,13,0.00
|
||||
40 , 0, 1,49.29,10.85,38.44,14,32.55
|
||||
41 , 0, 1,32.24,0.00,32.24,13,18.60
|
||||
42 , 0, 1,45.26,10.54,34.72,14,0.00
|
||||
43 , 0, 3,51.77,0.00,38.13,16,0.00
|
||||
44 , 0, 2,57.35,10.54,46.81,25,16.74
|
||||
45 , 0, 1,36.27,0.00,36.27,14,25.11
|
||||
46 , 0, 1,31.93,0.00,31.93,12,0.00
|
||||
|
104
test/swims/2019_09_23_18_30_00.csv
Normal file
104
test/swims/2019_09_23_18_30_00.csv
Normal file
@@ -0,0 +1,104 @@
|
||||
XDATA: SWIM
|
||||
SECS,KM,TYPE,DURATION:secs,STROKES
|
||||
0,0,1,26.5,11
|
||||
26,0.025,1,26.562,11
|
||||
53,0.05,1,26.125,11
|
||||
79,0.075,1,26.625,12
|
||||
105,0.1,1,25.5,10
|
||||
131,0.125,1,26.875,12
|
||||
158,0.15,1,25.125,11
|
||||
183,0.175,1,23.84,11
|
||||
207,0.2,0,37.003,0
|
||||
246,0.2,1,24.062,10
|
||||
270,0.225,1,26.5,11
|
||||
296,0.25,1,25.625,12
|
||||
322,0.275,1,24.187,11
|
||||
346,0.3,1,24,11
|
||||
370,0.325,1,26.812,12
|
||||
397,0.35,1,25,10
|
||||
422,0.375,1,21.756,11
|
||||
444,0.4,0,37.678,0
|
||||
481,0.4,1,24.437,10
|
||||
506,0.425,1,23.5,11
|
||||
529,0.45,1,24.812,11
|
||||
554,0.475,1,25.5,12
|
||||
580,0.5,1,24.375,11
|
||||
604,0.525,1,25.25,13
|
||||
629,0.55,1,24,10
|
||||
653,0.575,1,21.433,11
|
||||
675,0.6,0,32.484,0
|
||||
707,0.6,1,24.812,10
|
||||
732,0.625,1,23.812,12
|
||||
756,0.65,1,24.625,11
|
||||
780,0.675,1,25,11
|
||||
805,0.7,1,24.437,11
|
||||
830,0.725,1,25.875,12
|
||||
856,0.75,1,25.562,14
|
||||
881,0.775,1,22.578,10
|
||||
904,0.8,0,27.316,0
|
||||
931,0.8,1,24.437,10
|
||||
956,0.825,1,24.437,12
|
||||
980,0.85,1,23.5,12
|
||||
1003,0.875,1,27.5,13
|
||||
1031,0.9,1,23.187,11
|
||||
1054,0.925,1,24.875,12
|
||||
1079,0.95,1,24.5,12
|
||||
1104,0.975,1,21.435,11
|
||||
1125,1,0,564.684,0
|
||||
1690,1,1,25.687,10
|
||||
1715,1.025,1,22.375,11
|
||||
1738,1.05,1,25,13
|
||||
1763,1.075,1,24.25,12
|
||||
1787,1.1,1,21.427,12
|
||||
1808,1.125,0,33.911,0
|
||||
1842,1.125,1,25.187,11
|
||||
1867,1.15,1,24.25,11
|
||||
1892,1.175,1,25.375,11
|
||||
1917,1.2,1,25,12
|
||||
1942,1.225,1,19.08,11
|
||||
1961,1.25,0,30.837,0
|
||||
1992,1.25,1,25.062,11
|
||||
2017,1.275,1,25.375,13
|
||||
2042,1.3,1,23.75,13
|
||||
2066,1.325,1,25.437,11
|
||||
2092,1.35,1,20.867,12
|
||||
2113,1.375,0,31.266,0
|
||||
2144,1.375,1,24,12
|
||||
2168,1.4,1,25.312,12
|
||||
2193,1.425,1,23.687,12
|
||||
2217,1.45,1,24,12
|
||||
2241,1.475,1,21.06,12
|
||||
2262,1.5,0,31.053,0
|
||||
2293,1.5,1,24.312,11
|
||||
2317,1.525,1,25,13
|
||||
2342,1.55,1,22.75,13
|
||||
2365,1.575,1,24.75,13
|
||||
2390,1.6,1,22.443,12
|
||||
2412,1.625,0,29.42,0
|
||||
2442,1.625,1,24,11
|
||||
2466,1.65,1,23.812,13
|
||||
2489,1.675,1,25.75,13
|
||||
2515,1.7,1,25.375,13
|
||||
2540,1.725,1,21.358,12
|
||||
2562,1.75,0,27.151,0
|
||||
2589,1.75,1,25.625,11
|
||||
2615,1.775,1,23.312,12
|
||||
2638,1.8,1,24.312,13
|
||||
2662,1.825,1,25.375,13
|
||||
2688,1.85,1,21.275,12
|
||||
2709,1.875,0,29.959,0
|
||||
2739,1.875,1,24.187,12
|
||||
2763,1.9,1,25.062,12
|
||||
2788,1.925,1,24.562,12
|
||||
2813,1.95,1,25.125,13
|
||||
2838,1.975,1,22.26,12
|
||||
2860,2,0,66.925,0
|
||||
2927,2,1,30.312,12
|
||||
2957,2.025,1,31.812,14
|
||||
2989,2.05,1,29.625,12
|
||||
3019,2.075,1,30.687,14
|
||||
3049,2.1,1,29.375,14
|
||||
3079,2.125,1,31.125,14
|
||||
3110,2.15,1,27.187,12
|
||||
3137,2.175,1,26.583,12
|
||||
3164,2.2,0,40.065,0
|
||||
|
Reference in New Issue
Block a user