Line | Hits | Source |
---|---|---|
1 | package net.sf.tourviewer.ciclo; | |
2 | ||
3 | import java.io.IOException; | |
4 | import java.io.InputStream; | |
5 | import java.io.PrintStream; | |
6 | import java.util.Calendar; | |
7 | import net.sf.tourviewer.Bike; | |
8 | import net.sf.tourviewer.Marker; | |
9 | import net.sf.tourviewer.Tour; | |
10 | import net.sf.tourviewer.TourRecord; | |
11 | import net.sf.tourviewer.TourSet; | |
12 | ||
13 | 0 | public class CicloReader |
14 | { | |
15 | ||
16 | private static final String FILE_SIGNATURE = "AFRO"; | |
17 | ||
18 | private static final int CCHAC4_SIGNATURE = 0xB735; | |
19 | private static final int CM414M_SIGNATURE = 0xB723; | |
20 | ||
21 | private static final int RECORD_COUNT = 2048; | |
22 | private static final int WORDS_PER_RECORD = 8; | |
23 | private static final int BITS_PER_WORD = 32; | |
24 | ||
25 | private static final int INFO_RECORD_INDEX = 16; | |
26 | private static final int TOUR_RECORD_INDEX = 19; | |
27 | private static final int LAST_INDEX = RECORD_COUNT - 1; | |
28 | ||
29 | private static final int DATA_RECORD_INTERVAL = 20; | |
30 | ||
31 | 0 | private Record[] records = new Record[RECORD_COUNT]; |
32 | ||
33 | public boolean accept(InputStream in) throws IOException { | |
34 | 0 | byte[] buffer = new byte[5]; |
35 | 0 | in.mark(5); |
36 | 0 | in.read(buffer); |
37 | ||
38 | try { | |
39 | 0 | return FILE_SIGNATURE.equalsIgnoreCase(new String(buffer, 0, 4)); |
40 | } | |
41 | 0 | finally { |
42 | 0 | in.reset(); |
43 | 0 | } |
44 | } | |
45 | ||
46 | public TourSet read(InputStream in) throws IOException { | |
47 | 0 | int checksum = 0, lastValue = 0; |
48 | 0 | byte[] buffer = new byte[5]; |
49 | 0 | int[] data = new int[32768]; |
50 | 0 | int offset = 0; |
51 | ||
52 | 0 | in.read(buffer); |
53 | 0 | if (!FILE_SIGNATURE.equalsIgnoreCase(new String(buffer, 0, 4))) { |
54 | 0 | throw new IOException("Invalid file signature, expected " + FILE_SIGNATURE); |
55 | } | |
56 | ||
57 | 0 | StringBuffer recordBuffer = new StringBuffer(32); |
58 | 0 | while (in.read(buffer) != -1) { |
59 | 0 | checksum = (checksum + lastValue) & 0xFFFF; |
60 | ||
61 | 0 | String bufferText = new String(buffer, 0, 4); |
62 | 0 | lastValue = Integer.parseInt(bufferText, 16); |
63 | ||
64 | 0 | if (offset < data.length) { |
65 | 0 | data[offset] = lastValue; |
66 | } | |
67 | 0 | else if (offset > data.length) { |
68 | 0 | throw new IOException("File is too long, expected 81930 byte"); |
69 | } | |
70 | 0 | offset ++; |
71 | } | |
72 | ||
73 | 0 | System.out.println("Calculated Value: " + Integer.toHexString(checksum)); |
74 | 0 | System.out.println("File Checksum : " + Integer.toHexString(lastValue)); |
75 | ||
76 | 0 | return analyze(data); |
77 | } | |
78 | ||
79 | public void print(PrintStream out) | |
80 | { | |
81 | 0 | for (int i = 0; i < records.length; i++) { |
82 | 0 | System.out.printf("%04d: ", i); |
83 | 0 | if (records[i] != null) { |
84 | 0 | out.println(records[i]); |
85 | } | |
86 | else { | |
87 | 0 | out.println(); |
88 | } | |
89 | } | |
90 | 0 | } |
91 | ||
92 | private TourSet analyze(int[] data) | |
93 | { | |
94 | 0 | if (data[128] == CCHAC4_SIGNATURE) { |
95 | 0 | return analyzeCCHAC4(data); |
96 | } | |
97 | 0 | else if (data[128] == CM414M_SIGNATURE) { |
98 | 0 | return analyzeCM414M(data); |
99 | } | |
100 | 0 | return null; |
101 | } | |
102 | ||
103 | private TourSet analyzeCM414M(int[] data) | |
104 | { | |
105 | 0 | if (data.length != 32768) { |
106 | 0 | throw new IllegalArgumentException("Expected data.length == 32768"); |
107 | } | |
108 | ||
109 | // prepare records | |
110 | 0 | records[INFO_RECORD_INDEX] = new CM414MInfo1Record(getRecordData(data, INFO_RECORD_INDEX)); |
111 | 0 | records[INFO_RECORD_INDEX + 1] = new CM414MInfo2Record(getRecordData(data, INFO_RECORD_INDEX + 1)); |
112 | 0 | records[INFO_RECORD_INDEX + 2] = new CM414MInfo3Record(getRecordData(data, INFO_RECORD_INDEX + 2)); |
113 | 0 | analyzeTourData(data); |
114 | ||
115 | // parse records | |
116 | 0 | TourSet tourSet = new TourSet(); |
117 | ||
118 | 0 | CM414MInfo1Record info1Record = (CM414MInfo1Record)records[INFO_RECORD_INDEX]; |
119 | 0 | tourSet.setDeviceName("CM 414 Alti M/CM 436 Alti M"); |
120 | 0 | tourSet.setPersonWeight(info1Record.getWeight()); |
121 | ||
122 | 0 | CM414MInfo2Record info2Record = (CM414MInfo2Record)records[INFO_RECORD_INDEX + 1]; |
123 | 0 | CM414MInfo3Record info3Record = (CM414MInfo3Record)records[INFO_RECORD_INDEX + 2]; |
124 | ||
125 | 0 | Bike bike = new Bike(); |
126 | 0 | bike.setDistance(info3Record.getTotalDistance1()); |
127 | 0 | bike.setTravelTime(info3Record.getTotalTravelTime1()); |
128 | 0 | bike.setWheelPerimeter(info1Record.getWheelPerimeter1()); |
129 | 0 | tourSet.addBike(bike); |
130 | ||
131 | 0 | bike = new Bike(); |
132 | 0 | bike.setDistance(info3Record.getTotalDistance2()); |
133 | 0 | bike.setTravelTime(info3Record.getTotalTravelTime2()); |
134 | 0 | bike.setWheelPerimeter(info1Record.getWheelPerimeter2()); |
135 | 0 | tourSet.addBike(bike); |
136 | ||
137 | 0 | parseTourRecords(tourSet, info2Record.getLastDDOffset()); |
138 | ||
139 | 0 | return tourSet; |
140 | } | |
141 | ||
142 | private TourSet analyzeCCHAC4(int[] data) | |
143 | { | |
144 | 0 | if (data.length != 32768) { |
145 | 0 | throw new IllegalArgumentException("Expected data.length == 32768"); |
146 | } | |
147 | ||
148 | // prepare records | |
149 | 0 | records[INFO_RECORD_INDEX] = new HAC4Info1Record(getRecordData(data, INFO_RECORD_INDEX)); |
150 | 0 | records[INFO_RECORD_INDEX + 1] = new HAC4Info2Record(getRecordData(data, INFO_RECORD_INDEX + 1)); |
151 | 0 | records[INFO_RECORD_INDEX + 2] = new HAC4Info3Record(getRecordData(data, INFO_RECORD_INDEX + 2)); |
152 | 0 | analyzeTourData(data); |
153 | ||
154 | // parse records | |
155 | 0 | TourSet tourSet = new TourSet(); |
156 | ||
157 | 0 | HAC4Info1Record info1Record = (HAC4Info1Record)records[INFO_RECORD_INDEX]; |
158 | 0 | tourSet.setDeviceName("HAC4"); |
159 | 0 | tourSet.setPersonWeight(info1Record.getWeight()); |
160 | ||
161 | 0 | HAC4Info2Record info2Record = (HAC4Info2Record)records[INFO_RECORD_INDEX + 1]; |
162 | 0 | HAC4Info3Record info3Record = (HAC4Info3Record)records[INFO_RECORD_INDEX + 2]; |
163 | ||
164 | 0 | Bike bike = new Bike(); |
165 | 0 | bike.setDistance(info2Record.getTotalDistance()); |
166 | 0 | bike.setTravelTime(info3Record.getTotalTravelTime()); |
167 | 0 | bike.setWheelPerimeter(info1Record.getWheelPerimeter()); |
168 | 0 | tourSet.addBike(bike); |
169 | ||
170 | 0 | parseTourRecords(tourSet, info3Record.getLastDDOffset()); |
171 | ||
172 | 0 | return tourSet; |
173 | } | |
174 | ||
175 | private void parseTourRecords(TourSet tourSet, int lastDDOffset) | |
176 | { | |
177 | 0 | Bike[] bikes = tourSet.getBikes(); |
178 | 0 | if (bikes == null) { |
179 | 0 | throw new IllegalArgumentException("Bikes may not be null or empty"); |
180 | } | |
181 | ||
182 | 0 | int index = offsetToIndex(lastDDOffset); |
183 | 0 | int startIndex = index; |
184 | 0 | int lastIndex = index; |
185 | ||
186 | 0 | while ((index < startIndex) |
187 | 0 | ? (index < lastIndex) |
188 | 0 | : (unwrapIndex(index) < startIndex && unwrapIndex(index) < lastIndex)) { |
189 | 0 | if (records[index] instanceof DDRecord) { |
190 | 0 | DDRecord endRecord = (DDRecord)records[index]; |
191 | ||
192 | 0 | index = offsetToIndex(endRecord.getAAOffset()); |
193 | 0 | if (records[index] instanceof AARecord) { |
194 | 0 | AARecord startRecord = (AARecord)records[index]; |
195 | 0 | Tour tour = parseTour(startRecord, index); |
196 | 0 | if (tour != null) { |
197 | 0 | setTourType(tour, startRecord, bikes); |
198 | 0 | tourSet.addTour(tour); |
199 | } | |
200 | 0 | lastIndex = (index < startIndex) ? index : unwrapIndex(index); |
201 | 0 | index = decTourIndex(index); |
202 | } | |
203 | else { | |
204 | 0 | return; |
205 | } | |
206 | } | |
207 | else { | |
208 | 0 | return; |
209 | } | |
210 | } | |
211 | 0 | } |
212 | ||
213 | private void setTourType(Tour tour, AARecord startRecord, Bike[] bikes) | |
214 | { | |
215 | 0 | AARecord.TourType type = startRecord.getTourType(); |
216 | 0 | if (type == AARecord.TourType.BIKE || type == AARecord.TourType.BIKE1) { |
217 | 0 | tour.setType(Tour.Type.BIKE); |
218 | 0 | if (bikes.length >= 1) { |
219 | 0 | tour.setBike(bikes[0]); |
220 | } | |
221 | } | |
222 | 0 | else if (type == AARecord.TourType.SKI_BIKE) { |
223 | 0 | tour.setType(Tour.Type.SKI_BIKE); |
224 | 0 | if (bikes.length >= 1) { |
225 | 0 | tour.setBike(bikes[0]); |
226 | } | |
227 | } | |
228 | 0 | else if (type == AARecord.TourType.BIKE2) { |
229 | 0 | tour.setType(Tour.Type.BIKE); |
230 | 0 | if (bikes.length >= 2) { |
231 | 0 | tour.setBike(bikes[1]); |
232 | } | |
233 | } | |
234 | 0 | else if (type == AARecord.TourType.JOGGING) { |
235 | 0 | tour.setType(Tour.Type.JOGGING); |
236 | ||
237 | } | |
238 | 0 | } |
239 | ||
240 | private int unwrapIndex(int index) | |
241 | { | |
242 | 0 | return TOUR_RECORD_INDEX - (LAST_INDEX - index); |
243 | } | |
244 | ||
245 | private Tour parseTour(AARecord startRecord, int startIndex) | |
246 | { | |
247 | 0 | Calendar cal = Calendar.getInstance(); |
248 | ||
249 | 0 | Tour tour = new Tour(); |
250 | 0 | cal.set(Calendar.MONTH, startRecord.getTimeMonth() - 1); |
251 | 0 | cal.set(Calendar.DAY_OF_MONTH, startRecord.getTimeDay()); |
252 | 0 | cal.set(Calendar.HOUR_OF_DAY, startRecord.getTimeHour()); |
253 | 0 | cal.set(Calendar.MINUTE, startRecord.getTimeMinute()); |
254 | 0 | tour.setStartTime(cal.getTime()); |
255 | ||
256 | 0 | TourRecord prevRecord = new TourRecord(); |
257 | 0 | prevRecord.setAltitude(startRecord.getInitialAltitude()); |
258 | 0 | prevRecord.setDistance(0); |
259 | 0 | prevRecord.setPulse(startRecord.getInitialPulse()); |
260 | 0 | prevRecord.setTime(tour.getStartTime()); |
261 | 0 | tour.addRecord(prevRecord); |
262 | ||
263 | 0 | boolean valid = false; |
264 | 0 | int index = startIndex; |
265 | 0 | while (true) { |
266 | 0 | index = incTourIndex(index); |
267 | 0 | if (records[index] instanceof BBRecord) { |
268 | 0 | BBRecord dataRecord = (BBRecord)records[index]; |
269 | 0 | if (!valid) { |
270 | 0 | valid = true; |
271 | // the first record always has the temperature of the | |
272 | // first data record | |
273 | 0 | prevRecord.setTemperature(dataRecord.getTemperature()); |
274 | } | |
275 | 0 | if (dataRecord.getMarker() != 0) { |
276 | 0 | cal.add(Calendar.SECOND, dataRecord.getMarker()); |
277 | 0 | tour.addMarker(new Marker(cal.getTime())); |
278 | 0 | cal.add(Calendar.SECOND, -dataRecord.getMarker()); |
279 | } | |
280 | 0 | prevRecord = addTourRecord(cal, tour, prevRecord, dataRecord.getDataRecords(), |
281 | 0 | dataRecord.getCadence(), dataRecord.getTemperature()); |
282 | } | |
283 | 0 | else if (records[index] instanceof CCRecord) { |
284 | 0 | if (!valid) { |
285 | // tour contains no data | |
286 | 0 | return null; |
287 | } | |
288 | ||
289 | 0 | CCRecord dataRecord = (CCRecord)records[index]; |
290 | 0 | cal.add(Calendar.SECOND, dataRecord.getEndMarker()); |
291 | 0 | tour.setEndTime(cal.getTime()); |
292 | 0 | cal.add(Calendar.SECOND, -dataRecord.getEndMarker()); |
293 | // FIXME: do not add all 6 records | |
294 | 0 | prevRecord = addTourRecord(cal, tour, prevRecord, dataRecord.getDataRecords(), |
295 | 0 | dataRecord.getCadence(), dataRecord.getTemperature()); |
296 | 0 | tour.setDistance(prevRecord.getDistance()); |
297 | ||
298 | 0 | index = incTourIndex(index); |
299 | 0 | if (records[index] instanceof DDRecord) { |
300 | 0 | DDRecord endRecord = (DDRecord)records[index]; |
301 | 0 | return tour; |
302 | } | |
303 | else { | |
304 | 0 | return null; |
305 | } | |
306 | } | |
307 | else { | |
308 | 0 | return null; |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | private TourRecord addTourRecord(Calendar cal, Tour tour, TourRecord prevRecord, DataRecord[] data, int cadence, int temperature) | |
314 | { | |
315 | 0 | for (int i = 0; i < data.length; i++) { |
316 | 0 | TourRecord record = new TourRecord(); |
317 | 0 | record.setCadence(cadence); |
318 | 0 | record.setTemperature(temperature); |
319 | 0 | record.setAltitude(prevRecord.getAltitude() + data[i].getAltitudeDelta()); |
320 | 0 | record.setDistance(prevRecord.getDistance() + data[i].getDistanceDelta()); |
321 | 0 | record.setPulse(prevRecord.getPulse() + data[i].getPulseDelta()); |
322 | 0 | cal.add(Calendar.SECOND, DATA_RECORD_INTERVAL); |
323 | 0 | record.setTime(cal.getTime()); |
324 | 0 | tour.addRecord(record); |
325 | 0 | prevRecord = record; |
326 | } | |
327 | 0 | return prevRecord; |
328 | } | |
329 | ||
330 | private int decTourIndex(int index) | |
331 | { | |
332 | 0 | return (index == TOUR_RECORD_INDEX) ? LAST_INDEX : index - 1; |
333 | } | |
334 | ||
335 | private int incTourIndex(int index) | |
336 | { | |
337 | 0 | return (index == LAST_INDEX) ? TOUR_RECORD_INDEX : index + 1; |
338 | } | |
339 | ||
340 | protected void analyzeTourData(int[] data) | |
341 | { | |
342 | 0 | for (int i = TOUR_RECORD_INDEX; i < RECORD_COUNT; i++) { |
343 | 0 | int[] recordData = getRecordData(data, i); |
344 | 0 | int type = recordData[0] & 0xFF; |
345 | 0 | if (type == 0xAA) { |
346 | 0 | records[i] = new AARecord(recordData); |
347 | } | |
348 | 0 | else if (type == 0xBB) { |
349 | 0 | records[i] = new BBRecord(recordData); |
350 | } | |
351 | 0 | else if (type == 0xCC) { |
352 | 0 | records[i] = new CCRecord(recordData); |
353 | } | |
354 | 0 | else if (type == 0xDD) { |
355 | 0 | records[i] = new DDRecord(recordData); |
356 | } | |
357 | else { | |
358 | 0 | records[i] = new UnknownRecord(recordData); |
359 | } | |
360 | } | |
361 | 0 | } |
362 | ||
363 | private int[] getRecordData(int[] data, int index) | |
364 | { | |
365 | 0 | int[] recordData = new int[WORDS_PER_RECORD]; |
366 | 0 | System.arraycopy(data, index * WORDS_PER_RECORD, recordData, 0, WORDS_PER_RECORD); |
367 | 0 | return recordData; |
368 | } | |
369 | ||
370 | private int offsetToIndex(int offset) | |
371 | { | |
372 | 0 | return offset / WORDS_PER_RECORD / 2; |
373 | } | |
374 | ||
375 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |