/* * Copyright 2014 Range Networks, Inc. * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ // Written 3-2014 by Pat Thompson #define LOG_GROUP LogGroup::GSM // Can set Log.Level.GSM for debugging #include #include "GSMLogicalChannel.h" #include "NeighborTable.h" #include "GSMChannelHistory.h" #include "GSML1FEC.h" namespace GSM { // Return the averaged RXLEV from the serving BTS as reported by the handset. int ChannelHistory::getAvgRxlev() { ScopedLock lock(mSCellLock); FrameNum now = gBTS.time().FN(); return round(ComputeTrend(mSCellData,now,&SCellPoint::getFrameNum,&SCellPoint::getRxlev,gConfig.GSM.Handover.RXLEV_DL.History)); } bool NeighborHistory::nhGetAvgRxlev(FrameNum fn, float &avg, int algorithm) { ScopedLock lock(nhLock); if (nhList.size() == 0) { return false; } avg = ComputeTrend(nhList,fn,&NCellPoint::getFrameNum,&NCellPoint::getRxlev,gConfig.GSM.Handover.RXLEV_DL.History,algorithm); return true; } NeighborHistory& ChannelHistory::getNeighborData(unsigned arfcn, unsigned BSIC) { unsigned key = makeKey(arfcn,BSIC); NeighborMap::iterator it; if ((it = mNeighborData.find(key)) == mNeighborData.end()) { // Create a new entry. You would think this could be easier, but not if you want to avoid the extra unnecessary construction. // C++11 adds emplace but we cant use that yet. //std::pair mapentry(key,NeighborHistory(arfcn,BSIC)); //std::pair blah = mNeighborData.insert(mapentry); //return blah.first->second; //std::pair blah = mNeighborData.insert( NeighborMap::value_type(key,newone) ); NeighborHistory newone((unsigned)arfcn,BSIC); it = mNeighborData.insert( NeighborMap::value_type(key,newone) ).first; } return it->second; } // Clear all neighbor data. void ChannelHistory::neighborClearMeasurements() { LOG(DEBUG); mNeighborData.clear(); } void NeighborHistory::nhAddPoint(NCellPoint &npt, FrameNum now) { ScopedLock lock(nhLock); nhList.push_front(npt); // This makes a copy of npt. int maxlen = gConfig.GSM.Handover.History.Max; while ((int)nhList.size() > maxlen) { nhList.pop_back(); } // Throw away points that are too old. int maxage = (1+maxlen) * 2*52; // Each report requires 2 * 52-multiframes, 480ms. while (nhList.size()) { NCellPoint &bk = nhList.back(); if (FNDelta(now,bk.ncpFrame) > maxage) { nhList.pop_back(); continue; } break; } } void ChannelHistory::chAddPoint(SCellPoint &spt, FrameNum now) { LOG(DEBUG) << LOGVAR(spt.scpFrame) << LOGVAR(now); ScopedLock lock(mSCellLock); mSCellData.push_front(spt); int maxlen = gConfig.GSM.Handover.History.Max; while ((int)mSCellData.size() > maxlen) { mSCellData.pop_back(); } // Throw away points that are too old. int maxage = (1+maxlen) * 2*52; // Each report requires 2 * 52-multiframes, 480ms. while (mSCellData.size()) { SCellPoint &bk = mSCellData.back(); if (FNDelta(now,bk.scpFrame) > maxage) { mSCellData.pop_back(); continue; } break; } } // Find the neighbor with the highest RXLEV. // If none, the mValid in the result will be false. Control::BestNeighbor ChannelHistory::neighborFindBest(Control::NeighborPenalty penalty) { Control::BestNeighbor best; FrameNum sampleFN = gBTS.time().FN(); LOG(DEBUG) <second; LOG(DEBUG) < best.mRxlev) { // Check for congestion in the neighbor. if (gNeighborTable.neighborCongestion(nh.nhARFCN,nh.nhBSIC)) { LOG(DEBUG) << "skipping, neighborCongestion"; continue; } best.mValid = true; best.mRxlev = thisRxlev; unsigned bestkey = it->first; crackKey(bestkey,&best.mARFCN,&best.mBSIC); LOG(DEBUG) <<"found:"<getNeighborData(arfcn,BSIC); // creates an entry if necessary. { NCellPoint npt; npt.ncpFrame = when; npt.ncpRxlev = rxlevdb; nh.nhAddPoint(npt,when); LOG(DEBUG) <mReportTimestamp-1; LOG(DEBUG) <mReportTimestamp) <mReportTimestamp; } bool ChannelHistory::neighborAddMeasurements(SACCHLogicalChannel* SACCH,const L3MeasurementResults* measurements) { Time sampleTime = gBTS.time(); // This thread could be running behind the clock, but it is close enough for government work. this->mReportTimestamp++; { SCellPoint spt; spt.scpFrame = sampleTime.FN(); // These are reported by the handset. spt.scpValid = measurements->isServingCellValid(); if (spt.scpValid) { spt.scpRxlev = measurements->RXLEV_FULL_SERVING_CELL_dBm(); spt.scpRxqual = measurements->RXQUAL_FULL_SERVING_CELL(); } else { spt.scpRxlev = -1000; // Impossible value. spt.scpRxqual = 8; // Impossible value. } } // Save the RXLEV for the neighbors. if (7 == measurements->NO_NCELL()) { // This special value means neighbor cell information is not avaliable. Gotta love that. return false; } for (unsigned int i=0; iNO_NCELL(); i++) { int thisRxLevel = measurements->RXLEV_NCELL_dBm(i); int thisFreq = measurements->BCCH_FREQ_NCELL(i); if (thisFreq == 31) { // (pat) This is reserved for 3G in some weird way. // We support only 31 instead of 32 neighbors to avoid any confusion here. continue; } int thisBSCI = measurements->BSIC_NCELL(i); int arfcn = gNeighborTable.getARFCN(thisFreq); if (arfcn < 0) { LOG(INFO) << "Measurement report with invalid freq index:" << thisFreq << " arfcn:" << arfcn; // SVGDBG seeing this error (pat) Maybe fixed 10-17-2014 by ticket #1915 continue; } this->neighborAddMeasurement(sampleTime.FN(),(unsigned)arfcn,thisBSCI,thisRxLevel); } return measurements->isServingCellValid() && measurements->NO_NCELL() > 0; } };