Index: firmware/App/Controllers/Heaters.c =================================================================== diff -u -r1fa38184526c2b0c072eb8745c653f772156a824 -r3b84fc79b2fd6c7a447e54e4fe821447f613af59 --- firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 1fa38184526c2b0c072eb8745c653f772156a824) +++ firmware/App/Controllers/Heaters.c (.../Heaters.c) (revision 3b84fc79b2fd6c7a447e54e4fe821447f613af59) @@ -7,8 +7,8 @@ * * @file Heaters.c * -* @author (last) Vinayakam Mani -* @date (last) 03-Mar-2026 +* @author (last) Santhosh Reddy +* @date (last) 01-Jul-2026 * * @author (original) Vinayakam Mani * @date (original) 11-Oct-2024 @@ -18,11 +18,13 @@ #include // Used for mathematical operations #include "Conductivity.h" +#include "ConductivityTeensy.h" #include "FpgaDD.h" #include "Heaters.h" #include "Level.h" #include "MessageSupport.h" #include "Messaging.h" +#include "ModeGenDialysate.h" //#include "NVMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" @@ -52,19 +54,19 @@ #define HEATERS_DUTY_CYCLE_CONVERSION_FACTOR 100.0F ///< Heaters duty cycle 0: OFF, 100: 100% duty cycle. #define HEATERS_ZERO_EFFICIENCY 0.0F ///< Zero heater efficiency #define HEATER_CNTL_TRANSFER_DELTA_TEMP_C 0.50F ///< AC heater delta temperature to transfer control from open to close loop -#define ADJ_DELTA_TEMP_STEP 2.0F ///< Adjust target temperature based on D28 feedback per cycle. -#define MAX_ADJ_DELTA_TEMP_C 7.0F ///< Maximum adjusted delta temperature to add/remove from calculated target temperature - #define D5_HEAT_TX_INIT_FEED_FORWARD 0.0F ///< Initial Feed forward term for heater control -#define D5_HEAT_TX_P_COEFFICIENT 0.035F ///< P Term for AC primary heater control during treatment mode. -#define D5_HEAT_TX_I_COEFFICIENT 0.004F ///< I Term for AC primary heater control during treatment mode. +#define D5_HEAT_TX_P_COEFFICIENT 0.034F ///< P Term for AC primary heater control during treatment mode. +#define D5_HEAT_TX_I_COEFFICIENT 0.001F ///< I Term for AC primary heater control during treatment mode. -#define D45_HEAT_P_COEFFICIENT 0.20F ///< P Term for trimmer heater control. -#define D45_HEAT_I_COEFFICIENT 0.05F ///< I Term for trimmer heater control. +#define D45_HEAT_P_COEFFICIENT 0.05F ///< P 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. +#define D45_HEAT_HIGH_DIAL_FLOW_RATE 200.0F ///< Decide Trimmer heater - high gain PI control based on the dialysate flow rate #define HEATERS_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Heaters data publish interval. -#define HEATER_TEMP_CONTROL_TRANSFER 1.0F ///< Primary Heater temperature difference to switch to control function +#define HEATER_TEMP_CONTROL_TRANSFER 2.0F ///< Primary Heater temperature difference to switch to D28 outer loop control function #define HEATER_TARGET_TEMPERATURE_MIN 10.0F ///< Minimum allowed target temperature for the heaters. #define HEATER_TARGET_TEMPERATURE_MAX 90.0F ///< Maximum allowed target temperature for the heaters. #define DELTA_TEMP_ADJ_FLOW_RATE 150.0F ///< Fo Qd <= 150, the bottom range of second level temperature correction to be applied. @@ -74,25 +76,84 @@ #define HEATERS_MAX_OPERATING_VOLTAGE_V 24.0F ///< Heaters max operating voltage in volts. #define HEATERS_VOLTAGE_OUT_OF_RANGE_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Heaters voltage out of range time out in milliseconds. #define HEATERS_MAX_VOLTAGE_OUT_OF_RANGE_TOL 0.2F ///< Heaters max voltage out of range tolerance. -#define D5_HEATER_DEADBAND_CONTROL 0.1F ///< Heater deadband range for conrtol. +#define D5_HEATER_DEADBAND_CONTROL 0.1F ///< Heater dead band range for control. #define D5_HEAT_CONTROL_INTERVAL_MS 3000 /// Primary heater control interval in milli seconds #define D5_HEAT_CONTROL_INTERVAL_COUNT ( D5_HEAT_CONTROL_INTERVAL_MS / TASK_GENERAL_INTERVAL ) ///< Primary heater control interval count. #define D45_HEAT_CONTROL_INTERVAL_MS ( 1 * MS_PER_SECOND ) ///< Trimmer heater control interval in milli seconds #define D45_HEAT_CONTROL_INTERVAL_COUNT ( D45_HEAT_CONTROL_INTERVAL_MS / TASK_GENERAL_INTERVAL ) ///< Trimmer heater control interval count. -#define D5_TARGET_TEMP_ADJUST_INTERVAL_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Adjust primary target temperature -#define PRIMARY_HEATER_MAX_PWR_WATTS 1400.0F ///< AC Primary Heater Max Power consumeption in Watts -#define TX_PRIMARY_HEATER_MAX_PWR_WATTS 700.0F ///< Estimated power to be supplied to the primary heater during treatement mode +#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 #define MAX_INLET_FLOW_LPM ( 600.0F / 1000.0F ) ///< Maximum inlet flow to hydraulics chamber from FP #define LITER_IN_ML 1000.0F ///< Liter in milliliter units #define TRIMMER_HEATER_MAX_PWR_WATTS 120.0F ///< Maximum power supplied to trimmer heater -#define AC_HEATER_PWM_PERIOD 10000 ///< PWM period 100 ms( in 10us resoultion), 1/10Hz = 1000000us/10us = 10000. +#define AC_HEATER_PWM_PERIOD 10000 ///< PWM period 100ms( in 10us resolution), 1/10Hz = 1000000us/10us = 10000. #define AC_HEATER_EFFICIENCY 0.90F ///< Approximated AC heater efficiency to be used in energy calcualtions. #define DC_HEATER_EFFICIENCY 1.0F ///< DC heater efficiency #define D5_HEAT_CONTROL_INTERVAL_START_COUNT ( D5_HEAT_CONTROL_INTERVAL_COUNT - 10 ) ///< AC heater control interval start count to jump feedforward control from open loop. +#define RINSE_PUMP_EST_FLOWRATE 110 ///< Estimated rinse pump flow rate #define DATA_PUBLISH_COUNTER_START_COUNT 70 ///< Data publish counter start count. +#ifndef __USE_D1_TEMP_ +#define D5_HEAT_OUT_TX_P_COEFFICIENT 0.5F ///< P Term for AC primary heater outer loop control during treatment mode. +#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 ///< P Term for AC primary heater outer loop control during treatment mode. +#define D5_HEAT_OUT_TX_I_COEFFICIENT 0.15F ///< 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 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 +#define QUAD_THIRD_COEFFICIENT 17.3F ///< Third coefficient used in adjusted dialysate temperature quadratic calculation for low Qds +#define LINEAR_SLOPE_FACTOR -0.0029F ///< Slope factor used in adjusted dialysate temperature linear calculation for high Qds +#define LINEAR_INTERCEPT_FACTOR 3.47F ///< Intercept factor used in adjusted dialysate temperature linear calculation for high Qds +#define LOW_DIAL_FLOW_RATE 150.0F ///< Dialysate flow rate lesser than 150 considered to be low Qds. + +#define B1B2_HEAT_DIS_FIRST_COEFF 0.00000000487858F ///< First coefficient for Heat loss dissipation calculation from D4 to D28(cubic equation). +#define B1B2_HEAT_DIS_SEC_COEFF 0.0000063124F ///< Second coefficient for Heat loss dissipation calculation from D4 to D28. +#define B1B2_HEAT_DIS_THIRD_COEFF 0.00258513F ///< Third coefficient for Heat loss dissipation calculation from D4 to D28. +#define B1B2_HEAT_DIS_FOURTH_COEFF 0.242013F ///< Fourth coefficient for Heat loss dissipation calculation from D4 to D28. +#define B3_HEAT_DIS_FIRST_COEFF 0.0000000067756F ///< First coefficient for Heat loss dissipation calculation from D28 to Dialyzer(cubic equation). +#define B3_HEAT_DIS_SEC_COEFF 0.0000085278F ///< Second coefficient for Heat loss dissipation calculation from D28 to Dialyzer. +#define B3_HEAT_DIS_THIRD_COEFF 0.0035126F ///< Third coefficient for Heat loss dissipation calculation from D28 to Dialyzer. +#define B3_HEAT_DIS_FOURTH_COEFF 0.63893F ///< Fourth coefficient for Heat loss dissipation calculation from D28 to Dialyzer. + +#define BETA2_0_B1B2_HEAT_DIS_FIRST_COEFF -0.000000000005212F ///< First coefficient for Heat loss dissipation calculation from D4 to D28(Beta2.0 quartic equation). +#define BETA2_0_B1B2_HEAT_DIS_SEC_COEFF 0.000000009754882F ///< Second coefficient for Heat loss dissipation calculation from D4 to D28. +#define BETA2_0_B1B2_HEAT_DIS_THIRD_COEFF 0.000006714979798F ///< Third coefficient for Heat loss dissipation calculation from D4 to D28. +#define BETA2_0_B1B2_HEAT_DIS_FOURTH_COEFF 0.002289024242423F ///< Fourth coefficient for Heat loss dissipation calculation from D4 to D28. +#define BETA2_0_B1B2_HEAT_DIS_FIFTH_COEFF 0.317550545454563F ///< Fifth coefficient for Heat loss dissipation calculation from D4 to D28. +#define BETA2_0_B3_HEAT_DIS_FIRST_COEFF -0.0000000000389F ///< First coefficient for Heat loss dissipation calculation from D28 to Dialyzer(Beta2.0 quartic equation). +#define BETA2_0_B3_HEAT_DIS_SEC_COEFF 0.0000000619946F ///< Second coefficient for Heat loss dissipation calculation from D28 to Dialyzer. +#define BETA2_0_B3_HEAT_DIS_THIRD_COEFF 0.0000359241717F ///< Third coefficient for Heat loss dissipation calculation from D28 to Dialyzer. +#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 @@ -121,19 +182,31 @@ static HEATER_STATUS_T heatersStatus[ NUM_OF_DD_HEATERS ]; ///< Heaters status. static OVERRIDE_F32_T targetTempC[ NUM_OF_DD_HEATERS ]; ///< Heater target temperature. -static OVERRIDE_F32_T control[ NUM_OF_DD_HEATERS ]; ///< Heater control ( Primary : On/Off, Trimmer : Dutycycle). +static OVERRIDE_F32_T control[ NUM_OF_DD_HEATERS ]; ///< Heater control ( Primary : On/Off, Trimmer : Duty cycle). static OVERRIDE_F32_T pwmPeriod[ NUM_OF_DD_HEATERS ]; ///< Total PWM period ( ON state + Off State of PWM) 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. static OVERRIDE_U32_T heatersDataPublishInterval = { HEATERS_DATA_PUBLISH_INTERVAL, HEATERS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Heaters data publish time interval. static F32 convertDC; ///< AC Heater converted duty cycle +static F32 d5Efficiency; ///< AC heater efficiency factor. static F32 lastDialTargetTemperatureSet[ NUM_OF_DD_HEATERS ]; ///< last dialysate target temperature set for heater control -static BOOL startupHeaterControl; ///< First time control with the energy equation. +static F32 d5FeedForward; ///< AC heater feed forward calculated value +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. +static BOOL previousHeaterState[ NUM_OF_DD_HEATERS ]; ///< Previous State of Heater. //For testing #ifdef __HEATERS_DEBUG__ @@ -147,6 +220,15 @@ 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 ); static void setHeaterControl( DD_HEATERS_T heater ); @@ -191,17 +273,32 @@ convertDC = 0.0F; 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; + //Initialize Previous Heater State + previousHeaterState[ D5_HEAT ] = FALSE; + previousHeaterState[ D45_HEAT ] = FALSE; + // Assign counter close to the target period heatersStatus[ D5_HEAT ].controlIntervalCounter = D5_HEAT_CONTROL_INTERVAL_START_COUNT; heatersStatus[ D45_HEAT ].controlIntervalCounter = 0; - startupHeaterControl = TRUE; 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++ ) { @@ -228,10 +325,22 @@ 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 ); + // 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 ); + // Initialize the trimmer heater PI controller for Higher dialysate flow rate + initializePIController( PI_CONTROLLER_ID_D45_HEAT_HIGHER_QD, HEATERS_MIN_DUTY_CYCLE, D45_HEAT_HIGHER_QD_P_COEFFICIENT, D45_HEAT_HIGHER_QD_I_COEFFICIENT, + HEATERS_MIN_DUTY_CYCLE, DC_HEATER_MAX_DUTY_CYCLE, FALSE, D45_HEAT_TX_INIT_FEED_FORWARD ); + #ifdef __HEATERS_DEBUG__ for ( i = 0; i < NUM_OF_CONTROLLER_SIGNAL; i++ ) { @@ -276,11 +385,6 @@ heatersStatus[ heater ].hasTargetTempChanged = TRUE; result = TRUE; lastDialTargetTemperatureSet[ heater ] = targetTemperature; - - if ( D5_HEAT == heater ) - { - startupHeaterControl = TRUE; - } } } else @@ -359,32 +463,119 @@ * @param heater: heater ID to update the heater control. * @return none *************************************************************************/ -void signalHeaterControlOnQDUpdate( DD_HEATERS_T heater ) +void signalHeaterControlOnQDUpdate( F32 prevDialFlowrate ) { - if ( D5_HEAT == heater ) + F32 flowrate = getTDDialysateFlowrate(); + F32 dutyCycle = 0.0F; + + // check heater state + if ( HEATER_EXEC_STATE_CONTROL_TO_TARGET == heatersStatus[ D5_HEAT ].state ) { - // check heater state - if ( HEATER_EXEC_STATE_CONTROL_TO_TARGET == heatersStatus[ heater ].state ) + // 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 ) + { + F32 prevDuty = getHeaterControl( D45_HEAT ); + + // When Qd change happens, use previous PWM to calculate the new PWM + // as starting point. + if ( prevDuty > 0.0F ) { - // Set flag to recalculate the feedforward signals - startupHeaterControl = TRUE; - isTargetTempAdjusted = FALSE; + dutyCycle = prevDuty / ( prevDialFlowrate / flowrate ); } + + if ( flowrate >= D45_HEAT_HIGH_DIAL_FLOW_RATE ) + { + resetPIController( PI_CONTROLLER_ID_D45_HEAT_HIGHER_QD, dutyCycle, D45_HEAT_TX_INIT_FEED_FORWARD ); + } + else + { + resetPIController( PI_CONTROLLER_ID_D45_HEAT, dutyCycle, D45_HEAT_TX_INIT_FEED_FORWARD ); + } } } /*********************************************************************//** * @brief - * The signaltoResetPrimaryHeaterAdjustedTargetTemp function resets the flag to - * update/adjust the primary heater target dialysate temperature - * @details \b Inputs: none - * @details \b Outputs: isTargetTempAdjusted - * @param heater: heater ID to update the heater control. - * @return none + * The calculateTargetDialysateTemp function calculate the delta temperature + * required for dialysate temperature to meet the set temperature at dialyzer. + * @details \b Inputs: Qd and target temperature. + * @details \b Outputs: Adjusted Target temperature + * @return none. *************************************************************************/ -void signaltoResetPrimaryHeaterAdjustedTargetTemp( void ) +void calculateTargetDialysateTemp( void ) { - isTargetTempAdjusted = FALSE; + // Get the dialysate flow rate from TD + F32 dialFlowrate = getTDDialysateFlowrate(); + F32 heatDissipation_b1b2 = 0.0F; + F32 heatDissipation_b3 = 0.0F; + 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. + // Lets calculate the B1,B2 and B3 heat dissipation factors + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_9_HW ) ) + { + heatDissipation_b3 = calculateHeatDissipationB3(); + heatDissipation_b1b2 = calculateHeatDissipationB1andB2(); + } + else + { + // based on Beta2.0 heat loss model + heatDissipation_b3 = calculateBeta2HeatDissipationB3(); + heatDissipation_b1b2 = calculateBeta2HeatDissipationB1andB2(); + } + + // Reverse calculation of target temp at D28 to get the target dialyzer temperature + 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 ); } /*********************************************************************//** @@ -397,51 +588,153 @@ *************************************************************************/ static void updatePrimaryHeaterTargetTemp( void ) { - if ( ++primaryTargetTempAdjCounter >= D5_TARGET_TEMP_ADJUST_INTERVAL_MS ) + F32 calcTargetTemp = 0.0F; + F32 deltaTempC = 0.0F; + F32 ctrl = 0.0F; + F32 measuredTempAtDialyzer = 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 ) { - F32 targetTempfromTD = getTDTargetDialysateTemperature(); -#ifdef __TEENSY_CONDUCTIVITY_DRIVER__ - F32 measuredTempAtDialyzer = getTeensyConductivityTemperatureValue( D27_COND ); +#ifdef __USE_D1_TEMP_ + + // Use new RTD sensor placed closed to dialyzer, connected to D1 port + + measuredTempAtDialyzer = getFilteredTemperatureValue( D1_TEMP ); #else - F32 measuredTempAtDialyzer = getConductivityTemperatureValue( D27_COND ); + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_9_HW ) ) + { + measuredTempAtDialyzer = getTeensyConductivityTemperatureValue( D27_COND ); + } + else + { + measuredTempAtDialyzer = getFilteredConductivitySensorTemperature( D27_COND ); + } #endif - F32 calcTargetTemp = getHeaterTargetTemperature( D5_HEAT ); - F32 dialysateFlowrate = getTDDialysateFlowrate(); - F32 deltaTempC = targetTempfromTD - measuredTempAtDialyzer; - F32 capDeltaTempC = MIN( fabs(deltaTempC), ADJ_DELTA_TEMP_STEP ); + calcTargetTemp = getHeaterTargetTemperature( D5_HEAT ); + deltaTempC = fabs( targetTempfromTD - measuredTempAtDialyzer ); - //Assign the initial calcualted temp for adjsutment - if ( FALSE == isTargetTempAdjusted ) + 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; - } + startTimeToInitiateOuterloopTimeout = 0; - //Keep adjusting the temperature based on D28 feedback - if ( deltaTempC > HEATERS_ZERO_DELTA_TEMP_C ) - { - adjustedPrimaryTargetTemp = adjustedPrimaryTargetTemp + capDeltaTempC; + 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 + else if ( ( deltaTempC >= D5_HEAT_OUT_DEADBAND_CONTROL ) && ( TRUE == isTargetTempAdjusted ) ) { - adjustedPrimaryTargetTemp = adjustedPrimaryTargetTemp - capDeltaTempC; + 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; } - //Make sure the adjusted temp not crossing the max and min limits. - adjustedPrimaryTargetTemp = MIN( adjustedPrimaryTargetTemp, ( calcTargetTemp + MAX_ADJ_DELTA_TEMP_C ) ); + primaryTargetTempAdjCounter = 0; + } +} - // For Qd <= 150, The bottom range of second level temperature correction not to go below the initial - // Delta temperature adjustment. - if ( dialysateFlowrate > DELTA_TEMP_ADJ_FLOW_RATE ) +/*********************************************************************//** + * @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 ) ) ) { - adjustedPrimaryTargetTemp = MAX( adjustedPrimaryTargetTemp, ( calcTargetTemp - MAX_ADJ_DELTA_TEMP_C ) ); + offset = calculateAdditionalHeatOffsetForLowQds(); + targetTemp = getHeaterTargetTemperature( D5_HEAT ) ; + targetTemp = targetTemp - offset; + setHeaterTargetTemperature( D5_HEAT, targetTemp ); + isOffsetRemovedForLowQds = TRUE; } - else + + //Wait for uptrend temperature check timeout + if ( TRUE == didTimeout( startTimeToCheckUpTrendTemperature, UPTREND_TEMP_CHECK_TIMEOUT ) ) { - adjustedPrimaryTargetTemp = MAX( adjustedPrimaryTargetTemp, calcTargetTemp ); + isTimeoutCompleteforUptrendCheck = TRUE; } + } - isTargetTempAdjusted = TRUE; - primaryTargetTempAdjCounter = 0; + // 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 ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_9_HW ) ) + { + 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; + } } } @@ -492,7 +785,6 @@ if ( D5_HEAT == heater ) { heatersStatus[ D5_HEAT ].controlIntervalCounter = D5_HEAT_CONTROL_INTERVAL_START_COUNT; - startupHeaterControl = TRUE; } else { @@ -511,8 +803,8 @@ /*********************************************************************//** * @brief * The execHeatersControl function executes the heaters state machine. - * @details \b Inputs: heaterStatus - * @details \b Outputs: heaterStatus + * @details \b Inputs: heaterStatus, previousHeaterState + * @details \b Outputs: heaterStatus, previousHeaterState * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid heater * executive state found. * @return none @@ -553,6 +845,13 @@ // stop the heater stopHeater( heater ); } + + if ( heatersStatus[ heater ].heaterOnState != previousHeaterState[ heater ] ) + { + SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_HEATER_STATE_CHANGE, heater, + heatersStatus[ heater ].heaterOnState ); + previousHeaterState[ heater ] = heatersStatus[ heater ].heaterOnState; + } } } @@ -582,12 +881,12 @@ if ( D5_HEAT == heater ) { alarm = ALARM_ID_DD_FLUID_TOO_LOW_WHILE_D5_HEAT_IS_ON; - isLevelLow = ( ( getLevelStatus( D6_LEVL ) != 0 )? FALSE : TRUE ); + isLevelLow = ( ( getFloaterStatus( D6_LEVL ) != LVL_STATE_EMPTY ) ? FALSE : TRUE ); } else { alarm = ALARM_ID_DD_FLUID_TOO_LOW_WHILE_D45_HEAT_IS_ON; - isLevelLow = ( ( getLevelStatus( D46_LEVL ) != 0 )? FALSE : TRUE ); + isLevelLow = ( ( getLevelStatus( D46_LEVL ) != LEVEL_STATE_LOW ) ? FALSE : TRUE ); } checkPersistentAlarm( alarm, isLevelLow, 0.0F, 0.0F ); @@ -652,11 +951,12 @@ F32 ctrl = 0.0F; DD_OP_MODE_T opMode = getCurrentOperationMode(); F32 measuredTemperature = 0.0F; + F32 feedforward = 0.0F; F32 targetTemperature = getHeaterTargetTemperature( heater ); if ( D5_HEAT == heater ) { - measuredTemperature = getD4AverageTemperature(); + measuredTemperature = getFilteredTemperatureValue( D4_TEMP ); if ( DD_MODE_HEAT != opMode ) { @@ -665,6 +965,12 @@ if ( capDeltaTempC >= HEATER_CNTL_TRANSFER_DELTA_TEMP_C ) { + // 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; } @@ -682,19 +988,28 @@ } else { - measuredTemperature = getD50AverageTemperature(); + measuredTemperature = getFilteredTemperatureValue( D50_TEMP ); if ( DD_MODE_HEAT != opMode ) { F32 deltaTempC = targetTemperature - measuredTemperature; F32 capDeltaTempC = MAX( deltaTempC, HEATERS_ZERO_DELTA_TEMP_C ); - F32 flowrate = getTDDialysateFlowrate() / LITER_IN_ML ; - F32 dutyCycle = calculateDutyCycle( flowrate, capDeltaTempC, TRIMMER_HEATER_MAX_PWR_WATTS, DC_HEATER_EFFICIENCY, + F32 flowrate = getTDDialysateFlowrate(); + F32 flowrateInLtr = flowrate / LITER_IN_ML ; + F32 dutyCycle = calculateDutyCycle( flowrateInLtr, capDeltaTempC, TRIMMER_HEATER_MAX_PWR_WATTS, DC_HEATER_EFFICIENCY, HEATERS_MIN_DUTY_CYCLE, DC_HEATER_MAX_DUTY_CYCLE ); control[ heater ].data = dutyCycle; - resetPIController( PI_CONTROLLER_ID_D45_HEAT, dutyCycle, D45_HEAT_TX_INIT_FEED_FORWARD ); + if ( flowrate >= D45_HEAT_HIGH_DIAL_FLOW_RATE ) + { + resetPIController( PI_CONTROLLER_ID_D45_HEAT_HIGHER_QD, dutyCycle, D45_HEAT_TX_INIT_FEED_FORWARD ); + } + else + { + resetPIController( PI_CONTROLLER_ID_D45_HEAT, dutyCycle, D45_HEAT_TX_INIT_FEED_FORWARD ); + } + state = HEATER_EXEC_STATE_CONTROL_TO_TARGET; } else @@ -729,6 +1044,10 @@ F32 inletTemperature = 0.0F; F32 measuredTemperature = 0.0F; F32 ctrl = 0.0F; + F32 deltaTempC = 0.0F; + F32 absDeltaTempC = 0.0F; + F32 capDeltaTempC = 0.0F; + F32 flowrate = 0.0F; // Update primary heater target temperature at defined interval if ( D5_HEAT == heater ) @@ -740,64 +1059,80 @@ { if ( D5_HEAT == heater ) { - measuredTemperature = getD4AverageTemperature(); // Inlet temperature post heat exchanger - inletTemperature = getTemperatureValue( D78_TEMP ); + inletTemperature = getFilteredTemperatureValue( D78_TEMP ); + deltaTempC = targetTemperature - inletTemperature; + capDeltaTempC = MAX( deltaTempC, HEATERS_ZERO_DELTA_TEMP_C ); + flowrate = ( getTDDialysateFlowrate() + RINSE_PUMP_EST_FLOWRATE ) / LITER_IN_ML ; + d5FeedForward = calculateDutyCycle( flowrate, capDeltaTempC, PRIMARY_HEATER_MAX_PWR_WATTS, d5Efficiency, + HEATERS_MIN_DUTY_CYCLE, AC_HEATER_TX_MAX_DUTY_CYCLE ); - if ( TRUE == startupHeaterControl ) + //Update the calculated feed forward value + setPIControllerFeedForward( PI_CONTROLLER_ID_D5_HEAT, d5FeedForward ); + + // decide outer loop control interval based on the Qd + 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 ) ) { - F32 deltaTempC = targetTemperature - inletTemperature; - F32 capDeltaTempC = MAX( deltaTempC, HEATERS_ZERO_DELTA_TEMP_C ); - F32 flowrate = getTDDialysateFlowrate() / LITER_IN_ML ; - F32 feedforward = calculateDutyCycle( flowrate, capDeltaTempC, PRIMARY_HEATER_MAX_PWR_WATTS, AC_HEATER_EFFICIENCY, - HEATERS_MIN_DUTY_CYCLE, AC_HEATER_TX_MAX_DUTY_CYCLE ); - startupHeaterControl = FALSE; - control[ heater ].data = feedforward; - resetPIController( PI_CONTROLLER_ID_D5_HEAT, HEATERS_MIN_DUTY_CYCLE, feedforward ); + targetTemperature = adjustedPrimaryTargetTemp; + setHeaterTargetTemperature( D5_HEAT, targetTemperature ); } - else + + measuredTemperature = getFilteredTemperatureValue( D4_TEMP ); + deltaTempC = fabs( targetTemperature - measuredTemperature ); + + if ( deltaTempC >= D5_HEATER_DEADBAND_CONTROL ) { - F32 deltaTempC = 0.0F; + ctrl = runPIController( PI_CONTROLLER_ID_D5_HEAT, targetTemperature, measuredTemperature ); + control[ heater ].data = ctrl; + } - // If D28 feedback control is enabled and adjusted temp calculation is done - // then update the target temperature. - if ( ( TRUE == isTargetTempAdjusted ) && ( TRUE == isDialyzerTempFeedbackEnabled ) ) +#ifdef __HEATERS_DEBUG__ + U32 i; + + for ( i = 0; i < NUM_OF_CONTROLLER_SIGNAL; i++ ) + { + pIControlSignal[ i ] = getPIControllerSignals( PI_CONTROLLER_ID_D5_HEAT_OUTER_LOOP, (PI_CONTROLLER_SIGNALS_ID)i ); + } +#endif + } + else + { + measuredTemperature = getFilteredTemperatureValue( D50_TEMP ); + deltaTempC = targetTemperature - measuredTemperature; + absDeltaTempC = fabs( deltaTempC ); + + if ( flowrate >= D45_HEAT_HIGH_DIAL_FLOW_RATE ) + { + if ( absDeltaTempC > D45_HEATER_DEADBAND_CONTROL ) { - targetTemperature = adjustedPrimaryTargetTemp; + ctrl = runPIController( PI_CONTROLLER_ID_D45_HEAT_HIGHER_QD, targetTemperature, measuredTemperature ); + control[ heater ].data = ctrl; } - deltaTempC = fabs( targetTemperature - measuredTemperature ); - - if ( deltaTempC >= D5_HEATER_DEADBAND_CONTROL ) + } + else + { + if ( absDeltaTempC > D45_HEATER_DEADBAND_CONTROL ) { - ctrl = runPIController( PI_CONTROLLER_ID_D5_HEAT, targetTemperature, measuredTemperature ); + ctrl = runPIController( PI_CONTROLLER_ID_D45_HEAT, targetTemperature, measuredTemperature ); control[ heater ].data = ctrl; } } + //#ifdef __HEATERS_DEBUG__ // U32 i; // // for ( i = 0; i < NUM_OF_CONTROLLER_SIGNAL; i++ ) // { -// pIControlSignal[ i ] = getPIControllerSignals( PI_CONTROLLER_ID_D5_HEAT, (PI_CONTROLLER_SIGNALS_ID)i ); +// pIControlSignal[ i ] = getPIControllerSignals( PI_CONTROLLER_ID_D45_HEAT, (PI_CONTROLLER_SIGNALS_ID)i ); // } //#endif } - else - { - measuredTemperature = getD50AverageTemperature(); - ctrl = runPIController( PI_CONTROLLER_ID_D45_HEAT, targetTemperature, measuredTemperature ); - control[ heater ].data = ctrl; -#ifdef __HEATERS_DEBUG__ - U32 i; - - for ( i = 0; i < NUM_OF_CONTROLLER_SIGNAL; i++ ) - { - pIControlSignal[ i ] = getPIControllerSignals( PI_CONTROLLER_ID_D45_HEAT, (PI_CONTROLLER_SIGNALS_ID)i ); - } -#endif - } - heatersStatus[ heater ].hasTargetTempChanged = FALSE; heatersStatus[ heater ].controlIntervalCounter = 0; @@ -887,6 +1222,204 @@ /*********************************************************************//** * @brief + * The calculateBeta2HeatDissipationB1andB2 function calculates the heat dissipation + * constants called B1 and B2 that describes the heat loss between D4 to D28 in + * the P&ID flow path for Beta2.0 hardware. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated heat dissipation constants. + *************************************************************************/ +static F32 calculateBeta2HeatDissipationB1andB2( void ) +{ + // Get the dialysate flow rate from TD + F32 b1b2 = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 firstPart = BETA2_0_B1B2_HEAT_DIS_FIRST_COEFF * qd * qd * qd * qd; + F32 secondPart = BETA2_0_B1B2_HEAT_DIS_SEC_COEFF * qd * qd * qd; + F32 thirdPart = BETA2_0_B1B2_HEAT_DIS_THIRD_COEFF * qd * qd; + F32 fourthPart = BETA2_0_B1B2_HEAT_DIS_FOURTH_COEFF * qd; + + // B1B2 = (-0.000000000005212*x4)+(0.000000009754882*x3)-(0.000006714979798*x2)+(0.002289024242423*x)-0.317550545454563 + b1b2 = firstPart + secondPart - thirdPart + fourthPart - BETA2_0_B1B2_HEAT_DIS_FIFTH_COEFF; + + return b1b2; +} + +/*********************************************************************//** + * @brief + * The calculateBeta2HeatDissipationB3 function calculates the heat dissipation + * constants called B3 that describes the heat loss between D28 to dialyzer in + * the P&ID flow path for Beta2.0 hardware. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated heat dissipation constant. + *************************************************************************/ +static F32 calculateBeta2HeatDissipationB3( void ) +{ + // Get the dialysate flow rate from TD + F32 b3 = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 firstPart = BETA2_0_B3_HEAT_DIS_FIRST_COEFF * qd * qd * qd * qd; + F32 secondPart = BETA2_0_B3_HEAT_DIS_SEC_COEFF * qd * qd * qd; + F32 thirdPart = BETA2_0_B3_HEAT_DIS_THIRD_COEFF * qd * qd; + F32 fourthPart = BETA2_0_B3_HEAT_DIS_FOURTH_COEFF * qd; + + // B3 = (-0.0000000000389*x4)+(0.0000000619946*x3)-(0.0000359241717*x2)+(0.0092543272727*x)-1.079412363636 + b3 = firstPart + secondPart - thirdPart + fourthPart - BETA2_0_B3_HEAT_DIS_FIFTH_COEFF; + + return b3; +} + +/*********************************************************************//** + * @brief + * The calculateHeatDissipationB1andB2 function calculates the heat dissipation + * constants called B1 and B2 that describes the heat loss between D4 to D28 in + * the P&ID flow path. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated heat dissipation constants. + *************************************************************************/ +static F32 calculateHeatDissipationB1andB2( void ) +{ + // Get the dialysate flow rate from TD + F32 b1b2 = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 firstPart = B1B2_HEAT_DIS_FIRST_COEFF * qd * qd * qd; + F32 secondPart = B1B2_HEAT_DIS_SEC_COEFF * qd * qd; + F32 thirdPart = B1B2_HEAT_DIS_THIRD_COEFF * qd; + + // B1B2 = 4.87858E-09x3 - 6.31264E-06x2 + 2.58513E-03x - 2.42013E-01 + b1b2 = firstPart - secondPart + thirdPart - B1B2_HEAT_DIS_FOURTH_COEFF; + + return b1b2; +} + +/*********************************************************************//** + * @brief + * The calculateHeatDissipationB3 function calculates the heat dissipation + * constants called B3 that describes the heat loss between D28 to dialyzer in + * the P&ID flow path. + * @details \b Inputs: Qd. + * @details \b Outputs: none + * @return the calculated heat dissipation constant. + *************************************************************************/ +static F32 calculateHeatDissipationB3( void ) +{ + // Get the dialysate flow rate from TD + F32 b3 = 0.0F; + F32 qd = getTDDialysateFlowrate(); + F32 firstPart = B3_HEAT_DIS_FIRST_COEFF * qd * qd * qd; + F32 secondPart = B3_HEAT_DIS_SEC_COEFF * qd * qd; + F32 thirdPart = B3_HEAT_DIS_THIRD_COEFF * qd; + + // B3 = 6.7756E-09x3 - 8.5278E-06x2 + 3.5126E-03x - 6.3893E-01 + b3 = firstPart - secondPart + thirdPart - B3_HEAT_DIS_FOURTH_COEFF; + + return b3; +} + +/*********************************************************************//** + * @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 + * @details \b Outputs: none + * @param targetTemp the target/final temperature + * @param heatDissipation the heat dissipation constant + * @return the calculated initial temperature. + *************************************************************************/ +static F32 calculateInitialTemp( F32 targetTemp, F32 heatDissipation ) +{ + // Get the ambient temperature from TD + //TODO : replace once amb temp is in place. using board temperature + // to make use of feeding the value via dialin + F32 ambTemp = getTemperatureValue( BRD_TEMP ); + F32 expB = exp(-heatDissipation); + F32 tempDiff = targetTemp - ambTemp; + F32 T0 = 0.0F; + + // T0 = Tamb + (Tf -Tamb)e^-B + T0 = ambTemp + ( tempDiff * expB ); + + return T0; +} + +/*********************************************************************//** + * @brief * The calculateDutyCycle function calculates the heater's duty cycle * based on the delta temperature, flowrate and power applied to the heater. * @details \b Inputs: none @@ -978,7 +1511,7 @@ data.d45_HeaterTargetTemp = getHeaterTargetTemperature( D45_HEAT ); data.d5_HeaterState = heatersStatus[ D5_HEAT ].state; data.d45_HeaterState = heatersStatus[ D45_HEAT ].state; - data.d5_dutyCycleCnt = convertDC; + data.d5_feedforward = d5FeedForward * HEATERS_DUTY_CYCLE_CONVERSION_FACTOR; data.d5_PWMPeriod = getHeaterPWMPeriod( D5_HEAT ); data.d5_adjsutedTargetTemp = adjustedPrimaryTargetTemp; data.d5_targetTempFromTD = getTDTargetDialysateTemperature(); @@ -993,7 +1526,6 @@ data.dbg8 = pIControlSignal[ 7 ]; data.dbg9 = pIControlSignal[ 8 ]; #endif - dataPublicationTimerCounter = 0; broadcastData( MSG_ID_DD_HEATERS_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( HEATERS_DATA_T ) );