Mercurial > public > ostc4
view Discovery/Src/logbook.c @ 308:1203255481e4 cleanup-4
cleanup: introduce function setAvgDepth
The simulator and the realtime code shared a literally identical piece
of code to compute a running depth average. This is simply poor coding
style, so factor this out and create a function to do this work.
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
author | Jan Mulder <jlmulder@xs4all.nl> |
---|---|
date | Fri, 24 May 2019 09:02:46 +0200 |
parents | 50c26a4442af |
children | 8e62f4701676 |
line wrap: on
line source
/** ****************************************************************************** * @copyright heinrichs weikamp * @file logbook.c * @author heinrichs weikamp gmbh and heinrichs weikamp gmbh * @date 22-Apr-2014 * @version V0.0.3 * @since 03-Feb-2016 * @brief Everything about creating and evaluating the logbook * without the flash part which is included in externLogbookFlash.c * and the USB/Bluetooth part in tComm.c * CHANGE V0.0.3 hw: ppO2 sensor values * CHANGE V0.0.4 hw: fix event bytes according to hwos_interface.odt dated 160610 in bitbucket hwOS * @bug * @warning @verbatim ============================================================================== ##### Header ##### ============================================================================== [..] SLogbookHeader The order has changed in comparsion to OSTC3 for perfect alignment with 16bit and 32bit. The header is 256kB as well. DO NOT rearrange anything but add new data to a second page beyond diveHeaderEnd. Use extraPagesWithData to indicate that there is data available that was not available in the OSTC3 256KB This data will be behind the diveHeaderEnd. DO NOT delete diveHeaderEnd. [..] SLogbookHeaderOSTC3 is the format used by the OSTC3. logbook_getHeaderOSTC3() does the job using the global headers in logbook.c [..] SSmallHeader - is the format used by the OSTC3 [..] Summary The header format is not perfect and might be optimized prior to releasing the diving computer. For now it is good to be compatible with PC software available for checking the content of the logbook @endverbatim ****************************************************************************** * @attention * * <h2><center>© COPYRIGHT(c) 2014 heinrichs weikamp</center></h2> * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include <stdint.h> #include <string.h> #include "logbook.h" //#include "test_vpm.h" #include "externLogbookFlash.h" #include "data_exchange.h" #include "decom.h" #include "tHome.h" // for tHome_findNextStop() /* Private types -------------------------------------------------------------*/ #define NUM_GASES 5 #define LOGBOOK_VERSION (0x30) #define LOGBOOK_VERSION_OSTC3 (0x24) typedef struct /* don't forget to adjust void clear_divisor(void) */ { uint8_t temperature; uint8_t deco_ndl; uint8_t gradientFactor; uint8_t ppo2; uint8_t decoplan; uint8_t cns; uint8_t tank; } SDivisor; /* Exported variables --------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ static SLogbookHeader header; static SLogbookHeaderOSTC3 headerOSTC3; static SLogbookHeaderOSTC3compact headerOSTC3compact; static SSmallHeader smallHeader; static SDivisor divisor; static SDivisor divisorBackup; /* Private function prototypes -----------------------------------------------*/ static void clear_divisor(void); static void logbook_SetAverageDepth(float average_depth_meter); static void logbook_SetMinTemperature(float min_temperature_celsius); static void logbook_SetMaxCNS(float max_cns_percentage); static void logbook_SetCompartmentDesaturation(const SDiveState * pStateReal); static void logbook_SetLastStop(float last_stop_depth_bar); static void logbook_writedata(void * data, int length_byte); static void logbook_UpdateHeader(const SDiveState * pStateReal); /* Exported functions --------------------------------------------------------*/ void logbook_EndDive(void) { ext_flash_close_new_dive_log((uint8_t*) &header); } // =============================================================================== // logbook_last_totalDiveCount /// @brief Fix setting issues /// @date 04-April-2016 /// /// @return diveNumber (totalDiveCounter) of latest log entry, 0 if not a valid header // =============================================================================== uint16_t logbook_lastDive_diveNumber(void) { SLogbookHeader tempLogbookHeader; if(logbook_getHeader(0, &tempLogbookHeader)) { return tempLogbookHeader.diveNumber; } else { return 0; } } /** ****************************************************************************** * @brief logbook_getCurrentHeader. / * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @return SLogbookHeader*: */ SLogbookHeader* logbook_getCurrentHeader(void) { return &header; } /** ****************************************************************************** * @brief logbook_getNumberOfHeaders. / * @author heinrichs weikamp gmbh * @version V0.0.1 * @date 18-May-2016 ****************************************************************************** * * @return uint8_t : number of valid headers (0xFAFA) found. */ uint8_t logbook_getNumberOfHeaders(void) { return ext_flash_count_dive_headers(); } /** ****************************************************************************** * @brief logbook_getHeader. / * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @param StepBackwards : 0 Last lokbook entry, 1 second to last entry, etc. * @param SSLogbookHeader* pLogbookHeader: Output found LogbookHeader * @return uint8_t : 1 = success */ uint8_t logbook_getHeader(uint8_t StepBackwards,SLogbookHeader* pLogbookHeader) { ext_flash_read_dive_header((uint8_t *)pLogbookHeader, StepBackwards); if(pLogbookHeader->diveHeaderStart != 0xFAFA) return 0; else return 1; } /** ****************************************************************************** * @brief logbook_initNewdiveProfile. / * creates header and smallHeader from diveState and global Settings * and writes new lookboock entry on flash device * diveState * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @param SDiveState* pInfo: Input * @param SSettings* pSettings: Input */ void logbook_initNewdiveProfile(const SDiveState* pInfo, SSettings* pSettings) { RTC_DateTypeDef Sdate; RTC_TimeTypeDef Stime; for(int i = 0; i < sizeof(SLogbookHeader); i++) { ((uint8_t*)(&header))[i] = 0; } header.diveHeaderStart = 0xFAFA; header.diveHeaderEnd = 0xFBFB; header.samplingRate = 2; if(pInfo->diveSettings.diveMode == DIVEMODE_OC) { for(int i = 0; i < 5; i++) { header.gasordil[i].oxygen_percentage = pSettings->gas[i+1].oxygen_percentage; header.gasordil[i].helium_percentage = pSettings->gas[i+1].helium_percentage; header.gasordil[i].note.uw = pSettings->gas[i+1].note.uw; header.gasordil[i].depth_meter = pSettings->gas[i+1].depth_meter; } } else { for(int i = 0; i < 5; i++) { header.gasordil[i].oxygen_percentage = pSettings->gas[i+6].oxygen_percentage; header.gasordil[i].helium_percentage = pSettings->gas[i+6].helium_percentage; header.gasordil[i].note.uw = pSettings->gas[i+6].note.uw; header.gasordil[i].depth_meter = pSettings->gas[i+6].depth_meter; } for(int i = 0; i < 5; i++) { header.setpoint[i].setpoint_cbar = pSettings->setpoint[i+1].setpoint_cbar; header.setpoint[i].depth_meter = pSettings->setpoint[i+1].depth_meter; } } // header.gasordil[pInfo->lifeData.actualGas.GasIdInSettings].depth_meter = 0; translateDate(pInfo->lifeData.dateBinaryFormat, &Sdate); translateTime(pInfo->lifeData.timeBinaryFormat, &Stime); header.dateYear = Sdate.Year; header.dateMonth = Sdate.Month; header.dateDay = Sdate.Date; header.timeHour = Stime.Hours; header.timeMinute = Stime.Minutes; header.cnsAtBeginning = (uint16_t)pInfo->lifeData.cns; header.surfacePressure_mbar = (uint16_t)(pInfo->lifeData.pressure_surface_bar * 1000); header.firmwareVersionHigh = firmwareVersion_16bit_high(); header.firmwareVersionLow = firmwareVersion_16bit_low(); header.logbookProfileVersion = LOGBOOK_VERSION; header.salinity = pSettings->salinity; header.diveNumber = pSettings->totalDiveCounter; header.personalDiveCount = pSettings->personalDiveCount; header.diveMode = pInfo->diveSettings.diveMode; header.CCRmode = pInfo->diveSettings.CCR_Mode; header.lastDecostop_m = pSettings->last_stop_depth_meter; if(pInfo->diveSettings.deco_type.ub.standard == GF_MODE) { header.decoModel = 1; header.gfLow_or_Vpm_conservatism = pInfo->diveSettings.gf_low; header.gfHigh = pInfo->diveSettings.gf_high; } else { header.decoModel = 2; header.gfLow_or_Vpm_conservatism = pInfo->diveSettings.vpm_conservatism; header.gfHigh = 0; } memcpy(header.n2Compartments, pInfo->lifeData.tissue_nitrogen_bar, 64); memcpy(header.heCompartments, pInfo->lifeData.tissue_helium_bar, 64); logbook_SetCompartmentDesaturation(pInfo); ext_flash_start_new_dive_log_and_set_actualPointerSample((uint8_t*)&header); smallHeader.profileLength[0] = 0xFF; smallHeader.profileLength[1] = 0xFF; smallHeader.profileLength[2] = 0xFF; smallHeader.samplingRate_seconds = 2; smallHeader.numDivisors = 7; smallHeader.tempType = 0; smallHeader.tempLength = 2; smallHeader.tempDivisor = 6; smallHeader.deco_ndlType = 1; smallHeader.deco_ndlLength = 2; smallHeader.deco_ndlDivisor = 6; //= 6; /* GF in % at actual position */ smallHeader.gfType = 2; smallHeader.gfLength = 1; smallHeader.gfDivisor = 0; //12; /* 3 Sensors: 8bit ppO2 in 0.01bar, 16bit voltage in 0,1mV */ smallHeader.ppo2Type = 3; smallHeader.ppo2Length = 9; smallHeader.ppo2Divisor = 2; //2 /* last 15 stops in minutes (last, second_to_last, ... */ /* last stop depth is defined in header */ smallHeader.decoplanType = 4; smallHeader.decoplanLength = 15; smallHeader.decoplanDivisor = 12;//12; smallHeader.cnsType = 5; smallHeader.cnsLength = 2; smallHeader.cnsDivisor = 12; smallHeader.tankType = 6; smallHeader.tankLength = 0; smallHeader.tankDivisor = 0; logbook_writedata((void *) &smallHeader,sizeof(smallHeader)); clear_divisor(); } /** ****************************************************************************** * @brief clear_divisor / clears divisor struct * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * */ static void clear_divisor(void) { divisor.cns = smallHeader.cnsDivisor - 1; divisor.decoplan = smallHeader.decoplanDivisor - 1; divisor.deco_ndl = smallHeader.deco_ndlDivisor - 1; divisor.gradientFactor = smallHeader.gfDivisor -1 ; divisor.ppo2 = smallHeader.ppo2Divisor - 1; divisor.tank = smallHeader.tankDivisor - 1; divisor.temperature = smallHeader.tempDivisor - 1; } /** ****************************************************************************** * @brief add16. / adds 16 bit variable to 8 bit array * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @param uint8_t *pos: Output 8 bit array * @param uint16_t var: 16 bit variable */ static void addU16(uint8_t *pos, uint16_t var) { *((uint16_t*)pos) = var; } static void addS16(uint8_t *pos, int16_t var) { *((int16_t*)pos) = var; } /** ****************************************************************************** * @brief logbook_writeSample. / Writes one logbook sampl * @author heinrichs weikamp * @date 22-April-2014 * @version V0.0.2 * @since 20-June-2016 * @bug Deco/NDL Status fixed in V0.0.2 ****************************************************************************** * * @param SDiveState state: */ void logbook_writeSample(const SDiveState *state) { uint8_t sample[256]; // int position = 0; int length = 0; // _Bool bEvent = 0; uint8_t nextstopDepthMeter = 0; uint16_t nextstopLengthSeconds = 0; uint8_t nextstopLengthMinutes = 0; bit8_Type eventByte1, eventByte2; bit8_Type profileByteFlag; int i = 0; for(i = 0; i <256 ;i++) sample[i] = 0; addU16(sample, (uint16_t)(state->lifeData.depth_meter * 100)); length += 2; sample[2] = 0; length++; eventByte1.uw = 0; eventByte2.uw = 0; //uint16_t tmpU16 = 0; const SDecoinfo * pDecoinfo; // new hw 160620 //BuildEevntyte 1 // sub old 0-3 only one at a time if(state->events.manualMarker) { eventByte1.uw = 6; } else if(state->warnings.decoMissed) { eventByte1.uw = 2; } else if(state->warnings.ppO2Low) { eventByte1.uw = 4; } else if(state->warnings.ppO2High) { eventByte1.uw = 5; } else if(state->warnings.lowBattery) { eventByte1.uw = 7; } else if(state->warnings.slowWarning) { eventByte1.uw = 1; } // sub bit 4 to 7 if(state->events.manualGasSet) { eventByte1.ub.bit4 = 1; } if(state->events.gasChange) { eventByte1.ub.bit5 = 1; } if(state->events.setpointChange) { eventByte1.ub.bit6 = 1; } // sub bit 7 + eventbyte2 if(state->events.bailout) { eventByte1.ub.bit7 = 1; eventByte2.ub.bit0 = 1; } //Add EventByte 1 if(eventByte1.uw > 0) { sample[length] = eventByte1.uw; length++; } if(eventByte2.uw > 0) { sample[length] = eventByte2.uw; length++; } //Add EventInfos if(state->events.manualGasSet) { //manual gas in %O2 & %He sample[length] = state->events.info_manualGasSetO2; length += 1; sample[length] = state->events.info_manualGasSetHe; length += 1; } if(state->events.gasChange) { //Current gas (gasid) sample[length] = state->events.info_GasChange; length += 1; } if(state->events.setpointChange) { //New setpoint in cbar sample[length] = state->events.info_SetpointChange; length += 1; } if(state->events.bailout) { //bailout gas in % O2 & %He sample[length] = state->events.info_bailoutO2; length += 1; sample[length] = state->events.info_bailoutHe; length += 1; } if(divisor.temperature == 0) { divisor.temperature = smallHeader.tempDivisor - 1; addS16(&sample[length], (int16_t)((state->lifeData.temperature_celsius * 10.0f) + 0.5f)); length += 2; } else { divisor.temperature--; } if(smallHeader.deco_ndlDivisor > 0) { if(divisor.deco_ndl == 0) { divisor.deco_ndl = smallHeader.deco_ndlDivisor - 1; if(stateUsed->diveSettings.deco_type.ub.standard == GF_MODE) pDecoinfo = &stateUsed->decolistBuehlmann; else if(stateUsed->diveSettings.deco_type.ub.standard == VPM_MODE) pDecoinfo = &stateUsed->decolistVPM; else // should not happen as only GF and VPM at the moment { sample[length] = 0; length += 1; sample[length] = 0; length += 1; } if(pDecoinfo->output_ndl_seconds > 0) { sample[length] = 0; length += 1; sample[length] = (uint8_t)(pDecoinfo->output_ndl_seconds / 60); // Limit stored sample within 0 to 240 mins (Since it's 8bit UINT only) if ((pDecoinfo->output_ndl_seconds / 60) > 240) sample[length] = 240; if ((pDecoinfo->output_ndl_seconds / 60) < 0) sample[length] = 0; length += 1; } else if(pDecoinfo->output_time_to_surface_seconds) { tHome_findNextStop(pDecoinfo->output_stop_length_seconds, &nextstopDepthMeter, &nextstopLengthSeconds); nextstopLengthMinutes = (nextstopLengthSeconds +59 ) / 60; sample[length] = nextstopDepthMeter; length += 1; sample[length] = nextstopLengthMinutes; length += 1; } else { sample[length] = 0; length += 1; sample[length] = 0; length += 1; } } else { divisor.deco_ndl --; } } if(smallHeader.ppo2Divisor) { if(divisor.ppo2 == 0) { divisor.ppo2 = smallHeader.ppo2Divisor - 1; for(int i = 0; i <3; i++) { sample[length] = (uint8_t)(state->lifeData.ppO2Sensor_bar[i] * 100.0f + 0.5f); length += 1; addU16(&sample[length], (uint16_t)(state->lifeData.sensorVoltage_mV[i] * 10.0f + 0.5f)); length += 2; } } else { divisor.ppo2--; } } if(smallHeader.decoplanDivisor) { if(divisor.decoplan == 0) { divisor.decoplan = smallHeader.decoplanDivisor - 1; if(state->diveSettings.deco_type.ub.standard == VPM_MODE) { for(int i = 0; i <15; i++) { sample[length] = state->decolistVPM.output_stop_length_seconds[i] / 60; length += 1; } } else if(state->diveSettings.deco_type.ub.standard == GF_MODE) { for(int i = 0; i <15; i++) { sample[length] = state->decolistBuehlmann.output_stop_length_seconds[i] / 60; length += 1; } } else { for(int i = 0; i <15; i++) { sample[length] = 0; length += 1; } } // add16(&sample[length], state.temperature); //length += 2; } else { divisor.decoplan --; } } if(divisor.cns == 0) { divisor.cns = smallHeader.cnsDivisor - 1; addU16(&sample[length], (uint16_t)state->lifeData.cns); length += 2; } else { divisor.cns--; } profileByteFlag.uw = length - 3; if(eventByte1.uw) { profileByteFlag.ub.bit7 = 1; } sample[2] = profileByteFlag.uw; logbook_writedata((void *) sample,length); } /** ****************************************************************************** * @brief readSample. / Reads data of one logbook sample * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @param int32_t* depth: output Value * @param int16_t * gasid: output Value * @param int32_t* temperature: output Value * @param int32_t* sensor1, sensor2, sensor3: output Value * @param int32_t* cns: output Value * @return bytes read / 0 = reading Error */ 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) { int length = 0; _Bool bEvent = 0; bit8_Type eventByte1, eventByte2; bit8_Type profileByteFlag; eventByte1.uw = 0; eventByte2.uw = 0; uint8_t tempU8 = 0; uint16_t temp = 0; uint16_t bytesRead = 0; if(gasid) *gasid = -1; if(temperature) *temperature = -1000; if(sensor1) *sensor1 = -1; if(sensor2) *sensor2 = -1; if(sensor3) *sensor3 = -1; if(cns) *cns = -1; if(setpoint_cbar) *setpoint_cbar = -1; if(bailout) *bailout = -1; if(manualGas) { manualGas->percentageO2 =-1; manualGas->percentageHe =-1; } if(decostopDepth) *decostopDepth = -1; ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); if(depth) *depth = (int32_t)temp; bytesRead += 2; ext_flash_read_next_sample_part( &profileByteFlag.uw, 1); bytesRead ++; bEvent = profileByteFlag.ub.bit7; profileByteFlag.ub.bit7 = 0; length = profileByteFlag.uw; if(bEvent) { ext_flash_read_next_sample_part( &eventByte1.uw, 1); bytesRead ++; length--; //second event byte if(eventByte1.ub.bit7) { ext_flash_read_next_sample_part( &eventByte2.uw, 1); bytesRead ++; length--; } else { eventByte2.uw = 0; } //manual Gas Set if( eventByte1.ub.bit4) { //Evaluate manual Gas ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); bytesRead +=1; length -= 1; manualGas->percentageO2 = tempU8; ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); bytesRead +=1; length -= 1; manualGas->percentageHe = tempU8; if(gasid) *gasid = 0; } //gas change if( eventByte1.ub.bit5) { ext_flash_read_next_sample_part( &tempU8, 1); bytesRead +=1; length -= 1; if(gasid) *gasid = (uint16_t)tempU8; } //SetpointChange if( eventByte1.ub.bit6) { ext_flash_read_next_sample_part( &tempU8, 1); *setpoint_cbar = tempU8; bytesRead +=1; length -= 1; } // second event Byte //bailout if(eventByte2.ub.bit0) { //evaluate bailout gas Gas *bailout = 1; ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); bytesRead +=1; length -= 1; manualGas->percentageO2 = tempU8; ext_flash_read_next_sample_part( (uint8_t*)&tempU8, 1); bytesRead +=1; length -= 1; manualGas->percentageHe = tempU8; if(gasid) *gasid = 0; } } if(divisor.temperature == 0) { divisor.temperature = smallHeader.tempDivisor - 1; ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); bytesRead +=2; length -= 2; if(temperature) { *temperature = (int32_t)temp; } } else { divisor.temperature--; } if(smallHeader.deco_ndlDivisor > 0) { if(divisor.deco_ndl == 0) { divisor.deco_ndl = smallHeader.deco_ndlDivisor - 1; ext_flash_read_next_sample_part( &tempU8, 1); if(decostopDepth) { *decostopDepth = tempU8 * 100; } ext_flash_read_next_sample_part( &tempU8, 1); bytesRead += 2; length -= 2; } else { divisor.deco_ndl--; } } if(divisor.ppo2 == 0) { int32_t ppO2Tmp = 0; divisor.ppo2 = smallHeader.ppo2Divisor -1; for(int i = 0; i <3 ; i++) { ext_flash_read_next_sample_part( &tempU8, 1); ppO2Tmp += tempU8; bytesRead +=1; length -= 1; ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); bytesRead +=2; length -= 2; if(sensor1 && (i==0)) *sensor1 = (((int32_t)tempU8) * 0xFFFF) + temp; if(sensor2 && (i==1)) *sensor2 = (((int32_t)tempU8) * 0xFFFF) + temp; if(sensor3 && (i==2)) *sensor3 = (((int32_t)tempU8) * 0xFFFF) + temp; } } else { divisor.ppo2--; } if(smallHeader.decoplanDivisor > 0) { if(divisor.decoplan == 0) { divisor.decoplan = smallHeader.decoplanDivisor - 1; for(int i = 0; i <15; i++) ext_flash_read_next_sample_part( &tempU8, 1); bytesRead += 15; length -= 15; } else { divisor.decoplan--; } } if(divisor.cns == 0) { divisor.cns = smallHeader.cnsDivisor - 1; ext_flash_read_next_sample_part( (uint8_t*)&temp, 2); bytesRead +=2; length -= 2; if(cns) { *cns = (int32_t)temp; } } else { divisor.cns--; } if (length != 0) return 0; return bytesRead; } /** ****************************************************************************** * @brief logbook_readSampleData. / Reads sample data of whole logbook entry * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @param uint8_t StepBackwards: witch lookbook entry? * @param uint16_t length : maxlength of output arrays * @param int32_t* depth : output array * @param int16_t * gasid : output array * @param int32_t* temperature : output array * @param int32_t* ppo2 : output array * @param int32_t* cns : output array * @return length of output */ 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) { //Test read //SLogbookHeader header; //logbook_getHeader(&header); SLogbookHeader header; int iNum; int firstgasid = 0; int retVal = 0; int compression = 0; int i; // uint32_t diveTime_seconds; int32_t depthVal = 0; int16_t gasidVal = 0; int16_t setPointVal = 0; int16_t bailoutVal = 0; int16_t bailoutLast = 0; uint16_t setPointLast = 0; int32_t temperatureVal = 0; int32_t sensor1Val = 0; int32_t sensor2Val = 0; int32_t sensor3Val = 0; int32_t sensor1Last = 0; int32_t sensor2Last = 0; int32_t sensor3Last = 0; int32_t cnsVal = 0; int32_t depthLast = 0; int16_t gasidLast = 0; int32_t temperatureLast = 0; int32_t temperatureFirst = 0; int32_t cnsLast = 0; int16_t decostepDepthVal = 0; int16_t decostepDepthLast = 0; SManualGas manualGasVal; SManualGas manualGasLast; manualGasLast.percentageO2 = 0; manualGasLast.percentageHe = 0; float ambiant_pressure_bar = 0; float ppO2 = 0; ext_flash_read_dive_header((uint8_t*)&header, StepBackwards); for(i = 0;i< 5;i++) { if(header.gasordil[i].note.ub.first) break; } firstgasid = i + 1; if(header.diveMode == DIVEMODE_CCR) setPointLast = header.setpoint[0].setpoint_cbar; else setPointLast = 0; //diveTime_seconds = header.diveTime_seconds ; for(compression = 1; compression < 100; compression ++) { if((header.total_diveTime_seconds / header.samplingRate)/compression <= length) break; } for(i = 0;i< length;i++) { if(depth) depth[i] = 0; if(temperature) temperature[i] = 0; if(gasid) gasid[i] = 0; if(ppo2) ppo2[i] = 0; if(setpoint) setpoint[i] = 0; if(sensor1) sensor1[i] = 0; if(sensor2) sensor2[i] = 0; if(sensor3) sensor3[i] = 0; if(cns) cns[i] = 0; } //We start with fist gasid gasidLast = firstgasid; //uint16_t* ppo2, uint16_t* cns# uint32_t totalNumberOfBytes = 0; uint32_t bytesRead = 0; ext_flash_open_read_sample( StepBackwards,&totalNumberOfBytes); ext_flash_read_next_sample_part((uint8_t*)&smallHeader, sizeof(SSmallHeader)); bytesRead += sizeof(SSmallHeader); clear_divisor(); iNum = 0; int counter = 0; temperatureLast = -1000; while ((bytesRead < totalNumberOfBytes) && (iNum < length)) { ext_flash_set_entry_point(); divisorBackup = divisor; retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, &decostepDepthVal); if(retVal == 0) { //Error try to read again!!! ext_flash_reopen_read_sample_at_entry_point(); divisor = divisorBackup; retVal = readSample(&depthVal,&gasidVal,&setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, &decostepDepthVal); if(retVal == 0) break; } bytesRead +=retVal; //if for some variable no new value is in the sample for (z.B. gasidVal = -1), we take the last value if(depthVal == -1) depthVal = depthLast; else depthLast = depthVal; if(gasidVal == -1) gasidVal = gasidLast; else gasidLast = gasidVal; if(temperatureVal == -1000) temperatureVal = temperatureLast; else { if(temperatureLast == -1000) temperatureFirst = temperatureVal; temperatureLast = temperatureVal; } if(setPointVal == -1) setPointVal = setPointLast; else setPointLast = setPointVal; if(sensor1Val == -1) sensor1Val = sensor1Last; else sensor1Last = sensor1Val; if(sensor2Val == -1) sensor2Val = sensor2Last; else sensor2Last = sensor2Val; if(sensor3Val == -1) sensor3Val = sensor3Last; else sensor3Last = sensor3Val; if(cnsVal == -1) cnsVal = cnsLast; else cnsLast = cnsVal; if(manualGasVal.percentageO2 == -1) manualGasVal = manualGasLast; else manualGasLast = manualGasVal; if(bailoutVal == -1) bailoutVal = bailoutLast; else bailoutLast = bailoutVal; if(decostepDepthVal == -1) decostepDepthVal = decostepDepthLast; else decostepDepthLast = decostepDepthVal; counter++; // Heed compression // Write here to arrays if(counter == compression) { if(depth) depth[iNum] = depthVal; if(gasid) gasid[iNum] = gasidVal; if(temperature) temperature[iNum] = temperatureVal; if(cns) cns[iNum] = cnsVal; if(bailout) bailout[iNum] = bailoutVal; if(decostopDepth) decostopDepth[iNum] = decostepDepthVal; if(ppo2) { //Calc ppo2 - Values SGas gas; gas.setPoint_cbar = setPointVal; if(gasidVal > 0) { gas.helium_percentage = header.gasordil[gasidVal - 1].helium_percentage; gas.nitrogen_percentage = 100 - gas.helium_percentage - header.gasordil[gasidVal - 1].oxygen_percentage; } else { gas.helium_percentage = manualGasVal.percentageHe; gas.nitrogen_percentage = 100 - gas.helium_percentage - manualGasVal.percentageO2; } ambiant_pressure_bar =((float)(depthVal + header.surfacePressure_mbar))/1000; ppO2 = decom_calc_ppO2(ambiant_pressure_bar, &gas ); ppo2[iNum] = (uint16_t) ( ppO2 * 100); } if(setpoint) setpoint[iNum] = setPointVal; if(sensor1) sensor1[iNum] = (sensor1Val / 0xFFFF) & 0xFF; if(sensor2) sensor2[iNum] = (sensor2Val / 0xFFFF) & 0xFF; if(sensor3) sensor3[iNum] = (sensor3Val / 0xFFFF) & 0xFF; iNum++; counter = 0; } } // Fix first Temperature Entries 150930 hw if(temperature) { int i = 0; while((temperature[i] == -1000) && (i < iNum)) temperature[i++] = temperatureFirst; } ext_flash_close_read_sample(); return iNum; } /******************************************************************************** * @brief logbook_InitAndWrite. / Controls writing of logbook * Should be called ten times per second * Automatically Initializes logbook at beginning of dive, * write samples every 2 seconds * and finishes logbook after end of dive *********************************************************************************/ void logbook_InitAndWrite(const SDiveState *pStateReal) { SSettings *pSettings = settingsGetPointer(); static uint8_t bDiveMode = 0; static uint32_t tickstart = 0; uint32_t ticksdiff = 0; uint32_t lasttick = 0; static float min_temperature_float_celsius = 0; if(!bDiveMode) { if((pStateReal->mode == MODE_DIVE) && (pStateReal->diveSettings.diveMode != DIVEMODE_Apnea) && (pStateReal->lifeData.dive_time_seconds >= 5)) { //InitdiveProfile pSettings->totalDiveCounter++; logbook_initNewdiveProfile(pStateReal,settingsGetPointer()); min_temperature_float_celsius = pStateReal->lifeData.temperature_celsius; //Write logbook sample logbook_writeSample(pStateReal); resetEvents(pStateReal); tickstart = HAL_GetTick(); bDiveMode = 1; } } else if((pStateReal->mode == MODE_DIVE) && (pStateReal->diveSettings.diveMode != DIVEMODE_Apnea)) { lasttick = HAL_GetTick(); ticksdiff = time_elapsed_ms(tickstart,lasttick); // if(ticksdiff >= 2000) { //Write logbook sample logbook_writeSample(pStateReal); resetEvents(pStateReal); if(min_temperature_float_celsius > pStateReal->lifeData.temperature_celsius) min_temperature_float_celsius = pStateReal->lifeData.temperature_celsius; tickstart = lasttick; if((bDiveMode == 1) && (pStateReal->lifeData.dive_time_seconds >= pSettings->divetimeToCreateLogbook)) { ext_flash_create_new_dive_log((uint8_t*)&header); /** save settings * with new lastDiveLogId and time and day */ pSettings->personalDiveCount++; if(pSettings->logbookOffset) { pSettings->logbookOffset++; } ext_flash_write_settings(); ext_flash_disable_protection_for_logbook(); bDiveMode = 3; } if(bDiveMode == 3) logbook_UpdateHeader(pStateReal); } } else if(bDiveMode == 3) { //End of Dive logbook_SetAverageDepth(pStateReal->lifeData.average_depth_meter); logbook_SetMinTemperature(min_temperature_float_celsius); logbook_SetMaxCNS(pStateReal->lifeData.cns); logbook_SetCompartmentDesaturation(pStateReal); logbook_SetLastStop(pStateReal->diveSettings.last_stop_depth_bar); logbook_EndDive(); bDiveMode = 0; } else { ext_flash_enable_protection(); } } /* Private functions ---------------------------------------------------------*/ /******************************************************************************** * @brief logbook_UpdateHeader. / * set date, time, max depth. etc. pp. * the internal pointer to the end of profile and length will be set by ext_flash_close_new_dive_log() in externLogbookFlash.c * @author heinrichs weikamp gmbh * @version V0.0.1 * @date 27-Nov-2014 *********************************************************************************/ static void logbook_UpdateHeader(const SDiveState *pStateReal) { // uint16_t secondsAtShallow = 0; RTC_DateTypeDef Sdate; RTC_TimeTypeDef Stime; uint32_t time1_u32, time2_u32; uint32_t divetimeHelper; /* time and day */ /* don't update CHANGE 160224 hw, maybe save actual time and date at other place translateDate(pStateReal->lifeData.dateBinaryFormat, &Sdate); translateTime(pStateReal->lifeData.timeBinaryFormat, &Stime); header.dateYear = Sdate.Year; header.dateMonth = Sdate.Month; header.dateDay = Sdate.Date; header.timeHour = Stime.Hours; header.timeMinute = Stime.Minutes; */ /// 160315 Quick fix for empty date problem if((!(header.dateYear)) || (!(header.dateMonth)) || (!(header.dateDay))) { translateDate(pStateReal->lifeData.dateBinaryFormat, &Sdate); translateTime(pStateReal->lifeData.timeBinaryFormat, &Stime); header.dateYear = Sdate.Year; header.dateMonth = Sdate.Month; header.dateDay = Sdate.Date; time1_u32 = (uint32_t)header.timeMinute + (uint32_t)(header.timeHour * 60); time2_u32 = (uint32_t)Stime.Minutes + (uint32_t)(Stime.Hours * 60); if(time2_u32 < time1_u32) { if(header.dateDay > 1) { header.dateDay -= 1; } else { header.dateMonth --; if(!header.dateMonth) { header.dateYear--; header.dateMonth = 12; header.dateDay = 31; } else { if(header.dateMonth == 2) header.dateDay = 28; else if((header.dateMonth == 4) || (header.dateMonth == 6) || (header.dateMonth == 9) || (header.dateMonth == 11)) header.dateDay = 30; else header.dateDay = 31; } } } } /* duration */ header.total_diveTime_seconds = pStateReal->lifeData.dive_time_seconds; header.maxDepth = pStateReal->lifeData.max_depth_meter * 100; /* old: secondsAtShallow = pSettings->timeoutDiveReachedZeroDepth; if(pStateReal->lifeData.dive_time_seconds <= secondsAtShallow) secondsAtShallow = 0; header.diveTimeMinutes = (header.total_diveTime_seconds - secondsAtShallow )/ 60; header.diveTimeSeconds = header.total_diveTime_seconds - secondsAtShallow - (header.diveTimeMinutes * 60); */ divetimeHelper = pStateReal->lifeData.dive_time_seconds_without_surface_time; header.diveTimeMinutes = (uint16_t)(divetimeHelper/60); divetimeHelper -= 60 * (uint32_t)header.diveTimeMinutes; header.diveTimeSeconds = (uint16_t)divetimeHelper; /* deco algorithm (final) */ if(pStateReal->diveSettings.deco_type.ub.standard == GF_MODE) { header.decoModel = 1; header.gfLow_or_Vpm_conservatism = pStateReal->diveSettings.gf_low; header.gfHigh = pStateReal->diveSettings.gf_high; } else { header.decoModel = 2; header.gfLow_or_Vpm_conservatism = pStateReal->diveSettings.vpm_conservatism; header.gfHigh = 0; } /* tissue load */ memcpy(header.n2Compartments, pStateReal->lifeData.tissue_nitrogen_bar, 64); memcpy(header.heCompartments, pStateReal->lifeData.tissue_helium_bar, 64); } static void logbook_SetAverageDepth(float average_depth_meter) { header.averageDepth_mbar = (uint16_t)(average_depth_meter * 100); } static void logbook_SetMinTemperature(float min_temperature_celsius) { header.minTemp = (int16_t)((min_temperature_celsius * 10.0f) + 0.5f); } static void logbook_SetMaxCNS(float max_cns_percentage) { if(max_cns_percentage < 9999) header.maxCNS = (uint16_t)(max_cns_percentage); else header.maxCNS = 9999; } static void logbook_SetCompartmentDesaturation(const SDiveState * pStateReal) { SLifeData2 secondaryInformation = { 0 }; decom_tissues_desaturation_time(&pStateReal->lifeData, &secondaryInformation); for(int i=0;i<16;i++) { if(secondaryInformation.tissue_nitrogen_desaturation_time_minutes[i] <= (15 * 255)) header.n2CompartDesatTime_min[i] = (uint8_t)((secondaryInformation.tissue_nitrogen_desaturation_time_minutes[i] + 14) / 15); else header.n2CompartDesatTime_min[i] = 255; if(secondaryInformation.tissue_helium_desaturation_time_minutes[i] <= (15 * 255)) header.heCompartDesatTime_min[i] = (uint8_t)((secondaryInformation.tissue_helium_desaturation_time_minutes[i] + 14 )/ 15); else header.heCompartDesatTime_min[i] = 255; } } static void logbook_SetLastStop(float last_stop_depth_bar) { header.lastDecostop_m = (uint8_t)(last_stop_depth_bar / 10.0f); } static void logbook_writedata(void * data, int length_byte) { ext_flash_write_sample(data, length_byte); } /******************************************************************************** * @brief logbook_build_ostc3header. / * @author heinrichs weikamp gmbh * @version V0.0.2 * @date 27-Nov-2014 *********************************************************************************/ SLogbookHeaderOSTC3 * logbook_build_ostc3header(SLogbookHeader* pHead) { convert_Type data; memcpy(headerOSTC3.diveHeaderStart, &pHead->diveHeaderStart, 2); memcpy(headerOSTC3.pBeginProfileData, &pHead->pBeginProfileData, 3); memcpy(headerOSTC3.pEndProfileData, &pHead->pEndProfileData, 3); data.u8bit.byteHigh = 0; data.u8bit.byteLow = pHead->profileLength[0]; data.u8bit.byteMidLow = pHead->profileLength[1]; data.u8bit.byteMidHigh = pHead->profileLength[2]; if(data.u32bit != 0xFFFFFF) data.u32bit += 3; headerOSTC3.profileLength[0] = data.u8bit.byteLow; headerOSTC3.profileLength[1] = data.u8bit.byteMidLow; headerOSTC3.profileLength[2] = data.u8bit.byteMidHigh; memcpy(headerOSTC3.gasordil, pHead->gasordil, 20); if(pHead->logbookProfileVersion == LOGBOOK_VERSION) { headerOSTC3.logbookProfileVersion = LOGBOOK_VERSION_OSTC3; memcpy(headerOSTC3.personalDiveCount, &pHead->personalDiveCount, 2); headerOSTC3.safetyDistance_10cm = 0; for(int i=0;i<5;i++) { if(!pHead->gasordil[i].note.ub.active) headerOSTC3.gasordil[3 + (i*4)] = 0; else if(pHead->gasordil[i].note.ub.first) { /* depth = 0, note = 1 */ headerOSTC3.gasordil[2 + (i*4)] = 0; headerOSTC3.gasordil[3 + (i*4)] = 1; } else if( pHead->gasordil[i].depth_meter) { /* note = 3 */ headerOSTC3.gasordil[3 + (i*4)] = 3; } } } else { headerOSTC3.logbookProfileVersion = 0xFF; headerOSTC3.personalDiveCount[0] = 0xFF; headerOSTC3.personalDiveCount[1] = 0xFF; headerOSTC3.safetyDistance_10cm = 0xFF; } headerOSTC3.dateYear = pHead->dateYear; headerOSTC3.dateMonth = pHead->dateMonth; headerOSTC3.dateDay = pHead->dateDay; headerOSTC3.timeHour = pHead->timeHour; headerOSTC3.timeMinute = pHead->timeMinute; memcpy(headerOSTC3.maxDepth, &pHead->maxDepth, 2); memcpy(headerOSTC3.diveTimeMinutes, &pHead->diveTimeMinutes, 2); headerOSTC3.diveTimeSeconds = pHead->diveTimeSeconds; memcpy(headerOSTC3.minTemp, &pHead->minTemp, 2); memcpy(headerOSTC3.surfacePressure_mbar,&pHead->surfacePressure_mbar, 2); memcpy(headerOSTC3.desaturationTime, &pHead->desaturationTime, 2); headerOSTC3.firmwareVersionHigh = pHead->firmwareVersionHigh; headerOSTC3.firmwareVersionLow = pHead->firmwareVersionLow; memcpy(headerOSTC3.batteryVoltage, &pHead->batteryVoltage, 2); headerOSTC3.samplingRate = pHead->samplingRate; memcpy(headerOSTC3.cnsAtBeginning, &pHead->cnsAtBeginning, 2); headerOSTC3.gfAtBeginning = pHead->gfAtBeginning; headerOSTC3.gfAtEnd = pHead->gfAtEnd; memcpy(headerOSTC3.setpoint, pHead->setpoint, 10); headerOSTC3.salinity = pHead->salinity; memcpy(headerOSTC3.maxCNS, &pHead->maxCNS, 2); memcpy(headerOSTC3.averageDepth_mbar, &pHead->averageDepth_mbar, 2); memcpy(headerOSTC3.total_diveTime_seconds,&pHead->total_diveTime_seconds, 2); headerOSTC3.gfLow_or_Vpm_conservatism = pHead->gfLow_or_Vpm_conservatism; headerOSTC3.gfHigh = pHead->gfHigh; headerOSTC3.decoModel = pHead->decoModel; memcpy(headerOSTC3.diveNumber, &pHead->diveNumber, 2); headerOSTC3.diveMode = pHead->diveMode; headerOSTC3.CCRmode = pHead->CCRmode; memcpy(headerOSTC3.n2CompartDesatTime_min,pHead->n2CompartDesatTime_min, 16); memcpy(headerOSTC3.n2Compartments, pHead->n2Compartments, 64); memcpy(headerOSTC3.heCompartDesatTime_min,pHead->heCompartDesatTime_min, 16); memcpy(headerOSTC3.heCompartments, pHead->heCompartments, 64); headerOSTC3.lastDecostop_m = pHead->lastDecostop_m; memcpy(headerOSTC3.hwHudBattery_mV, &pHead->hwHudBattery_mV, 2); headerOSTC3.hwHudLastStatus = pHead->hwHudLastStatus; memcpy(headerOSTC3.batteryGaugeRegisters,&pHead->batteryGaugeRegisters, 6); memcpy(headerOSTC3.diveHeaderEnd, &pHead->diveHeaderEnd, 2); return &headerOSTC3; } /******************************************************************************** * @brief logbook_build_ostc3header_compact. / * @author heinrichs weikamp gmbh * @version V0.0.1 * @date 31-Juli-2015 *********************************************************************************/ SLogbookHeaderOSTC3compact * logbook_build_ostc3header_compact(SLogbookHeader* pHead) { convert_Type data; data.u8bit.byteHigh = 0; data.u8bit.byteLow = pHead->profileLength[0]; data.u8bit.byteMidLow = pHead->profileLength[1]; data.u8bit.byteMidHigh = pHead->profileLength[2]; if(data.u32bit != 0xFFFFFF) { data.u32bit += 3; headerOSTC3compact.profileLength[0] = data.u8bit.byteLow; headerOSTC3compact.profileLength[1] = data.u8bit.byteMidLow; headerOSTC3compact.profileLength[2] = data.u8bit.byteMidHigh; headerOSTC3compact.dateYear = pHead->dateYear; headerOSTC3compact.dateMonth = pHead->dateMonth; headerOSTC3compact.dateDay = pHead->dateDay; headerOSTC3compact.timeHour = pHead->timeHour; headerOSTC3compact.timeMinute = pHead->timeMinute; memcpy(headerOSTC3compact.maxDepth, &pHead->maxDepth, 2); memcpy(headerOSTC3compact.diveTimeMinutes, &pHead->diveTimeMinutes, 2); headerOSTC3compact.diveTimeSeconds = pHead->diveTimeSeconds; headerOSTC3compact.totalDiveNumberLow = pHead->diveNumber & 0xFF; headerOSTC3compact.totalDiveNumberHigh = (uint8_t)(pHead->diveNumber/256); headerOSTC3compact.profileVersion = 0x24; // Logbook-Profile version, 0x24 = date and time is start not end } else { memset(&headerOSTC3compact, 0xFF, sizeof(SLogbookHeaderOSTC3compact)); } return &headerOSTC3compact; } /** ****************************************************************************** * @brief logbook_readSampleData. / Reads sample data of whole logbook entry * @author heinrichs weikamp * @version V0.0.1 * @date 22-April-2014 ****************************************************************************** * * @param uint8_t StepBackwards: witch lookbook entry? * @param uint16_t length : maxlength of output arrays * @param int32_t* depth : output array * @param int16_t * gasid : output array * @param int32_t* temperature : output array * @param int32_t* ppo2 : output array * @param int32_t* cns : output array * @return length of output */ void logbook_recover_brokenlog(uint8_t headerId) { int16_t retVal; int32_t depthVal = 0; int16_t gasidVal = 0; int16_t setPointVal = 0; int16_t bailoutVal = 0; int32_t temperatureVal = 0; int32_t sensor1Val = 0; int32_t sensor2Val = 0; int32_t sensor3Val = 0; int32_t cnsVal = 0; SManualGas manualGasVal; //uint16_t* ppo2, uint16_t* cns# uint32_t bytesRead = 0; ext_flash_read_block_start(); ext_flash_read_next_sample_part((uint8_t*)&smallHeader, sizeof(SSmallHeader)); bytesRead += sizeof(SSmallHeader); clear_divisor(); int sampleCounter = 0; int maxdepth = 0; uint32_t avrdepth = 0; while (true) { ext_flash_set_entry_point(); divisorBackup = divisor; retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, NULL); if(retVal == 0) { //Error try to read again!!! ext_flash_reopen_read_sample_at_entry_point(); divisor = divisorBackup; retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, NULL); if(retVal == 0) { //Error try to read again!!! ext_flash_reopen_read_sample_at_entry_point(); divisor = divisorBackup; retVal = readSample(&depthVal,&gasidVal, &setPointVal, &temperatureVal, &sensor1Val, &sensor2Val, &sensor3Val, &cnsVal, &manualGasVal, &bailoutVal, NULL); if(retVal == 0) { ext_flash_reopen_read_sample_at_entry_point(); break; } } } if(depthVal > maxdepth) maxdepth = depthVal; avrdepth += depthVal; sampleCounter++; bytesRead +=retVal; } avrdepth/= sampleCounter; ext_flash_close_read_sample(); SLogbookHeader header; ext_flash_read_dive_header2((uint8_t*) &header, headerId, false); header.total_diveTime_seconds = sampleCounter * header.samplingRate; header.diveTimeMinutes = header.total_diveTime_seconds /60; header.diveTimeSeconds = header.total_diveTime_seconds - header.diveTimeMinutes * 60; header.maxDepth = maxdepth; header.averageDepth_mbar = avrdepth; SSettings * settings = settingsGetPointer(); settings->lastDiveLogId = headerId; ext_flash_close_new_dive_log((uint8_t *)&header); } /************************ (C) COPYRIGHT heinrichs weikamp *****END OF FILE****/