Fixed PDSCH bugs. Cleaned MCS interface in RA. Converted MODEM module to stdint

master
ismagom 11 years ago
parent 01ca2dcf28
commit 09243c7996

@ -48,6 +48,7 @@ lte_cell_t cell = {
};
uint32_t cfi=1;
uint32_t mcs_idx = 12;
int nof_frames = -1;
char *uhd_args = "";
@ -70,12 +71,12 @@ void usage(char *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-m MCS index [Default %d]\n", mcs_idx);
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", cell.nof_prb);
@ -92,15 +93,15 @@ void parse_args(int argc, char **argv) {
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 'm':
mcs_idx = atoi(argv[optind]);
break;
case 'n':
nof_frames = atoi(argv[optind]);
break;
@ -289,7 +290,7 @@ int main(int argc, char **argv) {
bzero(&ra_dl, sizeof(ra_pdsch_t));
ra_dl.harq_process = 0;
ra_pdsch_set_mcs(&ra_dl, QPSK, 5);
ra_dl.mcs_idx = mcs_idx;
ra_dl.ndi = 0;
ra_dl.rv_idx = 0;
ra_dl.alloc_type = alloc_type0;
@ -299,7 +300,7 @@ int main(int argc, char **argv) {
ra_prb_get_dl(&prb_alloc, &ra_dl, cell.nof_prb);
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, 1, cell.nof_prb<10?(cfi+1):cfi, CPNORM);
ra_dl.mcs.tbs = ra_tbs_from_idx(ra_dl.mcs.tbs_idx, cell.nof_prb);
ra_mcs_from_idx_dl(mcs_idx, cell.nof_prb, &ra_dl.mcs);
ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);
@ -322,7 +323,7 @@ int main(int argc, char **argv) {
}
while (nf < nof_frames || nof_frames == -1) {
for (sf_idx = 0; sf_idx < NSUBFRAMES_X_FRAME; sf_idx++) {
for (sf_idx = 0; sf_idx < NSUBFRAMES_X_FRAME && (nf < nof_frames || nof_frames == -1); sf_idx++) {
bzero(sf_buffer, sizeof(cf_t) * sf_n_re);
if (sf_idx == 0 || sf_idx == 5) {
@ -346,6 +347,7 @@ int main(int argc, char **argv) {
data[i] = rand()%2;
}
INFO("Puttting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L);
if (pdcch_encode(&pdcch, &dci_msg, locations[sf_idx][0], 1234, sf_symbols, sf_idx, cfi)) {
fprintf(stderr, "Error encoding DCI message\n");
exit(-1);
@ -366,11 +368,11 @@ int main(int argc, char **argv) {
cuhd_send(uhd, output_buffer, sf_n_samples, 1);
#endif
}
nf++;
}
mib.sfn = (mib.sfn + 1) % 1024;
printf("SFN: %4d\r", mib.sfn);
fflush(stdout);
nf++;
}
base_free();

@ -398,11 +398,10 @@ int cell_id_init(int nof_prb, int cell_id) {
char data[10000];
int rx_run(cf_t *input, int sf_idx) {
int pdsch_run(cf_t *input, uint32_t sf_idx) {
uint32_t cfi, cfi_distance, i;
cf_t *input_decim;
ra_pdsch_t ra_dl;
ra_prb_t prb_alloc;
dci_location_t locations[10];
dci_msg_t dci_msg;
uint32_t nof_locations;
@ -422,6 +421,7 @@ int rx_run(cf_t *input, int sf_idx) {
/* First decode PCFICH and obtain CFI */
if (pcfich_decode(&pcfich, fft_buffer, ce, sf_idx, &cfi, &cfi_distance)<0) {
fprintf(stderr, "Error decoding PCFICH\n");
return -1;
}
@ -441,56 +441,27 @@ int rx_run(cf_t *input, int sf_idx) {
fprintf(stderr, "Error extracting LLRs\n");
return -1;
}
if (pdcch_decode_msg(&pdcch, &dci_msg, Format1A, &crc_rem)) {
if (pdcch_decode_msg(&pdcch, &dci_msg, Format1, &crc_rem)) {
fprintf(stderr, "Error decoding DCI msg\n");
return -1;
}
}
if (crc_rem == 1234) {
dci_msg_type_t type;
if (dci_msg_get_type(&dci_msg, &type, cell.nof_prb, 1234, 1234)) {
fprintf(stderr, "Can't get DCI message type\n");
} else {
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_msg, &ra_dl, cell.nof_prb,
false)) {
fprintf(stderr, "Can't unpack PDSCH message\n");
break;
}
if (VERBOSE_ISINFO() || !pkts_total) {
printf("\n");
ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);
printf("\n");
}
if (ra_prb_get_dl(&prb_alloc, &ra_dl, cell.nof_prb)) {
fprintf(stderr, "Error computing resource allocation\n");
break;
}
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports,
cell.nof_prb<10?(cfi+1):cfi, CPNORM);
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
break;
}
if (pdsch_decode(&pdsch, fft_buffer, ce, data, sf_idx, &harq_process, ra_dl.rv_idx)) {
pkt_errors++;
}
pkts_total++;
break;
default:
fprintf(stderr, "Unsupported message type\n");
break;
}
if (dci_msg_to_ra_dl(&dci_msg, 1234, 1234, cell, cfi, &ra_dl)) {
fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n");
return -1;
}
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
return -1;
}
if (pdsch_decode(&pdsch, fft_buffer, ce, data, sf_idx, &harq_process, ra_dl.rv_idx)) {
pkt_errors++;
}
pkts_total++;
}
#ifndef DISABLE_GRAPHICS
@ -504,8 +475,8 @@ int rx_run(cf_t *input, int sf_idx) {
}
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]);
plot_scatter_setNewData(&pscatrecv, pdsch.pdsch_symbols[0], ra_dl.prb_alloc.re_sf[sf_idx]);
plot_scatter_setNewData(&pscatequal, pdsch.pdsch_d, ra_dl.prb_alloc.re_sf[sf_idx]);
}
#endif
@ -574,7 +545,8 @@ int run_receiver(cf_t *input, uint32_t cell_id, uint32_t sf_idx) {
}
}
if (cell.nof_prb && !pbch_only) {
if (rx_run(input, sf_idx)) {
if (pdsch_run(input, sf_idx)) {
fprintf(stderr, "\nError running PDSCH decoder\n");
return -1;
}
}
@ -665,20 +637,22 @@ int main(int argc, char **argv) {
sync_frame_set_threshold(&sframe, find_threshold);
sf_idx = 0;
cell_id = cell_id_file;
if (input_file_name) {
in_ptr = input_buffer;
} else {
in_ptr = sf_buffer;
}
while (!go_exit && (frame_cnt < nof_frames || nof_frames == -1)) {
read_io(input_buffer, sf_n_samples);
if (input_file_name) {
ret = 1;
sf_idx = 0;
cell_id = cell_id_file;
in_ptr = input_buffer;
} else {
if (!input_file_name) {
ret = sync_frame_push(&sframe, input_buffer, sf_buffer);
in_ptr = sf_buffer;
cell_id = 0;
sf_idx = 0;
} else {
ret = 1;
}
switch(ret ) {
case 0:
@ -694,10 +668,11 @@ int main(int argc, char **argv) {
if (!input_file_name) {
sf_idx = sync_frame_sfidx(&sframe);
cell_id = sync_frame_cell_id(&sframe);
}
}
/* synch'd and tracking */
if (run_receiver(in_ptr, cell_id, sf_idx)) {
fprintf(stderr, "\nError running receiver\n");fflush(stdout);
exit(-1);
}
@ -708,7 +683,9 @@ int main(int argc, char **argv) {
(float) pkt_errors / pkts_total);
fflush(stdout);
}
if (input_file_name) {
sf_idx++;
}
break;
default:
fprintf(stderr, "Error running automatic synchronization\n");

@ -34,40 +34,40 @@
#include "liblte/config.h"
#define NSUBFRAMES_X_FRAME 10
#define NSLOTS_X_FRAME (2*NSUBFRAMES_X_FRAME)
#define NSLOTS_X_FRAME (2*NSUBFRAMES_X_FRAME)
#define LTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE
#define LTE_NULL_BIT 0
#define LTE_NULL_BIT 0
#define LTE_NULL_SYMBOL 2
#define LTE_NIL_SYMBOL 2
#define MAX_PORTS 4
#define MAX_PORTS 4
#define MAX_LAYERS 8
#define MAX_CODEWORDS 2
#define LTE_CRC24A 0x1864CFB
#define LTE_CRC24B 0X1800063
#define LTE_CRC16 0x11021
#define LTE_CRC8 0x19B
#define LTE_CRC16 0x11021
#define LTE_CRC8 0x19B
typedef enum {CPNORM, CPEXT} lte_cp_t;
#define SIRNTI 0xFFFF
#define SIRNTI 0xFFFF
#define PRNTI 0xFFFE
#define MRNTI 0xFFFD
#define MAX_NSYMB 7
#define MAX_NSYMB 7
#define CPNORM_NSYMB 7
#define CPNORM_NSYMB 7
#define CPNORM_SF_NSYMB 2*CPNORM_NSYMB
#define CPNORM_0_LEN 160
#define CPNORM_LEN 144
#define CPNORM_0_LEN 160
#define CPNORM_LEN 144
#define CPEXT_NSYMB 6
#define CPEXT_NSYMB 6
#define CPEXT_SF_NSYMB 2*CPEXT_NSYMB
#define CPEXT_LEN 512
#define CPEXT_7_5_LEN 1024
#define CPEXT_LEN 512
#define CPEXT_7_5_LEN 1024
#define CP_ISNORM(cp) (cp==CPNORM)
#define CP_ISEXT(cp) (cp==CPEXT)
@ -120,6 +120,10 @@ typedef enum LIBLTE_API {
typedef enum LIBLTE_API { PHICH_NORM, PHICH_EXT} phich_length_t;
typedef enum LIBLTE_API { R_1_6, R_1_2, R_1, R_2} phich_resources_t;
typedef enum LIBLTE_API {
LTE_BPSK = 1, LTE_QPSK = 2, LTE_QAM16 = 4, LTE_QAM64 = 6
} lte_mod_t;
typedef struct LIBLTE_API {
int id;
@ -147,6 +151,10 @@ LIBLTE_API uint32_t lte_voffset(uint32_t symbol_id,
LIBLTE_API int lte_cb_size(uint32_t index);
LIBLTE_API char *lte_mod_string(lte_mod_t mod);
LIBLTE_API uint32_t lte_mod_bits_x_symbol(lte_mod_t mod);
LIBLTE_API int lte_find_cb_index(uint32_t long_cb);
LIBLTE_API float lte_band_fd(uint32_t earfcn);

@ -38,13 +38,13 @@
typedef _Complex float cf_t;
typedef struct LIBLTE_API {
enum modem_std table; /* In this implementation, mapping table is hard-coded */
lte_mod_t mod; /* In this implementation, mapping table is hard-coded */
}demod_hard_t;
LIBLTE_API void demod_hard_init(demod_hard_t* q);
LIBLTE_API void demod_hard_table_set(demod_hard_t* q, enum modem_std table);
LIBLTE_API int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, int nsymbols);
LIBLTE_API void demod_hard_table_set(demod_hard_t* q, lte_mod_t mod);
LIBLTE_API int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, uint32_t nsymbols);
@ -52,7 +52,7 @@ LIBLTE_API int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits,
typedef struct LIBLTE_API {
demod_hard_t obj;
struct demod_hard_init {
enum modem_std std; // Symbol mapping standard (see modem_table.h)
lte_mod_t std; // Symbol mapping standard (see modem_table.h)
} init;
cf_t* input;

@ -56,7 +56,7 @@ typedef struct LIBLTE_API {
modem_table_t table;
struct demod_soft_init{
enum modem_std std; // symbol mapping standard (see modem_table.h)
lte_mod_t std; // symbol mapping standard (see modem_table.h)
} init;
const cf_t* input;

@ -37,13 +37,13 @@
typedef _Complex float cf_t;
LIBLTE_API int mod_modulate(modem_table_t* table, const char *bits, cf_t* symbols, int nbits);
LIBLTE_API int mod_modulate(modem_table_t* table, const char *bits, cf_t* symbols, uint32_t nbits);
/* High-level API */
typedef struct LIBLTE_API {
modem_table_t obj;
struct mod_init {
enum modem_std std; // symbol mapping standard (see modem_table.h)
lte_mod_t std; // symbol mapping standard (see modem_table.h)
} init;
const char* input;

@ -34,30 +34,36 @@
#include <complex.h>
#include <stdint.h>
#include "liblte/phy/common/phy_common.h"
#include "liblte/config.h"
typedef _Complex float cf_t;
typedef struct LIBLTE_API {
int idx[2][6][32];
uint32_t idx[2][6][32];
}soft_table_t;
typedef struct LIBLTE_API {
cf_t* symbol_table; // bit-to-symbol mapping
soft_table_t soft_table; // symbol-to-bit mapping (used in soft demodulating)
int nsymbols; // number of modulation symbols
int nbits_x_symbol; // number of bits per symbol
uint32_t nsymbols; // number of modulation symbols
uint32_t nbits_x_symbol; // number of bits per symbol
}modem_table_t;
// Modulation standards
enum modem_std {
LTE_BPSK = 1, LTE_QPSK = 2, LTE_QAM16 = 4, LTE_QAM64 = 6
};
LIBLTE_API void modem_table_init(modem_table_t* q);
LIBLTE_API void modem_table_free(modem_table_t* q);
LIBLTE_API void modem_table_reset(modem_table_t* q);
LIBLTE_API int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol);
LIBLTE_API int modem_table_std(modem_table_t* q, enum modem_std table, bool compute_soft_demod);
LIBLTE_API int modem_table_set(modem_table_t* q,
cf_t* table,
soft_table_t *soft_table,
uint32_t nsymbols,
uint32_t nbits_x_symbol);
LIBLTE_API int modem_table_lte(modem_table_t* q,
lte_mod_t modulation,
bool compute_soft_demod);
#endif // MODEM_TABLE_

@ -71,7 +71,24 @@ typedef struct LIBLTE_API {
uint32_t nof_bits;
} dci_msg_t;
/* Converts a received PDSCH DL scheduling DCI message
* to ra structures ready to be passed to the harq setup function
*/
LIBLTE_API int dci_msg_to_ra_dl(dci_msg_t *msg,
uint16_t msg_rnti,
uint16_t c_rnti,
lte_cell_t cell,
uint32_t cfi,
ra_pdsch_t *ra_dl);
/* TODO
LIBLTE_API int dci_msg_to_ra_ul(dci_msg_t *msg,
uint16_t msg_rnti,
uint16_t c_rnti,
lte_cell_t cell,
uint32_t cfi,
ra_pusch_t *ra_ul);
*/
LIBLTE_API char* dci_format_string(dci_format_t format);
LIBLTE_API int dci_location_set(dci_location_t *c,

@ -57,7 +57,6 @@ typedef struct LIBLTE_API {
float **pdsch_w_buff_f;
char **pdsch_w_buff_c;
struct cb_segm {
uint32_t F;
uint32_t C;
@ -82,8 +81,8 @@ typedef struct LIBLTE_API {
cf_t *pdsch_symbols[MAX_PORTS];
cf_t *pdsch_x[MAX_PORTS];
cf_t *pdsch_d;
void *cb_in;
char *cb_out;
char *cb_in;
void *cb_out;
void *pdsch_e;
/* tx & rx objects */

@ -37,17 +37,9 @@
* allocation.
*/
typedef enum LIBLTE_API {
MOD_NULL = 0, BPSK = 1, QPSK = 2, QAM16 = 3, QAM64 = 4
} ra_mod_t;
typedef struct LIBLTE_API {
ra_mod_t mod; // By default, mod = MOD_NULL and the mcs_idx value is taken by the packing functions
// otherwise mod + tbs values are used to generate the mcs_idx automatically.
uint32_t tbs_idx;
uint32_t mcs_idx;
uint32_t tbs;// If tbs<=0, the tbs_idx value is taken by the packing functions to generate the DCI
// message. Otherwise the tbs_idx corresponding to the lower nearest TBS is taken.
lte_mod_t mod;
uint32_t tbs;
} ra_mcs_t;
typedef enum LIBLTE_API {
@ -60,7 +52,8 @@ typedef struct LIBLTE_API {
typedef struct LIBLTE_API {
uint32_t vrb_bitmask;
uint32_t rbg_subset;bool shift;
uint32_t rbg_subset;
bool shift;
} ra_type1_t;
typedef struct LIBLTE_API {
@ -78,6 +71,17 @@ typedef struct LIBLTE_API {
} mode;
} ra_type2_t;
typedef struct LIBLTE_API {
uint32_t prb_idx[MAX_PRB];
uint32_t nof_prb;
} ra_prb_slot_t;
typedef struct LIBLTE_API {
ra_prb_slot_t slot[2];
uint32_t lstart;
uint32_t re_sf[NSUBFRAMES_X_FRAME];
} ra_prb_t;
typedef struct LIBLTE_API {
uint16_t rnti;
ra_type_t alloc_type;
@ -86,6 +90,8 @@ typedef struct LIBLTE_API {
ra_type1_t type1_alloc;
ra_type2_t type2_alloc;
};
ra_prb_t prb_alloc;
uint32_t mcs_idx;
ra_mcs_t mcs;
uint32_t harq_process;
uint32_t rv_idx;
@ -93,7 +99,7 @@ typedef struct LIBLTE_API {
} ra_pdsch_t;
typedef struct LIBLTE_API {
/* 36.213 Table 8.4-2: hop_half is 0 for < 10 Mhz and 10 for > 10 Mh.
/* 36.213 Table 8.4-2: hop_half is 0 for < 10 Mhz and 10 for > 10 Mhz.
* hop_quart is 00 for > 10 Mhz and hop_quart_neg is 01 for > 10 Mhz.
*/
enum {
@ -104,7 +110,10 @@ typedef struct LIBLTE_API {
hop_type_2 = 3
} freq_hop_fl;
ra_prb_t prb_alloc;
ra_type2_t type2_alloc;
uint32_t mcs_idx;
ra_mcs_t mcs;
uint32_t rv_idx; // If set to non-zero, a retransmission is requested with the same modulation
// than before (Format0 message, see also 8.6.1 in 36.2313).
@ -113,17 +122,6 @@ typedef struct LIBLTE_API {
} ra_pusch_t;
typedef struct LIBLTE_API {
uint32_t prb_idx[MAX_PRB];
uint32_t nof_prb;
} ra_prb_slot_t;
typedef struct LIBLTE_API {
ra_prb_slot_t slot[2];
uint32_t lstart;
uint32_t re_sf[NSUBFRAMES_X_FRAME];
} ra_prb_t;
LIBLTE_API void ra_prb_fprint(FILE *f,
ra_prb_slot_t *prb);
@ -147,36 +145,22 @@ LIBLTE_API uint32_t ra_nprb_dl(ra_pdsch_t *ra,
LIBLTE_API uint32_t ra_nprb_ul(ra_pusch_t *ra,
uint32_t nof_prb);
LIBLTE_API uint32_t ra_mod_bits_x_symbol(ra_mod_t mod);
LIBLTE_API uint32_t ra_mcs_to_table_idx(ra_mcs_t *mcs);
LIBLTE_API int ra_mcs_from_idx_dl(uint32_t idx,
LIBLTE_API int ra_mcs_from_idx_dl(uint32_t mcs_idx,
uint32_t nof_prb,
ra_mcs_t *mcs);
LIBLTE_API int ra_mcs_from_idx_ul(uint32_t idx,
LIBLTE_API int ra_mcs_from_idx_ul(uint32_t mcs_idx,
uint32_t nof_prb,
ra_mcs_t *mcs);
LIBLTE_API int ra_tbs_from_idx_format1c(uint32_t tbs_idx);
LIBLTE_API int ra_tbs_to_table_idx_format1c(uint32_t tbs);
LIBLTE_API int ra_tbs_from_idx(uint32_t tbs_idx,
uint32_t n_prb);
LIBLTE_API int ra_tbs_to_table_idx(uint32_t tbs,
uint32_t n_prb);
LIBLTE_API uint32_t ra_mcs_to_table_idx(ra_mcs_t *mcs);
LIBLTE_API int ra_mcs_from_idx_dl(uint32_t idx,
ra_mcs_t *mcs);
LIBLTE_API int ra_mcs_from_idx_ul(uint32_t idx,
ra_mcs_t *mcs);
LIBLTE_API char *ra_mod_string(ra_mod_t mod);
LIBLTE_API uint32_t ra_type0_P(uint32_t nof_prb);
LIBLTE_API uint32_t ra_type2_to_riv(uint32_t L_crb,
@ -199,13 +183,6 @@ LIBLTE_API uint32_t ra_type2_ngap(uint32_t nof_prb,
LIBLTE_API uint32_t ra_type1_N_rb(uint32_t nof_prb);
LIBLTE_API void ra_pdsch_set_mcs_index(ra_pdsch_t *ra,
uint32_t mcs_idx);
LIBLTE_API void ra_pdsch_set_mcs(ra_pdsch_t *ra,
ra_mod_t mod,
uint32_t tbs_idx);
LIBLTE_API void ra_pdsch_fprint(FILE *f,
ra_pdsch_t *ra,
uint32_t nof_prb);

@ -76,6 +76,36 @@ int lte_cb_size(uint32_t index) {
}
}
char *lte_mod_string(lte_mod_t mod) {
switch (mod) {
case LTE_BPSK:
return "BPSK";
case LTE_QPSK:
return "QPSK";
case LTE_QAM16:
return "QAM16";
case LTE_QAM64:
return "QAM64";
default:
return "N/A";
}
}
uint32_t lte_mod_bits_x_symbol(lte_mod_t mod) {
switch (mod) {
case LTE_BPSK:
return 1;
case LTE_QPSK:
return 2;
case LTE_QAM16:
return 4;
case LTE_QAM64:
return 6;
default:
return 0;
}
}
/*
* Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212
*/

@ -37,14 +37,14 @@ void demod_hard_init(demod_hard_t* q) {
bzero((void*) q, sizeof(demod_hard_t));
}
void demod_hard_table_set(demod_hard_t* q, enum modem_std table) {
q->table = table;
void demod_hard_table_set(demod_hard_t* q, lte_mod_t mod) {
q->mod = mod;
}
int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, int nsymbols) {
int demod_hard_demodulate(demod_hard_t* q, cf_t* symbols, char *bits, uint32_t nsymbols) {
int nbits=-1;
switch(q->table) {
switch(q->mod) {
case LTE_BPSK:
hard_bpsk_demod(symbols,bits,nsymbols);
nbits=nsymbols;

@ -69,7 +69,7 @@ int demod_soft_demodulate(demod_soft_t *q, const cf_t* symbols, float* llr, int
/* High-Level API */
int demod_soft_initialize(demod_soft_hl* hl) {
modem_table_init(&hl->table);
if (modem_table_std(&hl->table,hl->init.std,true)) {
if (modem_table_lte(&hl->table,hl->init.std,true)) {
return -1;
}
demod_soft_init(&hl->obj);

@ -33,6 +33,7 @@
#include "liblte/phy/modem/demod_hard.h"
#include "hard_demod_lte.h"
/**
* @ingroup Hard BPSK demodulator
*
@ -46,9 +47,9 @@
* \param N Number of input symbols
* \param modulation Modulation type
*/
inline void hard_bpsk_demod(const cf_t* in, char* out, int N)
inline void hard_bpsk_demod(const cf_t* in, char* out, uint32_t N)
{
int s;
uint32_t s;
for (s=0; s<N; s++) { /* received symbols */
if (__real__ in[s] > 0) {
@ -81,9 +82,9 @@ inline void hard_bpsk_demod(const cf_t* in, char* out, int N)
* \param N Number of input symbols
* \param modulation Modulation type
*/
inline void hard_qpsk_demod(const cf_t* in, char* out, int N)
inline void hard_qpsk_demod(const cf_t* in, char* out, uint32_t N)
{
int s;
uint32_t s;
for (s=0; s<N; s++) {
if (__real__ in[s] > 0) {
@ -115,9 +116,9 @@ inline void hard_qpsk_demod(const cf_t* in, char* out, int N)
* \param N Number of input symbols
* \param modulation Modulation type
*/
inline void hard_qam16_demod(const cf_t* in, char* out, int N)
inline void hard_qam16_demod(const cf_t* in, char* out, uint32_t N)
{
int s;
uint32_t s;
for (s=0; s<N; s++) {
if (__real__ in[s] > 0) {
@ -157,9 +158,9 @@ inline void hard_qam16_demod(const cf_t* in, char* out, int N)
* \param N Number of input symbols
* \param modulation Modulation type
*/
inline void hard_qam64_demod(const cf_t* in, char* out, int N)
inline void hard_qam64_demod(const cf_t* in, char* out, uint32_t N)
{
int s;
uint32_t s;
for (s=0; s<N; s++) {
/* bits associated with/obtained from in-phase component: b0, b2, b4 */

@ -25,7 +25,6 @@
*
*/
/* Thresholds for Demodulation */
/* Assume perfect amplitude and phase alignment.
* Check threshold values for real case
@ -35,7 +34,18 @@
#define QAM64_THRESHOLD_2 4/sqrt(42)
#define QAM64_THRESHOLD_3 6/sqrt(42)
void hard_bpsk_demod(const cf_t* in, char* out, int N);
void hard_qpsk_demod(const cf_t* in, char* out, int N);
void hard_qam16_demod(const cf_t* in, char* out, int N);
void hard_qam64_demod(const cf_t* in, char* out, int N);
void hard_bpsk_demod(const cf_t* in,
char* out,
uint32_t N);
void hard_qpsk_demod(const cf_t* in,
char* out,
uint32_t N);
void hard_qam16_demod(const cf_t* in,
char* out,
uint32_t N);
void hard_qam64_demod(const cf_t* in,
char* out,
uint32_t N);

@ -59,7 +59,7 @@ void set_BPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demo
* Set the QPSK modulation table */
void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod)
{
int i,j;
uint32_t i,j;
// LTE-QPSK constellation:
// Q
@ -97,7 +97,7 @@ void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demo
* Set the 16QAM modulation table */
void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod)
{
int i,j;
uint32_t i,j;
// LTE-16QAM constellation:
// Q
// 1011 1001 | 0001 0011
@ -162,7 +162,7 @@ void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_dem
* Set the 64QAM modulation table */
void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod)
{
int i,j;
uint32_t i,j;
// LTE-64QAM constellation:
// see [3GPP TS 36.211 version 10.5.0 Release 10, Section 7.1.4]
table[0] = QAM64_LEVEL_2 + QAM64_LEVEL_2*_Complex_I;

@ -45,7 +45,18 @@
void set_BPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod);
void set_QPSKtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod);
void set_16QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod);
void set_64QAMtable(cf_t* table, soft_table_t *soft_table, bool compute_soft_demod);
void set_BPSKtable(cf_t* table,
soft_table_t *soft_table,
bool compute_soft_demod);
void set_QPSKtable(cf_t* table,
soft_table_t *soft_table,
bool compute_soft_demod);
void set_16QAMtable(cf_t* table,
soft_table_t *soft_table,
bool compute_soft_demod);
void set_64QAMtable(cf_t* table,
soft_table_t *soft_table,
bool compute_soft_demod);

@ -35,14 +35,17 @@
/** Low-level API */
int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, int nbits) {
int i,j,idx;
int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, uint32_t nbits) {
uint32_t i,j,idx;
char *b_ptr=(char*) bits;
j=0;
for (i=0;i<nbits;i+=q->nbits_x_symbol) {
idx = bit_unpack(&b_ptr,q->nbits_x_symbol);
assert(idx >= 0 && idx < q->nsymbols);
symbols[j] = q->symbol_table[idx];
if (idx < q->nsymbols) {
symbols[j] = q->symbol_table[idx];
} else {
return LIBLTE_ERROR;
}
j++;
}
return j;
@ -52,7 +55,7 @@ int mod_modulate(modem_table_t* q, const char *bits, cf_t* symbols, int nbits) {
/* High-Level API */
int mod_initialize(mod_hl* hl) {
modem_table_init(&hl->obj);
if (modem_table_std(&hl->obj,hl->init.std,false)) {
if (modem_table_lte(&hl->obj,hl->init.std,false)) {
return -1;
}

@ -33,6 +33,7 @@
#include <string.h>
#include <strings.h>
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/modem/modem_table.h"
#include "lte_tables.h"
@ -56,27 +57,27 @@ void modem_table_reset(modem_table_t* q) {
modem_table_init(q);
}
int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, int nsymbols, int nbits_x_symbol) {
int modem_table_set(modem_table_t* q, cf_t* table, soft_table_t *soft_table, uint32_t nsymbols, uint32_t nbits_x_symbol) {
if (q->nsymbols) {
return -1;
return LIBLTE_ERROR;
}
q->nsymbols = nsymbols;
if (table_create(q)) {
return -1;
return LIBLTE_ERROR;
}
memcpy(q->symbol_table,table,q->nsymbols*sizeof(cf_t));
memcpy(&q->soft_table,soft_table,sizeof(soft_table_t));
q->nbits_x_symbol = nbits_x_symbol;
return 0;
return LIBLTE_SUCCESS;
}
int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_demod) {
switch(std) {
int modem_table_lte(modem_table_t* q, lte_mod_t modulation, bool compute_soft_demod) {
switch(modulation) {
case LTE_BPSK:
q->nbits_x_symbol = 1;
q->nsymbols = 2;
if (table_create(q)) {
return -1;
return LIBLTE_ERROR;
}
set_BPSKtable(q->symbol_table, &q->soft_table, compute_soft_demod);
break;
@ -84,7 +85,7 @@ int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_dem
q->nbits_x_symbol = 2;
q->nsymbols = 4;
if (table_create(q)) {
return -1;
return LIBLTE_ERROR;
}
set_QPSKtable(q->symbol_table, &q->soft_table, compute_soft_demod);
break;
@ -92,7 +93,7 @@ int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_dem
q->nbits_x_symbol = 4;
q->nsymbols = 16;
if (table_create(q)) {
return -1;
return LIBLTE_ERROR;
}
set_16QAMtable(q->symbol_table, &q->soft_table, compute_soft_demod);
break;
@ -100,10 +101,10 @@ int modem_table_std(modem_table_t* q, enum modem_std std, bool compute_soft_dem
q->nbits_x_symbol = 6;
q->nsymbols = 64;
if (table_create(q)) {
return -1;
return LIBLTE_ERROR;
}
set_64QAMtable(q->symbol_table, &q->soft_table, compute_soft_demod);
break;
}
return 0;
return LIBLTE_SUCCESS;
}

@ -51,7 +51,7 @@
* \param sigma2 Noise vatiance
*/
void llr_approx(const _Complex float *in, float *out, int N, int M, int B,
_Complex float *symbols, int (*S)[6][32], float sigma2) {
_Complex float *symbols, uint32_t (*S)[6][32], float sigma2) {
int i, s, b;
float num, den;
float new_num, new_den;
@ -112,7 +112,7 @@ void llr_approx(const _Complex float *in, float *out, int N, int M, int B,
* \param sigma2 Noise vatiance
*/
void llr_exact(const _Complex float *in, float *out, int N, int M, int B,
_Complex float *symbols, int (*S)[6][32], float sigma2) {
_Complex float *symbols, uint32_t (*S)[6][32], float sigma2) {
int i, s, b;
float num, den;
float idiff0, qdiff0, idiff1, qdiff1;

@ -26,8 +26,20 @@
*/
void llr_approx(const _Complex float *in, float *out, int N, int M, int B,
_Complex float *symbols, int (*S)[6][32], float sigma2);
void llr_approx(const _Complex float *in,
float *out,
int N,
int M,
int B,
_Complex float *symbols,
uint32_t (*S)[6][32],
float sigma2);
void llr_exact(const _Complex float *in, float *out, int N, int M, int B,
_Complex float *symbols, int (*S)[6][32], float sigma2);
void llr_exact(const _Complex float *in,
float *out,
int N,
int M,
int B,
_Complex float *symbols,
uint32_t (*S)[6][32],
float sigma2);

@ -37,7 +37,7 @@
#include "liblte/phy/phy.h"
int num_bits = 1000;
enum modem_std modulation;
lte_mod_t modulation;
bool soft_output = false, soft_exact = false;
void usage(char *prog) {
@ -101,7 +101,7 @@ int main(int argc, char **argv) {
parse_args(argc, argv);
/* initialize objects */
if (modem_table_std(&mod, modulation, soft_output)) {
if (modem_table_lte(&mod, modulation, soft_output)) {
fprintf(stderr, "Error initializing modem table\n");
exit(-1);
}

@ -41,6 +41,54 @@
#include "liblte/phy/utils/debug.h"
int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
lte_cell_t cell, uint32_t cfi,
ra_pdsch_t *ra_dl)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (msg != NULL &&
ra_dl != NULL &&
lte_cell_isvalid(&cell) &&
cfi > 0 &&
cfi < 4)
{
ret = LIBLTE_ERROR;
dci_msg_type_t type;
if (dci_msg_get_type(msg, &type, cell.nof_prb, msg_rnti, c_rnti)) {
fprintf(stderr, "Can't get DCI message type\n");
return ret;
}
if (VERBOSE_ISINFO()) {
dci_msg_type_fprint(stdout, type);
}
if (type.type == PDSCH_SCHED) {
bzero(ra_dl, sizeof(ra_pdsch_t));
if (dci_msg_unpack_pdsch(msg, ra_dl, cell.nof_prb, msg_rnti != SIRNTI)) {
fprintf(stderr, "Can't unpack PDSCH message\n");
return ret;
}
if (VERBOSE_ISINFO()) {
ra_pdsch_fprint(stdout, ra_dl, cell.nof_prb);
}
if (ra_prb_get_dl(&ra_dl->prb_alloc, ra_dl, cell.nof_prb)) {
fprintf(stderr, "Error computing resource allocation\n");
return ret;
}
ra_prb_get_re_dl(&ra_dl->prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp);
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
int dci_location_set(dci_location_t *c, uint32_t L, uint32_t nCCE) {
if (L <= 3) {
c->L = L;
@ -186,31 +234,10 @@ int dci_format0_pack(ra_pusch_t *data, dci_msg_t *msg, uint32_t nof_prb) {
} else {
riv = data->type2_alloc.riv;
}
bit_pack((uint32_t) riv, &y, riv_nbits(nof_prb) - n_ul_hop);
bit_pack(riv, &y, riv_nbits(nof_prb) - n_ul_hop);
/* pack MCS according to 8.6.1 of 36.213 */
uint32_t mcs;
if (data->cqi_request) {
mcs = 29;
} else {
if (data->rv_idx) {
mcs = 28 + data->rv_idx;
} else {
if (data->mcs.mod == MOD_NULL) {
mcs = data->mcs.mcs_idx;
} else {
if (data->mcs.tbs) {
if (data->mcs.tbs) {
data->mcs.tbs_idx = ra_tbs_to_table_idx(data->mcs.tbs,
ra_nprb_ul(data, nof_prb));
}
}
mcs = ra_mcs_to_table_idx(&data->mcs);
}
}
}
bit_pack((uint32_t) mcs, &y, 5);
bit_pack(data->mcs_idx, &y, 5);
*y++ = data->ndi;
@ -276,7 +303,7 @@ int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, uint32_t nof_prb) {
data->type2_alloc.riv = riv;
/* unpack MCS according to 8.6 of 36.213 */
uint32_t mcs = bit_unpack(&y, 5);
data->mcs_idx = bit_unpack(&y, 5);
data->ndi = *y++ ? true : false;
@ -287,20 +314,16 @@ int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, uint32_t nof_prb) {
data->cqi_request = *y++ ? true : false;
// 8.6.2 First paragraph
if (mcs <= 28) {
ra_mcs_from_idx_ul(mcs, &data->mcs);
data->mcs.tbs = ra_tbs_from_idx(data->mcs.tbs_idx,
ra_nprb_ul(data, nof_prb));
}
// 8.6.1 and 8.6.2 36.213 second paragraph
if (mcs == 29 && data->cqi_request && ra_nprb_ul(data, nof_prb) <= 4) {
data->mcs.mod = QPSK;
}
if (mcs > 29) {
// Else leave MOD_NULL and use the previously used PUSCH modulation
data->mcs.mod = MOD_NULL;
data->rv_idx = mcs - 28;
if (data->mcs_idx <= 28) {
ra_mcs_from_idx_ul(data->mcs_idx, ra_nprb_ul(data, nof_prb), &data->mcs);
} else if (data->mcs_idx == 29 && data->cqi_request && ra_nprb_ul(data, nof_prb) <= 4) {
// 8.6.1 and 8.6.2 36.213 second paragraph
data->mcs.mod = LTE_QPSK;
data->mcs.tbs = 0;
} else if (data->mcs_idx >= 29) {
// Else leave TBS and use the previously used PUSCH modulation
data->mcs.tbs = 0;
data->rv_idx = data->mcs_idx - 28;
}
return LIBLTE_SUCCESS;
@ -340,27 +363,16 @@ int dci_format1_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb) {
return LIBLTE_ERROR;
}
/* pack MCS according to 7.1.7 of 36.213 */
uint32_t mcs;
if (data->mcs.mod == MOD_NULL) {
mcs = data->mcs.mcs_idx;
} else {
if (data->mcs.tbs) {
data->mcs.tbs_idx = ra_tbs_to_table_idx(data->mcs.tbs,
ra_nprb_dl(data, nof_prb));
}
mcs = ra_mcs_to_table_idx(&data->mcs);
data->mcs.mcs_idx = mcs;
}
bit_pack((uint32_t) mcs, &y, 5);
/* pack MCS */
bit_pack(data->mcs_idx, &y, 5);
/* harq process number */
bit_pack((uint32_t) data->harq_process, &y, 3);
bit_pack(data->harq_process, &y, 3);
*y++ = data->ndi;
// rv version
bit_pack((uint32_t) data->rv_idx, &y, 2);
bit_pack(data->rv_idx, &y, 2);
// TPC not implemented
*y++ = 0;
@ -412,20 +424,12 @@ int dci_format1_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb) {
}
/* unpack MCS according to 7.1.7 of 36.213 */
uint32_t mcs = bit_unpack(&y, 5);
data->mcs.mcs_idx = mcs;
if (ra_mcs_from_idx_dl(mcs, &data->mcs)) {
data->mcs_idx = bit_unpack(&y, 5);
if (ra_mcs_from_idx_dl(data->mcs_idx, ra_nprb_dl(data, nof_prb), &data->mcs)) {
fprintf(stderr, "Error getting MCS\n");
return LIBLTE_ERROR;
}
int t = ra_tbs_from_idx(data->mcs.tbs_idx, ra_nprb_dl(data, nof_prb));
if (t < 0) {
fprintf(stderr, "Error getting TBS\n");
return LIBLTE_ERROR;
}
data->mcs.tbs = (uint32_t) t;
/* harq process number */
data->harq_process = bit_unpack(&y, 3);
@ -491,28 +495,12 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb,
nb_gap = 1;
*y++ = data->type2_alloc.n_gap;
}
bit_pack((uint32_t) riv, &y, riv_nbits(nof_prb) - nb_gap);
bit_pack(riv, &y, riv_nbits(nof_prb) - nb_gap);
// in format1A, MCS = TBS according to 7.1.7.2 of 36.213
uint32_t mcs;
if (data->mcs.mod == MOD_NULL) {
mcs = data->mcs.mcs_idx;
} else {
if (data->mcs.tbs) {
// In format 1A, n_prb_1a is 2 or 3 if crc is not scrambled with C-RNTI
uint32_t n_prb;
if (!crc_is_crnti) {
n_prb = ra_nprb_dl(data, nof_prb);
} else {
n_prb = data->type2_alloc.n_prb1a == nprb1a_2 ? 2 : 3;
}
data->mcs.tbs_idx = ra_tbs_to_table_idx(data->mcs.tbs, n_prb);
}
mcs = data->mcs.tbs_idx;
}
bit_pack((uint32_t) mcs, &y, 5);
bit_pack(data->mcs_idx, &y, 5);
bit_pack((uint32_t) data->harq_process, &y, 3);
bit_pack(data->harq_process, &y, 3);
if (!crc_is_crnti && nof_prb >= 50 && data->type2_alloc.mode == t2_dist) {
*y++ = data->type2_alloc.n_gap;
@ -521,7 +509,7 @@ int dci_format1As_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb,
}
// rv version
bit_pack((uint32_t) data->rv_idx, &y, 2);
bit_pack(data->rv_idx, &y, 2);
if (crc_is_crnti) {
// TPC not implemented
@ -586,7 +574,7 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb,
data->type2_alloc.riv = riv;
// unpack MCS
data->mcs.mcs_idx = bit_unpack(&y, 5);
data->mcs_idx = bit_unpack(&y, 5);
data->harq_process = bit_unpack(&y, 3);
@ -597,7 +585,7 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb,
}
// rv version
bit_pack((uint32_t) data->rv_idx, &y, 2);
bit_pack(data->rv_idx, &y, 2);
if (crc_is_crnti) {
// TPC not implemented
@ -607,7 +595,6 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb,
y++; // MSB of TPC is reserved
data->type2_alloc.n_prb1a = *y++; // LSB indicates N_prb_1a for TBS
}
data->mcs.tbs_idx = data->mcs.mcs_idx;
uint32_t n_prb;
if (crc_is_crnti) {
@ -615,8 +602,8 @@ int dci_format1As_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb,
} else {
n_prb = data->type2_alloc.n_prb1a == nprb1a_2 ? 2 : 3;
}
data->mcs.tbs = ra_tbs_from_idx(data->mcs.tbs_idx, n_prb);
data->mcs.mod = QPSK;
data->mcs.tbs = ra_tbs_from_idx(data->mcs_idx, n_prb);
data->mcs.mod = LTE_QPSK;
return LIBLTE_SUCCESS;
}
@ -664,19 +651,10 @@ int dci_format1Cs_pack(ra_pdsch_t *data, dci_msg_t *msg, uint32_t nof_prb) {
} else {
riv = data->type2_alloc.riv;
}
bit_pack((uint32_t) riv, &y, riv_nbits((int) n_vrb_dl / n_step));
bit_pack(riv, &y, riv_nbits((int) n_vrb_dl / n_step));
// in format1C, MCS = TBS according to 7.1.7.2 of 36.213
uint32_t mcs;
if (data->mcs.mod == MOD_NULL) {
mcs = data->mcs.mcs_idx;
} else {
if (data->mcs.tbs) {
data->mcs.tbs_idx = ra_tbs_to_table_idx_format1c(data->mcs.tbs);
}
mcs = data->mcs.tbs_idx;
}
bit_pack((uint32_t) mcs, &y, 5);
bit_pack(data->mcs_idx, &y, 5);
msg->nof_bits = (y - msg->data);
@ -709,10 +687,9 @@ int dci_format1Cs_unpack(dci_msg_t *msg, ra_pdsch_t *data, uint32_t nof_prb) {
data->type2_alloc.RB_start = RB_p * n_step;
data->type2_alloc.riv = riv;
data->mcs.mcs_idx = bit_unpack(&y, 5);
data->mcs.tbs_idx = data->mcs.mcs_idx;
data->mcs.tbs = ra_tbs_from_idx_format1c(data->mcs.tbs_idx);
data->mcs.mod = QPSK;
data->mcs_idx = bit_unpack(&y, 5);
data->mcs.tbs = ra_tbs_from_idx_format1c(data->mcs_idx);
data->mcs.mod = LTE_QPSK;
msg->nof_bits = (y - msg->data);

@ -134,7 +134,7 @@ int pbch_init(pbch_t *q, lte_cell_t cell) {
bzero(q, sizeof(pbch_t));
q->cell = cell;
if (modem_table_std(&q->mod, LTE_QPSK, true)) {
if (modem_table_lte(&q->mod, LTE_QPSK, true)) {
goto clean;
}
demod_soft_init(&q->demod);

@ -70,7 +70,7 @@ int pcfich_init(pcfich_t *q, regs_t *regs, lte_cell_t cell) {
q->cell = cell;
q->regs = regs;
if (modem_table_std(&q->mod, LTE_QPSK, false)) {
if (modem_table_lte(&q->mod, LTE_QPSK, false)) {
goto clean;
}

@ -77,7 +77,7 @@ int pdcch_init(pdcch_t *q, regs_t *regs, lte_cell_t cell) {
INFO("Init PDCCH: %d bits, %d symbols, %d ports\n", q->max_bits, q->max_bits/2, q->cell.nof_ports);
if (modem_table_std(&q->mod, LTE_QPSK, true)) {
if (modem_table_lte(&q->mod, LTE_QPSK, true)) {
goto clean;
}
if (crc_init(&q->crc, LTE_CRC16, 16)) {
@ -89,7 +89,9 @@ int pdcch_init(pdcch_t *q, regs_t *regs, lte_cell_t cell) {
demod_soft_alg_set(&q->demod, APPROX);
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
if (sequence_pdcch(&q->seq_pdcch[i], 2 * i, q->cell.id, q->max_bits)) {
// we need to pregenerate the sequence for the maximum number of bits, which is 8 times
// the maximum number of REGs (for CFI=3)
if (sequence_pdcch(&q->seq_pdcch[i], 2 * i, q->cell.id, 8*regs_pdcch_nregs(q->regs, 3))) {
goto clean;
}
}
@ -178,7 +180,8 @@ void pdcch_free(pdcch_t *q) {
uint32_t pdcch_ue_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidates,
uint32_t nsubframe, uint32_t cfi, uint16_t rnti) {
uint32_t i, k, l, L, m;
int l; // this must be int because of the for(;;--) loop
uint32_t i, k, L, m;
uint32_t Yk, ncce;
const int S[4] = { 6, 12, 8, 16 };
@ -198,7 +201,7 @@ uint32_t pdcch_ue_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidat
for (i = 0; i < MIN(q->nof_cce / L, 16 / S[l]); i++) {
ncce = L * ((Yk + i) % (q->nof_cce / L));
if (k < max_candidates &&
ncce + PDCCH_FORMAT_NOF_CCE(L) < q->nof_cce)
ncce + PDCCH_FORMAT_NOF_CCE(l) <= q->nof_cce)
{
c[k].L = l;
c[k].ncce = ncce;
@ -352,8 +355,8 @@ int pdcch_extract_llr(pdcch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS],
if (location.ncce + PDCCH_FORMAT_NOF_CCE(location.L) <= q->nof_cce) {
INFO("Extracting LLRs: E: %d, nCCE: %d, L: %d\n",
q->e_bits, location.ncce, location.L);
INFO("Extracting LLRs: E: %d, nCCE: %d, L: %d, SF: %d, CFI: %d\n",
q->e_bits, location.ncce, location.L, nsubframe, cfi);
/* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) {
@ -518,7 +521,7 @@ int pdcch_encode(pdcch_t *q, dci_msg_t *msg, dci_location_t location, uint16_t r
if (VERBOSE_ISDEBUG()) {
vec_fprint_b(stdout, q->pdcch_e, q->e_bits);
}
mod_modulate(&q->mod, q->pdcch_e, q->pdcch_d, q->e_bits);
/* layer mapping & precoding */

@ -46,7 +46,7 @@
const enum modem_std modulations[4] =
const lte_mod_t modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 };
@ -191,7 +191,7 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
q->cell.nof_prb, q->max_symbols);
for (i = 0; i < 4; i++) {
if (modem_table_std(&q->mod[i], modulations[i], true)) {
if (modem_table_lte(&q->mod[i], modulations[i], true)) {
goto clean;
}
}
@ -220,12 +220,12 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
}
// Allocate floats for reception (LLRs)
q->cb_in = malloc(sizeof(float) * MAX_LONG_CB);
q->cb_in = malloc(sizeof(char) * MAX_LONG_CB);
if (!q->cb_in) {
goto clean;
}
q->cb_out = malloc(sizeof(char) * (3 * MAX_LONG_CB + 12));
q->cb_out = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12));
if (!q->cb_out) {
goto clean;
}
@ -417,8 +417,7 @@ int pdsch_harq_setup(pdsch_harq_t *p, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (p != NULL &&
mcs.tbs > 0 &&
mcs.mod != MOD_NULL)
mcs.tbs > 0)
{
uint32_t nof_bits, nof_bits_e, nof_symbols;
@ -429,7 +428,7 @@ int pdsch_harq_setup(pdsch_harq_t *p, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
nof_bits = mcs.tbs;
nof_symbols = prb_alloc->re_sf[1]; // Any subframe except 0 and 5 has maximum RE
nof_bits_e = nof_symbols * ra_mod_bits_x_symbol(mcs.mod);
nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(mcs.mod);
if (nof_bits > nof_bits_e) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e);
@ -506,18 +505,18 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
/* Rate Unmatching */
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
&e_bits[rp], n_e,
(float*) q->cb_in, 3 * cb_len + 12, rv_idx)) {
(float*) q->cb_out, 3 * cb_len + 12, rv_idx)) {
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
/* Turbo Decoding */
tdec_run_all(&q->decoder, (float*) q->cb_in, q->cb_out, TDEC_ITERATIONS,
tdec_run_all(&q->decoder, (float*) q->cb_out, q->cb_in, TDEC_ITERATIONS,
cb_len);
if (harq_process->cb_segm.C > 1) {
/* Check Codeblock CRC and stop early if incorrect */
if (crc_checksum(&q->crc_cb, q->cb_out, cb_len)) {
if (crc_checksum(&q->crc_cb, q->cb_in, cb_len)) {
INFO("Error in CB#%d\n",i);
return LIBLTE_ERROR;
}
@ -525,13 +524,13 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
/* Copy data to another buffer, removing the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_out[F], (rlen - F) * sizeof(char));
memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(char));
} else {
INFO("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
rlen - F - 24, wp, F, rlen - 24);
/* Append Transport Block parity bits to the last CB */
memcpy(&data[wp], &q->cb_out[F], (rlen - F - 24) * sizeof(char));
memcpy(parity, &q->cb_out[rlen - 24], 24 * sizeof(char));
memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(char));
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(char));
}
/* Set read/write pointers */
@ -552,8 +551,10 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
}
if (par_rx == par_tx) {
INFO("TB decoded OK\n",i);
return LIBLTE_SUCCESS;
} else {
INFO("Error in TB parity\n",i);
return LIBLTE_ERROR;
}
} else {
@ -649,7 +650,6 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
uint32_t par;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
char *cb_in = q->cb_in;
char *e_bits = q->pdsch_e;
if (q != NULL &&
@ -673,7 +673,7 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
/* Add filler bits to the new data buffer */
for (i = 0; i < harq_process->cb_segm.F; i++) {
cb_in[i] = LTE_NULL_BIT;
q->cb_in[i] = LTE_NULL_BIT;
}
}
@ -710,29 +710,29 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
if (rv_idx == 0) {
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&cb_in[F], &data[rp], (rlen - F) * sizeof(char));
memcpy(&q->cb_in[F], &data[rp], (rlen - F) * sizeof(char));
} else {
INFO("Last CB, appending parity: %d from %d and 24 to %d\n",
rlen - F - 24, rp, rlen - 24);
/* Append Transport Block parity bits to the last CB */
memcpy(&cb_in[F], &data[rp], (rlen - F - 24) * sizeof(char));
memcpy(&cb_in[rlen - 24], parity, 24 * sizeof(char));
memcpy(&q->cb_in[F], &data[rp], (rlen - F - 24) * sizeof(char));
memcpy(&q->cb_in[rlen - 24], parity, 24 * sizeof(char));
}
if (harq_process->cb_segm.C > 1) {
/* Attach Codeblock CRC */
crc_attach(&q->crc_cb, cb_in, rlen);
crc_attach(&q->crc_cb, q->cb_in, rlen);
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d Len=%d: ", i, cb_len);
vec_fprint_b(stdout, cb_in, cb_len);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, cb_in, q->cb_out, cb_len);
tcod_encode(&q->encoder, q->cb_in, (char*) q->cb_out, cb_len);
}
/* Rate matching */
if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size,
q->cb_out, 3 * cb_len + 12,
(char*) q->cb_out, 3 * cb_len + 12,
&e_bits[wp], n_e, rv_idx))
{
fprintf(stderr, "Error in rate matching\n");
@ -778,7 +778,7 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t s
nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol;
if (harq_process->mcs.mod == MOD_NULL) {
if (harq_process->mcs.tbs == 0) {
return LIBLTE_ERROR_INVALID_INPUTS;
}
@ -807,6 +807,7 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t s
fprintf(stderr, "Error encoding TB\n");
return LIBLTE_ERROR;
}
scrambling_b_offset(&q->seq_pdsch[subframe], (char*) q->pdsch_e, 0, nof_bits_e);

@ -76,7 +76,7 @@ int phich_init(phich_t *q, regs_t *regs, lte_cell_t cell) {
q->cell = cell;
q->regs = regs;
if (modem_table_std(&q->mod, LTE_BPSK, false)) {
if (modem_table_lte(&q->mod, LTE_BPSK, false)) {
goto clean;
}

@ -391,79 +391,44 @@ uint32_t ra_type2_n_vrb_dl(uint32_t nof_prb, bool ngap_is_1) {
}
}
/* Converts ra_mcs_t structure to MCS index for both Uplink and Downlink */
uint32_t ra_mcs_to_table_idx(ra_mcs_t *mcs) {
switch (mcs->mod) {
case QPSK:
return mcs->tbs_idx;
case QAM16:
return mcs->tbs_idx + 1;
case QAM64:
return mcs->tbs_idx + 2;
default:
return LIBLTE_SUCCESS;
}
}
uint32_t ra_mod_bits_x_symbol(ra_mod_t mod) {
switch(mod) {
case BPSK:
return 1;
case QPSK:
return 2;
case QAM16:
return 4;
case QAM64:
return 6;
default:
return 0;
}
return 0;
}
/* Converts MCS index to ra_mcs_t structure for Downlink as defined inTable 7.1.7.1-1 on 36.213 */
int ra_mcs_from_idx_dl(uint32_t idx, ra_mcs_t *mcs) {
if (idx < 10) {
mcs->mod = QPSK;
mcs->tbs_idx = idx;
} else if (idx < 17) {
mcs->mod = QAM16;
mcs->tbs_idx = idx - 1;
} else if (idx < 29) {
mcs->mod = QAM64;
mcs->tbs_idx = idx - 2;
} else if (idx == 29) {
mcs->mod = QPSK;
mcs->tbs_idx = 0;
} else if (idx == 30) {
mcs->mod = QAM16;
mcs->tbs_idx = 0;
} else if (idx == 31) {
mcs->mod = QAM64;
mcs->tbs_idx = 0;
int ra_mcs_from_idx_dl(uint32_t mcs_idx, uint32_t nof_prb, ra_mcs_t *mcs) {
if (mcs_idx < 10) {
mcs->mod = LTE_QPSK;
mcs->tbs = ra_tbs_from_idx(mcs_idx, nof_prb);
} else if (mcs_idx < 17) {
mcs->mod = LTE_QAM16;
mcs->tbs = ra_tbs_from_idx(mcs_idx - 1, nof_prb);
} else if (mcs_idx < 29) {
mcs->mod = LTE_QAM64;
mcs->tbs = ra_tbs_from_idx(mcs_idx - 2, nof_prb);
} else if (mcs_idx == 29) {
mcs->mod = LTE_QPSK;
mcs->tbs = 0;
} else if (mcs_idx == 30) {
mcs->mod = LTE_QAM16;
mcs->tbs = 0;
} else if (mcs_idx == 31) {
mcs->mod = LTE_QAM64;
mcs->tbs = 0;
} else {
mcs->mod = MOD_NULL;
mcs->tbs_idx = 0;
return LIBLTE_ERROR;
}
return LIBLTE_SUCCESS;
}
/* Converts MCS index to ra_mcs_t structure for Uplink as defined in Table 8.6.1-1 on 36.213 */
int ra_mcs_from_idx_ul(uint32_t idx, ra_mcs_t *mcs) {
if (idx < 11) {
mcs->mod = QPSK;
mcs->tbs_idx = idx;
} else if (idx < 21) {
mcs->mod = QAM16;
mcs->tbs_idx = idx - 1;
} else if (idx < 29) {
mcs->mod = QAM64;
mcs->tbs_idx = idx - 2;
int ra_mcs_from_idx_ul(uint32_t mcs_idx, uint32_t nof_prb, ra_mcs_t *mcs) {
if (mcs_idx < 11) {
mcs->mod = LTE_QPSK;
mcs->tbs = ra_tbs_from_idx(mcs_idx, nof_prb);
} else if (mcs_idx < 21) {
mcs->mod = LTE_QAM16;
mcs->tbs = ra_tbs_from_idx(mcs_idx - 1, nof_prb);
} else if (mcs_idx < 29) {
mcs->mod = LTE_QAM64;
mcs->tbs = ra_tbs_from_idx(mcs_idx - 2, nof_prb);
} else {
mcs->mod = MOD_NULL;
mcs->tbs_idx = 0;
return LIBLTE_ERROR;
}
return LIBLTE_SUCCESS;
@ -478,22 +443,6 @@ int ra_tbs_from_idx_format1c(uint32_t tbs_idx) {
}
}
/* Returns lowest nearest index of TBS value in table 7.1.7.2.2-1 on 36.213
* or -1 if the TBS value is not within the valid TBS values
*/
int ra_tbs_to_table_idx_format1c(uint32_t tbs) {
int idx;
if (tbs < tbs_format1c_table[0]) {
return LIBLTE_ERROR;
}
for (idx = 1; idx < 32; idx++) {
if (tbs_format1c_table[idx - 1] <= tbs && tbs_format1c_table[idx] >= tbs) {
return idx;
}
}
return LIBLTE_ERROR;
}
/* Downlink Transport Block size determination as defined in 7.1.7.2 on 36.213 */
int ra_tbs_from_idx(uint32_t tbs_idx, uint32_t n_prb) {
if (tbs_idx < 27 && n_prb > 0 && n_prb <= MAX_PRB) {
@ -507,7 +456,7 @@ int ra_tbs_from_idx(uint32_t tbs_idx, uint32_t n_prb) {
* or -1 if the TBS value is not within the valid TBS values
*/
int ra_tbs_to_table_idx(uint32_t tbs, uint32_t n_prb) {
int idx;
uint32_t idx;
if (n_prb > 0 && n_prb <= MAX_PRB) {
return LIBLTE_ERROR;
}
@ -522,19 +471,6 @@ int ra_tbs_to_table_idx(uint32_t tbs, uint32_t n_prb) {
return LIBLTE_ERROR;
}
char *ra_mod_string(ra_mod_t mod) {
switch (mod) {
case QPSK:
return "QPSK";
case QAM16:
return "QAM16";
case QAM64:
return "QAM64";
default:
return "N/A";
}
}
void ra_pusch_fprint(FILE *f, ra_pusch_t *ra, uint32_t nof_prb) {
fprintf(f, "Frequency Hopping:\t");
if (ra->freq_hop_fl == hop_disabled) {
@ -558,15 +494,6 @@ char *ra_type_string(ra_type_t alloc_type) {
}
}
void ra_pdsch_set_mcs_index(ra_pdsch_t *ra, uint32_t mcs_idx) {
ra->mcs.mod = MOD_NULL;
ra->mcs.mcs_idx = mcs_idx;
}
void ra_pdsch_set_mcs(ra_pdsch_t *ra, ra_mod_t mod, uint32_t tbs_idx) {
ra->mcs.mod = mod;
ra->mcs.tbs_idx = tbs_idx;
ra->mcs.tbs = 0;
}
void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, uint32_t nof_prb) {
fprintf(f, " - Resource Allocation Type:\t\t%s\n",
@ -609,8 +536,8 @@ void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, uint32_t nof_prb) {
}
fprintf(f, " - Number of PRBs:\t\t\t%d\n", ra_nprb_dl(ra, nof_prb));
fprintf(f, " - Modulation and coding scheme index:\t%d\n", ra->mcs.mcs_idx);
fprintf(f, " - Modulation type:\t\t\t%s\n", ra_mod_string(ra->mcs.mod));
fprintf(f, " - Modulation and coding scheme index:\t%d\n", ra->mcs_idx);
fprintf(f, " - Modulation type:\t\t\t%s\n", lte_mod_string(ra->mcs.mod));
fprintf(f, " - Transport block size:\t\t%d\n", ra->mcs.tbs);
fprintf(f, " - HARQ process:\t\t\t%d\n", ra->harq_process);
fprintf(f, " - New data indicator:\t\t\t%s\n", ra->ndi ? "Yes" : "No");

@ -292,7 +292,7 @@ int main(int argc, char **argv) {
ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);
if (ra_dl.alloc_type == alloc_type2 && ra_dl.type2_alloc.mode == t2_loc
&& ra_dl.type2_alloc.riv == 11 && ra_dl.rv_idx == 0
&& ra_dl.harq_process == 0 && ra_dl.mcs.mcs_idx == 2) {
&& ra_dl.harq_process == 0 && ra_dl.mcs_idx == 2) {
printf("This is the file signal.1.92M.amar.dat\n");
ret = 0;
}

@ -160,7 +160,7 @@ int main(int argc, char **argv) {
nof_dcis = 2;
bzero(&ra_dl, sizeof(ra_pdsch_t));
ra_dl.harq_process = 0;
ra_pdsch_set_mcs(&ra_dl, QAM16, 5);
ra_dl.mcs_idx = 5;
ra_dl.ndi = 0;
ra_dl.rv_idx = 0;
ra_dl.alloc_type = alloc_type0;
@ -169,7 +169,7 @@ int main(int argc, char **argv) {
dci_msg_pack_pdsch(&ra_dl, &dci_tx[0], Format1, cell.nof_prb, false);
dci_location_set(&dci_locations[0], 0, 0);
ra_pdsch_set_mcs(&ra_dl, QAM16, 15);
ra_dl.mcs_idx = 15;
dci_msg_pack_pdsch(&ra_dl, &dci_tx[1], Format1, cell.nof_prb, false);
dci_location_set(&dci_locations[1], 0, 1);

@ -223,7 +223,6 @@ void base_free() {
int main(int argc, char **argv) {
ra_pdsch_t ra_dl;
ra_prb_t prb_alloc;
int i;
int nof_frames;
int ret;
@ -300,39 +299,11 @@ int main(int argc, char **argv) {
}
if (crc_rem == rnti) {
dci_msg_type_t type;
if (dci_msg_get_type(&dci_msg, &type, cell.nof_prb, rnti, 1234)) {
fprintf(stderr, "Can't get DCI message type\n");
if (dci_msg_to_ra_dl(&dci_msg, rnti, 1234, cell, cfi, &ra_dl)) {
fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n");
goto goout;
}
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_msg, &ra_dl, cell.nof_prb, rnti != SIRNTI)) {
fprintf(stderr, "Can't unpack PDSCH message\n");
} else {
ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);
if (ra_dl.alloc_type == alloc_type2 && ra_dl.type2_alloc.mode == t2_loc
&& ra_dl.type2_alloc.riv == 11 && ra_dl.rv_idx == 0
&& ra_dl.harq_process == 0 && ra_dl.mcs.mcs_idx == 2) {
printf("This is the file signal.1.92M.amar.dat\n");
ret = 0;
}
}
break;
default:
fprintf(stderr, "Unsupported message type\n");
break;
}
if (ra_prb_get_dl(&prb_alloc, &ra_dl, cell.nof_prb)) {
fprintf(stderr, "Error computing resource allocation\n");
goto goout;
}
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp);
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) {
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
goto goout;
}
@ -348,6 +319,8 @@ int main(int argc, char **argv) {
nof_frames++;
} while (nof_frames <= max_frames);
ret = 0;
goout:
base_free();
exit(ret);

@ -44,7 +44,7 @@ lte_cell_t cell = {
uint32_t cfi = 1;
uint32_t tbs = 0;
uint32_t subframe = 1;
ra_mod_t modulation = BPSK;
lte_mod_t modulation = LTE_BPSK;
uint32_t rv_idx = 0;
void usage(char *prog) {
@ -66,16 +66,16 @@ void parse_args(int argc, char **argv) {
case 'm':
switch(atoi(argv[optind])) {
case 1:
modulation = BPSK;
modulation = LTE_BPSK;
break;
case 2:
modulation = QPSK;
modulation = LTE_QPSK;
break;
case 4:
modulation = QAM16;
modulation = LTE_QAM16;
break;
case 6:
modulation = QAM64;
modulation = LTE_QAM64;
break;
default:
fprintf(stderr, "Invalid modulation %d. Possible values: "

@ -36,7 +36,7 @@ void scrambling_f(sequence_t *s, float *data) {
}
void scrambling_f_offset(sequence_t *s, float *data, int offset, int len) {
int i;
int i;
assert (len + offset <= s->len);
for (i = 0; i < len; i++) {

Loading…
Cancel
Save