Index: firmware/App/Controllers/BalancingChamber.c =================================================================== diff -u -rbba59b7f6fd3d655a595fddb00af295ff895f593 -r3a22e84a4c5ff8d7ffc77faa2ed69af31ae5852d --- firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision bba59b7f6fd3d655a595fddb00af295ff895f593) +++ firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision 3a22e84a4c5ff8d7ffc77faa2ed69af31ae5852d) @@ -49,8 +49,10 @@ #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_QD_TWO_HIT_MAX_MLPM 150.0F ///< For Qd <= 150 mL/min, require two rise detections; above this and <=300, one rise detection is enough. -#define SPENT_FILL_COMPLETE_DP_RISE_PSIG 0.5F ///< Minimum pressure rise per 50 ms sample to indicate spent-side rising edge at low Qd. -#define SPENT_FILL_COMPLETE_DP_RISE_CONSEC_COUNT 2U ///< Maximum number of rise detections tracked before declaring completion. +#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_SLOPE_SPENT_PRESSURE_HIGH_PSIG 2.0F ///< Above this absolute spent pressure, only one rise hit is required. +#define SPENT_FILL_SLOPE_SPENT_PRESSURE_LOW_PSIG 0.5F ///< Below this absolute spent pressure, use Qd-based required rise hit count. +#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 0.20F ///< Allowed detection timing tolerance as a fraction of expected BC switching count. #define QD_THRESHOLD_LOW_MLPM 200.0F ///< Qd threshold (mL/min) below which low pressure limit applies. #define QD_THRESHOLD_HIGH_MLPM 400.0F ///< Qd threshold (mL/min) below which mid pressure limit applies. @@ -93,11 +95,10 @@ 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 spentFillSamplesInitialized; ///< Low-Qd slope: FALSE until first VALVES_CLOSE tick after dosing baseline is stored. 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 spentFillRiseConsecCounter; ///< Counter of consecutive low-Qd spent pressure rises above threshold. -static U32 spentFillRiseMissCounter; ///< Counter of misses after first low-Qd rise detection. +static U32 spentFillRiseHitCount; ///< Low-Qd slope: counted rise hits (only incremented when in timing window). +static U32 spentFillRiseMissCounter; ///< Missed samples after a rise hit before resetting hit count. //TODO: remove later once level sensor working static U32 bicarbChamberPeriodicFillCounter; @@ -159,10 +160,9 @@ balChamberFillTimeoutCount = 0; diffSpentFillCompleteCount = 0; isSpentFillComplete = FALSE; - spentFillSamplesInitialized = FALSE; lastPrevSpentDialPressure = 0.0F; prevSpentDialPressure = 0.0F; - spentFillRiseConsecCounter = 0; + spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; //TODO:remove once level sensor working bicarbChamberPeriodicFillCounter = 0; @@ -474,16 +474,16 @@ isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; isSpentFillComplete = FALSE; - spentFillSamplesInitialized = FALSE; lastPrevSpentDialPressure = 0.0F; prevSpentDialPressure = 0.0F; - spentFillRiseConsecCounter = 0; - spentFillRiseMissCounter = 0U; + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; - F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); - F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); - freshDialPressure = getFilteredPressure( D18_PRES ); - spentDialPressure = getFilteredPressure( D51_PRES ); + F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); + F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); + freshDialPressure = getFilteredPressure( D18_PRES ); + spentDialPressure = getFilteredPressure( D51_PRES ); + lastPrevSpentDialPressure = spentDialPressure; // Check fresh dialysate pressure in range or BC switch only flag set or BC pressure alarms are disabled if ( ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) || @@ -560,15 +560,13 @@ if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) { // Low-Qd spent slope: baseline = spent pressure on last dosing tick (this task period) before VALVES_CLOSE. - lastPrevSpentDialPressure = spentDialPressure; - spentFillSamplesInitialized = FALSE; - state = BAL_CHAMBER_STATE1_VALVES_CLOSE; + prevSpentDialPressure = spentDialPressure; + state = BAL_CHAMBER_STATE1_VALVES_CLOSE; } else { - lastPrevSpentDialPressure = spentDialPressure; - spentFillSamplesInitialized = FALSE; - state = BAL_CHAMBER_STATE2_VALVES_CLOSE; + prevSpentDialPressure = spentDialPressure; + state = BAL_CHAMBER_STATE2_VALVES_CLOSE; } } @@ -707,17 +705,17 @@ balChamberSWState = BAL_CHAMBER_SW_STATE2; currentBalChamberFillCounter = 0; balChamberFillTimeoutCount = 0; - spentFillSamplesInitialized = FALSE; lastPrevSpentDialPressure = 0.0F; prevSpentDialPressure = 0.0F; - spentFillRiseConsecCounter = 0; - spentFillRiseMissCounter = 0U; + spentFillRiseHitCount = 0; + spentFillRiseMissCounter = 0; isSpentFillComplete = FALSE; - F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); - F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); - freshDialPressure = getFilteredPressure( D18_PRES ); - spentDialPressure = getFilteredPressure( D51_PRES ); + F32 acidVolume = getF32OverrideValue( &acidDoseVolume ); + F32 bicarbVolume = getF32OverrideValue( &bicarbDoseVolume ); + freshDialPressure = getFilteredPressure( D18_PRES ); + spentDialPressure = getFilteredPressure( D51_PRES ); + lastPrevSpentDialPressure = spentDialPressure; // Check fresh dialysate pressure in range if ( ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) || @@ -896,8 +894,13 @@ F32 spentFillCompletePresPsig; BOOL useSlopeDetector = FALSE; BOOL isSpentFillCompleteDetected = FALSE; - BOOL isDetectionInCountWindow = TRUE; - U32 requiredRiseCount = SPENT_FILL_COMPLETE_DP_RISE_CONSEC_COUNT; + U32 requiredRiseCount = SPENT_FILL_SLOPE_MAX_RISE_HITS; + U32 detectTolCount = 0; + U32 minDetectCount = 0; + U32 maxDetectCount = 0; + BOOL isDetectionInCountWindow = FALSE; + F32 riseDeltaPsi = 0.0F; + BOOL riseHit = FALSE; // 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 ) ) @@ -926,87 +929,55 @@ if ( TRUE == useSlopeDetector ) { - F32 dp = 0.0F; - requiredRiseCount = ( qdMlpm <= SPENT_FILL_COMPLETE_QD_TWO_HIT_MAX_MLPM ) ? SPENT_FILL_COMPLETE_DP_RISE_CONSEC_COUNT : 1U; + detectTolCount = (U32)( (F32)balChamberValveClosePeriod * SPENT_FILL_DETECT_COUNT_TOL_PCT ); + maxDetectCount = balChamberValveClosePeriod + detectTolCount; + minDetectCount = balChamberValveClosePeriod - detectTolCount; - // First VALVES_CLOSE tick: prev = current spent; lastPrev was set at dosing completion (transition into VALVES_CLOSE). - if ( FALSE == spentFillSamplesInitialized ) + isDetectionInCountWindow = ( ( currentBalChamberFillCounter >= minDetectCount ) && + ( currentBalChamberFillCounter <= maxDetectCount ) ); + + riseDeltaPsi = spentDialPressure - lastPrevSpentDialPressure; + riseHit = ( riseDeltaPsi > SPENT_FILL_COMPLETE_DP_RISE_PSIG ); + + if ( spentDialPressure > SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG ) { - prevSpentDialPressure = spentDialPressure; - spentFillSamplesInitialized = TRUE; - return; + requiredRiseCount = 1; } + else if ( spentDialPressure < SPENT_FILL_SLOPE_SPENT_PRESSURE_LOW_PSIG ) + { + requiredRiseCount = ( qdMlpm <= SPENT_FILL_COMPLETE_QD_TWO_HIT_MAX_MLPM ) ? SPENT_FILL_SLOPE_MAX_RISE_HITS : 1U; + } + else + { + requiredRiseCount = 1; + } - // Compute rise using a 100 ms gap (n-2 sample minus current), then shift sample history by one. - dp = lastPrevSpentDialPressure - spentDialPressure; - - if ( dp > SPENT_FILL_COMPLETE_DP_RISE_PSIG ) + if ( ( TRUE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) { - spentFillRiseConsecCounter = MIN( spentFillRiseConsecCounter + 1U, requiredRiseCount ); + spentFillRiseHitCount += 1; spentFillRiseMissCounter = 0; } - else + else if ( spentFillRiseHitCount > 0 ) { - if ( spentFillRiseConsecCounter > 0 ) + if ( ( FALSE == riseHit ) && ( TRUE == isDetectionInCountWindow ) ) { - // Allow one missed sample before resetting so brief noise dip doesn't discard the first hit. - spentFillRiseMissCounter++; + spentFillRiseMissCounter += 1; if ( spentFillRiseMissCounter >= 2 ) { - spentFillRiseConsecCounter = 0; + spentFillRiseHitCount = 0; spentFillRiseMissCounter = 0; } } - else - { - spentFillRiseMissCounter = 0; - } } - isSpentFillCompleteDetected = ( spentFillRiseConsecCounter >= requiredRiseCount ); + isSpentFillCompleteDetected = ( spentFillRiseHitCount >= requiredRiseCount ); lastPrevSpentDialPressure = prevSpentDialPressure; prevSpentDialPressure = spentDialPressure; } - else - { - spentFillRiseConsecCounter = 0; - spentFillRiseMissCounter = 0; - spentFillSamplesInitialized = FALSE; - lastPrevSpentDialPressure = spentDialPressure; - prevSpentDialPressure = spentDialPressure; - isSpentFillCompleteDetected = ( spentDialPressure >= spentFillCompletePresPsig ); - } if ( ( TRUE == isSpentFillCompleteDetected ) && ( isSpentFillComplete != TRUE ) ) { - // Validate detection time against expected BC switching count to reject false early/late detections. - if ( TRUE == useSlopeDetector ) - { - U32 minDetectCount = 0; - U32 detectTolCount = (U32)( (F32)balChamberValveClosePeriod * SPENT_FILL_DETECT_COUNT_TOL_PCT ); - U32 maxDetectCount = balChamberValveClosePeriod + detectTolCount; - - if ( balChamberValveClosePeriod > detectTolCount ) - { - minDetectCount = balChamberValveClosePeriod - detectTolCount; - } - - isDetectionInCountWindow = ( ( currentBalChamberFillCounter >= minDetectCount ) && - ( currentBalChamberFillCounter <= maxDetectCount ) ); - - if ( FALSE == isDetectionInCountWindow ) - { - // Reject this detection and continue monitoring for a valid low-Qd rising edge. - spentFillRiseConsecCounter = 0; - spentFillRiseMissCounter = 0; - isSpentFillCompleteDetected = FALSE; - } - } - } - - if ( ( TRUE == isSpentFillCompleteDetected ) && ( isSpentFillComplete != TRUE ) ) - { // Check spent fill time against the balancing chamber closing period diffSpentFillCompleteCount = balChamberValveClosePeriod - currentBalChamberFillCounter; absDiffSpentFillCount = abs(diffSpentFillCompleteCount);