Index: firmware/App/Controllers/BalancingChamber.c =================================================================== diff -u -r9e0db1f82f13693d251e00e8958c8063dd89cae6 -r095866efd8f10bdead490578e7584051d79292f0 --- firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision 9e0db1f82f13693d251e00e8958c8063dd89cae6) +++ firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision 095866efd8f10bdead490578e7584051d79292f0) @@ -15,8 +15,6 @@ * ***************************************************************************/ -#include // For sqrtf - #include "BalancingChamber.h" #include "Conductivity.h" #include "ConcentratePumps.h" @@ -30,6 +28,7 @@ #include "MixingControl.h" #include "ModeGenDialysate.h" #include "ModeStandby.h" +#include "OperationModes.h" #include "Pressure.h" #include "TaskGeneral.h" #include "TDInterface.h" @@ -46,29 +45,24 @@ #define BAL_CHAMBER_DATA_PUBLISH_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the balancing chamber data published. #define BAL_CHAMBER_FILL_PRES_DROP_MS ( 200 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber filling started and corrosponding valves opened. -#define BAL_CHAMBER_FILL_COMPLETE_MS ( 100 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber fill completed and pressure is within range -#define SPENT_FILL_COMPLETE_QD_LOW_RISE_MAX_MLPM 300.0F ///< For Qd <= 300 mL/min (Diener 1000). +#define BAL_CHAMBER_FILL_COMPLETE_MS ( 300 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber fill completed and pressure is within range +#define SPENT_FILL_COMPLETE_PRES 25.0F ///< Spent fill complete pressure (PSI) for Diener 2000 pump. +#define SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG 10.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and 200 < Qd <= 400. +#define SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG 20.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and Qd > 400. +#define SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM 300.0F ///< For Qd <= 300 mL/min, spent fill completion uses pressure-rise slope detection. #define SPENT_FILL_COMPLETE_DP_RISE_PSIG 0.5F ///< Minimum 100 ms spent pressure rise (current minus n-2) to count as a rise hit at low Qd. -#define SPENT_FILL_COMPLETE_DP_RISE_HIGH_QD_PSIG 3.0F ///< Minimum spent pressure rise for Diener 2000 or Qd > 300 mL/min. -#define SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG 2.0F ///< Above this spent pressure rise, only one rise hit is required. +#define SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG 2.0F ///< Above this absolute spent pressure, only one rise hit is required. #define SPENT_FILL_SLOPE_MAX_RISE_HITS 2 ///< Rise hits required when Qd <= 150 in the low spent-pressure band. #define SPENT_FILL_DETECT_COUNT_TOL_PCT 1.0F ///< Allowed detection timing tolerance as a fraction of expected BC switching count. +#define QD_THRESHOLD_HIGH_MLPM 400.0F ///< Qd threshold (mL/min) below which mid pressure limit applies. #define SPENT_DIFF_COUNT_ZERO 0 ///< Zero count difference for spent side fill comparing target count #define D48_SPEED_ADJUST_FACTOR 0.5F ///< D48 speed adjustment factor ( 50% of speed adjustment = 0.5) #define D48_SPEED_RANGE_LIMIT 0.25F ///< D48 speed adjustment range check limit ( D48 speed can vary +/-25% of initial calculated speed) #define BICARB_CHAMBER_PERIODIC_FILL_TIME ( 1 * SEC_PER_MIN * \ ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) ///< Periodic bicarb chamber fill request 60 sec x 20 = 1200 -#define NUMBER_OF_CYCLES_BASED_ON_FLOWRATE 4 ///< NUmber of Balancing Chamber cycles required to update the new Qd rate. #define BAL_CHAMBER_FILL_TIMEOUT_FACTOR 3.0F ///< Balancing Chamber fill timeout factor (300% of observed fill count) #define COMP_SLOPE -0.000376F ///< Balancing chamber temperature compensation slope factor #define COMP_INTERCEPT 1.007269F ///< Balancing chamber temperature compensation intercept factor -/// Bicarb (D10) pump speed from instantaneous mix volume: speed = 240 - sqrt(230400 - 96000*v)/2 -#define BC_BICARB_SPEED_OFFSET_MLPM 240.0F ///< Bicarb speed formula offset (mL/min). -#define BC_BICARB_SPEED_DISC_CONST 230400.0F ///< Bicarb speed formula discriminant constant. -#define BC_BICARB_SPEED_DISC_VOL_COEF 96000.0F ///< Bicarb speed formula volume coefficient. -#define BC_ACID_SPEED_OFFSET_MLPM 160.0F -#define BC_ACID_SPEED_DISC_CONSTANT 102400.0F -#define BC_ACID_SPEED_DISC_VOL_COEF 96000.0F /// Payload record structure for balancing chamber switch only request typedef struct @@ -105,7 +99,6 @@ static U32 balChamberFillTimeoutCount; ///< Timeout count (in task interval) to detect BC fill timeout. static S32 diffSpentFillCompleteCount; ///< Difference between spent target fill to actual fill count static BOOL isSpentFillComplete; ///< Flag indicating spent side fill complete. -static BOOL isFreshSideValvesClosed; ///< Flag indicating fresh-side BC valves are closed while spent side may still be filling. static F32 lastPrevSpentDialPressure; ///< Low-Qd slope: spent pressure last sample of dosing (tick before VALVES_CLOSE); then n-2 in VC. static F32 prevSpentDialPressure; ///< Spent pressure previous sample (n-1) during VALVES_CLOSE for low-Qd slope detection. static U32 spentFillRiseHitCount; ///< Low-Qd slope: counted rise hits (only incremented when in timing window). @@ -119,7 +112,7 @@ static F32 balancingError; ///< Balancing error that has been calculated during balancing chamber switching. //TODO: remove later once level sensor working -static U32 bicarbChamberPeriodicFillCounter; +static U32 bicarbChamberPeriodicFillCounter; // Balancing chamber state change tracker static BAL_CHAMBER_EXEC_STATE_T prevBalChamberState; ///< Balancing chamber Previous State tracking variable @@ -136,17 +129,11 @@ static void publishBalChamberData( void ); static U32 getBalChamberDataPublishInterval( void ); static void checkSpentFillComplete( F32 spentDialPressure ); -static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm ); +static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm, F32 spentFillCompletePresPsig ); static void applyBalChamberSwitchingPeriod( F32 tdDialysateFlowrate ); static BOOL isBalChamberTimeBasedSwitching( void ); static void scheduleFirstCycleRelaxAfterQdApply( void ); -static void valveControlForBCState1FreshSideClose( void ); -static void valveControlForBCState2FreshSideClose( void ); -static void closeFreshSideValvesWhenFillComplete( void ); static void calculateBalancingChamberError( void ); -static F32 getBicarbConcentratePumpSpeedMlMin( F32 bicarbVolumeMl ); -static F32 getAcidConcentratePumpSpeedMlMin( F32 acidVolumeMl ); -static void startBalChamberConcPumpDosing( F32 acidVolume, F32 bicarbVolume ); /*********************************************************************//** * @brief @@ -199,7 +186,6 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - isFreshSideValvesClosed = FALSE; isBalChamberSwitchingActive = FALSE; isBalChamberSwitchingOnRequested = FALSE; isBalChamberSwitchingOffRequested = FALSE; @@ -214,98 +200,6 @@ /*********************************************************************//** * @brief - * The getAcidConcentratePumpSpeedMlMin function computes D11 pump speed - * from the instantaneous acid mix volume: - * speed = 160 - sqrt(102400 - 96000*v) / 2 - * Result is clamped to [CONCENTRATE_PUMP_MIN_SPEED, CONCENTRATE_PUMP_MAX_SPEED]. - * @details \b Inputs: acidVolumeMl - * @details \b Outputs: none - * @param acidVolumeMl Instantaneous acid mix volume (mL) - * @return Acid concentrate pump speed (mL/min) - *************************************************************************/ -static F32 getAcidConcentratePumpSpeedMlMin( F32 acidVolumeMl) -{ - - F32 discriminant = BC_ACID_SPEED_DISC_CONSTANT - ( BC_ACID_SPEED_DISC_VOL_COEF * acidVolumeMl ); - F32 result = 0.0F; - - if ( discriminant < 0.0F ) - { - discriminant = 0.0F; - } - - result = BC_ACID_SPEED_OFFSET_MLPM - ( sqrtf( discriminant ) / 2.0F ); - - if ( result > CONCENTRATE_PUMP_MAX_SPEED ) - { - result = CONCENTRATE_PUMP_MAX_SPEED; - } - else if ( result < 0.0F ) - { - result = CONCENTRATE_PUMP_MIN_SPEED; - } - - return result; -} -/*********************************************************************//** - * @brief - * The getBicarbConcentratePumpSpeedMlMin function computes D10 pump speed - * from the instantaneous bicarb mix volume: - * speed = 240 - sqrt(230400 - 96000*v) / 2 - * Result is clamped to [CONCENTRATE_PUMP_MIN_SPEED, CONCENTRATE_PUMP_MAX_SPEED]. - * @details \b Inputs: bicarbVolumeMl - * @details \b Outputs: none - * @param bicarbVolumeMl Instantaneous bicarb mix volume (mL) - * @return Bicarb concentrate pump speed (mL/min) - *************************************************************************/ -static F32 getBicarbConcentratePumpSpeedMlMin( F32 bicarbVolumeMl ) -{ - F32 discriminant = BC_BICARB_SPEED_DISC_CONST - ( BC_BICARB_SPEED_DISC_VOL_COEF * bicarbVolumeMl ); - F32 result = 0.0F; - - if ( discriminant < 0.0F ) - { - discriminant = 0.0F; - } - - result = BC_BICARB_SPEED_OFFSET_MLPM - ( sqrtf( discriminant ) / 2.0F ); - - if ( result > CONCENTRATE_PUMP_MAX_SPEED ) - { - result = CONCENTRATE_PUMP_MAX_SPEED; - } - else if ( result < 0.0F ) - { - result = CONCENTRATE_PUMP_MIN_SPEED; - } - - return result; -} - -/*********************************************************************//** - * @brief - * The startBalChamberConcPumpDosing function starts acid and bicarb dosing. - * Acid (D11) runs at fixed 100 mL/min. Bicarb (D10) speed is computed from - * the instantaneous bicarb mix volume on every BC dose start. - * @details \b Inputs: acidVolume, bicarbVolume - * @details \b Outputs: concentrate pumps - * @param acidVolume Acid dose volume (mL) - * @param bicarbVolume Bicarb dose volume (mL) - * @return none - *************************************************************************/ -static void startBalChamberConcPumpDosing( F32 acidVolume, F32 bicarbVolume ) -{ - F32 acidSpeed = getAcidConcentratePumpSpeedMlMin( acidVolume ); - F32 bicarbSpeed = getBicarbConcentratePumpSpeedMlMin( bicarbVolume ); - - setConcentratePumpTargetSpeed( D11_PUMP, acidSpeed, acidVolume ); - setConcentratePumpTargetSpeed( D10_PUMP, bicarbSpeed, bicarbVolume ); - requestConcentratePumpOn( D11_PUMP ); - requestConcentratePumpOn( D10_PUMP ); -} - -/*********************************************************************//** - * @brief * The transitionToBalChamberFill function prepares for transition to * balancing chamber fill/switching operations. * @details \b Inputs: none @@ -426,7 +320,6 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - isFreshSideValvesClosed = FALSE; spentDialPressure = getFilteredPressure( D51_PRES ); lastPrevSpentDialPressure = spentDialPressure; prevSpentDialPressure = spentDialPressure; @@ -450,7 +343,7 @@ *************************************************************************/ static void scheduleFirstCycleRelaxAfterQdApply( void ) { - bcSwitchingBasedOnClosedPeriodCounter = NUMBER_OF_CYCLES_BASED_ON_FLOWRATE; + bcSwitchingBasedOnClosedPeriodCounter = 2; } /*********************************************************************//** @@ -465,9 +358,7 @@ static BOOL isBalChamberTimeBasedSwitching( void ) { BOOL state = FALSE; - F32 dialFlowRate = getBalChamberActiveDialysateFlowrate(); - //state = ( getD48PumpSpeed() <= BAL_CHAMBER_TIME_BASED_D48_SPEED_RPM ) ? TRUE : FALSE; - state = ( dialFlowRate <= BAL_CHAMBER_OPEN_LOOP_FLOW_RATE_BAND_3 )? TRUE : FALSE; + state = ( getD48PumpSpeed() <= BAL_CHAMBER_TIME_BASED_D48_SPEED_RPM ) ? TRUE : FALSE; return state; } @@ -749,59 +640,6 @@ /*********************************************************************//** * @brief - * The valveControlForBCState1FreshSideClose function closes only the fresh-side - * balancing chamber valves opened during state 1 fill. - * @details \b Inputs: none - * @details \b Outputs: valve states - * @return none. - *************************************************************************/ -static void valveControlForBCState1FreshSideClose( void ) -{ - setValveState( D19_VALV, VALVE_STATE_CLOSED ); - setValveState( D24_VALV, VALVE_STATE_CLOSED ); -} - -/*********************************************************************//** - * @brief - * The valveControlForBCState2FreshSideClose function closes only the fresh-side - * balancing chamber valves opened during state 2 fill. - * @details \b Inputs: none - * @details \b Outputs: valve states - * @return none. - *************************************************************************/ -static void valveControlForBCState2FreshSideClose( void ) -{ - setValveState( D26_VALV, VALVE_STATE_CLOSED ); - setValveState( D21_VALV, VALVE_STATE_CLOSED ); -} - -/*********************************************************************//** - * @brief - * The closeFreshSideValvesWhenFillComplete function closes the fresh-side BC - * valves once fresh fill is detected, without waiting for spent fill. - * @details \b Inputs: balChamberSWState, isPressureStabilizedDuringFill, isFreshSideValvesClosed - * @details \b Outputs: isFreshSideValvesClosed, valve states - * @return none. - *************************************************************************/ -static void closeFreshSideValvesWhenFillComplete( void ) -{ - if ( ( TRUE == isPressureStabilizedDuringFill ) && ( FALSE == isFreshSideValvesClosed ) ) - { - if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) - { - valveControlForBCState1FreshSideClose(); - } - else - { - valveControlForBCState2FreshSideClose(); - } - - isFreshSideValvesClosed = TRUE; - } -} - -/*********************************************************************//** - * @brief * The handleBalChamberState1FillStart function handles the balancing chamber * state 1 fill and time to fill chamber counter being updated. * @details \b Inputs: Pressure @@ -827,7 +665,6 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - isFreshSideValvesClosed = FALSE; if ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) { @@ -843,7 +680,7 @@ freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) ) + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) { //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D4_TEMP ) ); @@ -867,7 +704,11 @@ // Deliver dosing during generate dialysate mode if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { - startBalChamberConcPumpDosing( acidVolume, bicarbVolume ); + // start acid and bicarb pump with the expected quantity + setConcentratePumpTargetSpeed( D11_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, acidVolume ); + setConcentratePumpTargetSpeed( D10_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, bicarbVolume ); + requestConcentratePumpOn( D11_PUMP ); + requestConcentratePumpOn( D10_PUMP ); } state = BAL_CHAMBER_STATE1_BICARB_ACID_DOSING_CNTRL; @@ -954,6 +795,7 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillDetectComplete( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_FILL_DETECT_COMPLETE; + BOOL isBothFillsComplete = FALSE; BOOL isFirstCycleNotDone = FALSE; BOOL isFillCompleteOrFirstCycle = FALSE; @@ -965,14 +807,17 @@ { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); - SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_BC_FILL_TIMEOUT, isPressureStabilizedDuringFill, isSpentFillComplete ); - // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } + // If the dry bicarbonate is enabled then we ignore looking at d18 pressure to be in range. + if ( ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) && ( TRUE == getBicarbChamberFillRequested() ) ) + { + isPressureStabilizedDuringFill = TRUE; + } // Check fresh dialysate pressure back in range to indicate fresh fill complete. - if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + else if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) { @@ -981,19 +826,16 @@ } } - // Close fresh side valves only upon fresh fill completion by detecting pressure rise. - closeFreshSideValvesWhenFillComplete(); - - // Check Spent side fill complete checkSpentFillComplete( spentDialPressure ); + // Check both spent and fresh side fill is complete + isBothFillsComplete = ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) ) ? TRUE : FALSE; isFirstCycleNotDone = ( ( FALSE == isFirstCycleBCSwitchingCompleted ) && ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) ) ? TRUE : FALSE; if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { - isFillCompleteOrFirstCycle = ( ( ( TRUE == isSpentFillComplete ) && ( TRUE == isFreshSideValvesClosed ) ) || - ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; + isFillCompleteOrFirstCycle = ( ( TRUE == isBothFillsComplete ) || ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; } if ( ( TRUE == isFillCompleteOrFirstCycle ) || @@ -1141,7 +983,6 @@ spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - isFreshSideValvesClosed = FALSE; if ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) { @@ -1153,11 +994,10 @@ acidVolume = getF32OverrideValue( &acidDoseVolume ); bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); } - freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) ) + if ( getTestConfigStatus( TEST_CONFIG_DD_FP_ENABLE_BETA_1_0_HW ) == TRUE ) { //Set Trimmer heater Target temp every BC cycle to catch up fresh dialysate temp setHeaterTargetTemperature( D45_HEAT, getFilteredTemperatureValue( D4_TEMP ) ); @@ -1181,7 +1021,11 @@ // Deliver dosing during generate dialysate mode if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { - startBalChamberConcPumpDosing( acidVolume, bicarbVolume ); + // start acid and bicarb pump with the expected quantity + setConcentratePumpTargetSpeed( D11_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, acidVolume ); + setConcentratePumpTargetSpeed( D10_PUMP, DOSING_CONCENTRATE_PUMP_SPEED, bicarbVolume ); + requestConcentratePumpOn( D11_PUMP ); + requestConcentratePumpOn( D10_PUMP ); } state = BAL_CHAMBER_STATE2_BICARB_ACID_DOSING_CNTRL; @@ -1212,24 +1056,38 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillDetectComplete( void ) { BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_FILL_DETECT_COMPLETE; + BOOL isBothFillsComplete = FALSE; BOOL isFirstCycleNotDone = FALSE; BOOL isFillCompleteOrFirstCycle = FALSE; freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); + // If the dry bicarbonate is enabled then we ignore looking at d18 pressure to be in range. + if ( ( FALSE == getTestConfigStatus( TEST_CONFIG_DD_DISABLE_DRY_BICARB ) ) && ( TRUE == getBicarbChamberFillRequested() ) ) + { + isPressureStabilizedDuringFill = TRUE; + } + // Check fresh dialysate pressure back in range to indicate fresh fill complete. + else if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + { + if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) + { + // stabilized pressure indicating fresh side fill is complete + isPressureStabilizedDuringFill = TRUE; + } + } + // After the first BC cycle, fault if fill never completes at the Qd-based timeout. if ( ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) && ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); - SEND_EVENT_WITH_2_U32_DATA( DD_EVENT_BC_FILL_TIMEOUT, isPressureStabilizedDuringFill, isSpentFillComplete ); - // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } - // Check fresh dialysate pressure back in range to indicate fresh fill complete. + // Check fresh dialysate pressure back in range to indicate fill complete. if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) @@ -1239,19 +1097,16 @@ } } - // Close fresh side valves only upon fresh fill completion by detecting pressure rise. - closeFreshSideValvesWhenFillComplete(); - - // Check Spent side fill complete checkSpentFillComplete( spentDialPressure ); + // Check switching cycle time or pressure check for valve closure + isBothFillsComplete = ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) ) ? TRUE : FALSE; isFirstCycleNotDone = ( ( FALSE == isFirstCycleBCSwitchingCompleted ) && ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) ) ? TRUE : FALSE; if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { - isFillCompleteOrFirstCycle = ( ( ( TRUE == isSpentFillComplete ) && ( TRUE == isFreshSideValvesClosed ) ) || - ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; + isFillCompleteOrFirstCycle = ( ( TRUE == isBothFillsComplete ) || ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; } if ( ( TRUE == isFillCompleteOrFirstCycle ) || @@ -1412,7 +1267,7 @@ // Check if a request made was to activate the balancing chamber switching. if ( TRUE == isBalChamberSwitchingOnRequested ) { - //Clear request was processed. + //Clear the request flag to indicate that the request was processed. isBalChamberSwitchingOnRequested = FALSE; // Set flag to indicate that balancing chamber switching is active @@ -1429,65 +1284,73 @@ * @brief * The isSpentFillCompleteByPressure function evaluates whether the spent side of the * balancing chamber fill is complete from D51 pressure. - * @details \b Inputs: spentDialPressure, qdMlpm, valve-close period, fill counter, - * Diener pump test config, and D51 slope history. + * @details \b Inputs: spentDialPressure, qdMlpm, spentFillCompletePresPsig, valve-close period, + * fill counter, Diener pump test config, and D51 slope history for low-Qd slope mode. * @details \b Outputs: spentFillRiseHitCount, spentFillRiseMissCounter, - * lastPrevSpentDialPressure, prevSpentDialPressure. + * lastPrevSpentDialPressure, prevSpentDialPressure (updated only in slope mode). * @param spentDialPressure filtered spent dialysate pressure (PSIG). * @param qdMlpm dialysate flow rate (mL/min). + * @param spentFillCompletePresPsig spent fill complete pressure threshold for current Qd and pump. * @return TRUE if spent fill is detected this task period; FALSE otherwise. *************************************************************************/ -static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm ) +static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm, F32 spentFillCompletePresPsig ) { - BOOL useLowQdRiseThreshold = ( ( ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) && - ( qdMlpm <= SPENT_FILL_COMPLETE_QD_LOW_RISE_MAX_MLPM ) ) ? TRUE : FALSE ); - F32 riseThresholdPsig = ( TRUE == useLowQdRiseThreshold ) ? SPENT_FILL_COMPLETE_DP_RISE_PSIG : - SPENT_FILL_COMPLETE_DP_RISE_HIGH_QD_PSIG; + BOOL useSlopeDetector = ( ( ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) && + ( qdMlpm <= SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM ) ) ? TRUE : FALSE ); BOOL state; - // Gate slope hits near expected valve-close count (current vs balChamberValveClosePeriod +/- tolerance). - U32 detectTolCount = (U32)( (F32)balChamberValveClosePeriod * SPENT_FILL_DETECT_COUNT_TOL_PCT ); - U32 maxDetectCount = balChamberValveClosePeriod + detectTolCount; - U32 minDetectCount = balChamberValveClosePeriod - detectTolCount; - BOOL isDetectionInCountWindow = ( ( currentBalChamberFillCounter >= minDetectCount ) && - ( currentBalChamberFillCounter <= maxDetectCount ) ) ? TRUE : FALSE; - F32 riseDeltaPsi = spentDialPressure - lastPrevSpentDialPressure; - BOOL riseHit = ( riseDeltaPsi > riseThresholdPsig ) ? TRUE : FALSE; - U32 requiredRiseCount; - - // Mid band between low/high spent pressure rise: require two rise hits. - if ( riseDeltaPsi > SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG ) + if ( TRUE == useSlopeDetector ) { - requiredRiseCount = 1U; - } - else - { - requiredRiseCount = 2U; - } + // Gate slope hits near expected valve-close count (current vs balChamberValveClosePeriod +/- tolerance). + U32 detectTolCount = (U32)( (F32)balChamberValveClosePeriod * SPENT_FILL_DETECT_COUNT_TOL_PCT ); + U32 maxDetectCount = balChamberValveClosePeriod + detectTolCount; + U32 minDetectCount = balChamberValveClosePeriod - detectTolCount; + BOOL isDetectionInCountWindow = ( ( currentBalChamberFillCounter >= minDetectCount ) && + ( currentBalChamberFillCounter <= maxDetectCount ) ) ? TRUE : FALSE; + F32 riseDeltaPsi = spentDialPressure - lastPrevSpentDialPressure; + BOOL riseHit = ( riseDeltaPsi > SPENT_FILL_COMPLETE_DP_RISE_PSIG ) ? TRUE : FALSE; + U32 requiredRiseCount; - if ( ( TRUE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) - { - spentFillRiseHitCount += 1; - spentFillRiseMissCounter = 0; - } - else if ( spentFillRiseHitCount > 0 ) - { - if ( ( FALSE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) + // Mid band between low/high spent pressure: require two rise hits. + if ( riseDeltaPsi > SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG ) { - spentFillRiseMissCounter += 1; + requiredRiseCount = 1U; + } + else + { + requiredRiseCount = 2U; + } - if ( spentFillRiseMissCounter >= 2 ) + if ( ( TRUE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) + { + spentFillRiseHitCount += 1; + spentFillRiseMissCounter = 0; + } + else if ( spentFillRiseHitCount > 0 ) + { + if ( ( FALSE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) { - spentFillRiseHitCount = 0; - spentFillRiseMissCounter = 0; + spentFillRiseMissCounter += 1; + + if ( spentFillRiseMissCounter >= 2 ) + { + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; + } } } - } - lastPrevSpentDialPressure = prevSpentDialPressure; - prevSpentDialPressure = spentDialPressure; + lastPrevSpentDialPressure = prevSpentDialPressure; + prevSpentDialPressure = spentDialPressure; - state = ( spentFillRiseHitCount >= requiredRiseCount ) ? TRUE : FALSE; + // Complete on slope hits, or on absolute pressure at period only if Qd > low band (low Qd: slope-only, no absolute-pressure fallback). + state = ( spentFillRiseHitCount >= requiredRiseCount ) ? TRUE : FALSE; + } + else + { + // Not in slope mode (Diener 2000 pump or Qd > SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM): threshold-only spent fill complete. + state = ( spentDialPressure >= spentFillCompletePresPsig ) ? TRUE : FALSE; + } return state; } @@ -1512,6 +1375,7 @@ U32 d48SpeedPostRangeCheck = 0; F32 qdMlpm = getBalChamberActiveDialysateFlowrate(); BOOL result = FALSE; + F32 spentFillCompletePresPsig; BOOL isSpentFillCompleteDetected = FALSE; if ( TRUE == isBalChamberTimeBasedSwitching() ) @@ -1523,8 +1387,25 @@ } else { - isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm ); + // Diener 2000 pump: spent fill complete pressure use legacy 25 PSI. Other pump: depends on Qd. + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) + { + spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES; + } + else + { + if ( qdMlpm <= QD_THRESHOLD_HIGH_MLPM ) + { + spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG; + } + else + { + spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG; + } + } + isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm, spentFillCompletePresPsig ); + if ( ( TRUE == isSpentFillCompleteDetected ) && ( isSpentFillComplete != TRUE ) ) { // Check spent fill time against the balancing chamber closing period @@ -1598,66 +1479,6 @@ /*********************************************************************//** * @brief - * The getBalChamberFreshSideValvesClosedStatus function returns whether the - * fresh-side balancing chamber valves are closed for the current switch state. - * @details \b Inputs: isFreshSideValvesClosed - * @details \b Outputs: none - * @return TRUE when fresh-side BC valves are closed. - *************************************************************************/ -BOOL getBalChamberFreshSideValvesClosedStatus( void ) -{ - return isFreshSideValvesClosed; -} - -/*********************************************************************//** - * @brief - * The getBalChamberFreshFillCompleteStatus function returns whether fresh-side - * fill completed during the current BC switch cycle. - * @details \b Inputs: isPressureStabilizedDuringFill - * @details \b Outputs: none - * @return TRUE when fresh-side fill is complete. - *************************************************************************/ -BOOL getBalChamberFreshFillCompleteStatus( void ) -{ - return isPressureStabilizedDuringFill; -} - -/*********************************************************************//** - * @brief - * The getBalChamberSpentFillCompleteStatus function returns whether spent-side - * fill completed during the current BC switch cycle. - * @details \b Inputs: isSpentFillComplete - * @details \b Outputs: none - * @return TRUE when spent-side fill is complete. - *************************************************************************/ -BOOL getBalChamberSpentFillCompleteStatus( void ) -{ - return isSpentFillComplete; -} - -/*********************************************************************//** - * @brief - * The isBalChamberSwitchImminent function returns whether the balancing chamber - * is within the valve-close window (50 ms) before switching. - * @details \b Inputs: currentBalChamberSwitchingCounter, balChamberValveClosePeriod - * @details \b Outputs: none - * @return TRUE when BC switch is imminent. - *************************************************************************/ -BOOL isBalChamberSwitchImminent( void ) -{ - BOOL result = FALSE; - - if ( ( balChamberValveClosePeriod > 0 ) && - ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) ) - { - result = TRUE; - } - - return result; -} - -/*********************************************************************//** - * @brief * The getBalChamberSwitchingFreq function gets the balancing chamber switching * frequency value. * @details \b Inputs: balChamberSwitchingFreq