Mercurial > public > ostc4
annotate Discovery/Src/logbook.c @ 266:fd47e5631b9d IPC_Sync_Improvment_2
Added batch to copy bins from release folder
author | ideenmodellierer |
---|---|
date | Sun, 14 Apr 2019 14:27:32 +0200 |
parents | 2bb1db22b5f5 |
children | 6e78137952af |
rev | line source |
---|---|
38 | 1 /** |
2 ****************************************************************************** | |
3 * @copyright heinrichs weikamp | |
4 * @file logbook.c | |
5 * @author heinrichs weikamp gmbh and heinrichs weikamp gmbh | |
6 * @date 22-Apr-2014 | |
7 * @version V0.0.3 | |
8 * @since 03-Feb-2016 | |
9 * @brief Everything about creating and evaluating the logbook | |
10 * without the flash part which is included in externLogbookFlash.c | |
11 * and the USB/Bluetooth part in tComm.c | |
12 * CHANGE V0.0.3 hw: ppO2 sensor values | |
13 * CHANGE V0.0.4 hw: fix event bytes according to hwos_interface.odt dated 160610 in bitbucket hwOS | |
14 * @bug | |
15 * @warning | |
16 @verbatim | |
17 ============================================================================== | |
18 ##### Header ##### | |
19 ============================================================================== | |
20 [..] SLogbookHeader | |
21 The order has changed in comparsion to OSTC3 for perfect alignment | |
22 with 16bit and 32bit. The header is 256kB as well. | |
23 DO NOT rearrange anything but add new data to a second page | |
24 beyond diveHeaderEnd. Use extraPagesWithData to indicate that there is | |
25 data available that was not available in the OSTC3 256KB | |
26 This data will be behind the diveHeaderEnd. DO NOT delete diveHeaderEnd. | |
27 | |
28 [..] SLogbookHeaderOSTC3 | |
29 is the format used by the OSTC3. | |
30 logbook_getHeaderOSTC3() does the job using the global headers in logbook.c | |
31 | |
32 [..] SSmallHeader | |
33 - is the format used by the OSTC3 | |
34 | |
35 [..] Summary | |
36 The header format is not perfect and might be optimized prior to | |
37 releasing the diving computer. For now it is good to be compatible | |
38 with PC software available for checking the content of the logbook | |
39 | |
40 | |
41 @endverbatim | |
42 ****************************************************************************** | |
43 * @attention | |
44 * | |
45 * <h2><center>© COPYRIGHT(c) 2014 heinrichs weikamp</center></h2> | |
46 * | |
47 ****************************************************************************** | |
48 */ | |
49 | |
50 /* Includes ------------------------------------------------------------------*/ | |
51 #include <stdint.h> | |
52 #include <string.h> | |
53 #include "logbook.h" | |
54 //#include "test_vpm.h" | |
55 #include "externLogbookFlash.h" | |
56 #include "data_exchange.h" | |
57 #include "decom.h" | |
58 #include "tHome.h" // for tHome_findNextStop() | |
59 | |
60 /* Private types -------------------------------------------------------------*/ | |
61 | |
62 #define NUM_GASES 5 | |
63 | |
64 #define LOGBOOK_VERSION (0x30) | |
65 #define LOGBOOK_VERSION_OSTC3 (0x24) | |
66 | |
67 typedef struct /* don't forget to adjust void clear_divisor(void) */ | |
68 { | |
69 uint8_t temperature; | |
70 uint8_t deco_ndl; | |
71 uint8_t gradientFactor; | |
72 uint8_t ppo2; | |
73 uint8_t decoplan; | |
74 uint8_t cns; | |
75 uint8_t tank; | |
76 } SDivisor; | |
77 | |
78 /* Exported variables --------------------------------------------------------*/ | |
79 | |
80 /* Private variables ---------------------------------------------------------*/ | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
81 static SLogbookHeader header; |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
82 static SLogbookHeaderOSTC3 headerOSTC3; |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
83 static SLogbookHeaderOSTC3compact headerOSTC3compact; |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
84 static SSmallHeader smallHeader; |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
85 static SDivisor divisor; |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
86 static SDivisor divisorBackup; |
38 | 87 |
88 /* Private function prototypes -----------------------------------------------*/ | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
89 static void clear_divisor(void); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
90 static void logbook_SetAverageDepth(float average_depth_meter); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
91 static void logbook_SetMinTemperature(float min_temperature_celsius); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
92 static void logbook_SetMaxCNS(float max_cns_percentage); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
93 static void logbook_SetCompartmentDesaturation(void); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
94 static void logbook_SetLastStop(float last_stop_depth_bar); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
95 static void logbook_writedata(void * data, int length_byte); |
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
96 static void logbook_UpdateHeader(void); |
38 | 97 |
98 /* Exported functions --------------------------------------------------------*/ | |
99 | |
100 void logbook_EndDive(void) | |
101 { | |
102 ext_flash_close_new_dive_log((uint8_t*) &header); | |
103 } | |
104 | |
105 | |
106 // =============================================================================== | |
107 // logbook_last_totalDiveCount | |
108 /// @brief Fix setting issues | |
109 /// @date 04-April-2016 | |
110 /// | |
111 /// @return diveNumber (totalDiveCounter) of latest log entry, 0 if not a valid header | |
112 // =============================================================================== | |
113 uint16_t logbook_lastDive_diveNumber(void) | |
114 { | |
115 SLogbookHeader tempLogbookHeader; | |
116 if(logbook_getHeader(0, &tempLogbookHeader)) | |
117 { | |
118 return tempLogbookHeader.diveNumber; | |
119 } | |
120 else | |
121 { | |
122 return 0; | |
123 } | |
124 } | |
125 | |
126 | |
127 /** | |
128 ****************************************************************************** | |
129 * @brief logbook_getCurrentHeader. / | |
130 * @author Peter Ryser | |
131 * @version V0.0.1 | |
132 * @date 22-April-2014 | |
133 ****************************************************************************** | |
134 * | |
135 * @return SLogbookHeader*: | |
136 */ | |
137 SLogbookHeader* logbook_getCurrentHeader(void) | |
138 { | |
139 return &header; | |
140 } | |
141 | |
142 /** | |
143 ****************************************************************************** | |
144 * @brief logbook_getNumberOfHeaders. / | |
145 * @author heinrichs weikamp gmbh | |
146 * @version V0.0.1 | |
147 * @date 18-May-2016 | |
148 ****************************************************************************** | |
149 * | |
150 * @return uint8_t : number of valid headers (0xFAFA) found. | |
151 */ | |
152 uint8_t logbook_getNumberOfHeaders(void) | |
153 { | |
154 return ext_flash_count_dive_headers(); | |
155 } | |
156 | |
157 | |
158 /** | |
159 ****************************************************************************** | |
160 * @brief logbook_getHeader. / | |
161 * @author Peter Ryser | |
162 * @version V0.0.1 | |
163 * @date 22-April-2014 | |
164 ****************************************************************************** | |
165 * | |
166 * @param StepBackwards : 0 Last lokbook entry, 1 second to last entry, etc. | |
167 * @param SSLogbookHeader* pLogbookHeader: Output found LogbookHeader | |
168 * @return uint8_t : 1 = success | |
169 */ | |
170 uint8_t logbook_getHeader(uint8_t StepBackwards,SLogbookHeader* pLogbookHeader) | |
171 { | |
172 ext_flash_read_dive_header((uint8_t *)pLogbookHeader, StepBackwards); | |
173 if(pLogbookHeader->diveHeaderStart != 0xFAFA) | |
174 return 0; | |
175 else | |
176 return 1; | |
177 } | |
178 | |
179 /** | |
180 ****************************************************************************** | |
181 * @brief logbook_initNewdiveProfile. / | |
182 * creates header and smallHeader from diveState and global Settings | |
183 * and writes new lookboock entry on flash device | |
184 * diveState | |
185 * @author Peter Ryser | |
186 * @version V0.0.1 | |
187 * @date 22-April-2014 | |
188 ****************************************************************************** | |
189 * | |
190 * @param SDiveState* pInfo: Input | |
191 * @param SSettings* pSettings: Input | |
192 */ | |
193 | |
194 void logbook_initNewdiveProfile(const SDiveState* pInfo, SSettings* pSettings) | |
195 { | |
196 RTC_DateTypeDef Sdate; | |
197 RTC_TimeTypeDef Stime; | |
198 | |
199 for(int i = 0; i < sizeof(SLogbookHeader); i++) | |
200 { | |
201 ((uint8_t*)(&header))[i] = 0; | |
202 } | |
203 header.diveHeaderStart = 0xFAFA; | |
204 header.diveHeaderEnd = 0xFBFB; | |
205 header.samplingRate = 2; | |
206 if(pInfo->diveSettings.diveMode == DIVEMODE_OC) | |
207 { | |
208 for(int i = 0; i < 5; i++) | |
209 { | |
210 header.gasordil[i].oxygen_percentage = pSettings->gas[i+1].oxygen_percentage; | |
211 header.gasordil[i].helium_percentage = pSettings->gas[i+1].helium_percentage; | |
212 header.gasordil[i].note.uw = pSettings->gas[i+1].note.uw; | |
213 header.gasordil[i].depth_meter = pSettings->gas[i+1].depth_meter; | |
214 } | |
215 } | |
216 else | |
217 { | |
218 for(int i = 0; i < 5; i++) | |
219 { | |
220 header.gasordil[i].oxygen_percentage = pSettings->gas[i+6].oxygen_percentage; | |
221 header.gasordil[i].helium_percentage = pSettings->gas[i+6].helium_percentage; | |
222 header.gasordil[i].note.uw = pSettings->gas[i+6].note.uw; | |
223 header.gasordil[i].depth_meter = pSettings->gas[i+6].depth_meter; | |
224 } | |
225 | |
226 for(int i = 0; i < 5; i++) | |
227 { | |
228 header.setpoint[i].setpoint_cbar = pSettings->setpoint[i+1].setpoint_cbar; | |
229 header.setpoint[i].depth_meter = pSettings->setpoint[i+1].depth_meter; | |
230 } | |
231 } | |
232 // header.gasordil[pInfo->lifeData.actualGas.GasIdInSettings].depth_meter = 0; | |
233 | |
234 translateDate(pInfo->lifeData.dateBinaryFormat, &Sdate); | |
235 translateTime(pInfo->lifeData.timeBinaryFormat, &Stime); | |
236 header.dateYear = Sdate.Year; | |
237 header.dateMonth = Sdate.Month; | |
238 header.dateDay = Sdate.Date; | |
239 header.timeHour = Stime.Hours; | |
240 header.timeMinute = Stime.Minutes; | |
199
ac58a9fb92ac
Bugfix: fix initial CNS data in the logbook header
Jan Mulder <jlmulder@xs4all.nl>
parents:
194
diff
changeset
|
241 header.cnsAtBeginning = (uint16_t)pInfo->lifeData.cns; |
38 | 242 header.surfacePressure_mbar = (uint16_t)(pInfo->lifeData.pressure_surface_bar * 1000); |
243 header.firmwareVersionHigh = firmwareVersion_16bit_high(); | |
244 header.firmwareVersionLow = firmwareVersion_16bit_low(); | |
245 header.logbookProfileVersion = LOGBOOK_VERSION; | |
246 header.salinity = pSettings->salinity; | |
247 header.diveNumber = pSettings->totalDiveCounter; | |
248 header.personalDiveCount = pSettings->personalDiveCount; | |
249 | |
250 header.diveMode = pInfo->diveSettings.diveMode; | |
251 header.CCRmode = pInfo->diveSettings.CCR_Mode; | |
252 header.lastDecostop_m = pSettings->last_stop_depth_meter; | |
253 | |
254 if(pInfo->diveSettings.deco_type.ub.standard == GF_MODE) | |
255 { | |
256 header.decoModel = 1; | |
257 header.gfLow_or_Vpm_conservatism = pInfo->diveSettings.gf_low; | |
258 header.gfHigh = pInfo->diveSettings.gf_high; | |
259 } | |
260 else | |
261 { | |
262 header.decoModel = 2; | |
263 header.gfLow_or_Vpm_conservatism = pInfo->diveSettings.vpm_conservatism; | |
264 header.gfHigh = 0; | |
265 } | |
266 | |
267 memcpy(header.n2Compartments, pInfo->lifeData.tissue_nitrogen_bar, 64); | |
268 memcpy(header.heCompartments, pInfo->lifeData.tissue_helium_bar, 64); | |
269 | |
270 logbook_SetCompartmentDesaturation(); | |
271 | |
272 ext_flash_start_new_dive_log_and_set_actualPointerSample((uint8_t*)&header); | |
273 | |
274 smallHeader.profileLength[0] = 0xFF; | |
275 smallHeader.profileLength[1] = 0xFF; | |
276 smallHeader.profileLength[2] = 0xFF; | |
277 smallHeader.samplingRate_seconds = 2; | |
278 smallHeader.numDivisors = 7; | |
279 | |
280 smallHeader.tempType = 0; | |
281 smallHeader.tempLength = 2; | |
282 smallHeader.tempDivisor = 6; | |
283 | |
284 smallHeader.deco_ndlType = 1; | |
285 smallHeader.deco_ndlLength = 2; | |
286 smallHeader.deco_ndlDivisor = 6; //= 6; | |
287 | |
288 /* GF in % at actual position */ | |
289 smallHeader.gfType = 2; | |
290 smallHeader.gfLength = 1; | |
291 smallHeader.gfDivisor = 0; //12; | |
292 | |
293 /* 3 Sensors: 8bit ppO2 in 0.01bar, 16bit voltage in 0,1mV */ | |
294 smallHeader.ppo2Type = 3; | |
295 smallHeader.ppo2Length = 9; | |
296 smallHeader.ppo2Divisor = 2; //2 | |
297 | |
298 /* last 15 stops in minutes (last, second_to_last, ... */ | |
299 /* last stop depth is defined in header */ | |
300 smallHeader.decoplanType = 4; | |
301 smallHeader.decoplanLength = 15; | |
302 smallHeader.decoplanDivisor = 12;//12; | |
303 | |
304 smallHeader.cnsType = 5; | |
305 smallHeader.cnsLength = 2; | |
306 smallHeader.cnsDivisor = 12; | |
307 | |
308 smallHeader.tankType = 6; | |
309 smallHeader.tankLength = 0; | |
310 smallHeader.tankDivisor = 0; | |
311 | |
312 logbook_writedata((void *) &smallHeader,sizeof(smallHeader)); | |
313 | |
314 clear_divisor(); | |
315 } | |
316 | |
317 /** | |
318 ****************************************************************************** | |
319 * @brief clear_divisor / clears divisor struct | |
320 * @author Peter Ryser | |
321 * @version V0.0.1 | |
322 * @date 22-April-2014 | |
323 ****************************************************************************** | |
324 * | |
325 */ | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
326 static void clear_divisor(void) |
38 | 327 { |
328 divisor.cns = smallHeader.cnsDivisor - 1; | |
329 divisor.decoplan = smallHeader.decoplanDivisor - 1; | |
330 divisor.deco_ndl = smallHeader.deco_ndlDivisor - 1; | |
331 divisor.gradientFactor = smallHeader.gfDivisor -1 ; | |
332 divisor.ppo2 = smallHeader.ppo2Divisor - 1; | |
333 divisor.tank = smallHeader.tankDivisor - 1; | |
334 divisor.temperature = smallHeader.tempDivisor - 1; | |
335 } | |
336 | |
337 | |
338 /** | |
339 ****************************************************************************** | |
340 * @brief add16. / adds 16 bit variable to 8 bit array | |
341 * @author Peter Ryser | |
342 * @version V0.0.1 | |
343 * @date 22-April-2014 | |
344 ****************************************************************************** | |
345 * | |
346 * @param uint8_t *pos: Output 8 bit array | |
347 * @param uint16_t var: 16 bit variable | |
348 */ | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
349 static void addU16(uint8_t *pos, uint16_t var) |
38 | 350 { |
351 *((uint16_t*)pos) = var; | |
352 } | |
353 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
354 static void addS16(uint8_t *pos, int16_t var) |
38 | 355 { |
356 *((int16_t*)pos) = var; | |
357 } | |
358 | |
359 /** | |
360 ****************************************************************************** | |
361 * @brief logbook_writeSample. / Writes one logbook sampl | |
362 * @author Peter Ryser | |
363 * @date 22-April-2014 | |
364 * @version V0.0.2 | |
365 * @since 20-June-2016 | |
366 * @bug Deco/NDL Status fixed in V0.0.2 | |
367 | |
368 | |
369 ****************************************************************************** | |
370 * | |
371 * @param SDiveState state: | |
372 */ | |
373 | |
374 void logbook_writeSample(SDiveState state) | |
375 { | |
376 uint8_t sample[256]; | |
377 // int position = 0; | |
378 int length = 0; | |
379 // _Bool bEvent = 0; | |
380 uint8_t nextstopDepthMeter = 0; | |
381 uint16_t nextstopLengthSeconds = 0; | |
382 uint8_t nextstopLengthMinutes = 0; | |
383 bit8_Type eventByte1, eventByte2; | |
384 bit8_Type profileByteFlag; | |
385 int i = 0; | |
386 for(i = 0; i <256 ;i++) | |
387 sample[i] = 0; | |
388 addU16(sample, (uint16_t)(state.lifeData.depth_meter * 100)); | |
389 length += 2; | |
390 sample[2] = 0; | |
391 length++; | |
392 eventByte1.uw = 0; | |
393 eventByte2.uw = 0; | |
394 //uint16_t tmpU16 = 0; | |
130
b7689d9e888a
Minor changes to improved code quality and to eliminate warnings
Ideenmodellierer
parents:
38
diff
changeset
|
395 const SDecoinfo * pDecoinfo; // new hw 160620 |
38 | 396 |
397 //BuildEevntyte 1 | |
398 // sub old 0-3 only one at a time | |
399 if(state.events.manualMarker) | |
400 { | |
401 eventByte1.uw = 6; | |
402 } | |
403 else | |
404 if(state.warnings.decoMissed) | |
405 { | |
406 eventByte1.uw = 2; | |
407 } | |
408 else | |
409 if(state.warnings.ppO2Low) | |
410 { | |
411 eventByte1.uw = 4; | |
412 } | |
413 else | |
414 if(state.warnings.ppO2High) | |
415 { | |
416 eventByte1.uw = 5; | |
417 } | |
418 else | |
419 if(state.warnings.lowBattery) | |
420 { | |
421 eventByte1.uw = 7; | |
422 } | |
423 else | |
424 if(state.warnings.slowWarning) | |
425 { | |
426 eventByte1.uw = 1; | |
427 } | |
428 // sub bit 4 to 7 | |
429 if(state.events.manuelGasSet) | |
430 { | |
431 eventByte1.ub.bit4 = 1; | |
432 } | |
433 if(state.events.gasChange) | |
434 { | |
435 eventByte1.ub.bit5 = 1; | |
436 } | |
437 if(state.events.setpointChange) | |
438 { | |
439 eventByte1.ub.bit6 = 1; | |
440 } | |
441 // sub bit 7 + eventbyte2 | |
442 if(state.events.bailout) | |
443 { | |
444 eventByte1.ub.bit7 = 1; | |
445 eventByte2.ub.bit0 = 1; | |
446 } | |
447 //Add EventByte 1 | |
448 if(eventByte1.uw > 0) | |
449 { | |
450 sample[length] = eventByte1.uw; | |
451 length++; | |
452 } | |
453 if(eventByte2.uw > 0) | |
454 { | |
455 sample[length] = eventByte2.uw; | |
456 length++; | |
457 } | |
458 //Add EventInfos | |
459 if(state.events.manuelGasSet) | |
460 { | |
461 //manual gas in %O2 & %He | |
462 sample[length] = state.events.info_manuelGasSetO2; | |
463 length += 1; | |
464 sample[length] = state.events.info_manuelGasSetHe; | |
465 length += 1; | |
466 } | |
467 if(state.events.gasChange) | |
468 { | |
469 //Current gas (gasid) | |
470 sample[length] = state.events.info_GasChange; | |
471 length += 1; | |
472 } | |
473 if(state.events.setpointChange) | |
474 { | |
475 //New setpoint in cbar | |
476 sample[length] = state.events.info_SetpointChange; | |
477 length += 1; | |
478 } | |
479 if(state.events.bailout) | |
480 { | |
481 //bailout gas in % O2 & %He | |
482 sample[length] = state.events.info_bailoutO2; | |
483 length += 1; | |
484 sample[length] = state.events.info_bailoutHe; | |
485 length += 1; | |
486 } | |
487 | |
488 | |
489 if(divisor.temperature == 0) | |
490 { | |
491 divisor.temperature = smallHeader.tempDivisor - 1; | |
492 addS16(&sample[length], (int16_t)((state.lifeData.temperature_celsius * 10.0f) + 0.5f)); | |
493 length += 2; | |
494 } | |
495 else | |
496 { | |
497 divisor.temperature--; | |
498 } | |
499 | |
500 | |
501 if(smallHeader.deco_ndlDivisor > 0) | |
502 { | |
503 if(divisor.deco_ndl == 0) | |
504 { | |
505 divisor.deco_ndl = smallHeader.deco_ndlDivisor - 1; | |
506 | |
507 if(stateUsed->diveSettings.deco_type.ub.standard == GF_MODE) | |
508 pDecoinfo = &stateUsed->decolistBuehlmann; | |
509 else if(stateUsed->diveSettings.deco_type.ub.standard == VPM_MODE) | |
510 pDecoinfo = &stateUsed->decolistVPM; | |
511 else // should not happen as only GF and VPM at the moment | |
512 { | |
513 sample[length] = 0; | |
514 length += 1; | |
515 sample[length] = 0; | |
516 length += 1; | |
517 } | |
518 | |
519 if(pDecoinfo->output_ndl_seconds > 0) | |
520 { | |
521 sample[length] = 0; | |
522 length += 1; | |
523 sample[length] = (uint8_t)pDecoinfo->output_ndl_seconds / 60; | |
524 length += 1; | |
525 } | |
526 else if(pDecoinfo->output_time_to_surface_seconds) | |
527 { | |
528 tHome_findNextStop(pDecoinfo->output_stop_length_seconds, &nextstopDepthMeter, &nextstopLengthSeconds); | |
529 nextstopLengthMinutes = (nextstopLengthSeconds +59 ) / 60; | |
530 | |
531 sample[length] = nextstopDepthMeter; | |
532 length += 1; | |
533 sample[length] = nextstopLengthMinutes; | |
534 length += 1; | |
535 } | |
536 else | |
537 { | |
538 sample[length] = 0; | |
539 length += 1; | |
540 sample[length] = 0; | |
541 length += 1; | |
542 } | |
543 } | |
544 else | |
545 { | |
546 divisor.deco_ndl --; | |
547 } | |
548 } | |
549 | |
550 | |
551 if(smallHeader.ppo2Divisor) | |
552 { | |
553 if(divisor.ppo2 == 0) | |
554 { | |
555 divisor.ppo2 = smallHeader.ppo2Divisor - 1; | |
556 | |
557 for(int i = 0; i <3; i++) | |
558 { | |
559 sample[length] = (uint8_t)(state.lifeData.ppO2Sensor_bar[i] * 100.0f + 0.5f); | |
560 length += 1; | |
561 addU16(&sample[length], (uint16_t)(state.lifeData.sensorVoltage_mV[i] * 10.0f + 0.5f)); | |
562 length += 2; | |
563 } | |
564 } | |
565 else | |
566 { | |
567 divisor.ppo2--; | |
568 } | |
569 } | |
570 | |
571 | |
572 if(smallHeader.decoplanDivisor) | |
573 { | |
574 if(divisor.decoplan == 0) | |
575 { | |
576 divisor.decoplan = smallHeader.decoplanDivisor - 1; | |
577 if(state.diveSettings.deco_type.ub.standard == VPM_MODE) | |
578 { | |
579 for(int i = 0; i <15; i++) | |
580 { | |
581 sample[length] = state.decolistVPM.output_stop_length_seconds[i] / 60; | |
582 length += 1; | |
583 } | |
584 } | |
585 else if(state.diveSettings.deco_type.ub.standard == GF_MODE) | |
586 { | |
587 for(int i = 0; i <15; i++) | |
588 { | |
589 sample[length] = state.decolistBuehlmann.output_stop_length_seconds[i] / 60; | |
590 length += 1; | |
591 } | |
592 } | |
593 else | |
594 { | |
595 for(int i = 0; i <15; i++) | |
596 { | |
597 sample[length] = 0; | |
598 length += 1; | |
599 } | |
600 } | |
601 // add16(&sample[length], state.temperature); | |
602 //length += 2; | |
603 } | |
604 else | |
605 { | |
606 divisor.decoplan --; | |
607 } | |
608 } | |
609 if(divisor.cns == 0) | |
610 { | |
611 divisor.cns = smallHeader.cnsDivisor - 1; | |
199
ac58a9fb92ac
Bugfix: fix initial CNS data in the logbook header
Jan Mulder <jlmulder@xs4all.nl>
parents:
194
diff
changeset
|
612 addU16(&sample[length], (uint16_t)state.lifeData.cns); |
38 | 613 length += 2; |
614 } | |
615 else | |
616 { | |
617 divisor.cns--; | |
618 } | |
619 | |
620 profileByteFlag.uw = length - 3; | |
621 if(eventByte1.uw) | |
622 { | |
623 profileByteFlag.ub.bit7 = 1; | |
624 } | |
625 sample[2] = profileByteFlag.uw; | |
626 logbook_writedata((void *) sample,length); | |
627 | |
628 } | |
629 | |
630 /** | |
631 ****************************************************************************** | |
632 * @brief readSample. / Reads data of one logbook sample | |
633 * @author Peter Ryser | |
634 * @version V0.0.1 | |
635 * @date 22-April-2014 | |
636 ****************************************************************************** | |
637 * | |
638 * @param int32_t* depth: output Value | |
639 * @param int16_t * gasid: output Value | |
640 * @param int32_t* temperature: output Value | |
641 * @param int32_t* sensor1, sensor2, sensor3: output Value | |
642 * @param int32_t* cns: output Value | |
643 * @return bytes read / 0 = reading Error | |
644 */ | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
645 static uint16_t readSample(int32_t* depth, int16_t * gasid, int16_t* setpoint_cbar, int32_t* temperature, int32_t* sensor1, int32_t* sensor2, int32_t* sensor3, int32_t* cns, SManualGas* manualGas, int16_t* bailout, int16_t* decostopDepth) |
38 | 646 { |
647 int length = 0; | |
648 _Bool bEvent = 0; | |
649 bit8_Type eventByte1, eventByte2; | |
650 bit8_Type profileByteFlag; | |
651 | |
652 eventByte1.uw = 0; | |
653 eventByte2.uw = 0; | |
654 uint8_t tempU8 = 0; | |
655 uint16_t temp = 0; | |
656 uint16_t bytesRead = 0; | |
657 | |
658 if(gasid) | |
659 *gasid = -1; | |
660 if(temperature) | |
661 *temperature = -1000; | |
662 if(sensor1) | |
663 *sensor1 = -1; | |
664 if(sensor2) | |
665 *sensor2 = -1; | |
666 if(sensor3) | |
667 *sensor3 = -1; | |
668 if(cns) | |
669 *cns = -1; | |
670 if(setpoint_cbar) | |
671 *setpoint_cbar = -1; | |
672 if(bailout) | |
673 *bailout = -1; | |
674 if(manualGas) | |
675 { | |
676 manualGas->percentageO2 =-1; | |
677 manualGas->percentageHe =-1; | |
678 } | |
679 if(decostopDepth) | |
680 *decostopDepth = -1; | |
681 | |
682 ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); | |
683 if(depth) | |
684 *depth = (int32_t)temp; | |
685 bytesRead += 2; | |
686 | |
687 ext_flash_read_next_sample_part( &profileByteFlag.uw, 1); | |
688 bytesRead ++; | |
689 | |
690 bEvent = profileByteFlag.ub.bit7; | |
691 profileByteFlag.ub.bit7 = 0; | |
692 length = profileByteFlag.uw; | |
693 | |
694 if(bEvent) | |
695 { | |
696 ext_flash_read_next_sample_part( &eventByte1.uw, 1); | |
697 bytesRead ++; | |
698 | |
699 length--; | |
700 | |
701 //second event byte | |
702 if(eventByte1.ub.bit7) | |
703 { | |
704 ext_flash_read_next_sample_part( &eventByte2.uw, 1); | |
705 bytesRead ++; | |
706 length--; | |
707 } | |
708 else | |
709 { | |
710 eventByte2.uw = 0; | |
711 } | |
712 | |
713 //manual Gas Set | |
714 if( eventByte1.ub.bit4) | |
715 { | |
716 //Evaluate manual Gas | |
717 ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); | |
718 bytesRead +=1; | |
719 length -= 1; | |
720 manualGas->percentageO2 = tempU8; | |
721 ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); | |
722 bytesRead +=1; | |
723 length -= 1; | |
724 manualGas->percentageHe = tempU8; | |
725 if(gasid) | |
726 *gasid = 0; | |
727 } | |
728 //gas change | |
729 if( eventByte1.ub.bit5) | |
730 { | |
731 ext_flash_read_next_sample_part( &tempU8, 1); | |
732 bytesRead +=1; | |
733 length -= 1; | |
734 if(gasid) | |
735 *gasid = (uint16_t)tempU8; | |
736 } | |
737 //SetpointChange | |
738 if( eventByte1.ub.bit6) | |
739 { | |
740 ext_flash_read_next_sample_part( &tempU8, 1); | |
741 *setpoint_cbar = tempU8; | |
742 bytesRead +=1; | |
743 length -= 1; | |
744 } | |
745 | |
746 // second event Byte | |
747 //bailout | |
748 if(eventByte2.ub.bit1) | |
749 { | |
750 //evaluate bailout gas Gas | |
751 *bailout = 1; | |
752 | |
753 ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); | |
754 bytesRead +=1; | |
755 length -= 1; | |
756 manualGas->percentageO2 = tempU8; | |
757 ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); | |
758 bytesRead +=1; | |
759 length -= 1; | |
760 manualGas->percentageHe = tempU8; | |
761 | |
762 if(gasid) | |
763 *gasid = 0; | |
764 } | |
765 } | |
766 | |
767 if(divisor.temperature == 0) | |
768 { | |
769 divisor.temperature = smallHeader.tempDivisor - 1; | |
770 ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); | |
771 bytesRead +=2; | |
772 length -= 2; | |
773 if(temperature) | |
774 { | |
775 *temperature = (int32_t)temp; | |
776 } | |
777 } | |
778 else | |
779 { | |
780 divisor.temperature--; | |
781 } | |
782 | |
783 if(smallHeader.deco_ndlDivisor > 0) | |
784 { | |
785 if(divisor.deco_ndl == 0) | |
786 { | |
787 divisor.deco_ndl = smallHeader.deco_ndlDivisor - 1; | |
788 ext_flash_read_next_sample_part( &tempU8, 1); | |
789 if(decostopDepth) | |
790 { | |
791 *decostopDepth = tempU8 * 100; | |
792 } | |
793 ext_flash_read_next_sample_part( &tempU8, 1); | |
794 bytesRead += 2; | |
795 length -= 2; | |
796 } | |
797 else | |
798 { | |
799 divisor.deco_ndl--; | |
800 } | |
801 } | |
802 | |
803 if(divisor.ppo2 == 0) | |
804 { | |
805 int32_t ppO2Tmp = 0; | |
806 divisor.ppo2 = smallHeader.ppo2Divisor -1; | |
807 for(int i = 0; i <3 ; i++) | |
808 { | |
809 ext_flash_read_next_sample_part( &tempU8, 1); | |
810 ppO2Tmp += tempU8; | |
811 bytesRead +=1; | |
812 length -= 1; | |
813 ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); | |
814 bytesRead +=2; | |
815 length -= 2; | |
816 if(sensor1 && (i==0)) | |
817 *sensor1 = (((int32_t)tempU8) * 0xFFFF) + temp; | |
818 if(sensor2 && (i==1)) | |
819 *sensor2 = (((int32_t)tempU8) * 0xFFFF) + temp; | |
820 if(sensor3 && (i==2)) | |
821 *sensor3 = (((int32_t)tempU8) * 0xFFFF) + temp; | |
822 } | |
823 } | |
824 else | |
825 { | |
826 divisor.ppo2--; | |
827 } | |
828 | |
829 if(smallHeader.decoplanDivisor > 0) | |
830 { | |
831 if(divisor.decoplan == 0) | |
832 { | |
833 divisor.decoplan = smallHeader.decoplanDivisor - 1; | |
834 for(int i = 0; i <15; i++) | |
835 ext_flash_read_next_sample_part( &tempU8, 1); | |
836 bytesRead += 15; | |
837 length -= 15; | |
838 } | |
839 else | |
840 { | |
841 divisor.decoplan--; | |
842 } | |
843 } | |
844 | |
845 | |
846 | |
847 if(divisor.cns == 0) | |
848 { | |
849 divisor.cns = smallHeader.cnsDivisor - 1; | |
850 | |
851 ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); | |
852 bytesRead +=2; | |
853 length -= 2; | |
854 if(cns) | |
855 { | |
856 *cns = (int32_t)temp; | |
857 } | |
858 } | |
859 else | |
860 { | |
861 divisor.cns--; | |
862 } | |
863 | |
864 if (length != 0) | |
865 return 0; | |
866 | |
867 return bytesRead; | |
868 } | |
869 /** | |
870 ****************************************************************************** | |
871 * @brief logbook_readSampleData. / Reads sample data of whole logbook entry | |
872 * @author Peter Ryser | |
873 * @version V0.0.1 | |
874 * @date 22-April-2014 | |
875 ****************************************************************************** | |
876 * | |
877 * @param uint8_t StepBackwards: witch lookbook entry? | |
878 * @param uint16_t length : maxlength of output arrays | |
879 * @param int32_t* depth : output array | |
880 * @param int16_t * gasid : output array | |
881 * @param int32_t* temperature : output array | |
882 * @param int32_t* ppo2 : output array | |
883 * @param int32_t* cns : output array | |
884 * @return length of output | |
885 */ | |
886 uint16_t logbook_readSampleData(uint8_t StepBackwards, uint16_t length,uint16_t* depth, uint8_t* gasid, int16_t* temperature, uint16_t* ppo2, uint16_t* setpoint, uint16_t* sensor1, uint16_t* sensor2, uint16_t* sensor3, uint16_t* cns, uint8_t* bailout, uint16_t* decostopDepth) | |
887 { | |
888 //Test read | |
889 //SLogbookHeader header; | |
890 | |
891 //logbook_getHeader(&header); | |
892 SLogbookHeader header; | |
893 int iNum; | |
894 int firstgasid = 0; | |
895 int retVal = 0; | |
896 int compression = 0; | |
897 int i; | |
898 // uint32_t diveTime_seconds; | |
899 int32_t depthVal = 0; | |
900 int16_t gasidVal = 0; | |
901 int16_t setPointVal = 0; | |
902 int16_t bailoutVal = 0; | |
903 int16_t bailoutLast = 0; | |
904 uint16_t setPointLast = 0; | |
905 int32_t temperatureVal = 0; | |
906 int32_t sensor1Val = 0; | |
907 int32_t sensor2Val = 0; | |
908 int32_t sensor3Val = 0; | |
909 int32_t sensor1Last = 0; | |
910 int32_t sensor2Last = 0; | |
911 int32_t sensor3Last = 0; | |
912 int32_t cnsVal = 0; | |
913 int32_t depthLast = 0; | |
914 int16_t gasidLast = 0; | |
915 int32_t temperatureLast = 0; | |
916 int32_t temperatureFirst = 0; | |
917 int32_t cnsLast = 0; | |
918 int16_t decostepDepthVal = 0; | |
919 int16_t decostepDepthLast = 0; | |
920 | |
921 SManualGas manualGasVal; | |
922 SManualGas manualGasLast; | |
923 manualGasLast.percentageO2 = 0; | |
924 manualGasLast.percentageHe = 0; | |
925 | |
926 float ambiant_pressure_bar = 0; | |
927 float ppO2 = 0; | |
928 ext_flash_read_dive_header((uint8_t*)&header, StepBackwards); | |
929 for(i = 0;i< 5;i++) | |
930 { | |
931 if(header.gasordil[i].note.ub.first) | |
932 break; | |
933 } | |
934 firstgasid = i + 1; | |
935 if(header.diveMode == DIVEMODE_CCR) | |
936 setPointLast = header.setpoint[0].setpoint_cbar; | |
937 else | |
938 setPointLast = 0; | |
939 //diveTime_seconds = header.diveTime_seconds ; | |
940 for(compression = 1; compression < 100; compression ++) | |
941 { | |
942 if((header.total_diveTime_seconds / header.samplingRate)/compression <= length) | |
943 break; | |
944 } | |
945 | |
946 | |
947 for(i = 0;i< length;i++) | |
948 { | |
949 if(depth) | |
950 depth[i] = 0; | |
951 if(temperature) | |
952 temperature[i] = 0; | |
953 if(gasid) | |
954 gasid[i] = 0; | |
955 if(ppo2) | |
956 ppo2[i] = 0; | |
957 if(setpoint) | |
958 setpoint[i] = 0; | |
959 if(sensor1) | |
960 sensor1[i] = 0; | |
961 if(sensor2) | |
962 sensor2[i] = 0; | |
963 if(sensor3) | |
964 sensor3[i] = 0; | |
965 if(cns) | |
966 cns[i] = 0; | |
967 } | |
968 //We start with fist gasid | |
969 gasidLast = firstgasid; | |
970 | |
971 | |
972 //uint16_t* ppo2, uint16_t* cns# | |
973 uint32_t totalNumberOfBytes = 0; | |
974 uint32_t bytesRead = 0; | |
975 ext_flash_open_read_sample( StepBackwards,&totalNumberOfBytes); | |
976 ext_flash_read_next_sample_part((uint8_t*)&smallHeader, sizeof(SSmallHeader)); | |
977 bytesRead += sizeof(SSmallHeader); | |
978 | |
979 clear_divisor(); | |
980 | |
981 iNum = 0; | |
982 int counter = 0; | |
983 temperatureLast = -1000; | |
984 while ((bytesRead < totalNumberOfBytes) && (iNum < length)) | |
985 { | |
986 ext_flash_set_entry_point(); | |
987 divisorBackup = divisor; | |
988 retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, &decostepDepthVal); | |
989 | |
990 if(retVal == 0) | |
991 { | |
992 //Error try to read again!!! | |
993 ext_flash_reopen_read_sample_at_entry_point(); | |
994 divisor = divisorBackup; | |
995 retVal = readSample(&depthVal,&gasidVal,&setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, &decostepDepthVal); | |
996 | |
997 if(retVal == 0) | |
998 break; | |
999 } | |
1000 bytesRead +=retVal; | |
1001 | |
1002 //if for some variable no new value is in the sample for (z.B. gasidVal = -1), we take the last value | |
1003 if(depthVal == -1) | |
1004 depthVal = depthLast; | |
1005 else | |
1006 depthLast = depthVal; | |
1007 | |
1008 if(gasidVal == -1) | |
1009 gasidVal = gasidLast; | |
1010 else | |
1011 gasidLast = gasidVal; | |
1012 | |
1013 if(temperatureVal == -1000) | |
1014 temperatureVal = temperatureLast; | |
1015 else | |
1016 { | |
1017 if(temperatureLast == -1000) | |
1018 temperatureFirst = temperatureVal; | |
1019 temperatureLast = temperatureVal; | |
1020 } | |
1021 | |
1022 if(setPointVal == -1) | |
1023 setPointVal = setPointLast; | |
1024 else | |
1025 setPointLast = setPointVal; | |
1026 | |
1027 if(sensor1Val == -1) | |
1028 sensor1Val = sensor1Last; | |
1029 else | |
1030 sensor1Last = sensor1Val; | |
1031 | |
1032 if(sensor2Val == -1) | |
1033 sensor2Val = sensor2Last; | |
1034 else | |
1035 sensor2Last = sensor2Val; | |
1036 | |
1037 if(sensor3Val == -1) | |
1038 sensor3Val = sensor3Last; | |
1039 else | |
1040 sensor3Last = sensor3Val; | |
1041 | |
1042 if(cnsVal == -1) | |
1043 cnsVal = cnsLast; | |
1044 else | |
1045 cnsLast = cnsVal; | |
1046 | |
1047 if(manualGasVal.percentageO2 == -1) | |
1048 manualGasVal = manualGasLast; | |
1049 else | |
1050 manualGasLast = manualGasVal; | |
1051 | |
1052 if(bailoutVal == -1) | |
1053 bailoutVal = bailoutLast; | |
1054 else | |
1055 bailoutLast = bailoutVal; | |
1056 | |
1057 if(decostepDepthVal == -1) | |
1058 decostepDepthVal = decostepDepthLast; | |
1059 else | |
1060 decostepDepthLast = decostepDepthVal; | |
1061 | |
1062 counter++; | |
1063 // Heed compression | |
1064 // Write here to arrays | |
1065 if(counter == compression) | |
1066 { | |
1067 if(depth) | |
1068 depth[iNum] = depthVal; | |
1069 if(gasid) | |
1070 gasid[iNum] = gasidVal; | |
1071 if(temperature) | |
1072 temperature[iNum] = temperatureVal; | |
1073 if(cns) | |
1074 cns[iNum] = cnsVal; | |
1075 if(bailout) | |
1076 bailout[iNum] = bailoutVal; | |
1077 if(decostopDepth) | |
1078 decostopDepth[iNum] = decostepDepthVal; | |
1079 | |
1080 if(ppo2) | |
1081 { | |
1082 //Calc ppo2 - Values | |
1083 SGas gas; | |
1084 gas.setPoint_cbar = setPointVal; | |
1085 if(gasidVal > 0) | |
1086 { | |
1087 gas.helium_percentage = header.gasordil[gasidVal - 1].helium_percentage; | |
1088 gas.nitrogen_percentage = 100 - gas.helium_percentage - header.gasordil[gasidVal - 1].oxygen_percentage; | |
1089 } | |
1090 else | |
1091 { | |
1092 gas.helium_percentage = manualGasVal.percentageHe; | |
1093 gas.nitrogen_percentage = 100 - gas.helium_percentage - manualGasVal.percentageO2; | |
1094 } | |
1095 ambiant_pressure_bar =((float)(depthVal + header.surfacePressure_mbar))/1000; | |
1096 ppO2 = decom_calc_ppO2(ambiant_pressure_bar, &gas ); | |
1097 ppo2[iNum] = (uint16_t) ( ppO2 * 100); | |
1098 } | |
1099 | |
1100 if(setpoint) | |
1101 setpoint[iNum] = setPointVal; | |
1102 | |
1103 if(sensor1) | |
1104 sensor1[iNum] = (sensor1Val / 0xFFFF) & 0xFF; | |
1105 if(sensor2) | |
1106 sensor2[iNum] = (sensor2Val / 0xFFFF) & 0xFF; | |
1107 if(sensor3) | |
1108 sensor3[iNum] = (sensor3Val / 0xFFFF) & 0xFF; | |
1109 iNum++; | |
1110 counter = 0; | |
1111 } | |
1112 } | |
1113 | |
1114 // Fix first Temperature Entries 150930 hw | |
1115 if(temperature) | |
1116 { | |
1117 int i = 0; | |
1118 while((temperature[i] == -1000) && (i < iNum)) | |
1119 temperature[i++] = temperatureFirst; | |
1120 } | |
1121 | |
1122 ext_flash_close_read_sample(); | |
1123 return iNum; | |
1124 } | |
1125 | |
1126 | |
1127 /******************************************************************************** | |
1128 * @brief logbook_InitAndWrite. / Controls writing of logbook | |
1129 * Should be called ten times per second | |
1130 * Automatically Initializes logbook at beginning of dive, | |
1131 * write samples every 2 seconds | |
1132 * and finishes logbook after end of dive | |
1133 *********************************************************************************/ | |
1134 | |
1135 void logbook_InitAndWrite(void) | |
1136 { | |
1137 SSettings *pSettings = settingsGetPointer(); | |
1138 static uint8_t bDiveMode = 0; | |
1139 static uint32_t tickstart = 0; | |
1140 uint32_t ticksdiff = 0; | |
1141 uint32_t lasttick = 0; | |
1142 static float min_temperature_float_celsius = 0; | |
1143 | |
1144 const SDiveState * pStateReal = stateRealGetPointer(); | |
1145 | |
1146 if(!bDiveMode) | |
1147 { | |
1148 if((pStateReal->mode == MODE_DIVE) && (pStateReal->diveSettings.diveMode != DIVEMODE_Apnea) && (pStateReal->lifeData.dive_time_seconds >= 5)) | |
1149 { | |
1150 //InitdiveProfile | |
1151 pSettings->totalDiveCounter++; | |
1152 logbook_initNewdiveProfile(pStateReal,settingsGetPointer()); | |
1153 min_temperature_float_celsius = pStateReal->lifeData.temperature_celsius; | |
1154 //Write logbook sample | |
1155 logbook_writeSample(*pStateReal); | |
1156 resetEvents(); | |
1157 tickstart = HAL_GetTick(); | |
1158 bDiveMode = 1; | |
1159 } | |
1160 } | |
1161 else if((pStateReal->mode == MODE_DIVE) && (pStateReal->diveSettings.diveMode != DIVEMODE_Apnea)) | |
1162 { | |
1163 lasttick = HAL_GetTick(); | |
1164 ticksdiff = time_elapsed_ms(tickstart,lasttick); | |
1165 // | |
1166 if(ticksdiff >= 2000) | |
1167 { | |
1168 //Write logbook sample | |
1169 logbook_writeSample(*pStateReal); | |
1170 resetEvents(); | |
1171 if(min_temperature_float_celsius > pStateReal->lifeData.temperature_celsius) | |
1172 min_temperature_float_celsius = pStateReal->lifeData.temperature_celsius; | |
1173 tickstart = lasttick; | |
1174 if((bDiveMode == 1) && (pStateReal->lifeData.dive_time_seconds >= pSettings->divetimeToCreateLogbook)) | |
1175 { | |
1176 ext_flash_create_new_dive_log((uint8_t*)&header); | |
1177 /** save settings | |
1178 * with new lastDiveLogId and time and day | |
1179 */ | |
1180 pSettings->personalDiveCount++; | |
1181 if(pSettings->logbookOffset) | |
1182 { | |
1183 pSettings->logbookOffset++; | |
1184 } | |
1185 ext_flash_write_settings(); | |
1186 ext_flash_disable_protection_for_logbook(); | |
1187 bDiveMode = 3; | |
1188 } | |
1189 if(bDiveMode == 3) | |
1190 logbook_UpdateHeader(); | |
1191 } | |
1192 } | |
1193 else if(bDiveMode == 3) | |
1194 { | |
1195 //End of Dive | |
1196 logbook_SetAverageDepth(pStateReal->lifeData.average_depth_meter); | |
1197 logbook_SetMinTemperature(min_temperature_float_celsius); | |
1198 logbook_SetMaxCNS(pStateReal->lifeData.cns); | |
1199 logbook_SetCompartmentDesaturation(); | |
1200 logbook_SetLastStop(pStateReal->diveSettings.last_stop_depth_bar); | |
1201 logbook_EndDive(); | |
1202 bDiveMode = 0; | |
1203 } else | |
1204 { | |
1205 ext_flash_enable_protection(); | |
1206 } | |
1207 } | |
1208 | |
1209 | |
1210 /* Private functions ---------------------------------------------------------*/ | |
1211 | |
1212 /******************************************************************************** | |
1213 * @brief logbook_UpdateHeader. / | |
1214 * set date, time, max depth. etc. pp. | |
1215 * the internal pointer to the end of profile and length will be set by | |
1216 ext_flash_close_new_dive_log() in externLogbookFlash.c | |
1217 * @author heinrichs weikamp gmbh | |
1218 * @version V0.0.1 | |
1219 * @date 27-Nov-2014 | |
1220 *********************************************************************************/ | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1221 static void logbook_UpdateHeader(void) |
38 | 1222 { |
1223 const SDiveState * pStateReal = stateRealGetPointer(); | |
149 | 1224 |
38 | 1225 // uint16_t secondsAtShallow = 0; |
1226 RTC_DateTypeDef Sdate; | |
1227 RTC_TimeTypeDef Stime; | |
1228 uint32_t time1_u32, time2_u32; | |
1229 uint32_t divetimeHelper; | |
1230 | |
1231 /* time and day */ | |
1232 /* don't update CHANGE 160224 hw, maybe save actual time and date at other place | |
1233 translateDate(pStateReal->lifeData.dateBinaryFormat, &Sdate); | |
1234 translateTime(pStateReal->lifeData.timeBinaryFormat, &Stime); | |
1235 | |
1236 header.dateYear = Sdate.Year; | |
1237 header.dateMonth = Sdate.Month; | |
1238 header.dateDay = Sdate.Date; | |
1239 header.timeHour = Stime.Hours; | |
1240 header.timeMinute = Stime.Minutes; | |
1241 */ | |
1242 /// 160315 Quick fix for empty date problem | |
1243 if((!(header.dateYear)) || (!(header.dateMonth)) || (!(header.dateDay))) | |
1244 { | |
1245 translateDate(pStateReal->lifeData.dateBinaryFormat, &Sdate); | |
1246 translateTime(pStateReal->lifeData.timeBinaryFormat, &Stime); | |
1247 | |
1248 header.dateYear = Sdate.Year; | |
1249 header.dateMonth = Sdate.Month; | |
1250 header.dateDay = Sdate.Date; | |
1251 | |
1252 time1_u32 = (uint32_t)header.timeMinute + (uint32_t)(header.timeHour * 60); | |
1253 time2_u32 = (uint32_t)Stime.Minutes + (uint32_t)(Stime.Hours * 60); | |
1254 if(time2_u32 < time1_u32) | |
1255 { | |
1256 if(header.dateDay > 1) | |
1257 { | |
1258 header.dateDay -= 1; | |
1259 } | |
1260 else | |
1261 { | |
1262 header.dateMonth --; | |
1263 if(!header.dateMonth) | |
1264 { | |
1265 header.dateYear--; | |
1266 header.dateMonth = 12; | |
1267 header.dateDay = 31; | |
1268 } | |
1269 else | |
1270 { | |
1271 if(header.dateMonth == 2) | |
1272 header.dateDay = 28; | |
1273 else | |
1274 if((header.dateMonth == 4) || (header.dateMonth == 6) || (header.dateMonth == 9) || (header.dateMonth == 11)) | |
1275 header.dateDay = 30; | |
1276 else | |
1277 header.dateDay = 31; | |
1278 } | |
1279 } | |
1280 } | |
1281 } | |
1282 | |
1283 /* duration */ | |
1284 header.total_diveTime_seconds = pStateReal->lifeData.dive_time_seconds; | |
1285 header.maxDepth = pStateReal->lifeData.max_depth_meter * 100; | |
1286 | |
1287 /* old: | |
1288 | |
1289 secondsAtShallow = pSettings->timeoutDiveReachedZeroDepth; | |
1290 if(pStateReal->lifeData.dive_time_seconds <= secondsAtShallow) | |
1291 secondsAtShallow = 0; | |
1292 header.diveTimeMinutes = (header.total_diveTime_seconds - secondsAtShallow )/ 60; | |
1293 header.diveTimeSeconds = header.total_diveTime_seconds - secondsAtShallow - (header.diveTimeMinutes * 60); | |
1294 */ | |
1295 divetimeHelper = pStateReal->lifeData.dive_time_seconds_without_surface_time; | |
1296 header.diveTimeMinutes = (uint16_t)(divetimeHelper/60); | |
1297 divetimeHelper -= 60 * (uint32_t)header.diveTimeMinutes; | |
1298 header.diveTimeSeconds = (uint16_t)divetimeHelper; | |
1299 | |
1300 /* deco algorithm (final) */ | |
1301 if(pStateReal->diveSettings.deco_type.ub.standard == GF_MODE) | |
1302 { | |
1303 header.decoModel = 1; | |
1304 header.gfLow_or_Vpm_conservatism = pStateReal->diveSettings.gf_low; | |
1305 header.gfHigh = pStateReal->diveSettings.gf_high; | |
1306 } | |
1307 else | |
1308 { | |
1309 header.decoModel = 2; | |
1310 header.gfLow_or_Vpm_conservatism = pStateReal->diveSettings.vpm_conservatism; | |
1311 header.gfHigh = 0; | |
1312 } | |
1313 | |
1314 /* tissue load */ | |
1315 memcpy(header.n2Compartments, pStateReal->lifeData.tissue_nitrogen_bar, 64); | |
1316 memcpy(header.heCompartments, pStateReal->lifeData.tissue_helium_bar, 64); | |
1317 | |
1318 } | |
1319 | |
1320 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1321 static void logbook_SetAverageDepth(float average_depth_meter) |
38 | 1322 { |
1323 header.averageDepth_mbar = (uint16_t)(average_depth_meter * 100); | |
1324 } | |
1325 | |
1326 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1327 static void logbook_SetMinTemperature(float min_temperature_celsius) |
38 | 1328 { |
1329 header.minTemp = (int16_t)((min_temperature_celsius * 10.0f) + 0.5f); | |
1330 } | |
1331 | |
1332 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1333 static void logbook_SetMaxCNS(float max_cns_percentage) |
38 | 1334 { |
1335 if(max_cns_percentage < 9999) | |
1336 header.maxCNS = (uint16_t)(max_cns_percentage); | |
1337 else | |
1338 header.maxCNS = 9999; | |
1339 } | |
1340 | |
1341 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1342 static void logbook_SetCompartmentDesaturation(void) |
38 | 1343 { |
1344 const SDiveState * pStateReal = stateRealGetPointer(); | |
225
2bb1db22b5f5
cleanup: random set of cleanups
Jan Mulder <jlmulder@xs4all.nl>
parents:
199
diff
changeset
|
1345 SLifeData2 secondaryInformation = { 0 }; |
38 | 1346 |
1347 decom_tissues_desaturation_time(&pStateReal->lifeData, &secondaryInformation); | |
1348 for(int i=0;i<16;i++) | |
1349 { | |
1350 if(secondaryInformation.tissue_nitrogen_desaturation_time_minutes[i] <= (15 * 255)) | |
1351 header.n2CompartDesatTime_min[i] = (uint8_t)((secondaryInformation.tissue_nitrogen_desaturation_time_minutes[i] + 14) / 15); | |
1352 else | |
1353 header.n2CompartDesatTime_min[i] = 255; | |
1354 if(secondaryInformation.tissue_helium_desaturation_time_minutes[i] <= (15 * 255)) | |
1355 header.heCompartDesatTime_min[i] = (uint8_t)((secondaryInformation.tissue_helium_desaturation_time_minutes[i] + 14 )/ 15); | |
1356 else | |
1357 header.heCompartDesatTime_min[i] = 255; | |
1358 } | |
1359 } | |
1360 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1361 static void logbook_SetLastStop(float last_stop_depth_bar) |
38 | 1362 { |
1363 header.lastDecostop_m = (uint8_t)(last_stop_depth_bar / 10.0f); | |
1364 } | |
1365 | |
194
f23b9055436f
cleanup: more trivial cleanup (logbook.c/h)
Jan Mulder <jlmulder@xs4all.nl>
parents:
149
diff
changeset
|
1366 static void logbook_writedata(void * data, int length_byte) |
38 | 1367 { |
1368 ext_flash_write_sample(data, length_byte); | |
1369 } | |
1370 | |
1371 | |
1372 /******************************************************************************** | |
1373 * @brief logbook_build_ostc3header. / | |
1374 * @author heinrichs weikamp gmbh | |
1375 * @version V0.0.2 | |
1376 * @date 27-Nov-2014 | |
1377 *********************************************************************************/ | |
1378 SLogbookHeaderOSTC3 * logbook_build_ostc3header(SLogbookHeader* pHead) | |
1379 { | |
1380 convert_Type data; | |
1381 | |
1382 memcpy(headerOSTC3.diveHeaderStart, &pHead->diveHeaderStart, 2); | |
1383 memcpy(headerOSTC3.pBeginProfileData, &pHead->pBeginProfileData, 3); | |
1384 memcpy(headerOSTC3.pEndProfileData, &pHead->pEndProfileData, 3); | |
1385 | |
1386 data.u8bit.byteHigh = 0; | |
1387 data.u8bit.byteLow = pHead->profileLength[0]; | |
1388 data.u8bit.byteMidLow = pHead->profileLength[1]; | |
1389 data.u8bit.byteMidHigh = pHead->profileLength[2]; | |
1390 | |
1391 if(data.u32bit != 0xFFFFFF) | |
1392 data.u32bit += 3; | |
1393 | |
1394 headerOSTC3.profileLength[0] = data.u8bit.byteLow; | |
1395 headerOSTC3.profileLength[1] = data.u8bit.byteMidLow; | |
1396 headerOSTC3.profileLength[2] = data.u8bit.byteMidHigh; | |
1397 | |
1398 memcpy(headerOSTC3.gasordil, pHead->gasordil, 20); | |
1399 | |
1400 if(pHead->logbookProfileVersion == LOGBOOK_VERSION) | |
1401 { | |
1402 headerOSTC3.logbookProfileVersion = LOGBOOK_VERSION_OSTC3; | |
1403 memcpy(headerOSTC3.personalDiveCount, &pHead->personalDiveCount, 2); | |
1404 headerOSTC3.safetyDistance_10cm = 0; | |
1405 | |
1406 for(int i=0;i<5;i++) | |
1407 { | |
1408 if(!pHead->gasordil[i].note.ub.active) | |
1409 headerOSTC3.gasordil[3 + (i*4)] = 0; | |
1410 else if(pHead->gasordil[i].note.ub.first) | |
1411 { | |
1412 /* depth = 0, note = 1 */ | |
1413 headerOSTC3.gasordil[2 + (i*4)] = 0; | |
1414 headerOSTC3.gasordil[3 + (i*4)] = 1; | |
1415 } | |
1416 else if( pHead->gasordil[i].depth_meter) | |
1417 { | |
1418 /* note = 3 */ | |
1419 headerOSTC3.gasordil[3 + (i*4)] = 3; | |
1420 } | |
1421 } | |
1422 } | |
1423 else | |
1424 { | |
1425 headerOSTC3.logbookProfileVersion = 0xFF; | |
1426 headerOSTC3.personalDiveCount[0] = 0xFF; | |
1427 headerOSTC3.personalDiveCount[1] = 0xFF; | |
1428 headerOSTC3.safetyDistance_10cm = 0xFF; | |
1429 } | |
1430 | |
1431 headerOSTC3.dateYear = pHead->dateYear; | |
1432 headerOSTC3.dateMonth = pHead->dateMonth; | |
1433 headerOSTC3.dateDay = pHead->dateDay; | |
1434 headerOSTC3.timeHour = pHead->timeHour; | |
1435 headerOSTC3.timeMinute = pHead->timeMinute; | |
1436 | |
1437 | |
1438 memcpy(headerOSTC3.maxDepth, &pHead->maxDepth, 2); | |
1439 memcpy(headerOSTC3.diveTimeMinutes, &pHead->diveTimeMinutes, 2); | |
1440 | |
1441 headerOSTC3.diveTimeSeconds = pHead->diveTimeSeconds; | |
1442 | |
1443 memcpy(headerOSTC3.minTemp, &pHead->minTemp, 2); | |
1444 memcpy(headerOSTC3.surfacePressure_mbar,&pHead->surfacePressure_mbar, 2); | |
1445 memcpy(headerOSTC3.desaturationTime, &pHead->desaturationTime, 2); | |
1446 | |
1447 headerOSTC3.firmwareVersionHigh = pHead->firmwareVersionHigh; | |
1448 headerOSTC3.firmwareVersionLow = pHead->firmwareVersionLow; | |
1449 | |
1450 memcpy(headerOSTC3.batteryVoltage, &pHead->batteryVoltage, 2); | |
1451 | |
1452 headerOSTC3.samplingRate = pHead->samplingRate; | |
1453 | |
1454 memcpy(headerOSTC3.cnsAtBeginning, &pHead->cnsAtBeginning, 2); | |
1455 | |
1456 headerOSTC3.gfAtBeginning = pHead->gfAtBeginning; | |
1457 headerOSTC3.gfAtEnd = pHead->gfAtEnd; | |
1458 | |
1459 memcpy(headerOSTC3.setpoint, pHead->setpoint, 10); | |
1460 | |
1461 headerOSTC3.salinity = pHead->salinity; | |
1462 | |
1463 memcpy(headerOSTC3.maxCNS, &pHead->maxCNS, 2); | |
1464 memcpy(headerOSTC3.averageDepth_mbar, &pHead->averageDepth_mbar, 2); | |
1465 memcpy(headerOSTC3.total_diveTime_seconds,&pHead->total_diveTime_seconds, 2); | |
1466 | |
1467 headerOSTC3.gfLow_or_Vpm_conservatism = pHead->gfLow_or_Vpm_conservatism; | |
1468 headerOSTC3.gfHigh = pHead->gfHigh; | |
1469 headerOSTC3.decoModel = pHead->decoModel; | |
1470 | |
1471 memcpy(headerOSTC3.diveNumber, &pHead->diveNumber, 2); | |
1472 | |
1473 headerOSTC3.diveMode = pHead->diveMode; | |
1474 headerOSTC3.CCRmode = pHead->CCRmode; | |
1475 | |
1476 memcpy(headerOSTC3.n2CompartDesatTime_min,pHead->n2CompartDesatTime_min, 16); | |
1477 memcpy(headerOSTC3.n2Compartments, pHead->n2Compartments, 64); | |
1478 memcpy(headerOSTC3.heCompartDesatTime_min,pHead->heCompartDesatTime_min, 16); | |
1479 memcpy(headerOSTC3.heCompartments, pHead->heCompartments, 64); | |
1480 | |
1481 headerOSTC3.lastDecostop_m = pHead->lastDecostop_m; | |
1482 | |
1483 memcpy(headerOSTC3.hwHudBattery_mV, &pHead->hwHudBattery_mV, 2); | |
1484 | |
1485 headerOSTC3.hwHudLastStatus = pHead->hwHudLastStatus; | |
1486 | |
1487 memcpy(headerOSTC3.batteryGaugeRegisters,&pHead->batteryGaugeRegisters, 6); | |
1488 | |
1489 | |
1490 memcpy(headerOSTC3.diveHeaderEnd, &pHead->diveHeaderEnd, 2); | |
1491 | |
1492 return &headerOSTC3; | |
1493 } | |
1494 | |
1495 | |
1496 /******************************************************************************** | |
1497 * @brief logbook_build_ostc3header_compact. / | |
1498 * @author heinrichs weikamp gmbh | |
1499 * @version V0.0.1 | |
1500 * @date 31-Juli-2015 | |
1501 *********************************************************************************/ | |
1502 SLogbookHeaderOSTC3compact * logbook_build_ostc3header_compact(SLogbookHeader* pHead) | |
1503 { | |
1504 convert_Type data; | |
1505 | |
1506 data.u8bit.byteHigh = 0; | |
1507 data.u8bit.byteLow = pHead->profileLength[0]; | |
1508 data.u8bit.byteMidLow = pHead->profileLength[1]; | |
1509 data.u8bit.byteMidHigh = pHead->profileLength[2]; | |
1510 | |
1511 if(data.u32bit != 0xFFFFFF) | |
1512 { | |
1513 data.u32bit += 3; | |
1514 | |
1515 headerOSTC3compact.profileLength[0] = data.u8bit.byteLow; | |
1516 headerOSTC3compact.profileLength[1] = data.u8bit.byteMidLow; | |
1517 headerOSTC3compact.profileLength[2] = data.u8bit.byteMidHigh; | |
1518 | |
1519 headerOSTC3compact.dateYear = pHead->dateYear; | |
1520 headerOSTC3compact.dateMonth = pHead->dateMonth; | |
1521 headerOSTC3compact.dateDay = pHead->dateDay; | |
1522 headerOSTC3compact.timeHour = pHead->timeHour; | |
1523 headerOSTC3compact.timeMinute = pHead->timeMinute; | |
1524 | |
1525 memcpy(headerOSTC3compact.maxDepth, &pHead->maxDepth, 2); | |
1526 memcpy(headerOSTC3compact.diveTimeMinutes, &pHead->diveTimeMinutes, 2); | |
1527 | |
1528 headerOSTC3compact.diveTimeSeconds = pHead->diveTimeSeconds; | |
1529 | |
1530 | |
1531 headerOSTC3compact.totalDiveNumberLow = pHead->diveNumber & 0xFF; | |
1532 headerOSTC3compact.totalDiveNumberHigh = (uint8_t)(pHead->diveNumber/256); | |
1533 headerOSTC3compact.profileVersion = 0x24; // Logbook-Profile version, 0x24 = date and time is start not end | |
1534 } | |
1535 else | |
1536 { | |
1537 memset(&headerOSTC3compact, 0xFF, sizeof(SLogbookHeaderOSTC3compact)); | |
1538 } | |
1539 return &headerOSTC3compact; | |
1540 } | |
1541 | |
1542 | |
1543 /** | |
1544 ****************************************************************************** | |
1545 * @brief logbook_readSampleData. / Reads sample data of whole logbook entry | |
1546 * @author Peter Ryser | |
1547 * @version V0.0.1 | |
1548 * @date 22-April-2014 | |
1549 ****************************************************************************** | |
1550 * | |
1551 * @param uint8_t StepBackwards: witch lookbook entry? | |
1552 * @param uint16_t length : maxlength of output arrays | |
1553 * @param int32_t* depth : output array | |
1554 * @param int16_t * gasid : output array | |
1555 * @param int32_t* temperature : output array | |
1556 * @param int32_t* ppo2 : output array | |
1557 * @param int32_t* cns : output array | |
1558 * @return length of output | |
1559 */ | |
1560 void logbook_recover_brokenlog(uint8_t headerId) | |
1561 { | |
1562 int16_t retVal; | |
1563 int32_t depthVal = 0; | |
1564 int16_t gasidVal = 0; | |
1565 int16_t setPointVal = 0; | |
1566 int16_t bailoutVal = 0; | |
1567 int32_t temperatureVal = 0; | |
1568 int32_t sensor1Val = 0; | |
1569 int32_t sensor2Val = 0; | |
1570 int32_t sensor3Val = 0; | |
1571 int32_t cnsVal = 0; | |
1572 SManualGas manualGasVal; | |
1573 | |
1574 //uint16_t* ppo2, uint16_t* cns# | |
1575 uint32_t bytesRead = 0; | |
1576 | |
1577 ext_flash_read_block_start(); | |
1578 ext_flash_read_next_sample_part((uint8_t*)&smallHeader, sizeof(SSmallHeader)); | |
1579 bytesRead += sizeof(SSmallHeader); | |
1580 | |
1581 clear_divisor(); | |
1582 | |
1583 | |
1584 int sampleCounter = 0; | |
1585 int maxdepth = 0; | |
1586 uint32_t avrdepth = 0; | |
1587 while (true) | |
1588 { | |
1589 | |
1590 ext_flash_set_entry_point(); | |
1591 divisorBackup = divisor; | |
1592 retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, NULL); | |
1593 if(retVal == 0) | |
1594 { | |
1595 //Error try to read again!!! | |
1596 ext_flash_reopen_read_sample_at_entry_point(); | |
1597 divisor = divisorBackup; | |
1598 retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, NULL); | |
1599 | |
1600 if(retVal == 0) | |
1601 { | |
1602 //Error try to read again!!! | |
1603 ext_flash_reopen_read_sample_at_entry_point(); | |
1604 divisor = divisorBackup; | |
1605 retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, NULL); | |
1606 | |
1607 if(retVal == 0) | |
1608 { | |
1609 ext_flash_reopen_read_sample_at_entry_point(); | |
1610 break; | |
1611 } | |
1612 | |
1613 } | |
1614 } | |
1615 if(depthVal > maxdepth) | |
1616 maxdepth = depthVal; | |
1617 avrdepth += depthVal; | |
1618 sampleCounter++; | |
1619 bytesRead +=retVal; | |
1620 } | |
1621 avrdepth/= sampleCounter; | |
1622 ext_flash_close_read_sample(); | |
1623 SLogbookHeader header; | |
1624 | |
1625 ext_flash_read_dive_header2((uint8_t*) &header, headerId, false); | |
1626 header.total_diveTime_seconds = sampleCounter * header.samplingRate; | |
1627 header.diveTimeMinutes = header.total_diveTime_seconds /60; | |
1628 header.diveTimeSeconds = header.total_diveTime_seconds - header.diveTimeMinutes * 60; | |
1629 header.maxDepth = maxdepth; | |
1630 header.averageDepth_mbar = avrdepth; | |
1631 SSettings * settings = settingsGetPointer(); | |
1632 settings->lastDiveLogId = headerId; | |
1633 ext_flash_close_new_dive_log((uint8_t *)&header); | |
1634 } | |
1635 | |
1636 /************************ (C) COPYRIGHT heinrichs weikamp *****END OF FILE****/ |