Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u -r87ee9712a928c58e3fe8b456bce567df0c6a2038 -rb085a36e252a01504b415bf145e4b1fc129cdcdf --- firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 87ee9712a928c58e3fe8b456bce567df0c6a2038) +++ firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision b085a36e252a01504b415bf145e4b1fc129cdcdf) @@ -59,7 +59,7 @@ #define D5_HEAT_TX_I_COEFFICIENT 0.001F ///< I Term for AC primary heater control during treatment mode. #define D45_HEAT_P_COEFFICIENT 0.05F //0.01F ///< P Term for trimmer heater control. -#define D45_HEAT_I_COEFFICIENT 0.0025F ///< I Term for trimmer heater control. +#define D45_HEAT_I_COEFFICIENT 0.0005F ///< I Term for trimmer heater control. #define D45_HEAT_TX_INIT_FEED_FORWARD 0.0F ///< Initial Feed forward term for heater control #define D45_HEAT_HIGHER_QD_P_COEFFICIENT 0.2F ///< P Term for trimmer heater control for higher dialysate flow rate. #define D45_HEAT_HIGHER_QD_I_COEFFICIENT 0.05F ///< I Term for trimmer heater control for higher dialysate flow rate. @@ -86,6 +86,10 @@ #define D45_HEATER_DEADBAND_CONTROL 0.1F ///< Trimmer heater dead band range for control. #define D5_TARGET_TEMP_ADJUST_LOW_QD_INTERVAL ( 3 * SEC_PER_MIN * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Adjust primary target temperature - outer loop control interval for low Qd. #define D5_TARGET_TEMP_ADJUST_HIGH_QD_INTERVAL ( 1 * SEC_PER_MIN * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Adjust primary target temperature - outer loop control interval for high Qd. +#define TEMP_RISE_MONITORING_LOW_QD_INTERVAL ( 2 * SEC_PER_MIN * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Temperature sample collection interval for LowQd outer temp control. +#define TIMEOUT_TO_INITIATE_OUTER_LOOP_CONTROL ( 25 * SEC_PER_MIN * MS_PER_SECOND ) ///< Timeout period to initiate outer temp control for Low Qds. +#define UPTREND_TEMP_CHECK_TIMEOUT ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Timeout period to start the dialyzer temperature uptrend check. +#define HEATLOSS_OFFSET_UNDO_TIMEOUT ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Timeout period to remove the initial offset added to the heat loss model. #define PRIMARY_HEATER_MAX_PWR_WATTS 1400.0F ///< AC Primary Heater Max Power consumption in Watts #define TX_PRIMARY_HEATER_MAX_PWR_WATTS 700.0F ///< Estimated power to be supplied to the primary heater during treatment mode #define HEAT_PRIMARY_HEATER_MAX_PWR_WATTS 980.0F ///< Estimated power to be supplied to the primary heater during heat disinfect mode @@ -104,12 +108,17 @@ #define D5_HEAT_OUT_TX_I_COEFFICIENT 0.4F ///< I Term for AC primary heater outer loop control during treatment mode. #else #define D5_HEAT_OUT_TX_P_COEFFICIENT 0.75F //2.00F //0.5F ///< P Term for AC primary heater outer loop control during treatment mode. -#define D5_HEAT_OUT_TX_I_COEFFICIENT 0.1F //0.04F //0.08F //0.4F ///< I Term for AC primary heater outer loop control during treatment mode. +#define D5_HEAT_OUT_TX_I_COEFFICIENT 0.15F //0.04F //0.08F //0.4F ///< I Term for AC primary heater outer loop control during treatment mode. #endif +#define D5_HEAT_OUT_LOWQD_TX_P_COEFFICIENT 0.5F ///< P Term for AC primary heater outer loop - low Qd(<200) control during treatment mode. +#define D5_HEAT_OUT_LOWQD_TX_I_COEFFICIENT 0.25F ///< I Term for AC primary heater outer loop - low Qd(<200) control during treatment mode. +#define D5_HEAT_HIGH_DIAL_FLOW_RATE 200.0F ///< Decide Primary heater outer loop - PI control based on the dialysate flow rate +#define D5_HEAT_TARGET_ESTIMATE_MAX 46.0F ///< Heat loss estimation maximum target temperature for D5 control + #define D5_HEAT_OUT_MIN_DELTA_TEMP 0.0F ///< Minimum Delta temperature that can be adjusted for D5 control #define D5_HEAT_OUT_MAX_DELTA_TEMP 50.0 ///< Maximum Delta temperature that can be adjusted for D5 control #define D5_HEAT_OUT_DEADBAND_CONTROL 0.1F ///< Heater outer loop dead band range for control. -#define HIGH_DIAL_FLOW_RATE 300.0F ///< Decide outer loop heater control based on the dialysate flow rate +#define TEMP_RISE_LOW_DIAL_FLOW_RATE 0.3F ///< Temperature difference between Tdtarget and dialyzer temp to start outer loop control. #define QUAD_FIRST_COEFFICIENT 0.0006F ///< First coefficient used in adjusted dialysate temperature quadratic calculation for low Qds #define QUAD_SECOND_COEFFICIENT -0.1743F ///< Second coefficient used in adjusted dialysate temperature quadratic calculation for low Qds @@ -138,6 +147,14 @@ #define BETA2_0_B3_HEAT_DIS_FOURTH_COEFF 0.0092543272727F ///< Fourth coefficient for Heat loss dissipation calculation from D28 to Dialyzer. #define BETA2_0_B3_HEAT_DIS_FIFTH_COEFF 1.079412363636F ///< Fifth coefficient for Heat loss dissipation calculation from D28 to Dialyzer. +#define BETA2_0_B_HEAT_DIS_FIRST_COEFF 0.38F ///< First coefficient for Heat loss dissipation calculation from D4 to D113 (natural logarthmic equation). +#define BETA2_0_B_HEAT_DIS_SEC_COEFF 2.72F ///< Second coefficient for Heat loss dissipation calculation from D4 to D113. +#define BETA2_0_B_HEAT_DIS_FIRST_COEFF_LOWQD -0.000082F ///< First coefficient for Heat loss dissipation calculation from D4 to D113 - For Low Qds. +#define BETA2_0_B_HEAT_DIS_SEC_COEFF_LOWQD 0.0227F ///< Second coefficient for Heat loss dissipation calculation from D4 to D113 - For Low Qds. +#define BETA2_0_B_HEAT_DIS_THIRD_COEFF_LOWQD 2.26F ///< Third coefficient for Heat loss dissipation calculation from D4 to D113 - For Low Qds. +#define BETA2_0_OFFSET_FIRST_COEFF_LOWQD -0.0086F ///< First coefficient for additional offset to be added on top of heatloss model - For Low Qds. +#define BETA2_0_OFFSET_SEC_COEFF_LOWQD 2.2F ///< Second coefficient for for additional offset to be added on top of heatloss model - For Low Qds. + //static const F32 HEATERS_VOLTAGE_TOLERANCE_V = HEATERS_MAX_OPERATING_VOLTAGE_V * HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL; ///< Heaters voltage tolerance in volts. /// Heaters data structure @@ -171,7 +188,11 @@ static U32 controlInterval[ NUM_OF_DD_HEATERS ]; ///< Heater control interval time. static U32 dataPublicationTimerCounter; ///< Data publication timer counter. static U32 primaryTargetTempAdjCounter; ///< Primary target temperature adjustment counter. +static U32 tempRiseMonitoringCounter; ///< For Low Qds,Kick start the outer temp control only when temperature is uptrending. static BOOL isTargetTempAdjusted; ///< Flag indicating that target temperature is adjusted +static BOOL isTempRiseDeteced; ///< Flag indicating that temperature rise is detected to initiate outer temp control +static BOOL isTimeoutCompleteforUptrendCheck; ///< Flag indicating that wait time to be completed to check the dialyzer uptrend temperature +static BOOL isOffsetRemovedForLowQds; ///< Flag indicating that offset added to heatloss model is undone or not. static F32 adjustedPrimaryTargetTemp; ///< Adjusted primary target temperature static BOOL isDialyzerTempFeedbackEnabled; ///< Flag indicating enable/disable the dilayser temp ( D28) based feedback adjustment static const F32 WATER_SPECIFIC_HEAT_DIVIDED_BY_MINUTES = 4184.0F / (F32)SEC_PER_MIN; ///< Water specific heat in J/KgC / 60. @@ -180,7 +201,12 @@ static F32 d5Efficiency; ///< AC heater efficiency factor. static F32 lastDialTargetTemperatureSet[ NUM_OF_DD_HEATERS ]; ///< last dialysate target temperature set for heater control static F32 d5FeedForward; ///< AC heater feed forward calculated value -static U32 d5OuterLoopControlInterval; ///< AC heater outer loop control valve +static U32 d5OuterLoopControlInterval; ///< AC heater outer loop control Interval +static U32 tempRiseMonitoringInterval; ///< Interval at which temperature sample collected to see the uptrend behavior +static F32 prevDialyzerTemperature; ///< Previous dialyzer temp collected to check temperature rise +static U32 startTimeToInitiateOuterloopTimeout; ///< Time to wait for temperature uptrend to seen,else initiate the outer loop control +static U32 startTimeToCheckUpTrendTemperature; ///< Time to wait for measured dialyzer temperature uptrend check. +static U32 startTimeToUndoOffsetFromHeatlossEstimation; ///< Time to remove the offset that is added to heatloss model for Low Qds. //For testing #ifdef __HEATERS_DEBUG__ @@ -194,10 +220,14 @@ static HEATERS_STATE_T handleHeaterStateControlToTarget( DD_HEATERS_T heater ); static HEATERS_STATE_T handleHeaterStateControlToDisinfectTarget( DD_HEATERS_T heater ); static void updatePrimaryHeaterTargetTemp( void ); +static void checkOuterLoopTempConrolInitiateForLowQd( void ); static F32 calculateBeta2HeatDissipationB1andB2( void ); static F32 calculateBeta2HeatDissipationB3( void ); static F32 calculateHeatDissipationB1andB2( void ); static F32 calculateHeatDissipationB3( void ); +static F32 calculateHeatDissipationB( void ); +static F32 calculateHeatDissipationBForLowQd( void ); +static F32 calculateAdditionalHeatOffsetForLowQds( void ); static F32 calculateInitialTemp( F32 targetTemp, F32 heatDissipation ); static F32 calculateDutyCycle( F32 flowrate, F32 deltaTemp, F32 power, F32 efficiency, F32 min, F32 max ); @@ -244,18 +274,27 @@ controlInterval[ D5_HEAT ] = D5_HEAT_CONTROL_INTERVAL_COUNT; controlInterval[ D45_HEAT ] = D45_HEAT_CONTROL_INTERVAL_COUNT; d5OuterLoopControlInterval = D5_TARGET_TEMP_ADJUST_HIGH_QD_INTERVAL; + tempRiseMonitoringInterval = TEMP_RISE_MONITORING_LOW_QD_INTERVAL; // Assign counter close to the target period heatersStatus[ D5_HEAT ].controlIntervalCounter = D5_HEAT_CONTROL_INTERVAL_START_COUNT; heatersStatus[ D45_HEAT ].controlIntervalCounter = 0; lastDialTargetTemperatureSet[ D5_HEAT ] = 0.0F; lastDialTargetTemperatureSet[ D45_HEAT ] = 0.0F; primaryTargetTempAdjCounter = 0; + tempRiseMonitoringCounter = 0; adjustedPrimaryTargetTemp = 0.0F; + prevDialyzerTemperature = 0.0F; d5Efficiency = AC_HEATER_EFFICIENCY; d5FeedForward = 0.0F; isTargetTempAdjusted = FALSE; + isTempRiseDeteced = FALSE; + isTimeoutCompleteforUptrendCheck = FALSE; + isOffsetRemovedForLowQds = FALSE; isDialyzerTempFeedbackEnabled = TRUE; + startTimeToInitiateOuterloopTimeout = 0; + startTimeToCheckUpTrendTemperature = 0; + startTimeToUndoOffsetFromHeatlossEstimation = 0; for ( heater = DD_HEATERS_FIRST; heater < NUM_OF_DD_HEATERS; heater++ ) { @@ -282,9 +321,14 @@ initializePIController( PI_CONTROLLER_ID_D5_HEAT, HEATERS_MIN_DUTY_CYCLE, D5_HEAT_TX_P_COEFFICIENT, D5_HEAT_TX_I_COEFFICIENT, HEATERS_MIN_DUTY_CYCLE, AC_HEATER_TX_MAX_DUTY_CYCLE, TRUE, D5_HEAT_TX_INIT_FEED_FORWARD ); - initializePIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, D5_HEAT_OUT_MIN_DELTA_TEMP, D5_HEAT_OUT_TX_P_COEFFICIENT, D5_HEAT_OUT_TX_I_COEFFICIENT, + // Outerloop control for high Qd (>200) + initializePIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_HIGHER_QD, D5_HEAT_OUT_MIN_DELTA_TEMP, D5_HEAT_OUT_TX_P_COEFFICIENT, D5_HEAT_OUT_TX_I_COEFFICIENT, D5_HEAT_OUT_MIN_DELTA_TEMP, D5_HEAT_OUT_MAX_DELTA_TEMP, FALSE, D5_HEAT_TX_INIT_FEED_FORWARD ); + // Outerloop control for Low Qd ( <200) + initializePIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, D5_HEAT_OUT_MIN_DELTA_TEMP, D5_HEAT_OUT_LOWQD_TX_P_COEFFICIENT, D5_HEAT_OUT_LOWQD_TX_I_COEFFICIENT, + D5_HEAT_OUT_MIN_DELTA_TEMP, D5_HEAT_OUT_MAX_DELTA_TEMP, FALSE, D5_HEAT_TX_INIT_FEED_FORWARD ); + // Initialize the trimmer heater PI controller initializePIController( PI_CONTROLLER_ID_D45_HEAT, HEATERS_MIN_DUTY_CYCLE, D45_HEAT_P_COEFFICIENT, D45_HEAT_I_COEFFICIENT, HEATERS_MIN_DUTY_CYCLE, DC_HEATER_MAX_DUTY_CYCLE, FALSE, D45_HEAT_TX_INIT_FEED_FORWARD ); @@ -425,12 +469,19 @@ { // Reset the adjusted temperature target isTargetTempAdjusted = FALSE; + isTempRiseDeteced = FALSE; + isTimeoutCompleteforUptrendCheck = FALSE; + prevDialyzerTemperature = 0.0F; + tempRiseMonitoringCounter = 0; + startTimeToInitiateOuterloopTimeout = getMSTimerCount(); + startTimeToCheckUpTrendTemperature = getMSTimerCount(); + // start outer loop control immediately upon changing Qd + flowrate = getTDDialysateFlowrate(); + primaryTargetTempAdjCounter = ( flowrate >= D5_HEAT_HIGH_DIAL_FLOW_RATE ? D5_TARGET_TEMP_ADJUST_HIGH_QD_INTERVAL : D5_TARGET_TEMP_ADJUST_LOW_QD_INTERVAL ); } if ( HEATER_EXEC_STATE_CONTROL_TO_TARGET == heatersStatus[ D45_HEAT ].state ) { - //Change back to ramp state to reset the controller - //heatersStatus[ D45_HEAT ].state = HEATER_EXEC_STATE_RAMP_TO_TARGET; F32 prevDuty = getHeaterControl( D45_HEAT ); // When Qd change happens, use previous PWM to calculate the new PWM @@ -468,7 +519,8 @@ F32 targetTemp = getTDTargetDialysateTemperature(); F32 targetTempAtD28 = 0.0F; F32 targetTempAtD4 = 0.0F; - + F32 offset = 0.0F; +#ifndef __USE_D1_TEMP_ // Heat loss model predicts the heat loss ( B1,B2 and B3) in the DD flow path and // finds the delta temperature to be added with the target temperature // to maintain the target temperature at dialyzer. @@ -489,6 +541,35 @@ targetTempAtD28 = calculateInitialTemp( targetTemp, heatDissipation_b3 ); // calculation of target temp at D4 to get the target temp at D28 level targetTempAtD4 = calculateInitialTemp( targetTempAtD28, heatDissipation_b1b2 ); +#else + // heat loss model based on the temp sensor (D113) closely located to dialyzer. + // Find the difference in temperature between dialyzer temperature(D113) and + // D4 temperature and that is considered as heat loss factor(B). + if ( dialFlowrate > LOW_DIAL_FLOW_RATE ) + { + // High Qd >150 to <=600: heat loss model + heatDissipation_b1b2 = calculateHeatDissipationB(); + } + else + { + // Low Qd 50 to 150: heat loss model + heatDissipation_b1b2 = calculateHeatDissipationBForLowQd(); + } + + // calculation of target temp at D4 to get the target temp at D113 level + targetTempAtD4 = calculateInitialTemp( targetTemp, heatDissipation_b1b2 ); + + // additional offset to be added for certain duration to improve undershoot + if ( dialFlowrate <= D5_HEAT_HIGH_DIAL_FLOW_RATE ) + { + offset = calculateAdditionalHeatOffsetForLowQds(); + targetTempAtD4 = targetTempAtD4 + offset; + startTimeToUndoOffsetFromHeatlossEstimation = getMSTimerCount(); + isOffsetRemovedForLowQds = FALSE; + } + // cap it to maximum value + targetTempAtD4 = CAP( targetTempAtD4, D5_HEAT_TARGET_ESTIMATE_MAX ); +#endif //Update target temperature setTargetHydChamberTemp( targetTempAtD4 ); } @@ -507,9 +588,17 @@ F32 deltaTempC = 0.0F; F32 ctrl = 0.0F; F32 measuredTempAtDialyzer = 0.0F; - F32 targetTempfromTD = 0.0F; - F32 diffTempC = 0.0F; + F32 flowrate = getTDDialysateFlowrate(); + F32 targetTempfromTD = getTDTargetDialysateTemperature(); + BOOL isTempRiseForLowQdMet = FALSE; + BOOL isQdHigh = FALSE; + // Check temp rise detected to initiate outer temp control loop + checkOuterLoopTempConrolInitiateForLowQd(); + // Low Qd and temp rise detected + isTempRiseForLowQdMet = ( ( flowrate <= D5_HEAT_HIGH_DIAL_FLOW_RATE ) && ( isTempRiseDeteced == TRUE ) ) ? TRUE : FALSE; + isQdHigh = ( flowrate > D5_HEAT_HIGH_DIAL_FLOW_RATE ) ? TRUE : FALSE; + if ( ++primaryTargetTempAdjCounter >= d5OuterLoopControlInterval ) { #ifdef __USE_D1_TEMP_ @@ -526,18 +615,35 @@ } #endif calcTargetTemp = getHeaterTargetTemperature( D5_HEAT ); - diffTempC = fabs( calcTargetTemp - measuredTempAtDialyzer ); - deltaTempC = targetTempfromTD - measuredTempAtDialyzer; + deltaTempC = fabs( targetTempfromTD - measuredTempAtDialyzer ); - if ( ( FALSE == isTargetTempAdjusted ) && ( diffTempC <= HEATER_TEMP_CONTROL_TRANSFER ) ) + if ( ( FALSE == isTargetTempAdjusted ) && ( deltaTempC <= HEATER_TEMP_CONTROL_TRANSFER ) && + ( ( TRUE == isTempRiseForLowQdMet ) || ( TRUE == isQdHigh ) || + ( TRUE == didTimeout( startTimeToInitiateOuterloopTimeout, TIMEOUT_TO_INITIATE_OUTER_LOOP_CONTROL ) ) ) ) { isTargetTempAdjusted = TRUE; adjustedPrimaryTargetTemp = calcTargetTemp; - resetPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, calcTargetTemp, HEATERS_MIN_DUTY_CYCLE ); + startTimeToInitiateOuterloopTimeout = 0; + + if ( flowrate >= D5_HEAT_HIGH_DIAL_FLOW_RATE ) + { + resetPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_HIGHER_QD, calcTargetTemp, HEATERS_MIN_DUTY_CYCLE ); + } + else + { + resetPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, calcTargetTemp, HEATERS_MIN_DUTY_CYCLE ); + } } - else if ( ( fabs(deltaTempC) >= D5_HEAT_OUT_DEADBAND_CONTROL ) && ( TRUE == isTargetTempAdjusted ) ) + else if ( ( deltaTempC >= D5_HEAT_OUT_DEADBAND_CONTROL ) && ( TRUE == isTargetTempAdjusted ) ) { - ctrl = runPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, getTDTargetDialysateTemperature(), measuredTempAtDialyzer ); + if ( flowrate >= D5_HEAT_HIGH_DIAL_FLOW_RATE ) + { + ctrl = runPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_HIGHER_QD, getTDTargetDialysateTemperature(), measuredTempAtDialyzer ); + } + else + { + ctrl = runPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, getTDTargetDialysateTemperature(), measuredTempAtDialyzer ); + } adjustedPrimaryTargetTemp = ctrl; } @@ -547,6 +653,87 @@ /*********************************************************************//** * @brief + * The checkOuterLoopTempConrolInitiateForLowQd function waits to kickstart + * the outer loop temp control for Low dialysate flow rate setting, to allow + * the previous heated fluid to flush through ( hydraulic to Dialyzer) and + * then Outer loop controls the target temperature at periodic interval. + * @details \b Inputs: Dialysate flowrate, D1 Temp + * @details \b Outputs: isTempRiseDeteced + * @return none + *************************************************************************/ +static void checkOuterLoopTempConrolInitiateForLowQd( void ) +{ + F32 diffTempC = 0.0F; + F32 deltaTempC = 0.0F; + F32 measuredTempAtDialyzer = 0.0F; + F32 offset = 0.0F; + F32 targetTemp = 0.0F; + F32 flowrate = getTDDialysateFlowrate(); + F32 targetTempfromTD = getTDTargetDialysateTemperature(); + + if ( flowrate <= D5_HEAT_HIGH_DIAL_FLOW_RATE ) + { + // Undo the offset after defined duration + if ( ( FALSE == isOffsetRemovedForLowQds ) && ( TRUE == didTimeout( startTimeToUndoOffsetFromHeatlossEstimation, HEATLOSS_OFFSET_UNDO_TIMEOUT ) ) ) + { + offset = calculateAdditionalHeatOffsetForLowQds(); + targetTemp = getHeaterTargetTemperature( D5_HEAT ) ; + targetTemp = targetTemp - offset; + setHeaterTargetTemperature( D5_HEAT, targetTemp ); + isOffsetRemovedForLowQds = TRUE; + } + + //Wait for uptrend temperature check timeout + if ( TRUE == didTimeout( startTimeToCheckUpTrendTemperature, UPTREND_TEMP_CHECK_TIMEOUT ) ) + { + isTimeoutCompleteforUptrendCheck = TRUE; + } + } + + // If the outer temp loop control was not started for low Qds, check for temp rise + // at specific interval (2 minutes) after initail time out over (10 minutes after Qd change). + // if temp rise is positive (Current dialyzer temp - last dialyzer temp) and + // the temp difference between target temp from TD and dialyzer temp is less than + // or equal to 0.3 Deg celcius,then set the temp rise detected flag to initiate D28 temp control + if ( ( isTargetTempAdjusted == FALSE ) && ( flowrate <= D5_HEAT_HIGH_DIAL_FLOW_RATE ) ) + { + if ( ( ++tempRiseMonitoringCounter >= tempRiseMonitoringInterval ) && ( TRUE == isTimeoutCompleteforUptrendCheck ) ) + { +#ifdef __USE_D1_TEMP_ + // Use new RTD sensor placed closed to dialyzer, connected to D1 port + measuredTempAtDialyzer = getFilteredTemperatureValue( D1_TEMP ); +#else + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_9_HW ) == TRUE ) + { + measuredTempAtDialyzer = getTeensyConductivityTemperatureValue( D27_COND ); + } + else + { + measuredTempAtDialyzer = getFilteredConductivitySensorTemperature( D27_COND ); + } +#endif + // Find the difference between Target Temp and Dilayzer temp + deltaTempC = targetTempfromTD - measuredTempAtDialyzer; + + if ( prevDialyzerTemperature > NEARLY_ZERO ) + { + diffTempC = measuredTempAtDialyzer - prevDialyzerTemperature; + } + + // Temp rise detected and delta temp is within 0.3 DegC. + if ( ( diffTempC > NEARLY_ZERO ) && ( ( deltaTempC > NEARLY_ZERO ) && ( deltaTempC <= TEMP_RISE_LOW_DIAL_FLOW_RATE ) ) ) + { + isTempRiseDeteced = TRUE; + } + + prevDialyzerTemperature = measuredTempAtDialyzer; + tempRiseMonitoringCounter = 0; + } + } +} + +/*********************************************************************//** + * @brief * The startHeater function starts the given heater by setting the flag. * @details \b Inputs: heatersStatus * @details \b Outputs: startHeaterSignal @@ -768,7 +955,9 @@ // Reset PI Controllers resetPIController( PI_CONTROLLER_ID_D5_HEAT, HEATERS_MIN_DUTY_CYCLE, feedforward ); resetPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, targetTemperature, HEATERS_MIN_DUTY_CYCLE ); - + resetPIController( PI_CONTROLLER_ID_D5_HEAT_OUTER_HIGHER_QD, targetTemperature, HEATERS_MIN_DUTY_CYCLE ); + // Qd <= 150 timeout to initiate outer loop control + startTimeToInitiateOuterloopTimeout = getMSTimerCount(); // Transfer Control to target when delta temp is minimal. state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; } @@ -869,14 +1058,15 @@ setPIControllerFeedForward( PI_CONTROLLER_ID_D5_HEAT, d5FeedForward ); // decide outer loop control interval based on the Qd - //flowrate = getTDDialysateFlowrate(); - //d5OuterLoopControlInterval = ( flowrate >= HIGH_DIAL_FLOW_RATE ? D5_TARGET_TEMP_ADJUST_HIGH_QD_INTERVAL : D5_TARGET_TEMP_ADJUST_LOW_QD_INTERVAL ); + flowrate = getTDDialysateFlowrate(); + d5OuterLoopControlInterval = ( flowrate >= D5_HEAT_HIGH_DIAL_FLOW_RATE ? D5_TARGET_TEMP_ADJUST_HIGH_QD_INTERVAL : D5_TARGET_TEMP_ADJUST_LOW_QD_INTERVAL ); // If D28 feedback control is enabled and adjusted temp calculation is done // then update the target temperature. - if ( ( TRUE == isTargetTempAdjusted ) && ( TRUE == isDialyzerTempFeedbackEnabled ) ) + if ( ( TRUE == isTargetTempAdjusted ) && ( TRUE == isDialyzerTempFeedbackEnabled ) ) { targetTemperature = adjustedPrimaryTargetTemp; + setHeaterTargetTemperature( D5_HEAT, targetTemperature ); } measuredTemperature = getFilteredTemperatureValue( D4_TEMP ); @@ -1021,7 +1211,7 @@ * @brief * The calculateBeta2HeatDissipationB1andB2 function calculates the heat dissipation * constants called B1 and B2 that describes the heat loss between D4 to D28 in - * the PID flow path for Beta2.0 hardware. + * the P&ID flow path for Beta2.0 hardware. * @details \b Inputs: Qd. * @details \b Outputs: none * @return the calculated heat dissipation constants. @@ -1046,7 +1236,7 @@ * @brief * The calculateBeta2HeatDissipationB3 function calculates the heat dissipation * constants called B3 that describes the heat loss between D28 to dialyzer in - * the PID flow path for Beta2.0 hardware. + * the P&ID flow path for Beta2.0 hardware. * @details \b Inputs: Qd. * @details \b Outputs: none * @return the calculated heat dissipation constant. @@ -1071,7 +1261,7 @@ * @brief * The calculateHeatDissipationB1andB2 function calculates the heat dissipation * constants called B1 and B2 that describes the heat loss between D4 to D28 in - * the PID flow path. + * the P&ID flow path. * @details \b Inputs: Qd. * @details \b Outputs: none * @return the calculated heat dissipation constants. @@ -1095,7 +1285,7 @@ * @brief * The calculateHeatDissipationB3 function calculates the heat dissipation * constants called B3 that describes the heat loss between D28 to dialyzer in - * the PID flow path. + * the P&ID flow path. * @details \b Inputs: Qd. * @details \b Outputs: none * @return the calculated heat dissipation constant. @@ -1117,6 +1307,80 @@ /*********************************************************************//** * @brief + * The calculateHeatDissipationB function calculates the heat dissipation + * constants called B that describes the heat loss between D4 to (D113)dialyzer in + * the P&ID flow path. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated heat dissipation constant. + *************************************************************************/ +static F32 calculateHeatDissipationB( void ) +{ + // Get the dialysate flow rate from TD + F32 b = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 logQd = logf(qd); // natural logarthemic + F32 firstPart = BETA2_0_B_HEAT_DIS_FIRST_COEFF * logQd; + + // B = 0.38*LN(qd)-2.72 + b = firstPart - BETA2_0_B_HEAT_DIS_SEC_COEFF; + + return b; +} + +/*********************************************************************//** + * @brief + * The calculateAdditionalHeatOffsetForLowQds function calculates the additional + * offset value on top of heat loss model that elevates the temperature for certain + * duration to avoid the heat undershoot (dropping below target) when low Qd + * switch over happens. + * the P&ID flow path. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated offset temperature to be added with heat loss model + * estimated temperature. + *************************************************************************/ +static F32 calculateAdditionalHeatOffsetForLowQds( void ) +{ + // Get the dialysate flow rate from TD + F32 offset = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 firstPart = BETA2_0_OFFSET_FIRST_COEFF_LOWQD * qd; + + // Offset = (-0.0086*qd)+2.2 + offset = firstPart + BETA2_0_OFFSET_SEC_COEFF_LOWQD; + + // For testing, remove the offset for now + offset = offset * 0.0F; + + return offset; +} + +/*********************************************************************//** + * @brief + * The calculateHeatDissipationBForLowQd function calculates the heat dissipation + * constants called B that describes the heat loss between D4 to (D113)dialyzer in + * the P&ID flow path, for low dialysate flow rates. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated heat dissipation constant. + *************************************************************************/ +static F32 calculateHeatDissipationBForLowQd( void ) +{ + // Get the dialysate flow rate from TD + F32 b = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 firstPart = BETA2_0_B_HEAT_DIS_FIRST_COEFF_LOWQD * qd * qd; + F32 secondPart = BETA2_0_B_HEAT_DIS_SEC_COEFF_LOWQD *qd; + + // B = (-0.000082*qd*qd)+(0.0227*qd)-2.26 + b = firstPart + secondPart - BETA2_0_B_HEAT_DIS_THIRD_COEFF_LOWQD; + + return b; +} + +/*********************************************************************//** + * @brief * The calculateInitialTemp function calculates the initial temperature * required to get the target temperature considering heat loss. * @details \b Inputs: ambient temperature Index: firmware/App/DDCommon.h =================================================================== diff -u -r90de09d56781a566bf16e462c08c98acd95c4c19 -rb085a36e252a01504b415bf145e4b1fc129cdcdf --- firmware/App/DDCommon.h (.../DDCommon.h) (revision 90de09d56781a566bf16e462c08c98acd95c4c19) +++ firmware/App/DDCommon.h (.../DDCommon.h) (revision b085a36e252a01504b415bf145e4b1fc129cdcdf) @@ -40,7 +40,7 @@ //#define __BARO_PRES_SENSOR__ 1 //Uncomment below to disable the communication to dialin for blood leak embedded mode -//#define __BLOOD_LEAK_EMB_MODE_RESPONSE_DISABLED__ 1 +#define __BLOOD_LEAK_EMB_MODE_RESPONSE_DISABLED__ 1 //Uncomment below once characterization/study completed //#define ENABLE_ALARM_2 @@ -57,20 +57,17 @@ //Uncomment below if D1 placement is used for Temp control at dialyzer //#define __USE_D1_TEMP_ 1 -//Uncomment below to disable revised heater model -#define __REVISED_HEATER_MODEL__ 1 - // comment below to disable bicarb multi level sensor for time based chamber F fill #define CONDUCTIVE_LEVEL_SENSOR_ENABLED 1 //Uncomment below to disable d30 heater alarm(as all units are not equipped with d30 temp sensor) -//#define __D30_TEMPERATURE_DISABLED__ +#define __D30_TEMPERATURE_DISABLED__ 1 //Uncomment below to disable the DD too-many-bad-message-CRCs alarm. -//#define __BAD_MSG_CRC_ALARM_DISABLED__ 1 +#define __BAD_MSG_CRC_ALARM_DISABLED__ 1 //Uncomment below to disable the conductivity sensor alarms -//#define __CONDUCTIVITY_SENSOR_ALARMS_DISABLED__ +#define __CONDUCTIVITY_SENSOR_ALARMS_DISABLED__ 1 #include Index: firmware/App/Monitors/Conductivity.c =================================================================== diff -u -ra7af93efacb700dc80504906356851e9991ba3b1 -rb085a36e252a01504b415bf145e4b1fc129cdcdf --- firmware/App/Monitors/Conductivity.c (.../Conductivity.c) (revision a7af93efacb700dc80504906356851e9991ba3b1) +++ firmware/App/Monitors/Conductivity.c (.../Conductivity.c) (revision b085a36e252a01504b415bf145e4b1fc129cdcdf) @@ -277,6 +277,7 @@ // Apply sample to filter if we know it's fresh, v2/v3 only. V1's will always update filter regardless of freshness. if ( TRUE == freshData ) { + freshData = FALSE; // TODO - calibrate if ( filteredConductivityReadings[ sensor ].conductivityReadingsCount >= SIZE_OF_COND_ROLLING_AVG ) { @@ -287,7 +288,6 @@ filteredConductivityReadings[ sensor ].conductivityReadingsIdx = INC_WRAP( filteredConductivityReadings[ sensor ].conductivityReadingsIdx, 0, SIZE_OF_COND_ROLLING_AVG - 1 ); filteredConductivityReadings[ sensor ].conductivityReadingsCount = INC_CAP( filteredConductivityReadings[ sensor ].conductivityReadingsCount, SIZE_OF_COND_ROLLING_AVG ); filteredcurrentConductivityReadings[ sensor ].data = filteredConductivityReadings[ sensor ].conductivityReadingsTotal / (F32)filteredConductivityReadings[ sensor ].conductivityReadingsCount; - freshData = FALSE; } } } @@ -365,6 +365,7 @@ // Apply sample to filter if we know it's fresh, v2/v3 only. V1's will always update filter regardless of freshness. if ( TRUE == freshData ) { + freshData = FALSE; // TODO - calibrate if ( filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsCount >= SIZE_OF_COND_TEMP_ROLLING_AVG ) { @@ -375,7 +376,6 @@ filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsIdx = INC_WRAP( filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsIdx, 0, SIZE_OF_COND_TEMP_ROLLING_AVG - 1 ); filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsCount = INC_CAP( filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsCount, SIZE_OF_COND_TEMP_ROLLING_AVG ); filteredcurrentTemperatureReadings[sensor].data = filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsTotal / (F32)filteredConductivityTemperatureReadings[sensor].conductivityTempReadingsCount; - freshData = FALSE; } } } Index: firmware/App/Monitors/Temperature.c =================================================================== diff -u -rdc451e960ab328d05a06137ffdc4d8b2b1601d55 -rb085a36e252a01504b415bf145e4b1fc129cdcdf --- firmware/App/Monitors/Temperature.c (.../Temperature.c) (revision dc451e960ab328d05a06137ffdc4d8b2b1601d55) +++ firmware/App/Monitors/Temperature.c (.../Temperature.c) (revision b085a36e252a01504b415bf145e4b1fc129cdcdf) @@ -514,13 +514,9 @@ data.d30AvgTemp = dialTempMovingAvgData[ DIAL_TEMP_D30 ].dialTempAvgC; data.d78AvgTemp = getFilteredTemperatureValue( D78_TEMP ); + data.d1AvgTemp = getFilteredTemperatureValue( D1_TEMP ); -#ifdef __USE_D1_TEMP_ - data.d9PresTemp = getFilteredTemperatureValue( D1_TEMP ); -#else data.d9PresTemp = getFilteredPressureSensorTemperature( D9_PRES ); -#endif - data.d66PresTemp = getFilteredPressureSensorTemperature( D66_PRES ); data.d51PresTemp = getFilteredPressureSensorTemperature( D51_PRES ); data.d18PresTemp = getFilteredPressureSensorTemperature( D18_PRES );