00001 /**************************************************************************** 00002 ** 00003 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 00004 ** All rights reserved. 00005 ** Contact: Nokia Corporation 00006 ** 00007 ** 00008 ** $QT_BEGIN_LICENSE:BSD$ 00009 ** You may use this file under the terms of the BSD license as follows: 00010 ** 00011 ** "Redistribution and use in source and binary forms, with or without 00012 ** modification, are permitted provided that the following conditions are 00013 ** met: 00014 ** * Redistributions of source code must retain the above copyright 00015 ** notice, this list of conditions and the following disclaimer. 00016 ** * Redistributions in binary form must reproduce the above copyright 00017 ** notice, this list of conditions and the following disclaimer in 00018 ** the documentation and/or other materials provided with the 00019 ** distribution. 00020 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 00021 ** the names of its contributors may be used to endorse or promote 00022 ** products derived from this software without specific prior written 00023 ** permission. 00024 ** 00025 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00026 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00027 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00028 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00029 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00030 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00031 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00032 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00033 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00034 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00035 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 00036 ** $QT_END_LICENSE$ 00037 ** 00038 ****************************************************************************/ 00039 // User includes 00040 #include "coverrecognizer.h" 00041 00042 // Constants 00043 // We would prefer accelerometer running at 2g range. 00044 const qreal idealRange = 19.62; 00045 // Default z value for moving average filter 00046 const qreal movingAverageDefaultZ = 10.0/movingAverageSamples; 00047 // Minimum time the phone should be covered and lying flat, face up and still before the 00048 // recognized signal is emmited. Time in ms. 00049 const uint minimumCoverTimeMS = 500; 00050 00051 00052 // Namespace needed to use Qt Mobility APIs 00053 // (defined in qmobilityglobal.h but implicitly included from any Qt Mobility header) 00054 QTM_USE_NAMESPACE 00055 00060 CoverRecognizer::CoverRecognizer() 00061 { 00062 mAccSensor = new QAccelerometer(); 00063 // Call the filter_accelerometer() function when new accelerometet data is ready. 00064 connect(mAccSensor, SIGNAL(readingChanged()), this, SLOT(filter_accelerometer())); 00065 00066 mProxSensor = new QProximitySensor(); 00067 // Call the filter_proximity() function when new proximity data is ready. 00068 connect(mProxSensor, SIGNAL(readingChanged()), this, SLOT(filter_proximity())); 00069 00070 mElapsedTimer = new QElapsedTimer(); 00071 00072 // Initialize moving average filter data index to 0. 00073 mMovingAverageIndex = 0; 00074 } 00075 00079 CoverRecognizer::~CoverRecognizer() 00080 { 00081 delete mAccSensor; 00082 delete mProxSensor; 00083 delete mElapsedTimer; 00084 } 00085 00090 bool CoverRecognizer::start() 00091 { 00092 // Test if accelerometer is functional, by starting it. 00093 bool result = mAccSensor->start(); 00094 // If accelerometer is functional we can continue start up of gesture detection. 00095 if (result) { 00096 // Stop accelerometer again, since it is not needed yet. 00097 mAccSensor->stop(); 00098 // Find the closest available accelerometer range to 2g. 00099 qoutputrangelist outputRangeList = mAccSensor->outputRanges(); 00100 int numOfItems = outputRangeList.count(); 00101 if (0 < numOfItems) { 00102 qreal rangeDiff = qAbs(outputRangeList.first().maximum - idealRange); 00103 int selectedIndex(0); 00104 for (int i(1); i < numOfItems; ++i) { 00105 if (qAbs(outputRangeList.at(i).maximum - idealRange) < rangeDiff) { 00106 // This is closer to 2g range than the previously checked one. 00107 selectedIndex = i; 00108 rangeDiff = qAbs(outputRangeList.at(i).maximum - idealRange); 00109 } 00110 } 00111 // Now we have the index to the range closest to 2g. 00112 mAccSensor->setOutputRange(selectedIndex); 00113 } 00114 00115 // Start proximity sensor in order to start detecting gesture. result will now 00116 // only be true if both accelerometer and proximity sensors are able to start. 00117 result &= mProxSensor->start(); 00118 } 00119 00120 return result; 00121 } 00122 00126 void CoverRecognizer::stop() 00127 { 00128 mAccSensor->stop(); 00129 mProxSensor->stop(); 00130 } 00131 00135 void CoverRecognizer::filter_accelerometer() 00136 { 00137 QAccelerometerReading *accReading = mAccSensor->reading(); 00138 if(mElapsedTimer->isValid()) 00139 { 00140 // Moving average filter is used to smooth out outliers that can occur for 00141 // example when a hand hits the phone in order to cover it. 00142 mXValues[mMovingAverageIndex] = accReading->x()/movingAverageSamples; 00143 mYValues[mMovingAverageIndex] = accReading->y()/movingAverageSamples; 00144 mZValues[mMovingAverageIndex] = accReading->z()/movingAverageSamples; 00145 if(++mMovingAverageIndex >= movingAverageSamples) 00146 { 00147 mMovingAverageIndex = 0; 00148 } 00149 qreal xAvg = 0.0; 00150 qreal yAvg = 0.0; 00151 qreal zAvg = 0.0; 00152 for (int i = 0; i < movingAverageSamples; i++) 00153 { 00154 xAvg += mXValues[i]; 00155 yAvg += mYValues[i]; 00156 zAvg += mZValues[i]; 00157 } 00158 00159 // Test for accepted accelerometer values. Values are tested within a margin 00160 // to compensate for the phone being placed on surfaces that are not accurately 00161 // perpendicular to the earths center of gravity (and accelerometer 00162 // inaccuracies). 00163 if(qAbs(xAvg) < 2 && qAbs(yAvg) < 2 && zAvg < 12 && zAvg > 7) 00164 { 00165 // Accelerometer values are within accepted range. Check if they have been 00166 // so for at least the minimum required amount of time. 00167 00168 if(mElapsedTimer->elapsed() >= minimumCoverTimeMS) 00169 { 00170 // The phone has been covered and lying flat, face up and still for the 00171 // required minimum amount of time. Emit signal to user that cover 00172 // gesture has occured. 00173 emitSignal(); 00174 // Stop accelerometer sensor and timer. Note that proximity sensor is 00175 // still running, so if user uncovers and covers phone again it will be 00176 // detected. 00177 mAccSensor->stop(); 00178 mElapsedTimer->invalidate(); 00179 } 00180 } 00181 else 00182 { 00183 // Accelerometer values outside accepted range. The phone is not lying flat, 00184 // face up and still. Restart timer. 00185 mElapsedTimer->restart(); 00186 } 00187 } else { 00188 // Do nothing. 00189 // Accelerometer is started and stopped during start() call. In case we get any 00190 // accelerometer data during that time we end up here. 00191 } 00192 } 00193 00197 void CoverRecognizer::filter_proximity() 00198 { 00199 if (mProxSensor->reading()->close()) { 00200 // Phone is covered. 00201 // Reset moving average filter sample values to prevent old data from influencing 00202 // gesture detection. 00203 for (int i = 0; i < movingAverageSamples; i++) 00204 { 00205 mXValues[i] = 0.0; 00206 mYValues[i] = 0.0; 00207 mZValues[i] = movingAverageDefaultZ; 00208 } 00209 // Start accelerometer and timer to check if the phone is lying flat, face up and 00210 // still for the minimum required amount of time. 00211 mAccSensor->start(); 00212 mElapsedTimer->start(); 00213 } else { 00214 // Phone is no longer covered. 00215 // Stop accelerometer and timer since they are no longer needed. 00216 mAccSensor->stop(); 00217 mElapsedTimer->invalidate(); 00218 } 00219 00220 } 00221 00225 void CoverRecognizer::emitSignal() 00226 { 00227 emit recognized(coverGestureType, QVariantHash()); 00228 }