// 5-27-2012 pat added: // Routines for CCCH messages to add real paging channels. // Added in the simplest possible way to avoid destabilizing anything. // GPRS still needs a pretty major rewrite of the underlying CCCHLogicalChannel class // to reduce the latency, but paging queues at least relieve the congestion on CCCH. // In DRX [Discontinuous Reception] mode the MS listens only to a subset of CCCH based on its IMSI. // This is a GPRS thing but dependent on the configuration of CCCH in our system. // See: GSM 05.02 6.5.2: Determination of CCCH_GROUP and PAGING_GROUP for MS in idle mode. void GSMConfig::crackPagingFromImsi( unsigned imsiMod1000 // The phones imsi mod 1000, so just atoi the last 3 digits. unsigned &paging_block_index, // Returns which of the paging ccchs to use. unsigned &multiframe_index // Returns which 51-multiframe to use. ) { L3ControlChannelDescription mCC; // BS_CCCH_SDCCH_COMB is defined in GSM 05.02 3.3.2.3; int bs_cc_chans; // The number of ccch timeslots per 51-multiframe. bool bs_ccch_sdcch_comb; // temp var indicates if sdcch is on same TS as ccch. switch (mCC.mCCCH_CONF) { case 0: bs_cc_chans=1; bs_ccch_sdcch_comb=false; break; case 1: bs_cc_chans=1; bs_ccch_sdcch_comb=true; break; case 2: bs_cc_chans=2; bs_ccch_sdcch_comb=false; break; case 4: bs_cc_chans=3; bs_ccch_sdcch_comb=false; break; case 6: bs_cc_chans=4; bs_ccch_sdcch_comb=false; break; default: LOG(ERR) << "Invalid GSM.CCCH.CCCH-CONF value:"<<mCC.mCCCH_CONF <<" GPRS will fail until fixed"; return NULL; // There will be no reliable GPRS service until you fix this. } // BS_PA_MFRMS is the number of 51-multiframes used for paging. unsigned bs_pa_mfrms = mCC.getBS_PA_MFRMS(); // Here are some example numbers: // We currently use CCCH_CONF=1 so cc_chans=1, so agch_avail=3. // Since BS_CC_CHANS=1, then CCCH_GROUP is always 0. // For BS_PA_MFRMS=2, BS_AG_BLKS_RES=2: // N=2; tmp = imsi % 2; CCCH_GROUP = 0; PAGING_GROUP = imsi % 2; // For BS_PA_MFRMS=2, BS_AG_BLKS_RES=1: // N=4; tmp = imsi % 4; PAGING_GROUP = imsi % 4; // For BS_PA_MFRMS=2, BS_AG_BLKS_RES=0: // N=6; tmp = imsi % 6; PAGING_GROUP = imsi % 6; // For BS_PA_MFRMS=3, BS_AG_BLKS_RES=0: // N=9; tmp = imsi % 9; PAGING_GROUP = imsi % 9; // Paging block index = PAGING_GROUP % BS_PA_MFRMS // Multiframe index = PAGING_GROUP / pch_avail; // correct multiframe when: multiframe_index == (FN div 51) % BA_PA_MFRMS // From GSM 05.02 Clause 7 table 5 (located after sec 6.5) unsigned agch_avail = bs_ccch_sdcch_comb ? 3 : 8; // If you hit this assertion, go fix L3ControlChannelDescription // to make sure you leave some paging channels available. assert(agch_avail > mPCC.mBS_AG_BLKS_RES); // GSM 05.02 6.5.2: N is number of paging blocks "available" on one CCCH. // The "available" is in quotes and not specifically defined, but I believe // they mean after subtracting out BS_AG_BLKS_RES, as per 6.5.1 paragraph v). unsigned pch_avail = agch_avail - mPCC.mBS_AG_BLKS_RES; unsigned Ntotal = pch_avail * bs_pa_mfrms; unsigned tmp = (imsiMod1000 % (bs_cc_chans * Ntotal)) % Ntotal; unsigned paging_group = tmp % Ntotal; paging_block_index = paging_group / (Ntotal / bs_pa_mfrms); // And I quote: The required 51-multiframe occurs when: // PAGING_GROUP div (N div BS_PA_MFRMS) = (FN div 51) mod (BS_PA_MFRMS) multiframe_index = paging_group / (Ntotal % bs_pa_mfrms); }
// (pat) BUG TODO: TO WHOM IT MAY CONCERN: // I am not sure this routine works properly. If there is no CCCH message (an L3Frame) // in the queue immediately after the previous frame is sent, an idle frame is inserted. // If a subsequent valid CCCH message (paging response or MS initiated RR call or packet // uplink request) arrives it will be blocked until the idle frame is sent. // Probably doesnt matter for RR establishment, but for packets, the extra 1/4 sec // delay (length of a 51-multiframe) is going to hurt. // Note that a GPRS Immediate Assignment message must know when this CCCH gets sent. // Right now, it has to guess. // pats TODO: Send the transceiver an idle frame rather than doing it here. // This should be architecturally changed to a pull-system instead of push. // Among other things, that would let us prioritize the responses // (eg, emergency calls go first) and let the packet Immediate Assignment message be // created right before being sent, when we are certain when the // Immediate Assignment is being sent. void CCCHLogicalChannel::serviceLoop() { // build the idle frame static const L3PagingRequestType1 filler; static const L3Frame idleFrame(filler,UNIT_DATA); #if ENABLE_PAGING_CHANNELS L3ControlChannelDescription mCC; unsigned bs_pa_mfrms = mCC.getBS_PA_MFRMS(); #endif // prime the first idle frame LogicalChannel::send(idleFrame); // run the loop while (true) { L3Frame* frame = NULL; #if ENABLE_PAGING_CHANNELS // Check for paging message for this specific paging slot first, // and if none, send any message in the mQ. // The multiframe paging logic is from GSM 05.02 6.5.3. // See documentation at crackPagingFromImsi() which is used to // get the messages into the proper mPagingQ. GSM::Time next = getNextWriteTime(); unsigned multiframe_index = (next.FN() / 51) % bs_pa_mfrms; frame = mPagingQ[multiframe_index].read(); #endif if (frame == NULL) { frame = mQ.read(); // (pat) This is a blocking read; mQ is an InterThreadQueue } if (frame) { // (pat) This tortuously calls XCCCHL1Encoder::transmit (see my documentation // at LogicalChannel::send), which blocks until L1Encoder::mPrevWriteTime. // Note: The q size is 0 while we are blocked here, so if we are trying // to determine the next write time by adding the qsize, we are way off. // Thats why there is an mWaitingToSend flag. mWaitingToSend = true; // Waiting to send this block at mNextWriteTime. LogicalChannel::send(*frame); mWaitingToSend = false; OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending " << *frame << " load: " << load() << " time: " << getNextWriteTime(); delete frame; } if (mQ.size()==0) { // (pat) The radio continues to send the last frame forever, // so we only send one idle frame here. // Unfortunately, this slows the response. // TODO: Send a static idle frame to the Transciever and rewrite this. mWaitingToSend = true; // Waiting to send an idle frame at mNextWriteTime. LogicalChannel::send(idleFrame); mWaitingToSend = false; OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending idle frame"; } } }
// (pat) This routine is going to be entirely replaced with one that works better for gprs. // In the meantime, just return a number that is large enough to cover // the worst case, which assumes that the messages in mQ also // must go out on the paging timeslot. Time GSM::CCCHLogicalChannel::getNextPchSendTime(unsigned multiframe_index) { L3ControlChannelDescription mCC; // Paging is distributed over this many multi-frames. unsigned bs_pa_mfrms = mCC.getBS_PA_MFRMS(); GSM::Time next = getNextWriteTime(); unsigned next_multiframe_index = (next.FN() / 51) % bs_pa_mfrms; assert(bs_pa_mfrms > 1); assert(multiframe_index < bs_pa_mfrms); assert(next_multiframe_index < bs_pa_mfrms); int achload = mQ.size(); if (mWaitingToSend) { achload++; } // Total wait time is time needed to empty queue, plus the time until the first // paging opportunity, plus 2 times the number of guys waiting in the paging queue, // but it is all nonsense because if a new agch comes in, // it will displace the paging message because the q is sent first. // This just needs to be totally redone, and the best way is not to figure out // when the message will be sent at all, but rather use a call-back to gprs // just before the message is finally sent. int multiframesToWait = 0; if (achload) { multiframesToWait = bs_pa_mfrms - 1; // Assume worst case. } else { // If there is nothing else waiting, we can estimate better: while (next_multiframe_index != multiframe_index) { multiframe_index = (multiframe_index+1) % bs_pa_mfrms; multiframesToWait++; } } int total = achload + multiframesToWait + bs_pa_mfrms * mPagingQ[multiframe_index].size(); int fnresult = (next.FN() + total * 51) % gHyperframe; GSM::Time result(fnresult); LOG(DEBUG) << "CCCHLogicalChannel::getNextSend="<< next.FN() <<" load="<<achload<<LOGVAR(mWaitingToSend) <<" now="<<gBTS.time().FN()<<LOGVAR(fnresult); return result; }