diff -r 000000000000 -r c40eb8fe8501 wlan_bearer/wlanldd/wlan_common/umac_common/src/umacsignalpredictor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wlan_bearer/wlanldd/wlan_common/umac_common/src/umacsignalpredictor.cpp Tue Feb 02 02:03:13 2010 +0200 @@ -0,0 +1,589 @@ +/* +* Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Implementation of the WlanSignalPredictor class. +* +*/ + +/* +* %version: 7 % +*/ + +#include "config.h" +#include "umacsignalpredictor.h" + + +static const TUint KThousand = 1000; + + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +WlanSignalPredictor::WlanSignalPredictor() + : + iTimeToWarnLevel( KTimeToIndicationLevel ), + iWindowSize( KWindowSizeDefault ), + iTimeToNextInd( KTimeToNextIndication ), + iWarningLevel( KRoamLevel ), + iValueCount( 0 ), + iMedianCount( 0 ), + iLatestMedian( 0 ), + iOldestValue( 0 ), + iLatestValue( 0 ), + iLeastRcpi( 0 ), + iMaxRcpi( 0 ), + iValueCountMax( KValueCountMax ), + iMinValueCountForPrediction( KMinValueCountForPrediction ), + iRunning( ETrue ) + { + os_memset( iLevelTable, KDummy, sizeof( iLevelTable ) ); + os_memset( iMedianTable,KDummy, sizeof( iMedianTable ) ); + iRoamed.iDone = EFalse; + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::WlanSignalPredictor") ); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void WlanSignalPredictor::ConfigureSignalPredictor( + TUint32 aTimeToWarnLevel, + TUint32 aTimeToNextInd, + WHA::TRcpi aRcpiWarnLevel ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::ConfigureSignalPredictor") ); + + iTimeToWarnLevel = ( aTimeToWarnLevel ) ? aTimeToWarnLevel / KThousand : + iTimeToWarnLevel; + + iTimeToNextInd = ( aTimeToNextInd ) ? aTimeToNextInd / KThousand : + iTimeToNextInd; + + iWarningLevel = ( aRcpiWarnLevel ) ? aRcpiWarnLevel : iWarningLevel; + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::iTimeToWarnLevel: %d"), + iTimeToWarnLevel ); + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::iTimeToNextInd: %d"), + iTimeToNextInd ); + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::iWarningLevel: %d"), + iWarningLevel ); + + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool WlanSignalPredictor::InsertNewRcpi( + TUint32 aTimestamp, + WHA::TRcpi aRcpi ) + { + // Check all preconditions for prediction + + // Check state of module SPC Stopped + if ( EFalse == iRunning ) + { + return EFalse; + } + + // Convert the timestamp from microseconds to milliseconds + aTimestamp = aTimestamp / KThousand; + + // If new timestamp is old enough from the latest one. + if ( iValueCount && + (aTimestamp < (iLevelTable[iLatestValue].iTimestamp + KValueDelay) ) ) + { + return EFalse; + } + + // Remove too old values + if ( iValueCount ) + { + RemoveOldValues( aTimestamp ); + } + + // Insert the new value + return InsertValue( aTimestamp, aRcpi ); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool WlanSignalPredictor::GetLatestMedian( + TUint32 aTimestamp, + TUint8& aLastMedian ) const + { + // Convert the timestamp into milliseconds + aTimestamp = aTimestamp / KThousand; + + if ( iMedianCount ) + { + // Check first if the latest median is too old already. + if ( iWindowSize >= + Delay( iMedianTable[iLatestMedian].iTimestamp, aTimestamp ) ) + { + aLastMedian = iMedianTable[iLatestMedian].iRcpi; + return ETrue; + } + } + return EFalse; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool WlanSignalPredictor::GetLatestRcpi( + TUint32 aTimestamp, + TUint8& aLatestRcpi ) + { + // Convert the timestamp into milliseconds + aTimestamp = aTimestamp / KThousand; + + RemoveOldValues( aTimestamp ); + + if ( iValueCount ) + { + aLatestRcpi = iLevelTable[iLatestValue].iRcpi; + return ETrue; + } + else + { + return EFalse; + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void WlanSignalPredictor::RemoveOldValues( + TUint32 aTimestamp ) + { + TBool oldFound( ETrue ); + + while ( oldFound && iValueCount ) + { + TUint32 deltaTime = + Delay( iLevelTable[iOldestValue].iTimestamp, aTimestamp ); + if ( deltaTime > iWindowSize ) + { + RemoveValue( iOldestValue ); + } + else + { + oldFound = EFalse; + } + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void WlanSignalPredictor::RemoveValue( + TUint8 aIndex ) + { + // The only one is removed. + if ( KOneValue == iValueCount ) + { + os_memset( iLevelTable, KDummy, sizeof( iLevelTable ) ); + iValueCount = 0; + iOldestValue = 0; + iLatestValue = 0; + iLeastRcpi = 0; + iMaxRcpi = 0; + return; + } + // The biggest rcpi is removed. + else if ( aIndex == iMaxRcpi ) + { + iLevelTable[iLevelTable[aIndex].iPrev].iNext = iValueCountMax; + iMaxRcpi = iLevelTable[aIndex].iPrev; + } + // The least rcpi is removed. + else if ( aIndex == iLeastRcpi ) + { + iLevelTable[iLevelTable[aIndex].iNext].iPrev = iValueCountMax; + iLeastRcpi = iLevelTable[aIndex].iNext; + } + // The middle rcpi is removed. + else + { + iLevelTable[iLevelTable[aIndex].iPrev].iNext = iLevelTable[aIndex].iNext; + iLevelTable[iLevelTable[aIndex].iNext].iPrev = iLevelTable[aIndex].iPrev; + } + + os_memset( &iLevelTable[aIndex], KDummy, sizeof( TValue ) ); + + iOldestValue++; + if ( iOldestValue == iValueCountMax ) + { + iOldestValue = 0; + } + iValueCount--; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool WlanSignalPredictor::InsertValue( + TUint32 aTimestamp, + WHA::TRcpi aRcpi ) + { + // If rcpi storage is full remove the oldest one to get space for new one + if ( iValueCount == iValueCountMax ) + { + RemoveValue( iOldestValue ); + } + // Insert the new value into rcpi storage + if ( !iValueCount ) + { + // First item in rcpi storage. + iOldestValue = 0; + iLatestValue = 0; + iLeastRcpi = 0; + iMaxRcpi = 0; + iLevelTable[iOldestValue].iTimestamp = aTimestamp; + iLevelTable[iOldestValue].iRcpi = aRcpi; + iLevelTable[iOldestValue].iPrev = iValueCountMax; + iLevelTable[iOldestValue].iNext = iValueCountMax; + } + else + { + // Already items in storage. + TBool biggerFound( EFalse ); + TUint8 tmpLeastIndex( iLeastRcpi ); + + iLatestValue++; + if ( iLatestValue == iValueCountMax ) + { + iLatestValue = 0; + } + iLevelTable[iLatestValue].iTimestamp = aTimestamp; + iLevelTable[iLatestValue].iRcpi = aRcpi; + + // Insert this right place in Rcpi order. + while ( !biggerFound ) + { + if ( iLevelTable[tmpLeastIndex].iRcpi > aRcpi ) + { + if ( tmpLeastIndex == iLeastRcpi ) + { + iLevelTable[iLatestValue ].iPrev = iValueCountMax; + iLevelTable[iLatestValue ].iNext = tmpLeastIndex; + iLevelTable[tmpLeastIndex].iPrev = iLatestValue; + iLeastRcpi = iLatestValue; + } + else + { + iLevelTable[iLatestValue ].iPrev = iLevelTable[tmpLeastIndex].iPrev; + iLevelTable[iLatestValue ].iNext = tmpLeastIndex; + iLevelTable[tmpLeastIndex].iPrev = iLatestValue; + iLevelTable[iLevelTable[iLatestValue].iPrev].iNext = iLatestValue; + } + biggerFound = ETrue; + } + else + { + if ( tmpLeastIndex == iMaxRcpi ) + { + // Biggest rcpi value. + iLevelTable[iMaxRcpi].iNext = iLatestValue; + iLevelTable[iLatestValue].iPrev = iMaxRcpi; + iLevelTable[iLatestValue].iNext = iValueCountMax; + iMaxRcpi = iLatestValue; + biggerFound = ETrue; + } + } + tmpLeastIndex = iLevelTable[tmpLeastIndex].iNext; + } + } + iValueCount++; + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertValue Rcpi: %d"), + aRcpi ); + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertValue Timestamp: %d"), + aTimestamp ); + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertValue Rcpi count: %d"), + iValueCount ); + + + // Check first if the latest filtered value is old enough so that new + // value can be added. If not, no new value is added. + TUint32 FilterDelay = Delay( iMedianTable[iLatestMedian].iTimestamp, + iLevelTable[iLatestValue].iTimestamp ); + + if ( FilterDelay < KMedianDelay ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertValue: Too small delay between medians %d, No prediction. "), + FilterDelay ); + + return EFalse; + } + + // Calculate weight to be used in mean filter. + // Median weight factor ratio 4/7 is experimental value. ratio 10/1 would + // mean in practise that NewMean = GetCurrentMedian() + // and Ratio 1/1000 NewMean = latest iLevelTable[iLatestValue].iRcpi + TUint8 WeightFactor = ( iValueCount * 4 ) / 7; + + // Get new filtered value using floating mean filter. + // Previous value (=LatestMedian at this point) is weighted using + // value above. + TUint8 NewMean = ( iMedianTable[iLatestMedian].iRcpi * WeightFactor + + GetCurrentMedian() ) / ( WeightFactor + 1 ); + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertValue WeightFactor: %d"), + WeightFactor ); + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertValue NewMean: %d"), + NewMean ); + + return InsertMedian( aTimestamp, NewMean ); + } + + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TUint8 WlanSignalPredictor::GetCurrentMedian() const + { + + TUint8 tempIdx( iLeastRcpi ); + + if ( KOneValue == iValueCount ) + { + return iLevelTable[tempIdx].iRcpi; + } + else if ( iValueCount % 2 ) + { + for ( TUint8 i = 1; i <= iValueCount/2; i++ ) + { + tempIdx = iLevelTable[tempIdx].iNext; + } + return iLevelTable[tempIdx].iRcpi; + } + else + { + for ( TUint8 i = 1; i < iValueCount/2; i++ ) + { + tempIdx = iLevelTable[tempIdx].iNext; + } + return ( (iLevelTable[tempIdx].iRcpi + + iLevelTable[iLevelTable[tempIdx].iNext].iRcpi)/2 ); + } + } + + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool WlanSignalPredictor::InsertMedian( + TUint32 aTimestamp, + TUint8 aMedian ) + { + TUint32 deltaTime( 0 ); + + if ( !iMedianCount ) + { + iMedianCount = KOneValue; + iLatestMedian = 0; + iMedianTable[iLatestMedian].iTimestamp = aTimestamp; + iMedianTable[iLatestMedian].iRcpi = aMedian; + } + else + { + deltaTime = Delay( iMedianTable[iLatestMedian].iTimestamp, aTimestamp ); + if ( deltaTime > iWindowSize ) + { + os_memset( iMedianTable,KDummy, sizeof( iMedianTable ) ); + iLatestMedian = 0; + iMedianCount = KOneValue; + iMedianTable[iLatestMedian].iTimestamp = aTimestamp; + iMedianTable[iLatestMedian].iRcpi = aMedian; + } + else + { + iLatestMedian = iLatestMedian ? 0 : 1; + iMedianCount = 2; + iMedianTable[iLatestMedian].iTimestamp = aTimestamp; + iMedianTable[iLatestMedian].iRcpi = aMedian; + } + } + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertMedian Median: %d"), + aMedian ); + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::InsertMedian MedianCount: %d"), + iMedianCount ); + + return GetPrediction(); + } + + + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool WlanSignalPredictor::GetPrediction() + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::GetPrediction") ); + + // Roaming indication delay check. + if ( iRoamed.iDone ) + { + if ( iTimeToNextInd >= + Delay(iRoamed.iTimestamp, iMedianTable[iLatestMedian].iTimestamp) ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::Latest roaming indication delay not past: No prediction") ); + + // Old indication still not enough old. + return EFalse; + } + else + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::Latest roaming indication delay past: Get prediction") ); + // Old indication old enough to send new one, clean the flag. + iRoamed.iDone = EFalse; + } + } + + // One median value, is it under warning level. + if ( iMinValueCountForPrediction > iValueCount ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor:: Count of rcpi values not enough:") ); + + // If latest filtered value is below warning level + if ( iMedianTable[iLatestMedian].iRcpi < iWarningLevel ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor:: Last median under Warning level: Send roam indication.") ); + } + else + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor:: No prediction.") ); + return EFalse; + } + } + + // Is the latest median bigger or equal than old one. + else if ( iMedianTable[iLatestMedian].iRcpi >= + iMedianTable[iLatestMedian?0:1].iRcpi ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor:: Last median above old one: No roam indication.") ); + return EFalse; + } + // Otherwise count the prediction for roaming indication. + else + { + TUint32 Drop( 0 ); + TUint32 DropPeriod( 0 ); + TUint32 DropToRoam( 0 ); + TUint32 TimeToRoam( 0 ); + + Drop = iMedianTable[iLatestMedian ? 0 : 1].iRcpi - + iMedianTable[iLatestMedian].iRcpi; + + DropToRoam = iMedianTable[iLatestMedian].iRcpi - iWarningLevel; + + DropPeriod = Delay( iMedianTable[iLatestMedian ? 0 : 1].iTimestamp, + iMedianTable[iLatestMedian].iTimestamp); + + TimeToRoam = ( DropPeriod * DropToRoam ) / Drop; + + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::GetPrediction TimeToRoam: %d ms."), + TimeToRoam ); + + // Level comparison changed to use + // filtered signal value. This significantly + // reduces amount of indications. + if ( TimeToRoam < iTimeToWarnLevel && + KDiffRoamLevel > iMedianTable[iLatestMedian].iRcpi ) + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::All conditions ok. Send roam indication.") ); + } + else + { + OsTracePrint( KRxFrame, (TUint8*) + ("UMAC: WlanSignalPredictor::Some conditions nok. No roam indication.") ); + return EFalse; + } + } + + // If we get this point, roaming indication is sent. Save the indication info. + iRoamed.iDone = ETrue; + iRoamed.iTimestamp = iMedianTable[iLatestMedian].iTimestamp; + + // Start roaming + return ETrue; + } + + + + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TUint32 WlanSignalPredictor::Delay( + TUint32 aOldTime, + TUint32 aNewTime ) const + { + TUint32 delay; + if ( aOldTime > aNewTime ) + { + delay = ~aOldTime + 1 + aNewTime; + } + else + { + delay = aNewTime - aOldTime; + } + return delay; + } + + + + +