mirror of https://github.com/pvnis/srsRAN_4G.git
Fixed PDSCH not working with odd number of PRB. Fixed PBCH resource allocation for PRB greater than 6. Other minor issues
parent
8f08380d46
commit
a08cdaecfc
@ -0,0 +1,367 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
void *uhd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *output_file_name = NULL;
|
||||||
|
int nof_frames = -1;
|
||||||
|
int cell_id = 1;
|
||||||
|
int nof_prb = 6;
|
||||||
|
char *uhd_args = "";
|
||||||
|
int cfi=1;
|
||||||
|
|
||||||
|
float uhd_amp = 0.25, uhd_gain = 10.0, uhd_freq = 2400000000;
|
||||||
|
|
||||||
|
filesink_t fsink;
|
||||||
|
lte_fft_t ifft;
|
||||||
|
pbch_t pbch;
|
||||||
|
pcfich_t pcfich;
|
||||||
|
pdcch_t pdcch;
|
||||||
|
pdsch_t pdsch;
|
||||||
|
regs_t regs;
|
||||||
|
|
||||||
|
cf_t *sf_buffer = NULL, *output_buffer = NULL;
|
||||||
|
int sf_n_re, sf_n_samples;
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [agmfoncvp]\n", prog);
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
printf("\t-a UHD args [Default %s]\n", uhd_args);
|
||||||
|
printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain);
|
||||||
|
printf("\t-m UHD signal amplitude [Default %.2f]\n", uhd_amp);
|
||||||
|
printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq / 1000000);
|
||||||
|
#else
|
||||||
|
printf("\t UHD is disabled. CUHD library not available\n");
|
||||||
|
#endif
|
||||||
|
printf("\t-o output_file [Default USRP]\n");
|
||||||
|
printf("\t-n number of frames [Default %d]\n", nof_frames);
|
||||||
|
printf("\t-c cell id [Default %d]\n", cell_id);
|
||||||
|
printf("\t-p nof_prb [Default %d]\n", nof_prb);
|
||||||
|
printf("\t-v [set verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "agfmoncpv")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'a':
|
||||||
|
uhd_args = argv[optind];
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
uhd_gain = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
uhd_amp = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
uhd_freq = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
output_file_name = argv[optind];
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
nof_frames = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
nof_prb = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
cell_id = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef DISABLE_UHD
|
||||||
|
if (!output_file_name) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void base_init() {
|
||||||
|
/* init memory */
|
||||||
|
sf_buffer = malloc(sizeof(cf_t) * sf_n_re);
|
||||||
|
if (!sf_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
output_buffer = malloc(sizeof(cf_t) * sf_n_samples);
|
||||||
|
if (!output_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
/* open file or USRP */
|
||||||
|
if (output_file_name) {
|
||||||
|
if (filesink_init(&fsink, output_file_name, COMPLEX_FLOAT_BIN)) {
|
||||||
|
fprintf(stderr, "Error opening file %s\n", output_file_name);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
printf("Opening UHD device...\n");
|
||||||
|
if (cuhd_open(uhd_args, &uhd)) {
|
||||||
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
printf("Error UHD not available. Select an output file\n");
|
||||||
|
exit(-1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create ifft object */
|
||||||
|
if (lte_ifft_init(&ifft, CPNORM, nof_prb)) {
|
||||||
|
fprintf(stderr, "Error creating iFFT object\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (pbch_init(&pbch, nof_prb, cell_id, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PBCH object\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regs_init(®s, cell_id, nof_prb, 1, R_1, PHICH_NORM, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error initiating regs\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcfich_init(&pcfich, ®s, cell_id, nof_prb, 1, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PBCH object\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regs_set_cfi(®s, cfi)) {
|
||||||
|
fprintf(stderr, "Error setting CFI\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdcch_init(&pdcch, ®s, nof_prb, 1, cell_id, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PDCCH object\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdsch_init(&pdsch, 1234, nof_prb, 1, cell_id, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PDSCH object\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void base_free() {
|
||||||
|
|
||||||
|
pdsch_free(&pdsch);
|
||||||
|
pdcch_free(&pdcch);
|
||||||
|
regs_free(®s);
|
||||||
|
pbch_free(&pbch);
|
||||||
|
|
||||||
|
lte_ifft_free(&ifft);
|
||||||
|
|
||||||
|
if (sf_buffer) {
|
||||||
|
free(sf_buffer);
|
||||||
|
}
|
||||||
|
if (output_buffer) {
|
||||||
|
free(output_buffer);
|
||||||
|
}
|
||||||
|
if (output_file_name) {
|
||||||
|
filesink_free(&fsink);
|
||||||
|
} else {
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
cuhd_close(&uhd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int nf, sf_idx, N_id_2;
|
||||||
|
cf_t pss_signal[PSS_LEN];
|
||||||
|
float sss_signal0[SSS_LEN]; // for subframe 0
|
||||||
|
float sss_signal5[SSS_LEN]; // for subframe 5
|
||||||
|
pbch_mib_t mib;
|
||||||
|
ra_pdsch_t ra_dl;
|
||||||
|
ra_prb_t prb_alloc;
|
||||||
|
refsignal_t refs[NSLOTS_X_FRAME];
|
||||||
|
int i, n;
|
||||||
|
char *data;
|
||||||
|
cf_t *sf_symbols[MAX_PORTS];
|
||||||
|
cf_t *slot1_symbols[MAX_PORTS];
|
||||||
|
dci_t dci_tx;
|
||||||
|
|
||||||
|
#ifdef DISABLE_UHD
|
||||||
|
if (argc < 3) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
N_id_2 = cell_id % 3;
|
||||||
|
sf_n_re = 2 * CPNORM_NSYMB * nof_prb * RE_X_RB;
|
||||||
|
sf_n_samples = 2 * SLOT_LEN_CPNORM(lte_symbol_sz(nof_prb));
|
||||||
|
|
||||||
|
/* this *must* be called after setting slot_len_* */
|
||||||
|
base_init();
|
||||||
|
|
||||||
|
/* Generate PSS/SSS signals */
|
||||||
|
pss_generate(pss_signal, N_id_2);
|
||||||
|
sss_generate(sss_signal0, sss_signal5, cell_id);
|
||||||
|
|
||||||
|
/* Generate CRS signals */
|
||||||
|
for (i = 0; i < NSLOTS_X_FRAME; i++) {
|
||||||
|
if (refsignal_init_LTEDL(&refs[i], 0, i, cell_id, CPNORM, nof_prb)) {
|
||||||
|
fprintf(stderr, "Error initiating CRS slot=%d\n", i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mib.nof_ports = 1;
|
||||||
|
mib.nof_prb = nof_prb;
|
||||||
|
mib.phich_length = PHICH_NORM;
|
||||||
|
mib.phich_resources = R_1;
|
||||||
|
mib.sfn = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_PORTS; i++) { // now there's only 1 port
|
||||||
|
sf_symbols[i] = sf_buffer;
|
||||||
|
slot1_symbols[i] = &sf_buffer[sf_n_re/2];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
if (!output_file_name) {
|
||||||
|
printf("Set TX rate: %.2f MHz\n",
|
||||||
|
cuhd_set_tx_srate(uhd, lte_sampling_freq_hz(nof_prb)) / 1000000);
|
||||||
|
printf("Set TX gain: %.1f dB\n", cuhd_set_tx_gain(uhd, uhd_gain));
|
||||||
|
printf("Set TX freq: %.2f MHz\n",
|
||||||
|
cuhd_set_tx_freq(uhd, uhd_freq) / 1000000);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
dci_init(&dci_tx, 1);
|
||||||
|
bzero(&ra_dl, sizeof(ra_pdsch_t));
|
||||||
|
ra_dl.harq_process = 0;
|
||||||
|
ra_pdsch_set_mcs(&ra_dl, QPSK, 5);
|
||||||
|
ra_dl.ndi = 0;
|
||||||
|
ra_dl.rv_idx = 0;
|
||||||
|
ra_dl.alloc_type = alloc_type0;
|
||||||
|
ra_dl.type0_alloc.rbg_bitmask = 0xffffffff;
|
||||||
|
|
||||||
|
dci_msg_pack_pdsch(&ra_dl, &dci_tx.msg[0], Format1, nof_prb, false);
|
||||||
|
dci_tx.nof_dcis++;
|
||||||
|
|
||||||
|
if (pdcch_set_cfi(&pdcch, cfi)) {
|
||||||
|
fprintf(stderr, "Error setting CFI\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdcch_init_search_ue(&pdcch, 1234);
|
||||||
|
|
||||||
|
ra_prb_get_dl(&prb_alloc, &ra_dl, nof_prb);
|
||||||
|
ra_prb_get_re(&prb_alloc, nof_prb, 1, nof_prb<10?(cfi+1):cfi, CPNORM);
|
||||||
|
ra_dl.mcs.tbs = ra_tbs_from_idx(ra_dl.mcs.tbs_idx, nof_prb);
|
||||||
|
|
||||||
|
ra_pdsch_fprint(stdout, &ra_dl, nof_prb);
|
||||||
|
|
||||||
|
data = malloc(sizeof(char) * ra_dl.mcs.tbs);
|
||||||
|
if (!data) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nf = 0;
|
||||||
|
|
||||||
|
while (nf < nof_frames || nof_frames == -1) {
|
||||||
|
for (sf_idx = 0; sf_idx < NSUBFRAMES_X_FRAME; sf_idx++) {
|
||||||
|
bzero(sf_buffer, sizeof(cf_t) * sf_n_re);
|
||||||
|
|
||||||
|
if (sf_idx == 0 || sf_idx == 5) {
|
||||||
|
pss_put_slot(pss_signal, sf_buffer, nof_prb, CPNORM);
|
||||||
|
sss_put_slot(sf_idx ? sss_signal5 : sss_signal0, sf_buffer, nof_prb,
|
||||||
|
CPNORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf_idx == 0) {
|
||||||
|
pbch_encode(&pbch, &mib, slot1_symbols, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n=0;n<2;n++) {
|
||||||
|
refsignal_put(&refs[2*sf_idx+n], &sf_buffer[n*sf_n_re/2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcfich_encode(&pcfich, cfi, sf_symbols, sf_idx);
|
||||||
|
|
||||||
|
INFO("SF: %d, Generating %d random bits\n", sf_idx, ra_dl.mcs.tbs);
|
||||||
|
for (i=0;i<ra_dl.mcs.tbs;i++) {
|
||||||
|
data[i] = rand()%2;
|
||||||
|
}
|
||||||
|
dci_msg_candidate_set(&dci_tx.msg[0], pdcch.search_mode[2].candidates[0][sf_idx].L,
|
||||||
|
pdcch.search_mode[2].candidates[0][sf_idx].ncce, 1234);
|
||||||
|
INFO("Setting DCI candidate L: %d nCCE: %d\n", pdcch.search_mode[2].candidates[0][sf_idx].L,
|
||||||
|
pdcch.search_mode[2].candidates[0][sf_idx].ncce);
|
||||||
|
pdcch_encode(&pdcch, &dci_tx, sf_symbols, sf_idx);
|
||||||
|
pdsch_encode(&pdsch, data, sf_symbols, sf_idx, ra_dl.mcs, &prb_alloc);
|
||||||
|
|
||||||
|
/* Transform to OFDM symbols */
|
||||||
|
lte_ifft_run_sf(&ifft, sf_buffer, output_buffer);
|
||||||
|
|
||||||
|
/* send to file or usrp */
|
||||||
|
if (output_file_name) {
|
||||||
|
filesink_write(&fsink, output_buffer, sf_n_samples);
|
||||||
|
usleep(5000);
|
||||||
|
} else {
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
vec_sc_prod_cfc(output_buffer, uhd_amp, output_buffer, sf_n_samples);
|
||||||
|
cuhd_send(uhd, output_buffer, sf_n_samples, 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mib.sfn = (mib.sfn + 1) % 1024;
|
||||||
|
printf("SFN: %4d\r", mib.sfn);
|
||||||
|
fflush(stdout);
|
||||||
|
nf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_free();
|
||||||
|
|
||||||
|
printf("Done\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,643 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 <assert.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
#include "liblte/cuhd/cuhd.h"
|
||||||
|
void *uhd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
#include "liblte/graphics/plot.h"
|
||||||
|
plot_real_t poutfft;
|
||||||
|
plot_complex_t pce;
|
||||||
|
plot_scatter_t pscatrecv, pscatequal;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MHZ 1000000
|
||||||
|
#define SAMP_FREQ 1920000
|
||||||
|
|
||||||
|
#define NOF_PORTS 2
|
||||||
|
|
||||||
|
float find_threshold = 10.0;
|
||||||
|
int nof_frames = -1;
|
||||||
|
int pdsch_errors = 0, pdsch_total = 0;
|
||||||
|
int frame_cnt;
|
||||||
|
char *input_file_name = NULL;
|
||||||
|
int disable_plots = 0;
|
||||||
|
|
||||||
|
/* These are the number of PRBs used during the SYNC procedure */
|
||||||
|
int sampling_nof_prb = 6;
|
||||||
|
|
||||||
|
/* Number of samples in a subframe */
|
||||||
|
int sf_n_samples;
|
||||||
|
|
||||||
|
int cell_id_initated = 0, mib_initiated = 0;
|
||||||
|
|
||||||
|
int go_exit = 0;
|
||||||
|
|
||||||
|
float uhd_freq = 2600000000.0, uhd_gain = 20.0;
|
||||||
|
char *uhd_args = "";
|
||||||
|
|
||||||
|
filesource_t fsrc;
|
||||||
|
cf_t *input_buffer, *sf_buffer, *fft_buffer, *input_decim_buffer, *ce[MAX_PORTS];
|
||||||
|
float *tmp_plot;
|
||||||
|
pbch_t pbch;
|
||||||
|
pbch_mib_t mib;
|
||||||
|
pcfich_t pcfich;
|
||||||
|
pdcch_t pdcch;
|
||||||
|
dci_t dci_set;
|
||||||
|
pdsch_t pdsch;
|
||||||
|
regs_t regs;
|
||||||
|
lte_fft_t fft;
|
||||||
|
chest_t chest;
|
||||||
|
sync_frame_t sframe;
|
||||||
|
|
||||||
|
#define CLRSTDOUT printf("\r\n"); fflush(stdout); printf("\r\n")
|
||||||
|
|
||||||
|
#define DOWNSAMPLE_FACTOR(x, y) lte_symbol_sz(x) / lte_symbol_sz(y)
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [iagfndvtp]\n", prog);
|
||||||
|
printf("\t-i input_file [Default use USRP]\n");
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
printf("\t-a UHD args [Default %s]\n", uhd_args);
|
||||||
|
printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain);
|
||||||
|
printf("\t-f UHD RX frequency [Default %.1f MHz]\n", uhd_freq / 1000000);
|
||||||
|
#else
|
||||||
|
printf("\t UHD is disabled. CUHD library not available\n");
|
||||||
|
#endif
|
||||||
|
printf("\t-p sampling_nof_prb [Default %d]\n", sampling_nof_prb);
|
||||||
|
printf("\t-n nof_frames [Default %d]\n", nof_frames);
|
||||||
|
printf("\t-t PSS threshold [Default %f]\n", find_threshold);
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
printf("\t-d disable plots [Default enabled]\n");
|
||||||
|
#else
|
||||||
|
printf("\t plots are disabled. Graphics library not available\n");
|
||||||
|
#endif
|
||||||
|
printf("\t-v [set verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "iagfndvtp")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'i':
|
||||||
|
input_file_name = 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':
|
||||||
|
find_threshold = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
sampling_nof_prb = atof(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
nof_frames = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
disable_plots = 1;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
|
||||||
|
void init_plots() {
|
||||||
|
plot_init();
|
||||||
|
plot_real_init(&poutfft);
|
||||||
|
plot_real_setTitle(&poutfft, "Output FFT - Magnitude");
|
||||||
|
plot_real_setLabels(&poutfft, "Index", "dB");
|
||||||
|
plot_real_setYAxisScale(&poutfft, -60, 0);
|
||||||
|
plot_real_setXAxisScale(&poutfft, 1, 504);
|
||||||
|
|
||||||
|
plot_complex_init(&pce);
|
||||||
|
plot_complex_setTitle(&pce, "Channel Estimates");
|
||||||
|
plot_complex_setYAxisScale(&pce, Ip, -0.01, 0.01);
|
||||||
|
plot_complex_setYAxisScale(&pce, Q, -0.01, 0.01);
|
||||||
|
plot_complex_setYAxisScale(&pce, Magnitude, 0, 0.01);
|
||||||
|
plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI);
|
||||||
|
|
||||||
|
plot_scatter_init(&pscatrecv);
|
||||||
|
plot_scatter_setTitle(&pscatrecv, "Received Symbols");
|
||||||
|
plot_scatter_setXAxisScale(&pscatrecv, -0.01, 0.01);
|
||||||
|
plot_scatter_setYAxisScale(&pscatrecv, -0.01, 0.01);
|
||||||
|
|
||||||
|
plot_scatter_init(&pscatequal);
|
||||||
|
plot_scatter_setTitle(&pscatequal, "Equalized Symbols");
|
||||||
|
plot_scatter_setXAxisScale(&pscatequal, -1, 1);
|
||||||
|
plot_scatter_setYAxisScale(&pscatequal, -1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* This function initializes the objects defined as global variables */
|
||||||
|
int base_init(int nof_prb) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
int sf_n_re = 2 * CPNORM_NSYMB * nof_prb * RE_X_RB;
|
||||||
|
int sf_n_samples = 2 * SLOT_LEN_CPNORM(lte_symbol_sz(nof_prb));
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
if (!disable_plots) {
|
||||||
|
tmp_plot = malloc(sizeof(cf_t) * sf_n_re);
|
||||||
|
if (!tmp_plot) {
|
||||||
|
perror("malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
init_plots();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
printf("-- PLOTS are disabled. Graphics library not available --\n\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (input_file_name) {
|
||||||
|
if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* open UHD device */
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
printf("Opening UHD device...\n");
|
||||||
|
if (cuhd_open(uhd_args, &uhd)) {
|
||||||
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
printf("Error UHD not available. Select an input file\n");
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For the input buffer, we allocate space for 1 ms of samples */
|
||||||
|
input_buffer = (cf_t*) malloc(sf_n_samples * sizeof(cf_t));
|
||||||
|
if (!input_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
input_decim_buffer = (cf_t*) malloc(sf_n_samples * sizeof(cf_t));
|
||||||
|
if (!input_decim_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This buffer is the aligned version of input_buffer */
|
||||||
|
sf_buffer = (cf_t*) malloc(sf_n_samples * sizeof(cf_t));
|
||||||
|
if (!sf_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For the rest of the buffers, we allocate for the number of RE in one subframe */
|
||||||
|
fft_buffer = (cf_t*) malloc(sf_n_re * sizeof(cf_t));
|
||||||
|
if (!fft_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_PORTS; i++) {
|
||||||
|
ce[i] = (cf_t*) malloc(sf_n_re * sizeof(cf_t));
|
||||||
|
if (!ce[i]) {
|
||||||
|
perror("malloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(&mib, sizeof(pbch_mib_t));
|
||||||
|
|
||||||
|
if (sync_frame_init(&sframe, DOWNSAMPLE_FACTOR(nof_prb,6))) {
|
||||||
|
fprintf(stderr, "Error initiating PSS/SSS\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chest_init(&chest, LINEAR, CPNORM, nof_prb, NOF_PORTS)) {
|
||||||
|
fprintf(stderr, "Error initializing equalizer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lte_fft_init(&fft, CPNORM, nof_prb)) {
|
||||||
|
fprintf(stderr, "Error initializing FFT\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dci_init(&dci_set, 10);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void base_free() {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (input_file_name) {
|
||||||
|
filesource_free(&fsrc);
|
||||||
|
} else {
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
cuhd_close(uhd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
if (!disable_plots) {
|
||||||
|
if (tmp_plot) {
|
||||||
|
free(tmp_plot);
|
||||||
|
}
|
||||||
|
plot_exit();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pbch_free(&pbch);
|
||||||
|
pdsch_free(&pdsch);
|
||||||
|
pdcch_free(&pdcch);
|
||||||
|
regs_free(®s);
|
||||||
|
sync_frame_free(&sframe);
|
||||||
|
lte_fft_free(&fft);
|
||||||
|
chest_free(&chest);
|
||||||
|
|
||||||
|
free(input_buffer);
|
||||||
|
free(input_decim_buffer);
|
||||||
|
free(fft_buffer);
|
||||||
|
for (i = 0; i < MAX_PORTS; i++) {
|
||||||
|
free(ce[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mib_init(int cell_id) {
|
||||||
|
|
||||||
|
if (mib.nof_prb > sampling_nof_prb) {
|
||||||
|
fprintf(stderr, "Error sampling frequency is %.2f Mhz but captured signal has %d PRB\n",
|
||||||
|
(float) lte_sampling_freq_hz(sampling_nof_prb)/MHZ, mib.nof_prb);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (regs_init(®s, cell_id, mib.nof_prb, mib.nof_ports,
|
||||||
|
mib.phich_resources, mib.phich_length, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error initiating regs\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcfich_init(&pcfich, ®s, cell_id, mib.nof_prb, mib.nof_ports, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PCFICH object\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdcch_init(&pdcch, ®s, mib.nof_prb, mib.nof_ports, cell_id, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PDCCH object\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdsch_init(&pdsch, 1234, mib.nof_prb, mib.nof_ports, cell_id, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error creating PDSCH object\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chest_set_nof_ports(&chest, mib.nof_ports);
|
||||||
|
|
||||||
|
mib_initiated = 1;
|
||||||
|
|
||||||
|
DEBUG("Receiver initiated cell_id=%d nof_prb=%d\n", cell_id, mib.nof_prb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cell_id_init(int nof_prb, int cell_id) {
|
||||||
|
|
||||||
|
if (chest_ref_LTEDL(&chest, cell_id)) {
|
||||||
|
fprintf(stderr, "Error initializing reference signal\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbch_init(&pbch, nof_prb, cell_id, CPNORM)) {
|
||||||
|
fprintf(stderr, "Error initiating PBCH\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_id_initated = 1;
|
||||||
|
DEBUG("PBCH initiated cell_id=%d\n", cell_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char data[10000];
|
||||||
|
|
||||||
|
int rx_run(cf_t *input, int sf_idx) {
|
||||||
|
int cfi, i, cfi_distance, nof_dcis;
|
||||||
|
cf_t *input_decim;
|
||||||
|
ra_pdsch_t ra_dl;
|
||||||
|
ra_prb_t prb_alloc;
|
||||||
|
|
||||||
|
/* Downsample if the signal bandwith is shorter */
|
||||||
|
if (sampling_nof_prb > mib.nof_prb) {
|
||||||
|
decim_c(input, input_decim_buffer, sf_n_samples, DOWNSAMPLE_FACTOR(sampling_nof_prb, mib.nof_prb));
|
||||||
|
input_decim = input_decim_buffer;
|
||||||
|
} else {
|
||||||
|
input_decim = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
lte_fft_run_sf(&fft, input_decim, fft_buffer);
|
||||||
|
|
||||||
|
/* Get channel estimates for each port */
|
||||||
|
chest_ce_sf(&chest, fft_buffer, ce, sf_idx);
|
||||||
|
|
||||||
|
/* First decode PCFICH and obtain CFI */
|
||||||
|
if (pcfich_decode(&pcfich, fft_buffer, ce, sf_idx, &cfi, &cfi_distance)<0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("Decoded CFI=%d with distance %d\n", cfi, cfi_distance);
|
||||||
|
|
||||||
|
if (regs_set_cfi(®s, cfi)) {
|
||||||
|
fprintf(stderr, "Error setting CFI\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pdcch_set_cfi(&pdcch, cfi)) {
|
||||||
|
fprintf(stderr, "Error setting CFI\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pdcch_init_search_ue(&pdcch, 1234);
|
||||||
|
|
||||||
|
dci_set.nof_dcis = 0;
|
||||||
|
nof_dcis = pdcch_decode(&pdcch, fft_buffer, ce, &dci_set, sf_idx);
|
||||||
|
INFO("Received %d DCIs\n", nof_dcis);
|
||||||
|
for (i=0;i<nof_dcis;i++) {
|
||||||
|
dci_msg_type_t type;
|
||||||
|
if (dci_msg_get_type(&dci_set.msg[i], &type, mib.nof_prb, 1234)) {
|
||||||
|
fprintf(stderr, "Can't get DCI message type\n");
|
||||||
|
} else {
|
||||||
|
INFO("MSG %d: L: %d nCCE: %d Nbits: %d. ",i,dci_set.msg[i].location.L,
|
||||||
|
dci_set.msg[i].location.ncce, dci_set.msg[i].location.nof_bits);
|
||||||
|
if (VERBOSE_ISINFO()) {
|
||||||
|
dci_msg_type_fprint(stdout, type);
|
||||||
|
}
|
||||||
|
switch(type.type) {
|
||||||
|
case PDSCH_SCHED:
|
||||||
|
bzero(&ra_dl, sizeof(ra_pdsch_t));
|
||||||
|
if (dci_msg_unpack_pdsch(&dci_set.msg[i], &ra_dl, mib.nof_prb,
|
||||||
|
false)) {
|
||||||
|
fprintf(stderr, "Can't unpack PDSCH message\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (VERBOSE_ISINFO() || !pdsch_total) {
|
||||||
|
printf("\n");
|
||||||
|
ra_pdsch_fprint(stdout, &ra_dl, mib.nof_prb);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
if (ra_prb_get_dl(&prb_alloc, &ra_dl, mib.nof_prb)) {
|
||||||
|
fprintf(stderr, "Error computing resource allocation\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ra_prb_get_re(&prb_alloc, mib.nof_prb, mib.nof_ports,
|
||||||
|
mib.nof_prb<10?(cfi+1):cfi, CPNORM);
|
||||||
|
|
||||||
|
if (pdsch_decode(&pdsch, fft_buffer, ce, data, sf_idx, ra_dl.mcs, &prb_alloc)) {
|
||||||
|
pdsch_errors++;
|
||||||
|
}
|
||||||
|
pdsch_total++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unsupported message type\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
if (!disable_plots && nof_dcis > 0) {
|
||||||
|
int n_re = 2 * RE_X_RB * CPNORM_NSYMB * mib.nof_prb;
|
||||||
|
for (i = 0; i < n_re; i++) {
|
||||||
|
tmp_plot[i] = 10 * log10f(cabsf(fft_buffer[i]));
|
||||||
|
if (isinf(tmp_plot[i])) {
|
||||||
|
tmp_plot[i] = -80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plot_real_setNewData(&poutfft, tmp_plot, n_re);
|
||||||
|
plot_complex_setNewData(&pce, ce[0], n_re);
|
||||||
|
plot_scatter_setNewData(&pscatrecv, pdsch.pdsch_symbols[0], prb_alloc.re_sf[sf_idx]);
|
||||||
|
plot_scatter_setNewData(&pscatequal, pdsch.pdsch_d, prb_alloc.re_sf[sf_idx]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mib_decoder_run(cf_t *input) {
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
lte_fft_run_slot(&fft, input, fft_buffer);
|
||||||
|
|
||||||
|
/* Get channel estimates for each port */
|
||||||
|
for (i = 0; i < NOF_PORTS; i++) {
|
||||||
|
chest_ce_slot_port(&chest, fft_buffer, ce[i], 1, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Decoding PBCH\n", 0);
|
||||||
|
n = pbch_decode(&pbch, fft_buffer, ce, &mib);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_receiver(cf_t *input, int cell_id, int sf_idx) {
|
||||||
|
|
||||||
|
if (!cell_id_initated) {
|
||||||
|
cell_id_init(sampling_nof_prb, cell_id);
|
||||||
|
}
|
||||||
|
if (!mib.nof_prb) {
|
||||||
|
|
||||||
|
if (!sf_idx) {
|
||||||
|
if (mib_decoder_run(&input[sf_n_samples/2])) {
|
||||||
|
INFO("MIB decoded!\n", 0);
|
||||||
|
if (!mib_initiated) {
|
||||||
|
if (mib_init(cell_id)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (VERBOSE_ISINFO() || !frame_cnt) {
|
||||||
|
CLRSTDOUT;
|
||||||
|
printf(" - Phy. CellId:\t %d\n", cell_id);
|
||||||
|
pbch_mib_fprint(stdout, &mib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mib.nof_prb) {
|
||||||
|
if (rx_run(input, sf_idx)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sigintHandler(int sig_num) {
|
||||||
|
go_exit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_uhd() {
|
||||||
|
double samp_freq;
|
||||||
|
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
/* Get the sampling rate from the number of PRB */
|
||||||
|
samp_freq = lte_sampling_freq_hz(sampling_nof_prb);
|
||||||
|
|
||||||
|
INFO("Setting sampling frequency %.2f MHz\n", (float) samp_freq/MHZ);
|
||||||
|
cuhd_set_rx_srate(uhd, samp_freq);
|
||||||
|
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);
|
||||||
|
|
||||||
|
DEBUG("Starting receiver...\n", 0);
|
||||||
|
cuhd_start_rx_stream(uhd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_io(cf_t *buffer, int nsamples) {
|
||||||
|
int n;
|
||||||
|
DEBUG(" ----- RECEIVING %d SAMPLES ---- \n", nsamples);
|
||||||
|
if (input_file_name) {
|
||||||
|
n = filesource_read(&fsrc, buffer, nsamples);
|
||||||
|
if (n == -1) {
|
||||||
|
fprintf(stderr, "Error reading file\n");
|
||||||
|
exit(-1);
|
||||||
|
/* wrap file if arrive to end */
|
||||||
|
} else if (n < nsamples) {
|
||||||
|
DEBUG("Read %d from file. Seeking to 0\n",n);
|
||||||
|
filesource_seek(&fsrc, 0);
|
||||||
|
filesource_read(&fsrc, buffer, nsamples);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#ifndef DISABLE_UHD
|
||||||
|
cuhd_recv(uhd, buffer, nsamples, 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
#ifdef DISABLE_UHD
|
||||||
|
if (argc < 3) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
if (base_init(sampling_nof_prb)) {
|
||||||
|
fprintf(stderr, "Error initializing memory\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If input_file_name is NULL, we read from the USRP */
|
||||||
|
if (!input_file_name) {
|
||||||
|
setup_uhd();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n --- Press Ctrl+C to exit --- \n");
|
||||||
|
signal(SIGINT, sigintHandler);
|
||||||
|
|
||||||
|
/* Initialize variables */
|
||||||
|
mib.sfn = -1;
|
||||||
|
frame_cnt = 0;
|
||||||
|
|
||||||
|
/* The number of samples read from the USRP or file corresponds to 1 ms (subframe) */
|
||||||
|
sf_n_samples = 1920 * lte_symbol_sz(sampling_nof_prb)/128;
|
||||||
|
|
||||||
|
sync_frame_set_threshold(&sframe, find_threshold);
|
||||||
|
|
||||||
|
while (!go_exit && (frame_cnt < nof_frames || nof_frames == -1)) {
|
||||||
|
|
||||||
|
read_io(input_buffer, sf_n_samples);
|
||||||
|
|
||||||
|
switch(sync_frame_push(&sframe, input_buffer, sf_buffer)) {
|
||||||
|
case 0:
|
||||||
|
/* not yet synched */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!(frame_cnt%10)) {
|
||||||
|
mib.sfn++;
|
||||||
|
}
|
||||||
|
/* synch'd and tracking */
|
||||||
|
if (run_receiver(sf_buffer, sync_frame_cell_id(&sframe), sync_frame_sfidx(&sframe))) {
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (!(frame_cnt % 10)) {
|
||||||
|
printf(
|
||||||
|
"SFN: %4d, CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Errors: %4d/%4d, BLER: %.1e\r",
|
||||||
|
mib.sfn, sframe.cur_cfo * 15, sframe.timeoffset / 5, sframe.peak_idx,
|
||||||
|
pdsch_errors, pdsch_total,
|
||||||
|
(float) pdsch_errors / pdsch_total);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Error running automatic synchronization\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_cnt++;
|
||||||
|
if (input_file_name) {
|
||||||
|
usleep(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base_free();
|
||||||
|
|
||||||
|
printf("\nBye\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 DECIM_H
|
||||||
|
#define DECIM_H_
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
|
||||||
|
typedef _Complex float cf_t;
|
||||||
|
|
||||||
|
|
||||||
|
LIBLTE_API void decim_c(cf_t *input, cf_t *output, int M, int len);
|
||||||
|
LIBLTE_API void decim_f(float *input, float *output, int M, int len);
|
||||||
|
|
||||||
|
#endif // DECIM_H
|
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SYNC_FRAME_
|
||||||
|
#define SYNC_FRAME_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/sync/sync.h"
|
||||||
|
#include "liblte/phy/sync/cfo.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Uses sync object to automatically manage the FIND and TRACKING states.
|
||||||
|
* It is suposed to work on a subframe basis. The input signal must be sampled at
|
||||||
|
* a frequency integer multiple of 1.92 MHz. The signal is internally downsampled
|
||||||
|
* and fed to the sync object.
|
||||||
|
*
|
||||||
|
* This object also deals with frame alignment and CFO correction, returning an
|
||||||
|
* output signal aligned both in time and frequency.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum sync_frame_state { SF_FIND, SF_TRACK };
|
||||||
|
|
||||||
|
#define SYNC_SF_LEN 1920 // 1ms at 1.92 MHz
|
||||||
|
|
||||||
|
#define TRACK_MAX_LOST 10
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct LIBLTE_API {
|
||||||
|
sync_t s;
|
||||||
|
enum sync_frame_state state;
|
||||||
|
int downsampling;
|
||||||
|
unsigned long frame_cnt;
|
||||||
|
bool fb_wp;
|
||||||
|
int frame_size;
|
||||||
|
cf_t *input_buffer;
|
||||||
|
cf_t *input_downsampled;
|
||||||
|
cfo_t cfocorr;
|
||||||
|
float cur_cfo;
|
||||||
|
int peak_idx;
|
||||||
|
int cell_id;
|
||||||
|
float timeoffset;
|
||||||
|
int last_found;
|
||||||
|
int sf_idx;
|
||||||
|
}sync_frame_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* Initializes the automatic tracker, setting the downsampling ratio for the input signal.
|
||||||
|
* upsampled is the ratio of the provided signal sampling frequency to 1.92 Mhz. E.g. if input is sampled at 3.84 Mhz,
|
||||||
|
* upsampled should be 2.
|
||||||
|
*/
|
||||||
|
LIBLTE_API int sync_frame_init(sync_frame_t *q, int upsampled);
|
||||||
|
|
||||||
|
LIBLTE_API void sync_frame_free(sync_frame_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API void sync_frame_set_threshold(sync_frame_t *q, float threshold);
|
||||||
|
|
||||||
|
LIBLTE_API int sync_frame_cell_id(sync_frame_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API int sync_frame_sfidx(sync_frame_t *q);
|
||||||
|
|
||||||
|
/* Automatically time/freq synchronizes the input signal. Returns 1 if the signal is synched and locked,
|
||||||
|
* and fills the output buffer with the time and frequency aligned version of the signal.
|
||||||
|
* If 0 is returned, the PSS was not found. -1 is returned in case of error.
|
||||||
|
*
|
||||||
|
* The provided signal can be sampled at an integer multiple of 1.92 Mhz.
|
||||||
|
* The sampling ratio is provided when calling the sync_auto_reset() function.
|
||||||
|
*
|
||||||
|
* The buffer input must have subframe_size samples (used in sync_init)
|
||||||
|
*/
|
||||||
|
LIBLTE_API int sync_frame_push(sync_frame_t *q, cf_t *input, cf_t *output);
|
||||||
|
|
||||||
|
/* Resets the automatic tracker */
|
||||||
|
LIBLTE_API void sync_frame_reset(sync_frame_t *q);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SYNC_FRAME_
|
||||||
|
|
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 <complex.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "liblte/phy/resampling/decim.h"
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Performs integer linear decimation by a factor of M */
|
||||||
|
void decim_c(cf_t *input, cf_t *output, int M, int len) {
|
||||||
|
int i;
|
||||||
|
for (i=0;i<len/M;i++) {
|
||||||
|
output[i] = input[i*M];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Performs integer linear decimation by a factor of M */
|
||||||
|
void decim_f(float *input, float *output, int M, int len) {
|
||||||
|
int i;
|
||||||
|
for (i=0;i<len/M;i++) {
|
||||||
|
output[i] = input[i*M];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "liblte/phy/resampling/decim.h"
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
#include "liblte/phy/sync/sync_frame.h"
|
||||||
|
|
||||||
|
|
||||||
|
int sync_frame_init(sync_frame_t *q, int downsampling) {
|
||||||
|
int ret = -1;
|
||||||
|
bzero(q, sizeof(sync_frame_t));
|
||||||
|
|
||||||
|
if(sync_init(&q->s, SYNC_SF_LEN)) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
sync_pss_det_peak_to_avg(&q->s);
|
||||||
|
|
||||||
|
if (cfo_init(&q->cfocorr, SYNC_SF_LEN * downsampling)) {
|
||||||
|
fprintf(stderr, "Error initiating CFO\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->input_buffer = malloc(2 * SYNC_SF_LEN * downsampling * sizeof(cf_t));
|
||||||
|
if (!q->input_buffer) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->input_downsampled = malloc(SYNC_SF_LEN * sizeof(cf_t));
|
||||||
|
if (!q->input_downsampled) {
|
||||||
|
perror("malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->downsampling = downsampling;
|
||||||
|
sync_frame_reset(q);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
if (ret == -1) {
|
||||||
|
sync_frame_free(q);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_frame_free(sync_frame_t *q) {
|
||||||
|
if (q->input_buffer) {
|
||||||
|
free(q->input_buffer);
|
||||||
|
}
|
||||||
|
if (q->input_downsampled) {
|
||||||
|
free(q->input_downsampled);
|
||||||
|
}
|
||||||
|
cfo_free(&q->cfocorr);
|
||||||
|
sync_free(&q->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_frame_run(sync_frame_t *q, cf_t *input) {
|
||||||
|
int track_idx;
|
||||||
|
|
||||||
|
switch (q->state) {
|
||||||
|
|
||||||
|
case SF_FIND:
|
||||||
|
q->peak_idx = sync_find(&q->s, input);
|
||||||
|
q->cell_id = sync_get_cell_id(&q->s);
|
||||||
|
|
||||||
|
INFO("FIND %3d:\tPAR=%.2f\n", (int) q->frame_cnt, sync_get_peak_to_avg(&q->s));
|
||||||
|
|
||||||
|
if (q->peak_idx != -1 && q->cell_id != -1) {
|
||||||
|
|
||||||
|
/* Get the subframe index (0 or 5) */
|
||||||
|
q->sf_idx = sync_get_slot_id(&q->s)/2;
|
||||||
|
|
||||||
|
/* Reset variables */
|
||||||
|
q->last_found = 0;
|
||||||
|
q->timeoffset = 0;
|
||||||
|
q->frame_cnt = 0;
|
||||||
|
|
||||||
|
/* Goto Tracking state */
|
||||||
|
q->state = SF_TRACK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SF_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) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
track_idx = sync_track(&q->s, &input[q->peak_idx - TRACK_LEN]);
|
||||||
|
|
||||||
|
INFO("TRACK %3d: SF=%d. Previous idx is %d New Offset is %d\n",
|
||||||
|
(int) q->frame_cnt, q->sf_idx, q->peak_idx, track_idx - TRACK_LEN);
|
||||||
|
|
||||||
|
if (track_idx != -1) {
|
||||||
|
INFO("Expected SF idx %d but got %d. Going back to FIND\n", q->sf_idx,
|
||||||
|
sync_get_slot_id(&q->s)/2);
|
||||||
|
|
||||||
|
/* Make sure subframe idx is what we expect */
|
||||||
|
if (q->sf_idx != sync_get_slot_id(&q->s)/2) {
|
||||||
|
INFO("Expected SF idx %d but got %d. Going back to FIND\n", q->sf_idx,
|
||||||
|
sync_get_slot_id(&q->s)/2);
|
||||||
|
q->state = SF_FIND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute cumulative moving average CFO */
|
||||||
|
q->cur_cfo = (sync_get_cfo(&q->s) + q->frame_cnt * q->cur_cfo) / (q->frame_cnt + 1);
|
||||||
|
|
||||||
|
/* compute cumulative moving average time offset */
|
||||||
|
q->timeoffset = (float) (track_idx - TRACK_LEN + q->timeoffset * q->frame_cnt)
|
||||||
|
/ (q->frame_cnt + 1);
|
||||||
|
|
||||||
|
q->last_found = q->frame_cnt;
|
||||||
|
q->peak_idx = (q->peak_idx + track_idx - TRACK_LEN) % SYNC_SF_LEN;
|
||||||
|
|
||||||
|
if (q->peak_idx < 0) {
|
||||||
|
INFO("PSS lost (peak_idx=%d). Going back to FIND\n", q->peak_idx);
|
||||||
|
q->state = SF_FIND;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* if sync not found, adjust time offset with the averaged value */
|
||||||
|
q->peak_idx = (q->peak_idx + (int) q->timeoffset) % SYNC_SF_LEN;
|
||||||
|
|
||||||
|
/* if we missed too many PSS go back to FIND */
|
||||||
|
if (q->frame_cnt - q->last_found > TRACK_MAX_LOST) {
|
||||||
|
INFO("%d frames lost. Going back to FIND", (int) q->frame_cnt - q->last_found);
|
||||||
|
q->state = SF_FIND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q->frame_cnt++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_frame_set_threshold(sync_frame_t *q, float threshold) {
|
||||||
|
sync_set_threshold(&q->s, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sync_frame_cell_id(sync_frame_t *q) {
|
||||||
|
return q->cell_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sync_frame_sfidx(sync_frame_t *q) {
|
||||||
|
return q->sf_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sync_frame_push(sync_frame_t *q, cf_t *input, cf_t *output) {
|
||||||
|
int retval = 0;
|
||||||
|
int frame_start;
|
||||||
|
cf_t *input_ds;
|
||||||
|
int sf_len;
|
||||||
|
|
||||||
|
if (q->downsampling == 1) {
|
||||||
|
input_ds = input;
|
||||||
|
} else {
|
||||||
|
decim_c(input, q->input_downsampled, q->downsampling, SYNC_SF_LEN * q->downsampling);
|
||||||
|
input_ds = q->input_downsampled;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_frame_run(q, input_ds);
|
||||||
|
|
||||||
|
sf_len = q->downsampling * SYNC_SF_LEN;
|
||||||
|
|
||||||
|
if (q->state == SF_FIND) {
|
||||||
|
memcpy(q->input_buffer, input, sf_len * sizeof(cf_t));
|
||||||
|
} else {
|
||||||
|
frame_start = q->downsampling * q->peak_idx - sf_len/2;
|
||||||
|
|
||||||
|
DEBUG("Peak_idx=%d, frame_start=%d cfo=%.3f\n",q->peak_idx,
|
||||||
|
frame_start, q->cur_cfo);
|
||||||
|
|
||||||
|
if (frame_start > 0) {
|
||||||
|
if (q->fb_wp) {
|
||||||
|
memcpy(&q->input_buffer[(sf_len - frame_start)], input, frame_start * sizeof(cf_t));
|
||||||
|
memcpy(output, q->input_buffer, sf_len * sizeof(cf_t));
|
||||||
|
retval = 1;
|
||||||
|
}
|
||||||
|
memcpy(q->input_buffer, &input[frame_start], (sf_len - frame_start) * sizeof(cf_t));
|
||||||
|
q->fb_wp = true;
|
||||||
|
} else {
|
||||||
|
memcpy(output, &q->input_buffer[sf_len + frame_start], (-frame_start) * sizeof(cf_t));
|
||||||
|
memcpy(&output[-frame_start], input, (sf_len + frame_start) * sizeof(cf_t));
|
||||||
|
memcpy(&q->input_buffer[sf_len + frame_start], &input[sf_len + frame_start], (-frame_start) * sizeof(cf_t));
|
||||||
|
retval = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Frequency Synchronization */
|
||||||
|
if (retval) {
|
||||||
|
cfo_correct(&q->cfocorr, output, -q->cur_cfo / 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!retval) {
|
||||||
|
DEBUG("Frame Buffered\n",0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_frame_reset(sync_frame_t *q) {
|
||||||
|
q->state = SF_FIND;
|
||||||
|
q->frame_cnt = 0;
|
||||||
|
q->fb_wp = false;
|
||||||
|
q->cur_cfo = 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue