diff --git a/src/Makefile.am b/src/Makefile.am index b4dcfed9..b20b0a98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ libgprs_la_SOURCES = \ gprs_rlcmac.cpp \ gprs_rlcmac_data.cpp \ gprs_rlcmac_sched.cpp \ + gprs_rlcmac_meas.cpp \ gsm_timer.cpp \ bitvector.cpp \ pcu_l1_if.cpp \ diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp index 17c3fe45..5dbfb2c1 100644 --- a/src/gprs_bssgp_pcu.cpp +++ b/src/gprs_bssgp_pcu.cpp @@ -244,6 +244,9 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi); } + /* store IMSI for debugging purpose */ + strncpy(tbf->meas.imsi, imsi, sizeof(tbf->meas.imsi) - 1); + return 0; } diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp index 6da0d516..6f9e310a 100644 --- a/src/gprs_debug.cpp +++ b/src/gprs_debug.cpp @@ -40,7 +40,7 @@ static const struct log_info_cat default_categories[] = { {"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_NOTICE, 1}, {"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1}, {"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1}, - {"DRLCMACBW", "\033[1;31m", "GPRS RLC/MAC layer Bandwidth (RLCMAC)", LOGL_INFO, 1}, + {"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1}, {"DBSSGP","\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1}, {"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1}, }; diff --git a/src/gprs_debug.h b/src/gprs_debug.h index abd5d95a..8b113a1b 100644 --- a/src/gprs_debug.h +++ b/src/gprs_debug.h @@ -38,7 +38,7 @@ enum { DRLCMACDL, DRLCMACUL, DRLCMACSCHED, - DRLCMACBW, + DRLCMACMEAS, DBSSGP, DPCU, aDebug_LastEntry diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp index ff4cbddb..2c873172 100644 --- a/src/gprs_rlcmac.cpp +++ b/src/gprs_rlcmac.cpp @@ -384,7 +384,9 @@ next_diagram: } /* set timestamp */ - gettimeofday(&tbf->bw_tv, NULL); + gettimeofday(&tbf->meas.dl_bw_tv, NULL); + gettimeofday(&tbf->meas.rssi_tv, NULL); + gettimeofday(&tbf->meas.dl_loss_tv, NULL); INIT_LLIST_HEAD(&tbf->llc_queue); if (dir == GPRS_RLCMAC_UL_TBF) @@ -914,6 +916,10 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf) { struct msgb *msg; + /* Give final measurement report */ + gprs_rlcmac_rssi_rep(tbf); + gprs_rlcmac_lost_rep(tbf); + debug_diagram(tbf->diag, "+---------------+"); debug_diagram(tbf->diag, "| THE END |"); debug_diagram(tbf->diag, "+---------------+"); diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h index 67edca1a..5badb120 100644 --- a/src/gprs_rlcmac.h +++ b/src/gprs_rlcmac.h @@ -226,8 +226,21 @@ struct gprs_rlcmac_tbf { unsigned int fT; /* fTxxxx number */ unsigned int num_fT_exp; /* number of consecutive fT expirations */ - struct timeval bw_tv; /* timestamp for bandwidth calculation */ - uint32_t bw_octets; /* number of octets transmitted since bw_tv */ + struct { + char imsi[16]; + + struct timeval dl_bw_tv; /* timestamp for dl bw calculation */ + uint32_t dl_bw_octets; /* number of octets since bw_tv */ + + struct timeval rssi_tv; /* timestamp for rssi calculation */ + int32_t rssi_sum; /* sum of rssi values */ + int rssi_num; /* number of rssi values added since rssi_tv */ + + struct timeval dl_loss_tv; /* timestamp for loss calculation */ + uint16_t dl_loss_lost; /* sum of lost packets */ + uint16_t dl_loss_received; /* sum of received packets */ + + } meas; uint8_t cs; /* current coding scheme */ @@ -278,6 +291,19 @@ void debug_diagram(int diag, const char *format, ...); #define debug_diagram(a, b, args...) ; #endif +int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, + uint16_t lost); + +int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf); + +int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr); + +int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi); + +int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf); + +int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets); + int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta); struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn); diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp index e7b29723..1109451e 100644 --- a/src/gprs_rlcmac_data.cpp +++ b/src/gprs_rlcmac_data.cpp @@ -468,6 +468,9 @@ int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts, } LOGP(DRLCMAC, LOGL_ERROR, "RX: [PCU <- BTS] %s TFI: %u TLLI: 0x%08x FIXME: Packet ressource request\n", (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, tbf->tlli); break; + case MT_PACKET_MEASUREMENT_REPORT: + gprs_rlcmac_meas_rep(&ul_control_block->u.Packet_Measurement_Report); + break; default: LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] unknown control block received\n"); } @@ -846,6 +849,9 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts, LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TBF=%d received (V(Q)=%d .. " "V(R)=%d)\n", rh->tfi, tbf->dir.ul.v_q, tbf->dir.ul.v_r); + /* process RSSI */ + gprs_rlcmac_rssi(tbf, rssi); + /* get TLLI */ if (!tbf->tlli_valid) { struct gprs_rlcmac_tbf *dl_tbf, *ul_tbf; @@ -1213,29 +1219,6 @@ static struct msgb *llc_dequeue(struct gprs_rlcmac_tbf *tbf) return msg; } -static int gprs_rlcmac_debug_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets) -{ - struct timeval now_tv, *bw_tv = &tbf->bw_tv; - uint32_t elapsed; - - tbf->bw_octets += octets; - - gettimeofday(&now_tv, NULL); - elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7) - + ((now_tv.tv_usec - bw_tv->tv_usec) << 7) / 1000000; - if (elapsed < 128) - return 0; - - LOGP(DRLCMACBW, LOGL_DEBUG, "DL Bandwitdh of TLLI=0x%08x: %d KBits/s\n", - tbf->tlli, tbf->bw_octets / elapsed); - - /* reset bandwidth values timestamp */ - memcpy(bw_tv, &now_tv, sizeof(struct timeval)); - tbf->bw_octets = 0; - - return 0; -} - /* send DL data block * * The messages are fragmented and forwarded as data blocks. @@ -1380,7 +1363,7 @@ do_resend: LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for " "TBF=%d that fits precisely in last block: " "len=%d\n", tbf->tfi, tbf->llc_length); - gprs_rlcmac_debug_bw(tbf, tbf->llc_length); + gprs_rlcmac_dl_bw(tbf, tbf->llc_length); /* block is filled, so there is no extension */ *e_pointer |= 0x01; /* fill space */ @@ -1440,7 +1423,7 @@ do_resend: space -= chunk; LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: " "len=%d\n", tbf->tfi, tbf->llc_length); - gprs_rlcmac_debug_bw(tbf, tbf->llc_length); + gprs_rlcmac_dl_bw(tbf, tbf->llc_length); /* reset LLC frame */ tbf->llc_index = tbf->llc_length = 0; /* dequeue next LLC frame, if any */ @@ -1568,6 +1551,7 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, uint8_t bit; uint16_t bsn; struct msgb *msg; + uint16_t lost = 0, received = 0; LOGP(DRLCMACDL, LOGL_DEBUG, "TBF=%d downlink acknowledge\n", tbf->tfi); @@ -1587,25 +1571,7 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, /* calculate distance of ssn from V(S) */ dist = (tbf->dir.dl.v_s - ssn) & mod_sns; /* check if distance is less than distance V(A)..V(S) */ - if (dist < ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns)) { - /* SSN - 1 is in range V(A)..V(S)-1 */ - for (i = 63, bsn = (ssn - 1) & mod_sns; - i >= 0 && bsn != ((tbf->dir.dl.v_a - 1) & mod_sns); - i--, bsn = (bsn - 1) & mod_sns) { - bit = (rbb[i >> 3] >> (7 - (i&7))) & 1; - if (bit) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- got " - "ack for BSN=%d\n", bsn); - tbf->dir.dl.v_b[bsn & mod_sns_half] - = 'A'; - } else { - LOGP(DRLCMACDL, LOGL_DEBUG, "- got " - "NACK for BSN=%d\n", bsn); - tbf->dir.dl.v_b[bsn & mod_sns_half] - = 'N'; - } - } - } else { + if (dist >= ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns)) { /* this might happpen, if the downlink assignment * was not received by ms and the ack refers * to previous TBF @@ -1616,6 +1582,27 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, tbf->tfi); return 1; /* indicate to free TBF */ } + /* SSN - 1 is in range V(A)..V(S)-1 */ + for (i = 63, bsn = (ssn - 1) & mod_sns; + i >= 0 && bsn != ((tbf->dir.dl.v_a - 1) & mod_sns); + i--, bsn = (bsn - 1) & mod_sns) { + bit = (rbb[i >> 3] >> (7 - (i&7))) & 1; + if (bit) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- got " + "ack for BSN=%d\n", bsn); + if (tbf->dir.dl.v_b[bsn & mod_sns_half] + != 'A') + received++; + tbf->dir.dl.v_b[bsn & mod_sns_half] = 'A'; + } else { + LOGP(DRLCMACDL, LOGL_DEBUG, "- got " + "NACK for BSN=%d\n", bsn); + tbf->dir.dl.v_b[bsn & mod_sns_half] = 'N'; + lost++; + } + } + /* report lost and received packets */ + gprs_rlcmac_received_lost(tbf, received, lost); /* raise V(A), if possible */ for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; @@ -1653,6 +1640,15 @@ int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final, LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); debug_diagram(tbf->diag, "got Final ACK"); + /* range V(A)..V(S)-1 */ + for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; + bsn = (bsn + 1) & mod_sns) { + if (tbf->dir.dl.v_b[bsn & mod_sns_half] != 'A') + received++; + } + + /* report all outstanding packets as received */ + gprs_rlcmac_received_lost(tbf, received, lost); /* check for LLC PDU in the LLC Queue */ msg = llc_dequeue(tbf); diff --git a/src/gprs_rlcmac_meas.cpp b/src/gprs_rlcmac_meas.cpp new file mode 100644 index 00000000..75da835a --- /dev/null +++ b/src/gprs_rlcmac_meas.cpp @@ -0,0 +1,190 @@ +/* Measurements + * + * Copyright (C) 2012 Andreas Eversberg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include +#include + +/* + * downlink measurement + */ + +/* received Measurement Report */ +int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr) +{ + NC_Measurement_Report_t *ncr; + NC_Measurements_t *nc; + int i; + + LOGP(DRLCMACMEAS, LOGL_INFO, "Measuement Report of TLLI=0x%08x:", + pmr->TLLI); + + switch (pmr->UnionType) { + case 0: + ncr = &pmr->u.NC_Measurement_Report; + LOGPC(DRLCMACMEAS, LOGL_INFO, " NC%u Serv %d dbm", + ncr->NC_MODE + 1, + ncr->Serving_Cell_Data.RXLEV_SERVING_CELL - 110); + for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) { + nc = &ncr->NC_Measurements[i]; + LOGPC(DRLCMACMEAS, LOGL_DEBUG, ", Neigh %u %d dbm", + nc->FREQUENCY_N, nc->RXLEV_N - 110); + } + LOGPC(DRLCMACMEAS, LOGL_INFO, "\n"); + + break; + case 1: + LOGPC(DRLCMACMEAS, LOGL_INFO, + " \n"); + break; + } + + return 0; +} + + +/* + * uplink measurement + */ + +/* RSSI values received from MS */ +int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi) +{ + struct timeval now_tv, *rssi_tv = &tbf->meas.rssi_tv; + uint32_t elapsed; + + tbf->meas.rssi_sum += rssi; + tbf->meas.rssi_num++; + + gettimeofday(&now_tv, NULL); + elapsed = ((now_tv.tv_sec - rssi_tv->tv_sec) << 7) + + ((now_tv.tv_usec - rssi_tv->tv_usec) << 7) / 1000000; + if (elapsed < 128) + return 0; + + gprs_rlcmac_rssi_rep(tbf); + + /* reset rssi values and timestamp */ + memcpy(rssi_tv, &now_tv, sizeof(struct timeval)); + tbf->meas.rssi_sum = 0; + tbf->meas.rssi_num = 0; + + return 0; +} + +/* Give RSSI report */ +int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf) +{ + /* No measurement values */ + if (!tbf->meas.rssi_num) + return -EINVAL; + + LOGP(DRLCMACMEAS, LOGL_INFO, "UL RSSI of TLLI=0x%08x: %d dBm\n", + tbf->tlli, tbf->meas.rssi_sum / tbf->meas.rssi_num); + + return 0; +} + + +/* + * lost frames + */ + +/* Lost frames reported from RLCMAC layer */ +int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received, + uint16_t lost) +{ + struct timeval now_tv, *loss_tv = &tbf->meas.dl_loss_tv; + uint32_t elapsed; + uint16_t sum = received + lost; + + /* No measurement values */ + if (!sum) + return -EINVAL; + + LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d " + "Lost: %4d Sum: %4d\n", tbf->tlli, received, lost, sum); + + tbf->meas.dl_loss_received += received; + tbf->meas.dl_loss_lost += lost; + + gettimeofday(&now_tv, NULL); + elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7) + + ((now_tv.tv_usec - loss_tv->tv_usec) << 7) / 1000000; + if (elapsed < 128) + return 0; + + gprs_rlcmac_lost_rep(tbf); + + /* reset lost values and timestamp */ + memcpy(loss_tv, &now_tv, sizeof(struct timeval)); + tbf->meas.dl_loss_received = 0; + tbf->meas.dl_loss_lost = 0; + + return 0; +} + +/* Give Lost report */ +int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf) +{ + uint16_t sum = tbf->meas.dl_loss_lost + tbf->meas.dl_loss_received; + + /* No measurement values */ + if (!sum) + return -EINVAL; + + LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: " + "%d%%\n", tbf->meas.imsi, tbf->tlli, + tbf->meas.dl_loss_lost * 100 / sum); + + return 0; +} + + +/* + * downlink bandwidth + */ + +int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets) +{ + struct timeval now_tv, *bw_tv = &tbf->meas.dl_bw_tv; + uint32_t elapsed; + + tbf->meas.dl_bw_octets += octets; + + gettimeofday(&now_tv, NULL); + elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7) + + ((now_tv.tv_usec - bw_tv->tv_usec) << 7) / 1000000; + if (elapsed < 128) + return 0; + + LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: " + "%d KBits/s\n", tbf->meas.imsi, tbf->tlli, + tbf->meas.dl_bw_octets / elapsed); + + /* reset bandwidth values timestamp */ + memcpy(bw_tv, &now_tv, sizeof(struct timeval)); + tbf->meas.dl_bw_octets = 0; + + return 0; +} +