diff --git a/src/FileIO/CsvRideFile.cpp b/src/FileIO/CsvRideFile.cpp index 521db5d43..f15a7b63e 100644 --- a/src/FileIO/CsvRideFile.cpp +++ b/src/FileIO/CsvRideFile.cpp @@ -129,6 +129,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors, QListaddXData("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, QListdatapoints.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; ivaluename << 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; inumber[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 diff --git a/src/FileIO/CsvRideFile.h b/src/FileIO/CsvRideFile.h index 4ea1cb901..5f29a7f75 100644 --- a/src/FileIO/CsvRideFile.h +++ b/src/FileIO/CsvRideFile.h @@ -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* = 0) const; diff --git a/src/FileIO/FixLapSwim.cpp b/src/FileIO/FixLapSwim.cpp index 0a8f9eddd..ce164f068 100644 --- a/src/FileIO/FixLapSwim.cpp +++ b/src/FileIO/FixLapSwim.cpp @@ -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; avaluename.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 newRows; + interval++; // pauses mark laps + for (int i=0; inumber[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(); diff --git a/test/rides/2019_10_10_09_56_00.csv b/test/rides/2019_10_10_09_56_00.csv new file mode 100644 index 000000000..e6c6cff1e --- /dev/null +++ b/test/rides/2019_10_10_09_56_00.csv @@ -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 diff --git a/test/swims/2019_09_23_06_33_00.csv b/test/swims/2019_09_23_06_33_00.csv new file mode 100644 index 000000000..8ceb10ee1 --- /dev/null +++ b/test/swims/2019_09_23_06_33_00.csv @@ -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 diff --git a/test/swims/2019_09_23_18_30_00.csv b/test/swims/2019_09_23_18_30_00.csv new file mode 100644 index 000000000..fb47b1d48 --- /dev/null +++ b/test/swims/2019_09_23_18_30_00.csv @@ -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