Index: firmware/App/Controllers/ConductivitySensors.c =================================================================== diff -u -r6085153faf2a176c51a34653bdd3a36a4699e528 -rd3819286869611f9c02add72a0f8e321598fdf42 --- firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 6085153faf2a176c51a34653bdd3a36a4699e528) +++ firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision d3819286869611f9c02add72a0f8e321598fdf42) @@ -14,6 +14,7 @@ * @date (original) 13-Jul-2020 * ***************************************************************************/ +#include // Used for calculating the polynomial calibration equation. #include "ConductivitySensors.h" #include "FPGA.h" @@ -44,17 +45,6 @@ #define COND_SENSOR_CPI_MAX_VALUE 2000 ///< Maximum inlet water conductivity. #define COND_SENSOR_CPI_MIN_VALUE 100 ///< Minimum inlet water conductivity. -#define MIN_ACID_CONCENTRATE_CONDUCTIVITY 11000 ///< Minimum conductivity for acid concentrate. -#define MAX_ACID_CONCENTRATE_CONDUCTIVITY 11850 ///< Maximum conductivity for acid concentrate. - -#define MIN_BICARB_CONCENTRATE_CONDUCTIVITY 2000 ///< Minimum conductivity for bicarbonate concentrate. -#define MAX_BICARB_CONCENTRATE_CONDUCTIVITY 3000 ///< Maximum conductivity for bicarbonate concentrate. - -///< Minimum conductivity value for dialysate solution. -#define MIN_DIALYSATE_CONDUCTIVITY ( MIN_ACID_CONCENTRATE_CONDUCTIVITY + MIN_BICARB_CONCENTRATE_CONDUCTIVITY ) -///< Maximum conductivity value for dialysate solution. -#define MAX_DIALYSATE_CONDUCTIVITY ( MAX_ACID_CONCENTRATE_CONDUCTIVITY + MAX_BICARB_CONCENTRATE_CONDUCTIVITY ) - #define MAX_ALLOWED_UNCHANGED_CONDUCTIVITY_READS ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< New reading every 800 ms, expect to get valid new reading in 1s. #define MAX_CONDUCTIVITY_SENSOR_FAILURES 5 ///< maximum number of conductivity sensor errors within window period before alarm. #define MAX_CONDUCTIVITY_SENSOR_FAILURE_WINDOW_MS ( 60 * MS_PER_SECOND ) ///< Conductivity sensor error window. @@ -68,6 +58,7 @@ #define EMSTAT_PICO_MEASUREMENT_OFFSET 0x8000000 ///< Measurement offset for emstat pico measurement data. #define EMSTAT_PICO_GOOD_STATUS 0x10 ///< Measurement good status. +#define EMSTAT_PICO_TIMING_NOT_MET_STATUS 0x11 ///< Measurement takes too long status. #define EMSTAT_PICO_FIFO_EMPTY_MASK 0x8000 ///< Emstat Pico buffer empty indication bit. #pragma pack(push,1) @@ -106,17 +97,19 @@ static BOOL packageStarted = FALSE; ///< Flag to indicate the start of a package measurement data. static U08 packageIndex = 0U; ///< Current package measurement data bytes index. static U08 package[ 50 ]; ///< Storage of package bytes until ready to process. +static DG_COND_SENSORS_CAL_RECORD_T condSensorsCalRecord; ///< Conductivity sensors' calibration record. // ********** private function prototypes ********** -static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature); +static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature ); static void calcRORejectionRatio( void ); static void processCPiCPoSensorRead( U32 sensorId, U32 fgpaRead, U08 fpgaReadCount, U08 fpgaErrorCount, U08 fpgaSensorFault ); static U32 prefixStrToSIFactor( U08 prefix ); static void processMeasurementDataPackage( U32 sensorId ); static void processCD1CD2SensorRead( U16 fpgaReadCount, U08 fpgaErrorCount ); -static U32 getConductivityDataPublishInterval( void ); +static BOOL processCalibrationData( void ); +static F32 getCalibrationAppliedConductivityValue( U32 sensorId, F32 compensatedValue ); /*********************************************************************//** * @brief @@ -155,16 +148,24 @@ * The execConductivitySensors function gets conductivity sensors' latest * readings from FPGA and advertises them over CAN. * @details Inputs: none - * @details Outputs: Conductivity sensors' latest reading is updated and advertised. + * @details Outputs: Conductivity sensors' latest reading is updated and + * advertised. * @return none *************************************************************************/ void execConductivitySensors( void ) { + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); + } + processCPiCPoSensorRead( CONDUCTIVITYSENSORS_CPI_SENSOR, getFPGACPi(), getFPGACPiReadCount(), getFPGACPiErrorCount(), getFPGACPiFault() ); processCPiCPoSensorRead( CONDUCTIVITYSENSORS_CPO_SENSOR, getFPGACPo(), getFPGACPoReadCount(), getFPGACPoErrorCount(), getFPGACPoFault() ); processCD1CD2SensorRead( getFPGAEmstatRxFifoCount(), getFPGAEmstatRxErrCount() ); - if ( ++conductivityDataPublicationTimerCounter >= getConductivityDataPublishInterval() ) + if ( ++conductivityDataPublicationTimerCounter >= getU32OverrideValue( &conductivityDataPublishInterval ) ) { CONDUCTIVITY_DATA_T data; @@ -179,7 +180,32 @@ broadcastConductivityData( &data ); } +} +/*********************************************************************//** + * @brief + * The execConductivitySensorsSelfTest function executes the conductivity + * sensors' self-test. + * @details Inputs: none + * @details Outputs: none + * @return PressuresSelfTestResult (SELF_TEST_STATUS_T) + *************************************************************************/ +SELF_TEST_STATUS_T execConductivitySensorsSelfTest( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; } /*********************************************************************//** @@ -223,54 +249,6 @@ /*********************************************************************//** * @brief - * The isAcidConductivityInRange function checks if acid conductivity value - * is in range. - * @details Inputs: CD1 sensor conductivity - * @details Outputs: none - * @return TRUE if acid conductivity is in range, otherwise FALSE - *************************************************************************/ -BOOL isAcidConductivityInRange( void ) -{ - F32 const acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); - BOOL const isConductivityInRange = ( MIN_ACID_CONCENTRATE_CONDUCTIVITY <= acidConductivity ) && ( acidConductivity <= MAX_ACID_CONCENTRATE_CONDUCTIVITY ); - - return isConductivityInRange; -} - -/*********************************************************************//** - * @brief - * The isBicarbConductivityInRange function checks if bicarbonate conductivity - * value is in range. - * @details Inputs: CD2 sensor conductivity - * @details Outputs: none - * @return TRUE if bicarb conductivity is in range, otherwise FALSE - *************************************************************************/ -BOOL isBicarbConductivityInRange( void ) -{ - F32 const bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - BOOL const isConductivityInRange = ( MIN_BICARB_CONCENTRATE_CONDUCTIVITY <= bicarbConductivity ) && ( bicarbConductivity <= MAX_BICARB_CONCENTRATE_CONDUCTIVITY ); - - return isConductivityInRange; -} - -/*********************************************************************//** - * @brief - * The isDialysateConductivityInRange function checks if dialysate conductivity - * value is in range. - * @details Inputs: CD2 sensor conductivity - * @details Outputs: none - * @return TRUE if dialysate conductivity is in range, otherwise FALSE - *************************************************************************/ -BOOL isDialysateConductivityInRange( void ) -{ - F32 const bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - BOOL const isConductivityInRange = isAcidConductivityInRange() && ( MIN_DIALYSATE_CONDUCTIVITY <= bicarbConductivity ) && ( bicarbConductivity <= MAX_DIALYSATE_CONDUCTIVITY ); - - return isConductivityInRange; -} - -/*********************************************************************//** - * @brief * The getConductivityValue function gets the compensated conductivity * value for a given conductivity sensor id. * @details Inputs: compensatedConductivityValues[] @@ -284,18 +262,11 @@ if ( sensorId < NUM_OF_CONDUCTIVITY_SENSORS ) { - if ( OVERRIDE_KEY == compensatedConductivityValues[ sensorId ].override ) - { - result = compensatedConductivityValues[ sensorId ].ovData; - } - else - { - result = compensatedConductivityValues[ sensorId ].data; - } + result = getF32OverrideValue( &compensatedConductivityValues[ sensorId ] ); } else { - activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_CONDUCTIVITY_SENSOR_ID, sensorId ); } return result; @@ -357,15 +328,17 @@ *************************************************************************/ static void processCPiCPoSensorRead( U32 sensorId, U32 fgpaRead, U08 fpgaReadCount, U08 fpgaErrorCount, U08 fpgaSensorFault ) { - if ( ( fpgaErrorCount == 0 ) && ( fpgaSensorFault == 0 ) ) + if ( ( 0 == fpgaErrorCount ) && ( 0 == fpgaSensorFault ) ) { if ( ( readCount[ sensorId ] != fpgaReadCount ) ) { F32 const temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); F32 const conductivity = ( (F32)( fgpaRead ) / COND_SENSOR_DECIMAL_CONVERSION ); + F32 const compensatedCond = calcCompensatedConductivity( conductivity, temperature ); + readCount[ sensorId ] = fpgaReadCount; internalErrorCount[ sensorId ] = 0; - compensatedConductivityValues[ sensorId ].data = calcCompensatedConductivity( conductivity, temperature ); + compensatedConductivityValues[ sensorId ].data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); } else { @@ -432,27 +405,36 @@ static void processMeasurementDataPackage( U32 sensorId ) { EMSTAT_VARIABLE_T const * const measurementPtr = (EMSTAT_VARIABLE_T *)&package; + U32 const status = hexStrToDec( (U08 *)&measurementPtr->status, sizeof( measurementPtr->status ) ); - if ( EMSTAT_PICO_GOOD_STATUS == hexStrToDec( (U08 *)&measurementPtr->status, sizeof( measurementPtr->status ) ) ) + if ( EMSTAT_PICO_GOOD_STATUS == status ) { U32 const prefix = prefixStrToSIFactor( measurementPtr->prefix ); F32 const resistance = ( ( F32 )( hexStrToDec( measurementPtr->value, sizeof( measurementPtr->value ) ) - EMSTAT_PICO_MEASUREMENT_OFFSET ) / prefix ); F32 const temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); F32 const conductivity = ( 1 / resistance * SIEMENS_TO_MICROSIEMENS_CONVERSION ); + F32 const compensatedCond = calcCompensatedConductivity( conductivity, temperature ); internalErrorCount[ sensorId ] = 0; - compensatedConductivityValues[ sensorId ].data = calcCompensatedConductivity( conductivity, temperature ); + compensatedConductivityValues[ sensorId ].data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); } else { + compensatedConductivityValues[ sensorId ].data = 0.0; + } + + if ( EMSTAT_PICO_TIMING_NOT_MET_STATUS == status ) + { ++internalErrorCount[ sensorId ]; - if ( internalErrorCount[ sensorId ] > MAX_ALLOWED_UNCHANGED_CONDUCTIVITY_READS ) + if ( internalErrorCount[ sensorId ] > MAX_CONDUCTIVITY_SENSOR_FAILURES ) { -#ifndef DISABLE_COND_SENSOR_CHECK SET_ALARM_WITH_1_U32_DATA( ALARM_ID_CONDUCTIVITY_SENSOR_FAULT, sensorId ); -#endif } } + else + { + internalErrorCount[ sensorId ] = 0; + } } /*********************************************************************//** @@ -511,33 +493,75 @@ if ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_FPGA_CONDUCTIVITY_SENSOR_ERROR ) ) { #ifndef DISABLE_COND_SENSOR_CHECK - SET_ALARM_WITH_1_U32_DATA( ALARM_ID_CONDUCTIVITY_SENSOR_FAULT, CONDUCTIVITYSENSORS_CD1_SENSOR ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_CONDUCTIVITY_SENSOR_FAULT, CONDUCTIVITYSENSORS_CD1_SENSOR, CONDUCTIVITYSENSORS_CD2_SENSOR ); #endif } } } /*********************************************************************//** * @brief - * The getConductivityDataPublishInterval function gets the conductivity - * data publication interval. - * @details Inputs: conductivityDataPublishInterval - * @details Outputs: none - * @return the current conductivity data publication interval (in ms/task interval). + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: condSensorsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE *************************************************************************/ -static U32 getConductivityDataPublishInterval( void ) +static BOOL processCalibrationData( void ) { - U32 result = conductivityDataPublishInterval.data; + BOOL status = TRUE; + U32 sensor; - if ( OVERRIDE_KEY == conductivityDataPublishInterval.override ) + // Get the calibration record from NVDataMgmt + DG_COND_SENSORS_CAL_RECORD_T calData = getDGConducitivitySensorsCalibrationRecord(); + + for ( sensor = 0; sensor < NUM_OF_CAL_DATA_COND_SENSORS; sensor++ ) { - result = conductivityDataPublishInterval.ovData; +#ifndef SKIP_CAL_CHECK + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.condSensors[ sensor ].calibrationTime ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD, (U32)sensor ); + status = FALSE; + } +#endif + + // The calibration data was valid, update the local copy + condSensorsCalRecord.condSensors[ sensor ].fourthOrderCoeff = calData.condSensors[ sensor ].fourthOrderCoeff; + condSensorsCalRecord.condSensors[ sensor ].thirdOrderCoeff = calData.condSensors[ sensor ].thirdOrderCoeff; + condSensorsCalRecord.condSensors[ sensor ].secondOrderCoeff = calData.condSensors[ sensor ].secondOrderCoeff; + condSensorsCalRecord.condSensors[ sensor ].gain = calData.condSensors[ sensor ].gain; + condSensorsCalRecord.condSensors[ sensor ].offset = calData.condSensors[ sensor ].offset; } - return result; + return status; } +/*********************************************************************//** + * @brief + * The getCalibrationAppliedConductivityValue function gets the temperature + * compensated conductivity value and applies calibration to it. + * @details Inputs: condSensorsCalRecord + * @details Outputs: none + * @param sensorId which is the conductivity sensor ID + * @param compensatedValue which is the temperature compensated conductivity + * value of the conductivity sensor + * @return the calibration applied conductivity value + *************************************************************************/ +static F32 getCalibrationAppliedConductivityValue( U32 sensorId, F32 compensatedValue ) +{ + F32 conductivity = pow(compensatedValue, 4) * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].fourthOrderCoeff + + pow(compensatedValue, 3) * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].thirdOrderCoeff + + pow(compensatedValue, 2) * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].secondOrderCoeff + + compensatedValue * condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].gain + + condSensorsCalRecord.condSensors[ (CAL_DATA_DG_COND_SENSORS_T)sensorId ].offset; + return conductivity; +} + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/