mirror of https://github.com/pvnis/srsRAN_4G.git
Merge pull request #47 from ismagom/master
Fixed problems with PDSCH UE example and synchronization.master
commit
750a23f1df
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
|
||||||
|
int decode_pbch(void *uhd, cf_t *buffer, ue_celldetect_result_t *found_cell, uint32_t nof_frames_total, pbch_mib_t *mib)
|
||||||
|
{
|
||||||
|
ue_mib_t uemib;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
bzero(mib, sizeof(pbch_mib_t));
|
||||||
|
|
||||||
|
uint32_t nof_frames = 0;
|
||||||
|
uint32_t flen = MIB_FRAME_SIZE;
|
||||||
|
|
||||||
|
if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) {
|
||||||
|
fprintf(stderr, "Error initiating PBCH decoder\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0);
|
||||||
|
cuhd_set_rx_srate(uhd, 1920000.0);
|
||||||
|
INFO("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (cuhd_recv(uhd, buffer, flen, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total);
|
||||||
|
|
||||||
|
n = ue_mib_decode(&uemib, buffer, flen, mib);
|
||||||
|
if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) {
|
||||||
|
fprintf(stderr, "Error calling ue_mib_decode()\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
if (n == MIB_FRAME_UNALIGNED) {
|
||||||
|
printf("Realigning frame\n");
|
||||||
|
if (cuhd_recv(uhd, buffer, flen/2, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nof_frames++;
|
||||||
|
} while (n != MIB_FOUND && nof_frames < 2*nof_frames_total);
|
||||||
|
if (n == MIB_FOUND) {
|
||||||
|
printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames);
|
||||||
|
pbch_mib_fprint(stdout, mib, found_cell->cell_id);
|
||||||
|
} else {
|
||||||
|
printf("\nCould not decode MIB\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
cuhd_flush_buffer(uhd);
|
||||||
|
|
||||||
|
ue_mib_free(&uemib);
|
||||||
|
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_cell(void *uhd, ue_celldetect_t *s, cf_t *buffer, ue_celldetect_result_t found_cell[3])
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
INFO("Setting sampling frequency 960 KHz for PSS search\n", 0);
|
||||||
|
cuhd_set_rx_srate(uhd, 960000.0);
|
||||||
|
INFO("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
uint32_t nof_scanned_cells = 0;
|
||||||
|
uint32_t flen = 4800;
|
||||||
|
int nof_detected_cells = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if (cuhd_recv(uhd, buffer, flen, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = ue_celldetect_scan(s, buffer, flen, &found_cell[nof_scanned_cells]);
|
||||||
|
switch(n) {
|
||||||
|
case CS_FRAME_UNALIGNED:
|
||||||
|
printf("Realigning frame\n");
|
||||||
|
if (cuhd_recv(uhd, buffer, flen/2, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
case CS_CELL_DETECTED:
|
||||||
|
nof_detected_cells++;
|
||||||
|
if (found_cell[nof_scanned_cells].peak > 0) {
|
||||||
|
printf("\n\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n",
|
||||||
|
found_cell[nof_scanned_cells].cell_id,
|
||||||
|
lte_cp_string(found_cell[nof_scanned_cells].cp),
|
||||||
|
found_cell[nof_scanned_cells].peak, found_cell[nof_scanned_cells].mode,
|
||||||
|
s->nof_frames_detected);
|
||||||
|
}
|
||||||
|
|
||||||
|
nof_scanned_cells++;
|
||||||
|
break;
|
||||||
|
case CS_CELL_NOT_DETECTED:
|
||||||
|
nof_scanned_cells++;
|
||||||
|
break;
|
||||||
|
case LIBLTE_ERROR:
|
||||||
|
case LIBLTE_ERROR_INVALID_INPUTS:
|
||||||
|
fprintf(stderr, "Error calling cellsearch_scan()\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
} while(nof_scanned_cells < 3);
|
||||||
|
|
||||||
|
INFO("Stopping receiver...\n", 0);
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
cuhd_flush_buffer(uhd);
|
||||||
|
|
||||||
|
return nof_detected_cells;
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
int decode_pbch(void *uhd,
|
||||||
|
cf_t *buffer,
|
||||||
|
ue_celldetect_result_t *found_cell,
|
||||||
|
uint32_t nof_frames_total,
|
||||||
|
pbch_mib_t *mib);
|
||||||
|
|
||||||
|
int find_cell(void *uhd,
|
||||||
|
ue_celldetect_t *s,
|
||||||
|
cf_t *buffer,
|
||||||
|
ue_celldetect_result_t found_cell[3]);
|
@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UE_CELLSEARCH_
|
||||||
|
#define UE_CELLSEARCH_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/sync/sync.h"
|
||||||
|
#include "liblte/phy/sync/cfo.h"
|
||||||
|
#include "liblte/phy/ch_estimation/chest.h"
|
||||||
|
#include "liblte/phy/phch/pbch.h"
|
||||||
|
#include "liblte/phy/common/fft.h"
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
*
|
||||||
|
* This object scans a signal for LTE cells using the known PSS
|
||||||
|
* and SSS sequences.
|
||||||
|
*
|
||||||
|
* The function ue_celldetect_scan() shall be called multiple times,
|
||||||
|
* each passing a number of samples multiple of 4800, sampled at 960 KHz
|
||||||
|
* (that is, 5 ms of samples).
|
||||||
|
*
|
||||||
|
* The function returns 0 until a signal is found nof_frames_detected times or
|
||||||
|
* after nof_frames_total with no signal detected.
|
||||||
|
*
|
||||||
|
* See ue_cell_detect.c for an example.
|
||||||
|
*
|
||||||
|
************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Check also peak offset
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CS_DEFAULT_MAXFRAMES_TOTAL 300
|
||||||
|
#define CS_DEFAULT_MAXFRAMES_DETECTED 30
|
||||||
|
|
||||||
|
#define CS_DEFAULT_NOFFRAMES_TOTAL 100
|
||||||
|
#define CS_DEFAULT_NOFFRAMES_DETECTED 10
|
||||||
|
|
||||||
|
#define CS_FIND_THRESHOLD 0.6
|
||||||
|
|
||||||
|
#define CS_FRAME_UNALIGNED -3
|
||||||
|
#define CS_CELL_DETECTED 2
|
||||||
|
#define CS_CELL_NOT_DETECTED 1
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
uint32_t cell_id;
|
||||||
|
lte_cp_t cp;
|
||||||
|
float peak;
|
||||||
|
uint32_t mode;
|
||||||
|
} ue_celldetect_result_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
sync_t sfind;
|
||||||
|
uint32_t max_frames_total;
|
||||||
|
uint32_t max_frames_detected;
|
||||||
|
uint32_t nof_frames_total;
|
||||||
|
uint32_t nof_frames_detected;
|
||||||
|
|
||||||
|
uint32_t current_nof_detected;
|
||||||
|
uint32_t current_nof_total;
|
||||||
|
|
||||||
|
uint32_t current_N_id_2;
|
||||||
|
|
||||||
|
uint32_t *mode_ntimes;
|
||||||
|
char *mode_counted;
|
||||||
|
|
||||||
|
ue_celldetect_result_t *candidates;
|
||||||
|
} ue_celldetect_t;
|
||||||
|
|
||||||
|
|
||||||
|
LIBLTE_API int ue_celldetect_init(ue_celldetect_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_celldetect_init_max(ue_celldetect_t *q,
|
||||||
|
uint32_t max_frames_total,
|
||||||
|
uint32_t max_frames_detected);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_celldetect_free(ue_celldetect_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_celldetect_scan(ue_celldetect_t *q,
|
||||||
|
cf_t *signal,
|
||||||
|
uint32_t nsamples,
|
||||||
|
ue_celldetect_result_t *found_cell);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_celldetect_set_nof_frames_total(ue_celldetect_t *q,
|
||||||
|
uint32_t nof_frames);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_celldetect_set_nof_frames_detected(ue_celldetect_t *q,
|
||||||
|
uint32_t nof_frames);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_celldetect_set_threshold(ue_celldetect_t *q,
|
||||||
|
float threshold);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SYNC_FRAME_
|
||||||
|
|
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UE_MIB_
|
||||||
|
#define UE_MIB_
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
*
|
||||||
|
* This object decodes the MIB from the PBCH of an LTE signal.
|
||||||
|
*
|
||||||
|
* The function ue_mib_decode() shall be called multiple times,
|
||||||
|
* each passing a number of samples multiple of 19200, sampled at 1.92 MHz
|
||||||
|
* (that is, 10 ms of samples).
|
||||||
|
*
|
||||||
|
* The function uses the sync_t object to find the PSS sequence and
|
||||||
|
* decode the PBCH to obtain the MIB.
|
||||||
|
*
|
||||||
|
* The function returns 0 until the MIB is decoded.
|
||||||
|
*
|
||||||
|
* See ue_cell_detect.c for an example.
|
||||||
|
*
|
||||||
|
************************************************************/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/sync/sync.h"
|
||||||
|
#include "liblte/phy/sync/cfo.h"
|
||||||
|
#include "liblte/phy/ch_estimation/chest.h"
|
||||||
|
#include "liblte/phy/phch/pbch.h"
|
||||||
|
#include "liblte/phy/common/fft.h"
|
||||||
|
|
||||||
|
#define MIB_FIND_THRESHOLD 0.6
|
||||||
|
|
||||||
|
#define MIB_NOF_PORTS 2
|
||||||
|
|
||||||
|
#define MIB_FRAME_SIZE 9600
|
||||||
|
|
||||||
|
#define MIB_FRAME_UNALIGNED -3
|
||||||
|
#define MIB_FOUND 1
|
||||||
|
#define MIB_NOTFOUND 0
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
sync_t sfind;
|
||||||
|
|
||||||
|
uint32_t cell_id;
|
||||||
|
|
||||||
|
cf_t *slot1_symbols;
|
||||||
|
cf_t *ce[MIB_NOF_PORTS];
|
||||||
|
|
||||||
|
lte_fft_t fft;
|
||||||
|
chest_t chest;
|
||||||
|
pbch_t pbch;
|
||||||
|
|
||||||
|
uint32_t frame_cnt;
|
||||||
|
uint32_t last_frame_trial;
|
||||||
|
} ue_mib_t;
|
||||||
|
|
||||||
|
|
||||||
|
LIBLTE_API int ue_mib_init(ue_mib_t *q,
|
||||||
|
uint32_t cell_id,
|
||||||
|
lte_cp_t cp);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_mib_free(ue_mib_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_mib_reset(ue_mib_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int ue_mib_decode(ue_mib_t *q,
|
||||||
|
cf_t *signal,
|
||||||
|
uint32_t nsamples,
|
||||||
|
pbch_mib_t *mib);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_mib_set_threshold(ue_mib_t *q,
|
||||||
|
float threshold);
|
||||||
|
|
||||||
|
LIBLTE_API void ue_mib_reset(ue_mib_t *q);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SYNC_FRAME_
|
||||||
|
|
@ -0,0 +1,229 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
int nof_frames = 10;
|
||||||
|
int num_bits = 1000;
|
||||||
|
lte_mod_t modulation = 0;
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [nfv] -m modulation (1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", prog);
|
||||||
|
printf("\t-n num_bits [Default %d]\n", num_bits);
|
||||||
|
printf("\t-f nof_frames [Default %d]\n", nof_frames);
|
||||||
|
printf("\t-v verbose [Default None]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "nmvf")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'n':
|
||||||
|
num_bits = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
nof_frames = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
switch(atoi(argv[optind])) {
|
||||||
|
case 1:
|
||||||
|
modulation = LTE_BPSK;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
modulation = LTE_QPSK;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
modulation = LTE_QAM16;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
modulation = LTE_QAM64;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Invalid modulation %d. Possible values: "
|
||||||
|
"(1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", atoi(argv[optind]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modulation == 0) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float mse_threshold() {
|
||||||
|
switch(modulation) {
|
||||||
|
case LTE_BPSK:
|
||||||
|
return 1.0e-6;
|
||||||
|
case LTE_QPSK:
|
||||||
|
return 1.0e-6;
|
||||||
|
case LTE_QAM16:
|
||||||
|
return 0.11;
|
||||||
|
case LTE_QAM64:
|
||||||
|
return 0.18;
|
||||||
|
default:
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int i;
|
||||||
|
modem_table_t mod;
|
||||||
|
demod_soft_t demod_soft;
|
||||||
|
char *input, *output;
|
||||||
|
cf_t *symbols;
|
||||||
|
float *llr_exact, *llr_approx;
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
/* initialize objects */
|
||||||
|
if (modem_table_lte(&mod, modulation, true)) {
|
||||||
|
fprintf(stderr, "Error initializing modem table\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that num_bits is multiple of num_bits x symbol */
|
||||||
|
num_bits = mod.nbits_x_symbol * (num_bits / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
demod_soft_init(&demod_soft);
|
||||||
|
demod_soft_table_set(&demod_soft, &mod);
|
||||||
|
demod_soft_sigma_set(&demod_soft, 2.0 / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
/* allocate buffers */
|
||||||
|
input = malloc(sizeof(char) * num_bits);
|
||||||
|
if (!input) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
output = malloc(sizeof(char) * num_bits);
|
||||||
|
if (!output) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
symbols = malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol);
|
||||||
|
if (!symbols) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
llr_exact = malloc(sizeof(float) * num_bits);
|
||||||
|
if (!llr_exact) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
llr_approx = malloc(sizeof(float) * num_bits);
|
||||||
|
if (!llr_approx) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generate random data */
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
|
double mse;
|
||||||
|
struct timeval t[3];
|
||||||
|
float mean_texec = 0.0;
|
||||||
|
for (int n=0;n<nof_frames;n++) {
|
||||||
|
for (i=0;i<num_bits;i++) {
|
||||||
|
input[i] = rand()%2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* modulate */
|
||||||
|
mod_modulate(&mod, input, symbols, num_bits);
|
||||||
|
|
||||||
|
/* add noise */
|
||||||
|
ch_awgn_c(symbols, symbols, ch_awgn_get_variance(5.0, mod.nbits_x_symbol), num_bits / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
/* Compare exact with approximation algorithms */
|
||||||
|
demod_soft_alg_set(&demod_soft, EXACT);
|
||||||
|
demod_soft_demodulate(&demod_soft, symbols, llr_exact, num_bits / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
demod_soft_alg_set(&demod_soft, APPROX);
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
demod_soft_demodulate(&demod_soft, symbols, llr_approx, num_bits / mod.nbits_x_symbol);
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
|
||||||
|
/* compute exponentially averaged execution time */
|
||||||
|
mean_texec = EXPAVERAGE((float) t[0].tv_usec, mean_texec, n);
|
||||||
|
|
||||||
|
/* check MSE */
|
||||||
|
mse = 0.0;
|
||||||
|
for (i=0;i<num_bits;i++) {
|
||||||
|
float e = llr_exact[i] - llr_approx[i];
|
||||||
|
mse += e*e;
|
||||||
|
}
|
||||||
|
mse/=num_bits;
|
||||||
|
|
||||||
|
if (VERBOSE_ISDEBUG()) {
|
||||||
|
printf("exact=");
|
||||||
|
vec_fprint_f(stdout, llr_exact, num_bits);
|
||||||
|
|
||||||
|
printf("approx=");
|
||||||
|
vec_fprint_f(stdout, llr_approx, num_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mse > mse_threshold()) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
free(llr_exact);
|
||||||
|
free(llr_approx);
|
||||||
|
free(symbols);
|
||||||
|
free(output);
|
||||||
|
free(input);
|
||||||
|
|
||||||
|
modem_table_free(&mod);
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
printf("Ok Mean Throughput: %.2f. Mbps ExTime: %.2f us\n", num_bits/mean_texec, mean_texec);
|
||||||
|
} else {
|
||||||
|
printf("Error: MSE too large (%f > %f)\n", mse, mse_threshold());
|
||||||
|
}
|
||||||
|
exit(ret);
|
||||||
|
}
|
@ -1,628 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE 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 Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "liblte/phy/phch/ue_sync.h"
|
|
||||||
|
|
||||||
#include "liblte/phy/utils/debug.h"
|
|
||||||
#include "liblte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
#define MAX_TIME_OFFSET 128
|
|
||||||
cf_t dummy[MAX_TIME_OFFSET];
|
|
||||||
|
|
||||||
#define EXPAVERAGE(data, average, nframes) ((data + average * nframes) / (nframes + 1))
|
|
||||||
|
|
||||||
#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb)
|
|
||||||
#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE, q->cell.cp)
|
|
||||||
|
|
||||||
#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)
|
|
||||||
#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp)
|
|
||||||
|
|
||||||
#define MAXIMUM_SFLEN SF_LEN(2048, CPNORM)
|
|
||||||
#define MAXIMUM_SFLEN_RE SF_LEN_RE(110, CPNORM)
|
|
||||||
|
|
||||||
static int mib_decoder_initialize(ue_sync_t *q);
|
|
||||||
static void mib_decoder_free(ue_sync_t *q);
|
|
||||||
|
|
||||||
|
|
||||||
static void update_threshold(ue_sync_t *q) {
|
|
||||||
int symbol_sz = lte_symbol_sz(q->cell.nof_prb);
|
|
||||||
if (symbol_sz > 0) {
|
|
||||||
switch (symbol_sz) {
|
|
||||||
case 128:
|
|
||||||
sync_set_threshold(&q->s, 20000, 2000);
|
|
||||||
break;
|
|
||||||
case 256:
|
|
||||||
sync_set_threshold(&q->s, 25000, 2500);
|
|
||||||
break;
|
|
||||||
case 512:
|
|
||||||
sync_set_threshold(&q->s, 38000, 3800);
|
|
||||||
break;
|
|
||||||
case 1024:
|
|
||||||
sync_set_threshold(&q->s, 50000, 5000);
|
|
||||||
break;
|
|
||||||
case 2048:
|
|
||||||
sync_set_threshold(&q->s, 80000, 4000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ue_sync_init(ue_sync_t *q,
|
|
||||||
double (set_rate_callback)(void*, double),
|
|
||||||
int (recv_callback)(void*, void*, uint32_t),
|
|
||||||
void *stream_handler)
|
|
||||||
{
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
stream_handler != NULL &&
|
|
||||||
set_rate_callback != NULL &&
|
|
||||||
recv_callback != NULL)
|
|
||||||
{
|
|
||||||
ret = LIBLTE_ERROR;
|
|
||||||
|
|
||||||
bzero(q, sizeof(ue_sync_t));
|
|
||||||
|
|
||||||
ue_sync_reset(q);
|
|
||||||
|
|
||||||
q->cell.nof_prb = SYNC_PBCH_NOF_PRB;
|
|
||||||
q->cell.nof_ports = SYNC_PBCH_NOF_PORTS;
|
|
||||||
q->cell.id = 0;
|
|
||||||
q->cell.cp = CPNORM;
|
|
||||||
|
|
||||||
q->pbch_decoded = false;
|
|
||||||
q->pbch_initialized = false;
|
|
||||||
q->pbch_decoder_enabled = true;
|
|
||||||
q->pbch_decode_always = false;
|
|
||||||
q->decode_sss_on_track = false;
|
|
||||||
q->change_srate = true;
|
|
||||||
q->nof_mib_decodes = DEFAULT_NOF_MIB_DECODES;
|
|
||||||
q->stream = stream_handler;
|
|
||||||
q->recv_callback = recv_callback;
|
|
||||||
q->set_rate_callback = set_rate_callback;
|
|
||||||
|
|
||||||
INFO("Setting sampling frequency 1.92 MHz\n",0);
|
|
||||||
q->set_rate_callback(q->stream, 1920000.0);
|
|
||||||
|
|
||||||
if (agc_init(&q->agc)) {
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sync_init(&q->s, CURRENT_SFLEN, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) {
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
sync_pss_det_absolute(&q->s);
|
|
||||||
|
|
||||||
if (cfo_init(&q->cfocorr, MAXIMUM_SFLEN)) {
|
|
||||||
fprintf(stderr, "Error initiating CFO\n");
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->input_buffer = vec_malloc(3 * MAXIMUM_SFLEN * sizeof(cf_t));
|
|
||||||
if (!q->input_buffer) {
|
|
||||||
perror("malloc");
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->receive_buffer = vec_malloc(3 * MAXIMUM_SFLEN * sizeof(cf_t));
|
|
||||||
if (!q->receive_buffer) {
|
|
||||||
perror("malloc");
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
q->sf_symbols = vec_malloc(MAXIMUM_SFLEN_RE * sizeof(cf_t));
|
|
||||||
if (!q->sf_symbols) {
|
|
||||||
perror("malloc");
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
for (int i=0;i<SYNC_PBCH_NOF_PORTS;i++) {
|
|
||||||
q->ce[i] = vec_malloc(MAXIMUM_SFLEN_RE * sizeof(cf_t));
|
|
||||||
if (!q->ce[i]) {
|
|
||||||
perror("malloc");
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_threshold(q);
|
|
||||||
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
clean_exit:
|
|
||||||
if (ret == LIBLTE_ERROR) {
|
|
||||||
ue_sync_free(q);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_free(ue_sync_t *q) {
|
|
||||||
if (q->input_buffer) {
|
|
||||||
free(q->input_buffer);
|
|
||||||
}
|
|
||||||
if (q->receive_buffer) {
|
|
||||||
free(q->receive_buffer);
|
|
||||||
}
|
|
||||||
if (q->sf_symbols) {
|
|
||||||
free(q->sf_symbols);
|
|
||||||
}
|
|
||||||
for (int i=0;i<SYNC_PBCH_NOF_PORTS;i++) {
|
|
||||||
if (q->ce[i]) {
|
|
||||||
free(q->ce[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mib_decoder_free(q);
|
|
||||||
cfo_free(&q->cfocorr);
|
|
||||||
sync_free(&q->s);
|
|
||||||
agc_free(&q->agc);
|
|
||||||
}
|
|
||||||
|
|
||||||
lte_cell_t ue_sync_get_cell(ue_sync_t *q) {
|
|
||||||
return q->cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
pbch_mib_t ue_sync_get_mib(ue_sync_t *q) {
|
|
||||||
return q->mib;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ue_sync_peak_idx(ue_sync_t *q) {
|
|
||||||
return q->peak_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
ue_sync_state_t ue_sync_get_state(ue_sync_t *q) {
|
|
||||||
return q->state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_change_srate(ue_sync_t *q, bool enabled) {
|
|
||||||
q->change_srate = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int update_srate(ue_sync_t *q) {
|
|
||||||
struct timeval t[3];
|
|
||||||
|
|
||||||
gettimeofday(&t[1], NULL);
|
|
||||||
if (sync_realloc(&q->s, CURRENT_SFLEN, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) {
|
|
||||||
fprintf(stderr, "Error realloc'ing SYNC\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
gettimeofday(&t[2], NULL);
|
|
||||||
get_time_interval(t);
|
|
||||||
|
|
||||||
if (q->nof_mib_decodes > 1) {
|
|
||||||
mib_decoder_free(q);
|
|
||||||
if (mib_decoder_initialize(q)) {
|
|
||||||
fprintf(stderr, "Error reinitializing MIB decoder\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally set the new sampling rate
|
|
||||||
q->set_rate_callback(q->stream, (float) lte_sampling_freq_hz(q->cell.nof_prb));
|
|
||||||
|
|
||||||
update_threshold(q);
|
|
||||||
|
|
||||||
ue_sync_reset(q);
|
|
||||||
INFO("Set sampling rate %.2f MHz, fft_size=%d, sf_len=%d Threshold=%.2f/%.2f Texec=%d us\n",
|
|
||||||
(float) lte_sampling_freq_hz(q->cell.nof_prb)/1000000,
|
|
||||||
CURRENT_FFTSIZE, CURRENT_SFLEN, q->s.find_threshold, q->s.track_threshold, (int) t[0].tv_usec);
|
|
||||||
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ue_sync_get_sfidx(ue_sync_t *q) {
|
|
||||||
return q->sf_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ue_sync_get_cfo(ue_sync_t *q) {
|
|
||||||
return 15000 * q->cur_cfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ue_sync_get_sfo(ue_sync_t *q) {
|
|
||||||
return 1000*q->mean_time_offset/5;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ue_sync_is_mib_decoded(ue_sync_t *q) {
|
|
||||||
return q->pbch_decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_pbch_enable(ue_sync_t *q, bool enabled) {
|
|
||||||
q->pbch_decoder_enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_pbch_always(ue_sync_t *q, bool enabled) {
|
|
||||||
q->pbch_decode_always = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) {
|
|
||||||
q->decode_sss_on_track = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_set_nof_pbch_decodes(ue_sync_t *q, uint32_t nof_pbch_decodes) {
|
|
||||||
q->nof_mib_decodes = nof_pbch_decodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mib_decoder_initialize(ue_sync_t *q) {
|
|
||||||
|
|
||||||
if (lte_fft_init(&q->fft, q->cell.cp, q->cell.nof_prb)) {
|
|
||||||
fprintf(stderr, "Error initializing FFT\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (chest_init_LTEDL(&q->chest, q->cell)) {
|
|
||||||
fprintf(stderr, "Error initializing reference signal\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (pbch_init(&q->pbch, q->cell)) {
|
|
||||||
fprintf(stderr, "Error initiating PBCH\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
q->pbch_initialized = 1;
|
|
||||||
DEBUG("PBCH initiated cell_id=%d\n", q->cell.id);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mib_decoder_free(ue_sync_t *q) {
|
|
||||||
chest_free(&q->chest);
|
|
||||||
pbch_free(&q->pbch);
|
|
||||||
lte_fft_free(&q->fft);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mib_decoder_run(ue_sync_t *q) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Run FFT for the second slot */
|
|
||||||
lte_fft_run_sf(&q->fft, q->input_buffer, q->sf_symbols);
|
|
||||||
|
|
||||||
/* Get channel estimates of slot #1 for each port */
|
|
||||||
chest_ce_sf(&q->chest, q->sf_symbols, q->ce, 0);
|
|
||||||
|
|
||||||
if (q->pbch_last_trial &&
|
|
||||||
(q->frame_total_cnt - q->pbch_last_trial > 2))
|
|
||||||
{
|
|
||||||
pbch_decode_reset(&q->pbch);
|
|
||||||
INFO("Resetting PBCH decoder: last trial %d, now is %d\n",
|
|
||||||
q->pbch_last_trial, q->frame_total_cnt);
|
|
||||||
q->pbch_last_trial = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pbch_decode(&q->pbch, q->sf_symbols, q->ce, &q->mib) == 1) {
|
|
||||||
q->frame_number = q->mib.sfn;
|
|
||||||
q->cell.nof_ports = q->mib.nof_ports;
|
|
||||||
q->cell.nof_prb = q->mib.nof_prb;
|
|
||||||
|
|
||||||
if (!q->pbch_decoded) {
|
|
||||||
printf("\n\nMIB decoded:\n");
|
|
||||||
pbch_mib_fprint(stdout, &q->mib, q->cell.id);
|
|
||||||
if (q->change_srate) {
|
|
||||||
ret = update_srate(q);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
INFO("MIB decoded #%d SFN: %d\n", q->pbch_decoded, q->mib.sfn);
|
|
||||||
}
|
|
||||||
q->pbch_decoded++;
|
|
||||||
|
|
||||||
pbch_decode_reset(&q->pbch);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
INFO("MIB not decoded: %d\n", q->frame_total_cnt/2);
|
|
||||||
q->pbch_last_trial = q->frame_total_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_peak_ok(ue_sync_t *q) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (q->peak_idx < CURRENT_SFLEN) {
|
|
||||||
/* Receive the rest of the next subframe */
|
|
||||||
if (q->recv_callback(q->stream, &q->input_buffer[CURRENT_SFLEN], q->peak_idx+CURRENT_SFLEN/2) < 0) {
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync_sss_detected(&q->s)) {
|
|
||||||
ret = sync_get_cell_id(&q->s);
|
|
||||||
if (ret >= 0) {
|
|
||||||
q->cell.id = (uint32_t) ret;
|
|
||||||
q->cell.cp = sync_get_cp(&q->s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the subframe index (0 or 5) */
|
|
||||||
q->sf_idx = sync_get_slot_id(&q->s)/2;
|
|
||||||
|
|
||||||
/* Reset variables */
|
|
||||||
q->frame_ok_cnt = 0;
|
|
||||||
q->frame_no_cnt = 0;
|
|
||||||
q->frame_total_cnt = 0;
|
|
||||||
|
|
||||||
/* Goto Tracking state */
|
|
||||||
q->state = SF_TRACK;
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
|
|
||||||
INFO("Found peak at %d, value %.3f, SF_idx: %d, Cell_id: %d CP: %s\n",
|
|
||||||
q->peak_idx, sync_get_peak_value(&q->s), q->sf_idx, q->cell.id, lte_cp_string(q->cell.cp));
|
|
||||||
|
|
||||||
if (q->peak_idx < CURRENT_SFLEN) {
|
|
||||||
q->sf_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
INFO("Found peak at %d, SSS not detected\n", q->peak_idx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int track_peak_ok(ue_sync_t *q, uint32_t track_idx) {
|
|
||||||
int ret = LIBLTE_SUCCESS;
|
|
||||||
|
|
||||||
/* Make sure subframe idx is what we expect */
|
|
||||||
if ((q->sf_idx != sync_get_slot_id(&q->s)/2) && q->decode_sss_on_track) {
|
|
||||||
INFO("\nWarning: Expected SF idx %d but got %d!\n",
|
|
||||||
q->sf_idx, sync_get_slot_id(&q->s)/2);
|
|
||||||
q->sf_idx = sync_get_slot_id(&q->s)/2;
|
|
||||||
} else {
|
|
||||||
q->time_offset = ((int) track_idx - (int) CURRENT_FFTSIZE);
|
|
||||||
|
|
||||||
/* If the PSS peak is beyond the frame (we sample too slowly),
|
|
||||||
discard the offseted samples to align next frame */
|
|
||||||
if (q->time_offset > 0 && q->time_offset < MAX_TIME_OFFSET) {
|
|
||||||
ret = q->recv_callback(q->stream, dummy, (uint32_t) q->time_offset);
|
|
||||||
} else {
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* compute cumulative moving average CFO */
|
|
||||||
q->cur_cfo = EXPAVERAGE(sync_get_cfo(&q->s), q->cur_cfo, q->frame_ok_cnt);
|
|
||||||
|
|
||||||
/* compute cumulative moving average time offset */
|
|
||||||
q->mean_time_offset = (float) EXPAVERAGE((float) q->time_offset, q->mean_time_offset, q->frame_ok_cnt);
|
|
||||||
|
|
||||||
q->peak_idx = CURRENT_SFLEN/2 + q->time_offset;
|
|
||||||
q->frame_ok_cnt++;
|
|
||||||
q->frame_no_cnt = 0;
|
|
||||||
|
|
||||||
|
|
||||||
if (ret >= LIBLTE_SUCCESS) {
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int track_peak_no(ue_sync_t *q) {
|
|
||||||
|
|
||||||
/* if we missed too many PSS go back to FIND */
|
|
||||||
q->frame_no_cnt++;
|
|
||||||
if (q->frame_no_cnt >= TRACK_MAX_LOST) {
|
|
||||||
printf("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt);
|
|
||||||
q->state = SF_FIND;
|
|
||||||
} else {
|
|
||||||
INFO("Tracking peak not found. Peak %.3f, %d lost\n", sync_get_peak_value(&q->s), (int) q->frame_no_cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int receive_samples(ue_sync_t *q) {
|
|
||||||
|
|
||||||
if (q->cell.nof_prb >= 6 && q->cell.nof_prb <= 100) {
|
|
||||||
/* A negative time offset means there are samples in our buffer for the next subframe,
|
|
||||||
because we are sampling too fast.
|
|
||||||
*/
|
|
||||||
if (q->time_offset < 0) {
|
|
||||||
q->time_offset = -q->time_offset;
|
|
||||||
}
|
|
||||||
/* copy last part of the last subframe (use move since there could be overlapping) */
|
|
||||||
memcpy(q->receive_buffer, &q->input_buffer[CURRENT_SFLEN-q->time_offset], q->time_offset*sizeof(cf_t));
|
|
||||||
|
|
||||||
/* Get 1 subframe from the USRP getting more samples and keeping the previous samples, if any */
|
|
||||||
if (q->recv_callback(q->stream, &q->receive_buffer[q->time_offset], CURRENT_SFLEN - q->time_offset) < 0) {
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset time offset */
|
|
||||||
q->time_offset = 0;
|
|
||||||
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
} else {
|
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ue_sync_get_buffer(ue_sync_t *q, cf_t **sf_symbols) {
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
|
||||||
uint32_t track_idx;
|
|
||||||
struct timeval t[3];
|
|
||||||
|
|
||||||
if (q != NULL &&
|
|
||||||
sf_symbols != NULL &&
|
|
||||||
q->input_buffer != NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (receive_samples(q)) {
|
|
||||||
fprintf(stderr, "Error receiving samples\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
agc_process(&q->agc, q->receive_buffer, q->input_buffer, CURRENT_SFLEN);
|
|
||||||
|
|
||||||
switch (q->state) {
|
|
||||||
case SF_AGC:
|
|
||||||
q->frame_total_cnt++;
|
|
||||||
if (q->frame_total_cnt >= AGC_NOF_FRAMES) {
|
|
||||||
q->state = SF_FIND;
|
|
||||||
q->frame_total_cnt = 0;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case SF_FIND:
|
|
||||||
q->s.sss_en = true;
|
|
||||||
|
|
||||||
/* Find peak and cell id */
|
|
||||||
ret = sync_find(&q->s, q->input_buffer, &q->peak_idx);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("Find PAR=%.2f\n", sync_get_peak_value(&q->s));
|
|
||||||
|
|
||||||
if (ret == 1) {
|
|
||||||
ret = find_peak_ok(q);
|
|
||||||
/* Initialize PBCH decoder */
|
|
||||||
if (ret == LIBLTE_SUCCESS) {
|
|
||||||
if (!q->pbch_initialized && q->pbch_decoder_enabled) {
|
|
||||||
ret = mib_decoder_initialize(q);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error initializing MIB decoder\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (ret < 0) {
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error processing find peak \n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (q->peak_idx != 0) {
|
|
||||||
uint32_t rlen;
|
|
||||||
if (q->peak_idx < CURRENT_SFLEN/2) {
|
|
||||||
rlen = CURRENT_SFLEN/2-q->peak_idx;
|
|
||||||
} else {
|
|
||||||
rlen = q->peak_idx;
|
|
||||||
}
|
|
||||||
if (q->recv_callback(q->stream, q->receive_buffer, rlen) < 0) {
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SF_TRACK:
|
|
||||||
ret = LIBLTE_SUCCESS;
|
|
||||||
|
|
||||||
q->s.sss_en = q->decode_sss_on_track;
|
|
||||||
|
|
||||||
q->sf_idx = (q->sf_idx + 1) % 10;
|
|
||||||
|
|
||||||
DEBUG("TRACK: SF=%d FrameCNT: %d\n", q->sf_idx, q->frame_total_cnt);
|
|
||||||
|
|
||||||
/* Every SF idx 0 and 5, find peak around known position q->peak_idx */
|
|
||||||
if (q->sf_idx == 0 || q->sf_idx == 5) {
|
|
||||||
|
|
||||||
#ifdef MEASURE_EXEC_TIME
|
|
||||||
gettimeofday(&t[1], NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
track_idx = 0;
|
|
||||||
|
|
||||||
/* track pss around the middle of the subframe, where the PSS is */
|
|
||||||
ret = sync_track(&q->s, q->input_buffer, CURRENT_SFLEN/2-CURRENT_FFTSIZE, &track_idx);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error tracking correlation peak\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MEASURE_EXEC_TIME
|
|
||||||
gettimeofday(&t[2], NULL);
|
|
||||||
get_time_interval(t);
|
|
||||||
q->mean_exec_time = (float) EXPAVERAGE((float) t[0].tv_usec, q->mean_exec_time, q->frame_total_cnt);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret == 1) {
|
|
||||||
ret = track_peak_ok(q, track_idx);
|
|
||||||
} else {
|
|
||||||
ret = track_peak_no(q);
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO("TRACK %3d: Value=%.3f SF=%d Track_idx=%d Offset=%d CFO: %f\n",
|
|
||||||
(int) q->frame_total_cnt, sync_get_peak_value(&q->s), q->sf_idx, track_idx, q->time_offset, sync_get_cfo(&q->s));
|
|
||||||
|
|
||||||
q->frame_total_cnt++;
|
|
||||||
|
|
||||||
if (ret == LIBLTE_ERROR) {
|
|
||||||
fprintf(stderr, "Error processing tracking peak\n");
|
|
||||||
q->state = SF_FIND;
|
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do CFO Correction and deliver the frame */
|
|
||||||
cfo_correct(&q->cfocorr, q->input_buffer, q->input_buffer, -q->cur_cfo / CURRENT_FFTSIZE);
|
|
||||||
*sf_symbols = q->input_buffer;
|
|
||||||
|
|
||||||
/* At subframe 0, try to decode PBCH if not yet decoded */
|
|
||||||
if (q->sf_idx == 0) {
|
|
||||||
if(q->pbch_decoder_enabled &&
|
|
||||||
(q->pbch_decoded < q->nof_mib_decodes || q->pbch_decode_always))
|
|
||||||
{
|
|
||||||
mib_decoder_run(q);
|
|
||||||
} else {
|
|
||||||
q->mib.sfn = (q->mib.sfn + 1) % 1024;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == LIBLTE_SUCCESS) {
|
|
||||||
if (q->pbch_decoder_enabled) {
|
|
||||||
if (q->pbch_decoded >= q->nof_mib_decodes) {
|
|
||||||
ret = 1;
|
|
||||||
} else {
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUG("UE SYNC returns %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ue_sync_reset(ue_sync_t *q) {
|
|
||||||
q->state = SF_AGC;
|
|
||||||
|
|
||||||
q->pbch_last_trial = 0;
|
|
||||||
q->frame_ok_cnt = 0;
|
|
||||||
q->frame_no_cnt = 0;
|
|
||||||
q->frame_total_cnt = 0;
|
|
||||||
q->cur_cfo = 0;
|
|
||||||
q->mean_time_offset = 0;
|
|
||||||
q->time_offset = 0;
|
|
||||||
#ifdef MEASURE_EXEC_TIME
|
|
||||||
q->mean_exec_time = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pbch_decode_reset(&q->pbch);
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,208 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
|
||||||
|
uint32_t N_id_2 = 100;
|
||||||
|
char *uhd_args="";
|
||||||
|
float uhd_gain=40.0, uhd_freq=-1.0;
|
||||||
|
int nof_frames = -1;
|
||||||
|
uint32_t fft_size=64;
|
||||||
|
float threshold = 0.4;
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [agtvnp] -f rx_frequency_hz -i N_id_2\n", prog);
|
||||||
|
printf("\t-a UHD args [Default %s]\n", uhd_args);
|
||||||
|
printf("\t-g UHD Gain [Default %.2f dB]\n", uhd_gain);
|
||||||
|
printf("\t-n nof_frames [Default %d]\n", nof_frames);
|
||||||
|
printf("\t-s symbol_sz [Default %d]\n", fft_size);
|
||||||
|
printf("\t-t threshold [Default %.2f]\n", threshold);
|
||||||
|
printf("\t-v verbose\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "agtvsfi")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'a':
|
||||||
|
uhd_args = argv[optind];
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
uhd_gain = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
uhd_freq = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
threshold = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
N_id_2 = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
fft_size = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
nof_frames = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (N_id_2 > 2 || uhd_freq < 0) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
cf_t *buffer;
|
||||||
|
int frame_cnt, n;
|
||||||
|
void *uhd;
|
||||||
|
pss_synch_t pss;
|
||||||
|
int32_t flen;
|
||||||
|
int peak_idx, last_peak;
|
||||||
|
float peak_value;
|
||||||
|
float mean_peak;
|
||||||
|
uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet;
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
flen = 4800*(fft_size/64);
|
||||||
|
|
||||||
|
buffer = malloc(sizeof(cf_t) * flen);
|
||||||
|
if (!buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pss_synch_init_fft(&pss, flen, fft_size)) {
|
||||||
|
fprintf(stderr, "Error initiating PSS\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (pss_synch_set_N_id_2(&pss, N_id_2)) {
|
||||||
|
fprintf(stderr, "Error setting N_id_2=%d\n",N_id_2);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Opening UHD device...\n");
|
||||||
|
if (cuhd_open(uhd_args, &uhd)) {
|
||||||
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
printf("Set RX rate: %.2f MHz\n", cuhd_set_rx_srate(uhd, flen*2*100) / 1000000);
|
||||||
|
printf("Set RX gain: %.1f dB\n", cuhd_set_rx_gain(uhd, uhd_gain));
|
||||||
|
printf("Set RX freq: %.2f MHz\n", cuhd_set_rx_freq(uhd, uhd_freq) / 1000000);
|
||||||
|
cuhd_rx_wait_lo_locked(uhd);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
printf("Frame length %d samples\n", flen);
|
||||||
|
printf("PSS detection threshold: %.2f\n", threshold);
|
||||||
|
|
||||||
|
nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0;
|
||||||
|
frame_cnt = 0;
|
||||||
|
last_peak = 0;
|
||||||
|
mean_peak = 0;
|
||||||
|
while(frame_cnt < nof_frames || nof_frames == -1) {
|
||||||
|
n = cuhd_recv(uhd, buffer, flen, 1);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error receiving samples\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
peak_idx = pss_synch_find_pss(&pss, buffer, &peak_value);
|
||||||
|
if (peak_idx < 0) {
|
||||||
|
fprintf(stderr, "Error finding PSS peak\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float y = sqrtf(crealf(vec_dot_prod_conj_ccc(&buffer[peak_idx-fft_size],
|
||||||
|
&buffer[peak_idx-fft_size],
|
||||||
|
fft_size)) /
|
||||||
|
fft_size);
|
||||||
|
float x = peak_value/y;
|
||||||
|
|
||||||
|
mean_peak = EXPAVERAGE(x, mean_peak, frame_cnt);
|
||||||
|
|
||||||
|
if (x >= threshold) {
|
||||||
|
nof_det++;
|
||||||
|
} else {
|
||||||
|
nof_nodet++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_cnt > 100) {
|
||||||
|
if (abs(last_peak-peak_idx) > 10) {
|
||||||
|
if (x >= threshold) {
|
||||||
|
nof_nopeakdet++;
|
||||||
|
} else {
|
||||||
|
if (nof_nodet > 0) {
|
||||||
|
nof_nodet--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nof_nopeak++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_cnt++;
|
||||||
|
|
||||||
|
printf("[%5d]: Pos: %5d, En: %.4f Val: %.3f MeanVal: %.3f, Det: %.3f, No-Det: %.3f, NoPeak: %.3f, NoPeakDet: %.3f\r",
|
||||||
|
frame_cnt,
|
||||||
|
peak_idx, y, x, mean_peak,
|
||||||
|
(float) nof_det/frame_cnt, (float) nof_nodet/frame_cnt,
|
||||||
|
(float) nof_nopeak/frame_cnt, (float) nof_nopeakdet/nof_nopeak);
|
||||||
|
|
||||||
|
if (VERBOSE_ISINFO()) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
last_peak = peak_idx;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pss_synch_free(&pss);
|
||||||
|
free(buffer);
|
||||||
|
cuhd_close(uhd);
|
||||||
|
|
||||||
|
printf("Ok\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,268 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/ue/ue_celldetect.h"
|
||||||
|
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
|
||||||
|
#define FIND_FFTSIZE 64
|
||||||
|
#define FIND_SFLEN 5*SF_LEN(FIND_FFTSIZE)
|
||||||
|
|
||||||
|
int ue_celldetect_init(ue_celldetect_t * q) {
|
||||||
|
return ue_celldetect_init_max(q, CS_DEFAULT_MAXFRAMES_TOTAL, CS_DEFAULT_MAXFRAMES_DETECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ue_celldetect_init_max(ue_celldetect_t * q, uint32_t max_frames_total, uint32_t max_frames_detected) {
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
|
if (q != NULL) {
|
||||||
|
ret = LIBLTE_ERROR;
|
||||||
|
|
||||||
|
bzero(q, sizeof(ue_celldetect_t));
|
||||||
|
|
||||||
|
q->candidates = malloc(sizeof(ue_celldetect_result_t) * max_frames_detected);
|
||||||
|
if (!q->candidates) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
if (sync_init(&q->sfind, FIND_SFLEN, FIND_FFTSIZE)) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
q->mode_ntimes = malloc(sizeof(uint32_t) * max_frames_detected);
|
||||||
|
if (!q->mode_ntimes) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
q->mode_counted = malloc(sizeof(char) * max_frames_detected);
|
||||||
|
if (!q->mode_counted) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_set_threshold(&q->sfind, CS_FIND_THRESHOLD);
|
||||||
|
sync_sss_en(&q->sfind, true);
|
||||||
|
|
||||||
|
q->max_frames_total = max_frames_total;
|
||||||
|
q->max_frames_detected = max_frames_detected;
|
||||||
|
q->nof_frames_total = CS_DEFAULT_NOFFRAMES_TOTAL;
|
||||||
|
q->nof_frames_detected = CS_DEFAULT_NOFFRAMES_DETECTED;
|
||||||
|
|
||||||
|
ue_celldetect_reset(q);
|
||||||
|
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
if (ret == LIBLTE_ERROR) {
|
||||||
|
ue_celldetect_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_celldetect_free(ue_celldetect_t * q)
|
||||||
|
{
|
||||||
|
if (q->candidates) {
|
||||||
|
free(q->candidates);
|
||||||
|
}
|
||||||
|
if (q->mode_counted) {
|
||||||
|
free(q->mode_counted);
|
||||||
|
}
|
||||||
|
if (q->mode_ntimes) {
|
||||||
|
free(q->mode_ntimes);
|
||||||
|
}
|
||||||
|
sync_free(&q->sfind);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ue_celldetect_reset(ue_celldetect_t * q)
|
||||||
|
{
|
||||||
|
q->current_nof_detected = 0;
|
||||||
|
q->current_nof_total = 0;
|
||||||
|
q->current_N_id_2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_celldetect_set_threshold(ue_celldetect_t * q, float threshold)
|
||||||
|
{
|
||||||
|
sync_set_threshold(&q->sfind, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ue_celldetect_set_nof_frames_total(ue_celldetect_t * q, uint32_t nof_frames)
|
||||||
|
{
|
||||||
|
if (nof_frames <= q->max_frames_total) {
|
||||||
|
q->nof_frames_total = nof_frames;
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ue_celldetect_set_nof_frames_detected(ue_celldetect_t * q, uint32_t nof_frames)
|
||||||
|
{
|
||||||
|
if (nof_frames <= q->max_frames_detected) {
|
||||||
|
q->nof_frames_detected = nof_frames;
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decide the most likely cell based on the mode */
|
||||||
|
void decide_cell(ue_celldetect_t * q, ue_celldetect_result_t *found_cell)
|
||||||
|
{
|
||||||
|
uint32_t i, j;
|
||||||
|
|
||||||
|
bzero(q->mode_counted, q->nof_frames_detected);
|
||||||
|
bzero(q->mode_ntimes, sizeof(uint32_t) * q->nof_frames_detected);
|
||||||
|
|
||||||
|
/* First find mode of CELL IDs */
|
||||||
|
for (i = 0; i < q->nof_frames_detected; i++) {
|
||||||
|
uint32_t cnt = 1;
|
||||||
|
for (j=i+1;j<q->nof_frames_detected;j++) {
|
||||||
|
if (q->candidates[j].cell_id == q->candidates[i].cell_id && !q->mode_counted[j]) {
|
||||||
|
q->mode_counted[j]=1;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q->mode_ntimes[i] = cnt;
|
||||||
|
}
|
||||||
|
uint32_t max_times=0, mode_pos=0;
|
||||||
|
for (i=0;i<q->nof_frames_detected;i++) {
|
||||||
|
if (q->mode_ntimes[i] > 0) {
|
||||||
|
DEBUG("ntimes[%d]=%d (CID: %d)\n",i,q->mode_ntimes[i],q->candidates[i].cell_id);
|
||||||
|
}
|
||||||
|
if (q->mode_ntimes[i] > max_times) {
|
||||||
|
max_times = q->mode_ntimes[i];
|
||||||
|
mode_pos = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found_cell->cell_id = q->candidates[mode_pos].cell_id;
|
||||||
|
/* Now in all these cell IDs, find most frequent CP */
|
||||||
|
uint32_t nof_normal = 0;
|
||||||
|
found_cell->peak = 0;
|
||||||
|
for (i=0;i<q->nof_frames_detected;i++) {
|
||||||
|
if (q->candidates[i].cell_id == found_cell->cell_id) {
|
||||||
|
if (CP_ISNORM(q->candidates[i].cp)) {
|
||||||
|
nof_normal++;
|
||||||
|
}
|
||||||
|
found_cell->peak += q->candidates[i].peak/q->mode_ntimes[mode_pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nof_normal > q->mode_ntimes[mode_pos]/2) {
|
||||||
|
found_cell->cp = CPNORM;
|
||||||
|
} else {
|
||||||
|
found_cell->cp = CPEXT;
|
||||||
|
}
|
||||||
|
found_cell->mode = q->mode_ntimes[mode_pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ue_celldetect_scan(ue_celldetect_t * q,
|
||||||
|
cf_t *signal,
|
||||||
|
uint32_t nsamples,
|
||||||
|
ue_celldetect_result_t *found_cell)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
uint32_t peak_idx;
|
||||||
|
uint32_t nof_input_frames;
|
||||||
|
|
||||||
|
|
||||||
|
if (q != NULL &&
|
||||||
|
signal != NULL &&
|
||||||
|
nsamples >= 4800)
|
||||||
|
{
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
|
||||||
|
if (nsamples % 4800) {
|
||||||
|
printf("Warning: nsamples must be a multiple of 4800. Some samples will be ignored\n");
|
||||||
|
nsamples = (nsamples/4800) * 4800;
|
||||||
|
}
|
||||||
|
nof_input_frames = nsamples/4800;
|
||||||
|
|
||||||
|
for (uint32_t nf=0;nf<nof_input_frames;nf++) {
|
||||||
|
sync_set_N_id_2(&q->sfind, q->current_N_id_2);
|
||||||
|
|
||||||
|
DEBUG("[%3d/%3d]: Searching cells with N_id_2=%d. %d frames\n",
|
||||||
|
q->current_nof_detected, q->current_nof_total, q->current_N_id_2, nof_input_frames);
|
||||||
|
|
||||||
|
/* Find peak and cell id */
|
||||||
|
ret = sync_find(&q->sfind, &signal[nf*4800], 0, &peak_idx);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If peak position does not allow to read SSS, return error -3 */
|
||||||
|
if (ret == LIBLTE_SUCCESS && peak_idx != 0) {
|
||||||
|
return CS_FRAME_UNALIGNED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the peak result */
|
||||||
|
if (ret == 1) {
|
||||||
|
if (sync_sss_detected(&q->sfind)) {
|
||||||
|
ret = sync_get_cell_id(&q->sfind);
|
||||||
|
if (ret >= 0) {
|
||||||
|
/* Save cell id, cp and peak */
|
||||||
|
q->candidates[q->current_nof_detected].cell_id = (uint32_t) ret;
|
||||||
|
q->candidates[q->current_nof_detected].cp = sync_get_cp(&q->sfind);
|
||||||
|
q->candidates[q->current_nof_detected].peak = sync_get_last_peak_value(&q->sfind);
|
||||||
|
}
|
||||||
|
INFO
|
||||||
|
("[%3d/%3d]: Found peak at %4d, value %.3f, Cell_id: %d CP: %s\n",
|
||||||
|
q->current_nof_detected, q->current_nof_total, peak_idx,
|
||||||
|
q->candidates[q->current_nof_detected].peak, q->candidates[q->current_nof_detected].cell_id,
|
||||||
|
lte_cp_string(q->candidates[q->current_nof_detected].cp));
|
||||||
|
q->current_nof_detected++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q->current_nof_total++;
|
||||||
|
|
||||||
|
/* Decide cell ID and CP if we detected up to nof_frames_detected */
|
||||||
|
if (q->current_nof_detected == q->nof_frames_detected) {
|
||||||
|
decide_cell(q, found_cell);
|
||||||
|
q->current_N_id_2++;
|
||||||
|
q->current_nof_detected = q->current_nof_total = 0;
|
||||||
|
ret = CS_CELL_DETECTED;
|
||||||
|
/* Or go to the next N_id_2 if we didn't detect the cell */
|
||||||
|
} else if (q->current_nof_total == q->nof_frames_total) {
|
||||||
|
q->current_N_id_2++;
|
||||||
|
q->current_nof_detected = q->current_nof_total = 0;
|
||||||
|
ret = CS_CELL_NOT_DETECTED;
|
||||||
|
}
|
||||||
|
if (q->current_N_id_2 == 3) {
|
||||||
|
q->current_N_id_2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
@ -0,0 +1,225 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/ue/ue_mib.h"
|
||||||
|
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
|
||||||
|
#define FIND_FFTSIZE 128
|
||||||
|
#define FIND_SFLEN 10*SF_LEN(FIND_FFTSIZE)
|
||||||
|
|
||||||
|
int ue_mib_init(ue_mib_t * q,
|
||||||
|
uint32_t cell_id,
|
||||||
|
lte_cp_t cp)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
|
if (q != NULL) {
|
||||||
|
|
||||||
|
ret = LIBLTE_ERROR;
|
||||||
|
|
||||||
|
lte_cell_t cell;
|
||||||
|
cell.nof_ports = MIB_NOF_PORTS;
|
||||||
|
cell.nof_prb = 6;
|
||||||
|
cell.id = cell_id;
|
||||||
|
cell.cp = cp;
|
||||||
|
|
||||||
|
q->cell_id = cell_id;
|
||||||
|
|
||||||
|
bzero(q, sizeof(ue_mib_t));
|
||||||
|
|
||||||
|
q->slot1_symbols = malloc(SLOT_LEN_RE(6, cp) * sizeof(cf_t));
|
||||||
|
if (!q->slot1_symbols) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0;i<MIB_NOF_PORTS;i++) {
|
||||||
|
q->ce[i] = malloc(SLOT_LEN_RE(6, cp) * sizeof(cf_t));
|
||||||
|
if (!q->ce[i]) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync_init(&q->sfind, FIND_SFLEN, FIND_FFTSIZE)) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_set_threshold(&q->sfind, MIB_FIND_THRESHOLD);
|
||||||
|
sync_sss_en(&q->sfind, true);
|
||||||
|
sync_set_N_id_2(&q->sfind, cell_id % 3);
|
||||||
|
|
||||||
|
if (lte_fft_init(&q->fft, cp, cell.nof_prb)) {
|
||||||
|
fprintf(stderr, "Error initializing FFT\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
if (chest_init_LTEDL(&q->chest, cell)) {
|
||||||
|
fprintf(stderr, "Error initializing reference signal\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
if (pbch_init(&q->pbch, cell)) {
|
||||||
|
fprintf(stderr, "Error initiating PBCH\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
ue_mib_reset(q);
|
||||||
|
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
if (ret == LIBLTE_ERROR) {
|
||||||
|
ue_mib_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_mib_free(ue_mib_t * q)
|
||||||
|
{
|
||||||
|
if (q->slot1_symbols) {
|
||||||
|
free(q->slot1_symbols);
|
||||||
|
}
|
||||||
|
for (int i=0;i<MIB_NOF_PORTS;i++) {
|
||||||
|
if (q->ce[i]) {
|
||||||
|
free(q->ce[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync_free(&q->sfind);
|
||||||
|
chest_free(&q->chest);
|
||||||
|
pbch_free(&q->pbch);
|
||||||
|
lte_fft_free(&q->fft);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ue_mib_reset(ue_mib_t * q)
|
||||||
|
{
|
||||||
|
q->frame_cnt = 0;
|
||||||
|
q->last_frame_trial = 0;
|
||||||
|
|
||||||
|
pbch_decode_reset(&q->pbch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_mib_set_threshold(ue_mib_t * q, float threshold)
|
||||||
|
{
|
||||||
|
sync_set_threshold(&q->sfind, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mib_decoder_run(ue_mib_t * q, cf_t *input, pbch_mib_t *mib)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Run FFT for the slot symbols */
|
||||||
|
lte_fft_run_slot(&q->fft, input, q->slot1_symbols);
|
||||||
|
|
||||||
|
/* Get channel estimates of slot #1 for each port */
|
||||||
|
ret = chest_ce_slot(&q->chest, q->slot1_symbols, q->ce, 1);
|
||||||
|
if (ret == LIBLTE_SUCCESS) {
|
||||||
|
|
||||||
|
/* Reset decoder if we missed a frame */
|
||||||
|
if ((q->last_frame_trial && (q->frame_cnt - q->last_frame_trial > 2)) ||
|
||||||
|
q->frame_cnt > 10)
|
||||||
|
{
|
||||||
|
ue_mib_reset(q);
|
||||||
|
INFO("Resetting PBCH decoder: last trial %u, now is %u\n",
|
||||||
|
q->last_frame_trial, q->frame_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode PBCH */
|
||||||
|
ret = pbch_decode(&q->pbch, q->slot1_symbols, q->ce, mib);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error decoding PBCH\n");
|
||||||
|
} else if (ret == 1) {
|
||||||
|
INFO("MIB decoded: %u\n", q->frame_cnt/2);
|
||||||
|
ue_mib_reset(q);
|
||||||
|
ret = 1;
|
||||||
|
} else {
|
||||||
|
INFO("MIB not decoded: %u\n", q->frame_cnt / 2);
|
||||||
|
q->last_frame_trial = q->frame_cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ue_mib_decode(ue_mib_t * q,
|
||||||
|
cf_t *signal,
|
||||||
|
uint32_t nsamples,
|
||||||
|
pbch_mib_t *mib)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
uint32_t peak_idx;
|
||||||
|
uint32_t nof_input_frames;
|
||||||
|
|
||||||
|
|
||||||
|
if (q != NULL &&
|
||||||
|
signal != NULL)
|
||||||
|
{
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
|
||||||
|
if (nsamples % MIB_FRAME_SIZE) {
|
||||||
|
printf("Warning: nsamples must be a multiple of %d. Some samples will be ignored\n", MIB_FRAME_SIZE);
|
||||||
|
nsamples = (nsamples/MIB_FRAME_SIZE) * MIB_FRAME_SIZE;
|
||||||
|
}
|
||||||
|
nof_input_frames = nsamples/MIB_FRAME_SIZE;
|
||||||
|
|
||||||
|
for (uint32_t nf=0;nf<nof_input_frames;nf++) {
|
||||||
|
|
||||||
|
/* Find peak and cell id */
|
||||||
|
ret = sync_find(&q->sfind, signal, nf*MIB_FRAME_SIZE, &peak_idx);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If peak position does not allow to read SSS, return error -3 */
|
||||||
|
if (ret == 1 &&
|
||||||
|
nf*MIB_FRAME_SIZE + peak_idx + 960 <= nsamples &&
|
||||||
|
sync_sss_detected(&q->sfind) &&
|
||||||
|
sync_get_sf_idx(&q->sfind) == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
ret = mib_decoder_run(q, &signal[nf*MIB_FRAME_SIZE+peak_idx], mib);
|
||||||
|
|
||||||
|
} else if ((ret == LIBLTE_SUCCESS && peak_idx != 0) ||
|
||||||
|
(ret == 1 && nf*MIB_FRAME_SIZE + peak_idx + 960 > nsamples))
|
||||||
|
{
|
||||||
|
ret = MIB_FRAME_UNALIGNED;
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->frame_cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
@ -0,0 +1,353 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "liblte/phy/ue/ue_sync.h"
|
||||||
|
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
|
||||||
|
#define MAX_TIME_OFFSET 128
|
||||||
|
cf_t dummy[MAX_TIME_OFFSET];
|
||||||
|
|
||||||
|
#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb)
|
||||||
|
#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE)
|
||||||
|
|
||||||
|
#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)
|
||||||
|
#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp)
|
||||||
|
|
||||||
|
#define FIND_THRESHOLD 1.0
|
||||||
|
#define TRACK_THRESHOLD 0.2
|
||||||
|
|
||||||
|
|
||||||
|
int ue_sync_init(ue_sync_t *q,
|
||||||
|
lte_cell_t cell,
|
||||||
|
int (recv_callback)(void*, void*, uint32_t),
|
||||||
|
void *stream_handler)
|
||||||
|
{
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
|
if (q != NULL &&
|
||||||
|
stream_handler != NULL &&
|
||||||
|
lte_cell_isvalid(&cell) &&
|
||||||
|
recv_callback != NULL)
|
||||||
|
{
|
||||||
|
ret = LIBLTE_ERROR;
|
||||||
|
|
||||||
|
bzero(q, sizeof(ue_sync_t));
|
||||||
|
|
||||||
|
ue_sync_reset(q);
|
||||||
|
|
||||||
|
q->decode_sss_on_track = false;
|
||||||
|
q->stream = stream_handler;
|
||||||
|
q->recv_callback = recv_callback;
|
||||||
|
q->cell = cell;
|
||||||
|
|
||||||
|
if(sync_init(&q->sfind, CURRENT_SFLEN, CURRENT_FFTSIZE)) {
|
||||||
|
fprintf(stderr, "Error initiating sync find\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
if(sync_init(&q->strack, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) {
|
||||||
|
fprintf(stderr, "Error initiating sync track\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_set_N_id_2(&q->sfind, cell.id%3);
|
||||||
|
sync_set_threshold(&q->sfind, FIND_THRESHOLD);
|
||||||
|
q->sfind.cp = cell.cp;
|
||||||
|
sync_cp_en(&q->sfind, false);
|
||||||
|
|
||||||
|
sync_set_N_id_2(&q->strack, cell.id%3);
|
||||||
|
sync_set_threshold(&q->strack, TRACK_THRESHOLD);
|
||||||
|
q->strack.cp = cell.cp;
|
||||||
|
sync_cp_en(&q->strack, false);
|
||||||
|
|
||||||
|
if (cfo_init(&q->cfocorr, CURRENT_SFLEN)) {
|
||||||
|
fprintf(stderr, "Error initiating CFO\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->input_buffer = vec_malloc(5 * CURRENT_SFLEN * sizeof(cf_t));
|
||||||
|
if (!q->input_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
if (ret == LIBLTE_ERROR) {
|
||||||
|
ue_sync_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_sync_free(ue_sync_t *q) {
|
||||||
|
if (q->input_buffer) {
|
||||||
|
free(q->input_buffer);
|
||||||
|
}
|
||||||
|
cfo_free(&q->cfocorr);
|
||||||
|
sync_free(&q->sfind);
|
||||||
|
sync_free(&q->strack);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ue_sync_peak_idx(ue_sync_t *q) {
|
||||||
|
return q->peak_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
ue_sync_state_t ue_sync_get_state(ue_sync_t *q) {
|
||||||
|
return q->state;
|
||||||
|
}
|
||||||
|
uint32_t ue_sync_get_sfidx(ue_sync_t *q) {
|
||||||
|
return q->sf_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ue_sync_get_cfo(ue_sync_t *q) {
|
||||||
|
return 15000 * q->cur_cfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ue_sync_get_sfo(ue_sync_t *q) {
|
||||||
|
return 1000*q->mean_time_offset/5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) {
|
||||||
|
q->decode_sss_on_track = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int find_peak_ok(ue_sync_t *q) {
|
||||||
|
|
||||||
|
/* Receive the rest of the next subframe */
|
||||||
|
if (q->recv_callback(q->stream, q->input_buffer, q->peak_idx+CURRENT_SFLEN/2) < 0) {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync_sss_detected(&q->sfind)) {
|
||||||
|
|
||||||
|
/* Get the subframe index (0 or 5) */
|
||||||
|
q->sf_idx = sync_get_sf_idx(&q->sfind) + 1;
|
||||||
|
|
||||||
|
/* Reset variables */
|
||||||
|
q->frame_ok_cnt = 0;
|
||||||
|
q->frame_no_cnt = 0;
|
||||||
|
q->frame_total_cnt = 0;
|
||||||
|
|
||||||
|
/* Goto Tracking state */
|
||||||
|
q->state = SF_TRACK;
|
||||||
|
|
||||||
|
INFO("Found peak at %d, value %.3f, SF_idx: %d, Cell_id: %d CP: %s\n",
|
||||||
|
q->peak_idx, sync_get_peak_value(&q->sfind), q->sf_idx, q->cell.id, lte_cp_string(q->cell.cp));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
INFO("Found peak at %d, SSS not detected\n", q->peak_idx);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int track_peak_ok(ue_sync_t *q, uint32_t track_idx) {
|
||||||
|
|
||||||
|
/* Make sure subframe idx is what we expect */
|
||||||
|
if ((q->sf_idx != sync_get_sf_idx(&q->strack)) && q->decode_sss_on_track) {
|
||||||
|
INFO("Warning: Expected SF idx %d but got %d!\n",
|
||||||
|
q->sf_idx, sync_get_sf_idx(&q->strack));
|
||||||
|
q->sf_idx = sync_get_sf_idx(&q->strack);
|
||||||
|
q->state = SF_TRACK;
|
||||||
|
} else {
|
||||||
|
q->time_offset = ((int) track_idx - (int) CURRENT_FFTSIZE);
|
||||||
|
|
||||||
|
/* If the PSS peak is beyond the frame (we sample too slowly),
|
||||||
|
discard the offseted samples to align next frame */
|
||||||
|
if (q->time_offset > 0 && q->time_offset < MAX_TIME_OFFSET) {
|
||||||
|
if (q->recv_callback(q->stream, dummy, (uint32_t) q->time_offset) < 0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute cumulative moving average CFO */
|
||||||
|
q->cur_cfo = EXPAVERAGE(sync_get_cfo(&q->strack), q->cur_cfo, q->frame_ok_cnt);
|
||||||
|
|
||||||
|
/* compute cumulative moving average time offset */
|
||||||
|
q->mean_time_offset = (float) EXPAVERAGE((float) q->time_offset, q->mean_time_offset, q->frame_ok_cnt);
|
||||||
|
|
||||||
|
q->peak_idx = CURRENT_SFLEN/2 + q->time_offset;
|
||||||
|
q->frame_ok_cnt++;
|
||||||
|
q->frame_no_cnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int track_peak_no(ue_sync_t *q) {
|
||||||
|
|
||||||
|
/* if we missed too many PSS go back to FIND */
|
||||||
|
q->frame_no_cnt++;
|
||||||
|
if (q->frame_no_cnt >= TRACK_MAX_LOST) {
|
||||||
|
printf("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt);
|
||||||
|
q->state = SF_FIND;
|
||||||
|
} else {
|
||||||
|
INFO("Tracking peak not found. Peak %.3f, %d lost\n",
|
||||||
|
sync_get_peak_value(&q->strack), (int) q->frame_no_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int receive_samples(ue_sync_t *q) {
|
||||||
|
|
||||||
|
/* A negative time offset means there are samples in our buffer for the next subframe,
|
||||||
|
because we are sampling too fast.
|
||||||
|
*/
|
||||||
|
if (q->time_offset < 0) {
|
||||||
|
q->time_offset = -q->time_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy last part of the last subframe (use move since there could be overlapping) */
|
||||||
|
//memcpy(q->input_buffer, &q->input_buffer[CURRENT_SFLEN-q->time_offset], q->time_offset*sizeof(cf_t));
|
||||||
|
|
||||||
|
/* Get 1 subframe from the USRP getting more samples and keeping the previous samples, if any */
|
||||||
|
if (q->recv_callback(q->stream, &q->input_buffer[q->time_offset], CURRENT_SFLEN - q->time_offset) < 0) {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset time offset */
|
||||||
|
q->time_offset = 0;
|
||||||
|
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ue_sync_get_buffer(ue_sync_t *q, cf_t **sf_symbols) {
|
||||||
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
uint32_t track_idx;
|
||||||
|
struct timeval t[3];
|
||||||
|
|
||||||
|
if (q != NULL &&
|
||||||
|
sf_symbols != NULL &&
|
||||||
|
q->input_buffer != NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (receive_samples(q)) {
|
||||||
|
fprintf(stderr, "Error receiving samples\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (q->state) {
|
||||||
|
case SF_FIND:
|
||||||
|
ret = sync_find(&q->sfind, q->input_buffer, 0, &q->peak_idx);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 1) {
|
||||||
|
ret = find_peak_ok(q);
|
||||||
|
} else if (q->peak_idx != 0) {
|
||||||
|
uint32_t rlen;
|
||||||
|
if (q->peak_idx < CURRENT_SFLEN/2) {
|
||||||
|
rlen = CURRENT_SFLEN/2-q->peak_idx;
|
||||||
|
} else {
|
||||||
|
rlen = q->peak_idx;
|
||||||
|
}
|
||||||
|
if (q->recv_callback(q->stream, q->input_buffer, rlen) < 0) {
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SF_TRACK:
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
q->strack.sss_en = q->decode_sss_on_track;
|
||||||
|
|
||||||
|
q->sf_idx = (q->sf_idx + 1) % 10;
|
||||||
|
|
||||||
|
/* Every SF idx 0 and 5, find peak around known position q->peak_idx */
|
||||||
|
if (q->sf_idx == 0 || q->sf_idx == 5) {
|
||||||
|
|
||||||
|
#ifdef MEASURE_EXEC_TIME
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
track_idx = 0;
|
||||||
|
|
||||||
|
/* track pss around the middle of the subframe, where the PSS is */
|
||||||
|
ret = sync_find(&q->strack, q->input_buffer, CURRENT_SFLEN/2-CURRENT_FFTSIZE, &track_idx);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error tracking correlation peak\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MEASURE_EXEC_TIME
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
q->mean_exec_time = (float) EXPAVERAGE((float) t[0].tv_usec, q->mean_exec_time, q->frame_total_cnt);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ret == 1) {
|
||||||
|
ret = track_peak_ok(q, track_idx);
|
||||||
|
} else {
|
||||||
|
ret = track_peak_no(q);
|
||||||
|
}
|
||||||
|
if (ret == LIBLTE_ERROR) {
|
||||||
|
fprintf(stderr, "Error processing tracking peak\n");
|
||||||
|
q->state = SF_FIND;
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->frame_total_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do CFO Correction and deliver the frame */
|
||||||
|
cfo_correct(&q->cfocorr, q->input_buffer, q->input_buffer, -q->cur_cfo / CURRENT_FFTSIZE);
|
||||||
|
*sf_symbols = q->input_buffer;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ue_sync_reset(ue_sync_t *q) {
|
||||||
|
q->state = SF_FIND;
|
||||||
|
|
||||||
|
q->frame_ok_cnt = 0;
|
||||||
|
q->frame_no_cnt = 0;
|
||||||
|
q->frame_total_cnt = 0;
|
||||||
|
q->cur_cfo = 0;
|
||||||
|
q->mean_time_offset = 0;
|
||||||
|
q->time_offset = 0;
|
||||||
|
#ifdef MEASURE_EXEC_TIME
|
||||||
|
q->mean_exec_time = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2012-2013 The libLTE Developers. See the
|
||||||
|
# COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
#
|
||||||
|
# This file is part of the libLTE library.
|
||||||
|
#
|
||||||
|
# libLTE is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of
|
||||||
|
# the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# libLTE 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 Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# A copy of the GNU Lesser General Public License can be found in
|
||||||
|
# the LICENSE file in the top-level directory of this distribution
|
||||||
|
# and at http://www.gnu.org/licenses/.
|
||||||
|
#
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# UE SYNC TEST (Only compiled if CUHD is available)
|
||||||
|
########################################################################
|
||||||
|
LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND)
|
||||||
|
LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND)
|
||||||
|
|
||||||
|
IF(${CUHD_FIND} GREATER -1)
|
||||||
|
ADD_EXECUTABLE(ue_sync_usrp ue_sync_usrp.c)
|
||||||
|
TARGET_LINK_LIBRARIES(ue_sync_usrp lte_phy cuhd)
|
||||||
|
|
||||||
|
ADD_EXECUTABLE(ue_celldetect_mib_test ue_celldetect_mib_test.c)
|
||||||
|
TARGET_LINK_LIBRARIES(ue_celldetect_mib_test lte_phy cuhd)
|
||||||
|
ENDIF(${CUHD_FIND} GREATER -1)
|
||||||
|
|
||||||
|
IF(${GRAPHICS_FIND} EQUAL -1)
|
||||||
|
SET_TARGET_PROPERTIES(ue_sync_usrp PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS")
|
||||||
|
ELSE(${GRAPHICS_FIND} EQUAL -1)
|
||||||
|
target_link_libraries(ue_sync_usrp graphics)
|
||||||
|
ENDIF(${GRAPHICS_FIND} EQUAL -1)
|
||||||
|
|
@ -0,0 +1,265 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE 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 Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
|
||||||
|
int nof_frames_total = CS_DEFAULT_NOFFRAMES_TOTAL;
|
||||||
|
int nof_frames_detected = CS_DEFAULT_NOFFRAMES_DETECTED;
|
||||||
|
float threshold = -1;
|
||||||
|
|
||||||
|
float uhd_freq = 0.0, uhd_gain = 20.0;
|
||||||
|
char *uhd_args = "";
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [agntdv] -f uhd_freq\n", prog);
|
||||||
|
printf("\t-a UHD args [Default %s]\n", uhd_args);
|
||||||
|
printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain);
|
||||||
|
printf("\t-n nof_frames_total [Default 100]\n");
|
||||||
|
printf("\t-d nof_frames_detected [Default 10]\n");
|
||||||
|
printf("\t-t threshold [Default %.2f]\n",threshold);
|
||||||
|
printf("\t-v [set verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "agndtvf")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'n':
|
||||||
|
nof_frames_total = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
nof_frames_detected = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
uhd_args = argv[optind];
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
uhd_gain = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
uhd_freq = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
threshold = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uhd_freq == 0.0) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int decode_pbch(void *uhd, cf_t *buffer, ue_celldetect_result_t *found_cell)
|
||||||
|
{
|
||||||
|
ue_mib_t uemib;
|
||||||
|
pbch_mib_t mib;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
uint32_t nof_frames = 0;
|
||||||
|
uint32_t flen = MIB_FRAME_SIZE;
|
||||||
|
|
||||||
|
if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) {
|
||||||
|
fprintf(stderr, "Error initiating PBCH decoder\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0);
|
||||||
|
cuhd_set_rx_srate(uhd, 1920000.0);
|
||||||
|
INFO("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (cuhd_recv(uhd, buffer, flen, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total);
|
||||||
|
|
||||||
|
n = ue_mib_decode(&uemib, buffer, flen, &mib);
|
||||||
|
if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) {
|
||||||
|
fprintf(stderr, "Error calling ue_mib_decode()\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
if (n == MIB_FRAME_UNALIGNED) {
|
||||||
|
printf("Realigning frame\n");
|
||||||
|
if (cuhd_recv(uhd, buffer, flen/2, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nof_frames++;
|
||||||
|
} while (n != MIB_FOUND && nof_frames < nof_frames_total);
|
||||||
|
if (n == MIB_FOUND) {
|
||||||
|
printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames);
|
||||||
|
pbch_mib_fprint(stdout, &mib, found_cell->cell_id);
|
||||||
|
} else {
|
||||||
|
printf("\nCould not decode MIB\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
cuhd_flush_buffer(uhd);
|
||||||
|
|
||||||
|
ue_mib_free(&uemib);
|
||||||
|
|
||||||
|
return LIBLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_cell(void *uhd, ue_celldetect_t *s, cf_t *buffer, ue_celldetect_result_t *found_cell)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
INFO("Setting sampling frequency 960 KHz for PSS search\n", 0);
|
||||||
|
cuhd_set_rx_srate(uhd, 960000.0);
|
||||||
|
INFO("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
uint32_t nof_scanned_cells = 0;
|
||||||
|
uint32_t flen = 4800;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if (cuhd_recv(uhd, buffer, flen, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = ue_celldetect_scan(s, buffer, flen, found_cell);
|
||||||
|
switch(n) {
|
||||||
|
case CS_FRAME_UNALIGNED:
|
||||||
|
printf("Realigning frame\n");
|
||||||
|
if (cuhd_recv(uhd, buffer, flen/2, 1)<0) {
|
||||||
|
fprintf(stderr, "Error receiving from USRP\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
case CS_CELL_DETECTED:
|
||||||
|
if (found_cell->peak > 0) {
|
||||||
|
printf("\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n",
|
||||||
|
found_cell->cell_id, lte_cp_string(found_cell->cp),
|
||||||
|
found_cell->peak, found_cell->mode, s->nof_frames_detected);
|
||||||
|
}
|
||||||
|
nof_scanned_cells++;
|
||||||
|
break;
|
||||||
|
case CS_CELL_NOT_DETECTED:
|
||||||
|
nof_scanned_cells++;
|
||||||
|
break;
|
||||||
|
case LIBLTE_ERROR:
|
||||||
|
case LIBLTE_ERROR_INVALID_INPUTS:
|
||||||
|
fprintf(stderr, "Error calling cellsearch_scan()\n");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
} while(nof_scanned_cells < 3 && n != CS_CELL_DETECTED);
|
||||||
|
|
||||||
|
INFO("Stopping receiver...\n", 0);
|
||||||
|
cuhd_stop_rx_stream(uhd);
|
||||||
|
cuhd_flush_buffer(uhd);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int n;
|
||||||
|
void *uhd;
|
||||||
|
ue_celldetect_t s;
|
||||||
|
ue_celldetect_result_t found_cell;
|
||||||
|
cf_t *buffer;
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
printf("Opening UHD device...\n");
|
||||||
|
if (cuhd_open(uhd_args, &uhd)) {
|
||||||
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
cuhd_set_rx_gain(uhd, uhd_gain);
|
||||||
|
|
||||||
|
/* set uhd_freq */
|
||||||
|
cuhd_set_rx_freq(uhd, (double) uhd_freq);
|
||||||
|
cuhd_rx_wait_lo_locked(uhd);
|
||||||
|
DEBUG("Set uhd_freq to %.3f MHz\n", (double ) uhd_freq/1000000);
|
||||||
|
|
||||||
|
buffer = vec_malloc(sizeof(cf_t) * 96000);
|
||||||
|
if (!buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
return LIBLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ue_celldetect_init(&s)) {
|
||||||
|
fprintf(stderr, "Error initiating UE sync module\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (threshold > 0) {
|
||||||
|
ue_celldetect_set_threshold(&s, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nof_frames_total > 0) {
|
||||||
|
ue_celldetect_set_nof_frames_total(&s, nof_frames_total);
|
||||||
|
}
|
||||||
|
if (nof_frames_detected > 0) {
|
||||||
|
ue_celldetect_set_nof_frames_detected(&s, nof_frames_detected);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = find_cell(uhd, &s, buffer, &found_cell);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error searching cell\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (n == CS_CELL_DETECTED) {
|
||||||
|
if (decode_pbch(uhd, buffer, &found_cell)) {
|
||||||
|
fprintf(stderr, "Error decoding PBCH\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ue_celldetect_free(&s);
|
||||||
|
cuhd_close(uhd);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
function [ y ] = addnoise( x, snr_db )
|
||||||
|
v = 10^(-snr_db/10);
|
||||||
|
y=x+sqrt(v)*(randn(size(x))+1i*randn(size(x)))/sqrt(2);
|
||||||
|
y=y/sqrt(mean(y.*conj(y)));
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
function [peaks] = find_peaks(x, N_id_2, fft_size)
|
||||||
|
|
||||||
|
flen=4800*(ceil(fft_size/64));
|
||||||
|
|
||||||
|
n=floor(length(x)/flen)*flen;
|
||||||
|
xf=reshape(x(1:n),flen,[]);
|
||||||
|
|
||||||
|
[n m] = size(xf);
|
||||||
|
|
||||||
|
peaks=zeros(m,1);
|
||||||
|
for i=1:m
|
||||||
|
[w, peaks(i)]= find_pss2(xf(:,i),N_id_2,fft_size);
|
||||||
|
end
|
@ -1,18 +1,14 @@
|
|||||||
function [ fs eps p_m w2] = find_pss( x, N_id_2, fft_size)
|
function [w2, m, idx] = find_pss2( x, N_id_2, fft_size)
|
||||||
c=lte_pss_zc(N_id_2);
|
c=lte_pss_zc(N_id_2);
|
||||||
cc=[zeros(fft_size/2-31,1); c; zeros(fft_size/2-31,1)];
|
cc=[zeros(fft_size/2-31,1); c; zeros(fft_size/2-31,1)];
|
||||||
cc=[0; cc(fft_size/2+1:fft_size); cc(2:fft_size/2)];
|
ccd=[0; cc(fft_size/2+1:fft_size); cc(2:fft_size/2)];
|
||||||
ccf=conj(ifft(cc));
|
ccf=sqrt(fft_size)*conj(ifft(ccd));
|
||||||
|
|
||||||
w2=conv(x,ccf);
|
w2=abs(conv(x,ccf/62)).^2/var(x,1)/sqrt(2);
|
||||||
%plot(10*log10(abs(w2)));%./mean(abs(w2))));
|
plot(w2)
|
||||||
plot(abs(w2))
|
[m, idx]=max(w2);
|
||||||
%axis([0 length(w2) 0 20])
|
|
||||||
[m i]=max(abs(w2));
|
|
||||||
p_m = m/mean(abs(w2));
|
|
||||||
|
|
||||||
fprintf('Frame starts at %d, m=%g, p=%g, p/m=%g dB\n',i, ...
|
%fprintf('Frame starts at %d, energy=%g, p=%g, p/en=%g dB\n',i, ...
|
||||||
mean(abs(w2)), m, 10*log10(m/mean(abs(w2))));
|
% en, m, m/en);
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue