1 /*
2 Handle energympro (GPS training watch) .cpo files
3
4 Copyright (c) 2014 Zingo Andersen zingo@vectrace.com
5 Copyright (C) 2014 Robert Lipe, robertlipe+source@gpsbabel.org
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 */
22
23 #include <cstdint> // for int32_t
24 #include <cstdio> // for printf, SEEK_SET, SEEK_CUR, SEEK_END
25
26 #include <QtCore/QDate> // for QDate
27 #include <QtCore/QDateTime> // for QDateTime
28 #include <QtCore/QDebug> // for QDebug
29 #include <QtCore/QString> // for QString
30 #include <QtCore/QTime> // for QTime
31 #include <QtCore/QTimeZone> // for QTimeZone
32 #include <QtCore/Qt> // for UTC
33
34 #include "defs.h"
35 #include "energympro.h"
36 #include "gbfile.h" // for gbfgetc, gbfseek, gbfclose, gbfopen, gbfread, gbfgetuint32, gbfcopyfrom, gbfgetuint16, gbfile, gbsize_t
37 #include "src/core/datetime.h" // for DateTime
38
39
40 #define MYNAME "energympro"
41
42 //*******************************************************************************
43 // local helper functions
44 //*******************************************************************************
45 void
read_point(route_head * gpsbabel_route,gpsbabel::DateTime & gpsDateTime) const46 EnergymproFormat::read_point(route_head* gpsbabel_route, gpsbabel::DateTime& gpsDateTime) const
47 {
48 tw_point point{};
49 gbfread(&point, sizeof(tw_point), 1, file_in);
50 if (global_opts.debug_level > 1) {
51 printf("Point: lat:%8u long:%8u alt:%8d ", point.Latitude, point.Longitude, point.Altitude);
52 printf("speed:%6u dist:%5u time:%5u Status:%1u", point.Speed, point.IntervalDist, point.lntervalTime, point.Status);
53 printf("HR:(%3d,%1d)", point.HR_Heartrate, point.HR_Status);
54 printf("Speed:(%8u,%1d)", point.Speed_Speed, point.Speed_Status);
55 printf("Cad:(%3d,%1d)", point.Cadence_Cadence, point.Cadence_Status);
56 printf("Power (Cad:%6d Pow:%6d,%2d)Temp:%3d\n", point.Power_Cadence, point.Power_Power, point.Power_Status, point.Temp);
57
58 qDebug() << "DateTime1:" << gpsDateTime.toString();
59 qDebug() << "point.lntervalTime:" << point.lntervalTime;
60 }
61
62 //Time from last point in sec's * 10 (e.g. point.lntervalTime is sec multiplied with 10)
63 // convert to millisecs
64 gpsDateTime = gpsDateTime.addMSecs(point.lntervalTime*100);
65
66 auto waypt = new Waypoint;
67 waypt->latitude = (point.Latitude / 1000000.0);
68 waypt->longitude = (point.Longitude / 1000000.0);
69 waypt->altitude = point.Altitude;
70
71 if (global_opts.debug_level > 1) {
72 qDebug() << "DateTime2:" << gpsDateTime.toString();
73 }
74
75 waypt->SetCreationTime(gpsDateTime);
76
77 if (point.Speed_Status == 0) {
78 WAYPT_SET(waypt, speed, point.Speed_Speed / 100.0f);
79 }
80 if (point.HR_Status == 0) {
81 waypt->heartrate = point.HR_Heartrate;
82 }
83 if (point.Cadence_Status == 0) {
84 waypt->cadence = point.Cadence_Cadence;
85 }
86 if (point.Power_Status == 0) {
87 waypt->power = point.Power_Power;
88 }
89 WAYPT_SET(waypt, temperature, point.Temp);
90 track_add_wpt(gpsbabel_route, waypt);
91 }
92
93
94 void
read_lap() const95 EnergymproFormat::read_lap() const
96 {
97 tw_lap lap{};
98 gbfread(&lap, sizeof(tw_lap), 1, file_in);
99 if (global_opts.debug_level > 1) {
100 printf("LAP: splitTime:%6us TotalTime:%6us LapNumber:%5d ", lap.splitTime/10, lap.TotalTime/10, lap.Number);
101 printf("dist:%08um Cal:%5u Speed:(%6u,%6u) ", lap.lDistance, lap.Calorie, lap.MaxSpeed, lap.AvgSpeed);
102 printf("HR:(%3d,%3d)", lap.MaxHeartrate, lap.AvgHeartrate);
103 printf("Alt:(%6d,%6d) ", lap.MinAlti, lap.MaxAlti);
104 printf("Cad:(%3d,%3d) ", lap.AvgCad, lap.MaxCad);
105 printf("Power:(%3d,%3d)w ", lap.AvgPower, lap.MaxPower);
106 printf("Pt:(%6d,%6d)\n", lap.StartRecPt, lap.FinishRecPt);
107 }
108 }
109
110 //*******************************************************************************
111 // global callbacks called by gpsbabel main process
112 //*******************************************************************************
113
114 void
rd_init(const QString & fname)115 EnergymproFormat::rd_init(const QString& fname)
116 {
117 if (global_opts.debug_level > 1) {
118 printf(MYNAME " rd_deinit()\n");
119 }
120 gbfile* fileorg_in = gbfopen(fname, "rb", MYNAME);
121
122 /* copy file to memory stream (needed for seek-ops and piped commands) */
123 file_in = gbfopen(nullptr, "wb", MYNAME);
124 gbsize_t size = gbfcopyfrom(file_in, fileorg_in, 0x7FFFFFFF);
125 if (global_opts.debug_level > 1) {
126 printf(MYNAME " filesize=%u\n", size);
127 }
128 gbfclose(fileorg_in);
129 if (opt_timezone) {
130 if (QTimeZone::isTimeZoneIdAvailable(opt_timezone)) {
131 timezn = new QTimeZone(opt_timezone);
132 } else {
133 list_timezones();
134 fatal(MYNAME ": Requested time zone \"%s\" is not available.\n", opt_timezone);
135 }
136 } else {
137 timezn = nullptr;
138 }
139 }
140
141 void
rd_deinit()142 EnergymproFormat::rd_deinit()
143 {
144 if (timezn != nullptr) {
145 delete timezn;
146 timezn = nullptr;
147 }
148 if (global_opts.debug_level > 1) {
149 printf(MYNAME " rd_deinit()\n");
150 }
151 gbfclose(file_in);
152 }
153
154 void
track_read()155 EnergymproFormat::track_read()
156 {
157 if (global_opts.debug_level > 1) {
158 printf(MYNAME " waypoint_read()\n");
159 }
160
161 gbfseek(file_in, 0L, SEEK_END);
162 gbfseek(file_in, -(int32_t)(sizeof(tw_workout)), SEEK_CUR);
163 tw_workout workout{};
164 workout.dateStart.Year = gbfgetc(file_in);
165 workout.dateStart.Month = gbfgetc(file_in);
166 workout.dateStart.Day = gbfgetc(file_in);
167 workout.timeStart.Hour = gbfgetc(file_in);
168 workout.timeStart.Minute = gbfgetc(file_in);
169 workout.timeStart.Second = gbfgetc(file_in);
170 workout.TotalRecPt = gbfgetuint16(file_in);
171 workout.TotalTime = gbfgetuint32(file_in);
172 workout.TotalDist = gbfgetuint32(file_in);
173 workout.LapNumber = gbfgetuint16(file_in);
174 workout.Calory = gbfgetuint16(file_in);
175 workout.MaxSpeed = gbfgetuint32(file_in);
176 workout.AvgSpeed = gbfgetuint32(file_in);
177 workout.MaxHeart = gbfgetc(file_in);
178 workout.AvgHeart = gbfgetc(file_in);
179
180 if (global_opts.debug_level > 1) {
181 printf("%04d-%02d-%02d ", workout.dateStart.Year+2000, workout.dateStart.Month, workout.dateStart.Day);
182 printf("%02d:%02d:%02d ", workout.timeStart.Hour, workout.timeStart.Minute, workout.timeStart.Second);
183 printf("Total(RecPt:%6d Time:%6us Dist:%9um) LapNumber:%5d \n", workout.TotalRecPt, workout.TotalTime/10, workout.TotalDist, workout.LapNumber);
184 }
185
186 /*
187 * GPS year: 2000+; struct tm year: 1900+
188 */
189 QDate gpsDate = QDate(workout.dateStart.Year+2000, workout.dateStart.Month, workout.dateStart.Day);
190 QTime gpsTime = QTime(workout.timeStart.Hour, workout.timeStart.Minute, workout.timeStart.Second);
191 gpsbabel::DateTime gpsDateTime;
192 if (timezn != nullptr) {
193 gpsDateTime = gpsbabel::DateTime(QDateTime(gpsDate, gpsTime, *timezn).toUTC());
194 } else {
195 gpsDateTime = gpsbabel::DateTime(QDateTime(gpsDate, gpsTime, Qt::LocalTime).toUTC());
196 }
197
198 auto* gpsbabel_route = new route_head;
199
200 track_add_head(gpsbabel_route);
201 gbfseek(file_in, 0L, SEEK_SET);
202
203 for (int point=0; point<workout.TotalRecPt; point++) {
204 read_point(gpsbabel_route, gpsDateTime);
205 }
206
207 gbfseek(file_in, sizeof(tw_point)*(workout.TotalRecPt), SEEK_SET);
208 for (int lap=0; lap<workout.LapNumber; lap++) {
209 read_lap();
210 }
211 }
212
213 void
read()214 EnergymproFormat::read()
215 {
216 if (global_opts.debug_level > 1) {
217 printf(MYNAME " data_read()\n");
218 }
219
220 track_read();
221 }
222