mirror of https://github.com/pvnis/srsRAN_4G.git
New equalizer working
parent
61ebfaf3b2
commit
f505a75382
@ -0,0 +1,57 @@
|
||||
/**
|
||||
*
|
||||
* \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 CHEQ_DL_
|
||||
#define CHEQ_DL_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "liblte/config.h"
|
||||
#include "liblte/phy/common/phy_common.h"
|
||||
|
||||
typedef _Complex float cf_t;
|
||||
|
||||
/** Generic OFDM channel equalizer
|
||||
*/
|
||||
|
||||
|
||||
LIBLTE_API int cheq_zf(cf_t *input,
|
||||
cf_t *ce,
|
||||
cf_t *output,
|
||||
uint32_t len);
|
||||
|
||||
LIBLTE_API int cheq_mmse(cf_t *input,
|
||||
cf_t *ce,
|
||||
cf_t *output,
|
||||
uint32_t len,
|
||||
float noise_estimate);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -1,238 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2014 The libLTE Developers. See the
|
||||
* COPYRIGHT file at the top-level directory of this distribution.
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the libLTE library.
|
||||
*
|
||||
* libLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* libLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Lesser General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef CHEST_
|
||||
#define CHEST_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "liblte/config.h"
|
||||
|
||||
#include "liblte/phy/resampling/interp.h"
|
||||
#include "liblte/phy/ch_estimation/refsignal.h"
|
||||
#include "liblte/phy/common/phy_common.h"
|
||||
|
||||
typedef _Complex float cf_t; /* this is only a shortcut */
|
||||
|
||||
typedef void (*interpolate_fnc_t) (cf_t *input,
|
||||
cf_t *output,
|
||||
uint32_t M,
|
||||
uint32_t len,
|
||||
uint32_t off_st,
|
||||
uint32_t off_end);
|
||||
|
||||
/** This is an OFDM channel estimator.
|
||||
* It works with any reference signal pattern, provided by the object
|
||||
* refsignal_t
|
||||
* A 2-D filter is used for freq and time channel interpolation.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Low-level API */
|
||||
typedef struct LIBLTE_API {
|
||||
uint32_t nof_ports;
|
||||
uint32_t nof_re;
|
||||
uint32_t nof_symbols;
|
||||
|
||||
refsignal_t refsignal[MAX_PORTS][NSLOTS_X_FRAME];
|
||||
interp_t interp_time[MAX_PORTS];
|
||||
interp_t interp_freq[MAX_PORTS];
|
||||
|
||||
}chest_t;
|
||||
|
||||
LIBLTE_API int chest_init(chest_t *q,
|
||||
uint32_t nof_re,
|
||||
uint32_t nof_symbols,
|
||||
uint32_t nof_ports);
|
||||
|
||||
LIBLTE_API void chest_free(chest_t *q);
|
||||
|
||||
LIBLTE_API int chest_set_nof_ports(chest_t *q,
|
||||
uint32_t nof_ports);
|
||||
|
||||
|
||||
LIBLTE_API float chest_rsrp(chest_t *q,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API float chest_rsrp_sf(chest_t *q,
|
||||
uint32_t sf_idx);
|
||||
|
||||
LIBLTE_API float chest_rssi(chest_t *q,
|
||||
cf_t *input);
|
||||
|
||||
LIBLTE_API float chest_rssi_sf(chest_t *q,
|
||||
cf_t *input);
|
||||
|
||||
LIBLTE_API float chest_rsrq(chest_t *q,
|
||||
cf_t *input,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API float chest_rsrq_sf(chest_t *q,
|
||||
cf_t *input,
|
||||
uint32_t sf_idx);
|
||||
|
||||
LIBLTE_API int chest_measure_ref(chest_t *q,
|
||||
cf_t *input,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id,
|
||||
uint32_t nref);
|
||||
|
||||
LIBLTE_API void chest_measure_slot(chest_t *q,
|
||||
cf_t *input,
|
||||
uint32_t nslot);
|
||||
|
||||
LIBLTE_API void chest_measure_sf(chest_t *q,
|
||||
cf_t *input,
|
||||
uint32_t sf_idx);
|
||||
|
||||
LIBLTE_API int chest_ce_slot_port(chest_t *q,
|
||||
cf_t *input,
|
||||
cf_t *ce,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API int chest_ce_sf_port(chest_t *q,
|
||||
cf_t *input,
|
||||
cf_t *ce,
|
||||
uint32_t sf_idx,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API int chest_ce_slot(chest_t *q,
|
||||
cf_t *input,
|
||||
cf_t *ce[MAX_PORTS],
|
||||
uint32_t nslot);
|
||||
|
||||
LIBLTE_API int chest_ce_sf(chest_t *q,
|
||||
cf_t *input,
|
||||
cf_t *ce[MAX_PORTS],
|
||||
uint32_t sf_idx);
|
||||
|
||||
LIBLTE_API void chest_fprint(chest_t *q,
|
||||
FILE *stream,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API void chest_ref_fprint(chest_t *q,
|
||||
FILE *stream,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API void chest_recvsig_fprint(chest_t *q,
|
||||
FILE *stream,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API void chest_ce_fprint(chest_t *q,
|
||||
FILE *stream,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id);
|
||||
|
||||
LIBLTE_API int chest_ref_get_symbols(chest_t *q,
|
||||
uint32_t port_id,
|
||||
uint32_t nslot,
|
||||
uint32_t l[2]);
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************************************
|
||||
*
|
||||
* Downlink Channel Estimator
|
||||
*
|
||||
*********************************************************/
|
||||
LIBLTE_API int chest_init_LTEDL(chest_t *q,
|
||||
lte_cell_t cell);
|
||||
|
||||
LIBLTE_API int chest_ref_set_LTEDL_slot_port(chest_t *q,
|
||||
uint32_t nslot,
|
||||
uint32_t port_id,
|
||||
lte_cell_t cell);
|
||||
|
||||
LIBLTE_API int chest_ref_set_LTEDL_slot(chest_t *q,
|
||||
uint32_t nslot,
|
||||
lte_cell_t cell);
|
||||
|
||||
LIBLTE_API int chest_ref_set_LTEDL(chest_t *q,
|
||||
lte_cell_t cell);
|
||||
|
||||
|
||||
/*********************************************************
|
||||
*
|
||||
* Uplink Channel Estimator
|
||||
*
|
||||
*********************************************************/
|
||||
LIBLTE_API int chest_init_LTEUL(chest_t *q,
|
||||
lte_cell_t cell);
|
||||
|
||||
LIBLTE_API int chest_ref_set_LTEUL_slot(chest_t *q,
|
||||
uint32_t nslot,
|
||||
lte_cell_t cell);
|
||||
|
||||
LIBLTE_API int chest_ref_set_LTEUL(chest_t *q,
|
||||
lte_cell_t cell);
|
||||
|
||||
|
||||
/* High-level API */
|
||||
|
||||
/** TODO: The high-level API has N interfaces, one for each port */
|
||||
|
||||
typedef struct LIBLTE_API{
|
||||
chest_t obj;
|
||||
struct chest_init {
|
||||
int nof_symbols; // 7 for normal cp, 6 for extended
|
||||
int nof_ports;
|
||||
int nof_prb;
|
||||
int cell_id; // set to -1 to init at runtime
|
||||
} init;
|
||||
cf_t *input;
|
||||
int in_len;
|
||||
struct chest_ctrl_in {
|
||||
int sf_idx; // subframe id in the 10ms frame
|
||||
} ctrl_in;
|
||||
cf_t *output[MAX_PORTS];
|
||||
int out_len[MAX_PORTS];
|
||||
}chest_hl;
|
||||
|
||||
#define DEFAULT_FRAME_SIZE 2048
|
||||
|
||||
LIBLTE_API int chest_initialize(chest_hl* h);
|
||||
LIBLTE_API int chest_work(chest_hl* hl);
|
||||
LIBLTE_API int chest_stop(chest_hl* hl);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,92 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2014 The libLTE Developers. See the
|
||||
* COPYRIGHT file at the top-level directory of this distribution.
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the libLTE library.
|
||||
*
|
||||
* libLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* libLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Lesser General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef REFSIGNAL_
|
||||
#define REFSIGNAL_
|
||||
|
||||
|
||||
/* Object to manage reference signals for OFDM channel equalization.
|
||||
*
|
||||
* It generates the reference signals for LTE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "liblte/config.h"
|
||||
#include "liblte/phy/common/phy_common.h"
|
||||
|
||||
typedef _Complex float cf_t;
|
||||
|
||||
typedef struct LIBLTE_API{
|
||||
uint32_t time_idx;
|
||||
uint32_t freq_idx;
|
||||
cf_t symbol;
|
||||
}ref_t;
|
||||
|
||||
typedef struct LIBLTE_API{
|
||||
uint32_t nof_refs; // number of reference signals
|
||||
uint32_t *symbols_ref; // symbols with at least one reference
|
||||
uint32_t nsymbols; // number of symbols with at least one reference
|
||||
uint32_t voffset; // offset of the first reference in the freq domain
|
||||
uint32_t nof_prb;
|
||||
ref_t *refs;
|
||||
cf_t *ch_est;
|
||||
cf_t *recv_symbol;
|
||||
} refsignal_t;
|
||||
|
||||
|
||||
typedef struct LIBLTE_API {
|
||||
float beta; // amplitude scaling
|
||||
uint32_t delta_ss; // Set to 0 for PUCCH
|
||||
uint32_t cyclic_shift;
|
||||
uint32_t cyclic_shift_for_drms; /* From DCI 0. Set to 0 if no PDCCH with DCI 0 for the same TB
|
||||
or if the initial PUSCH is semi-persisently scheduled or
|
||||
if the initial PUSCH is scheduled by the RA response grant */
|
||||
bool group_hopping_en;
|
||||
bool sequence_hopping_en;
|
||||
} refsignal_ul_cfg_t;
|
||||
|
||||
|
||||
LIBLTE_API int refsignal_init_LTEDL(refsignal_t *q,
|
||||
uint32_t port_id,
|
||||
uint32_t nslot,
|
||||
lte_cell_t cell);
|
||||
|
||||
LIBLTE_API int refsignal_init_LTEUL_drms_pusch(refsignal_t *q,
|
||||
uint32_t nof_prb,
|
||||
uint32_t prb_start,
|
||||
uint32_t nslot,
|
||||
lte_cell_t cell,
|
||||
refsignal_ul_cfg_t *drms_cfg);
|
||||
|
||||
LIBLTE_API void refsignal_free(refsignal_t *q);
|
||||
|
||||
LIBLTE_API int refsignal_put(refsignal_t *q,
|
||||
cf_t *slot_symbols);
|
||||
|
||||
#endif
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
*
|
||||
* \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 <strings.h>
|
||||
#include <string.h>
|
||||
#include <complex.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "liblte/config.h"
|
||||
|
||||
#include "liblte/phy/ch_estimation/cheq.h"
|
||||
#include "liblte/phy/utils/vector.h"
|
||||
|
||||
|
||||
int cheq_zf(cf_t *input, cf_t *ce, cf_t *output, uint32_t len)
|
||||
{
|
||||
fprintf(stderr, "Not implemented\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cheq_mmse(cf_t *input, cf_t *ce, cf_t *output, uint32_t len, float noise_estimate)
|
||||
{
|
||||
fprintf(stderr, "Not implemented\n");
|
||||
return -1;
|
||||
}
|
@ -1,503 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2014 The libLTE Developers. See the
|
||||
* COPYRIGHT file at the top-level directory of this distribution.
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the libLTE library.
|
||||
*
|
||||
* libLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* libLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Lesser General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <complex.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "liblte/phy/ch_estimation/chest.h"
|
||||
#include "liblte/phy/utils/vector.h"
|
||||
#include "liblte/phy/utils/debug.h"
|
||||
|
||||
#define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz)
|
||||
#define SF_SZ(q) (2 * SLOT_SZ(q))
|
||||
|
||||
#define VOLK_INTERP
|
||||
|
||||
void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
||||
chest_ref_fprint(q, stream, nslot, port_id);
|
||||
chest_recvsig_fprint(q, stream, nslot, port_id);
|
||||
chest_ce_fprint(q, stream, nslot, port_id);
|
||||
}
|
||||
|
||||
/* Sets the number of ports to estimate. nof_ports must be smaler than nof_ports
|
||||
* used during the call to chest_init().
|
||||
*/
|
||||
int chest_set_nof_ports(chest_t *q, uint32_t nof_ports) {
|
||||
if (nof_ports < q->nof_ports) {
|
||||
q->nof_ports = nof_ports;
|
||||
return LIBLTE_SUCCESS;
|
||||
} else {
|
||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
}
|
||||
|
||||
void chest_ref_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
||||
int i;
|
||||
fprintf(stream, "refs%d=[",port_id);
|
||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
||||
fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].refs[i].symbol,
|
||||
__imag__ q->refsignal[port_id][nslot].refs[i].symbol);
|
||||
}
|
||||
fprintf(stream, "];\n");
|
||||
}
|
||||
|
||||
void chest_recvsig_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
||||
int i;
|
||||
fprintf(stream, "recvsig%d=[",port_id);
|
||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
||||
fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].recv_symbol[i],
|
||||
__imag__ q->refsignal[port_id][nslot].recv_symbol[i]);
|
||||
}
|
||||
fprintf(stream, "];\n");
|
||||
}
|
||||
|
||||
void chest_ce_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
||||
int i;
|
||||
fprintf(stream, "mag%d=[",port_id);
|
||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
||||
fprintf(stream, "%3.3f, ", cabsf(q->refsignal[port_id][nslot].ch_est[i]));
|
||||
}
|
||||
fprintf(stream, "];\nphase%d=[",port_id);
|
||||
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
|
||||
fprintf(stream, "%3.3f, ", atan2f(__imag__ q->refsignal[port_id][nslot].ch_est[i],
|
||||
__real__ q->refsignal[port_id][nslot].ch_est[i]));
|
||||
}
|
||||
fprintf(stream, "];\n");
|
||||
}
|
||||
|
||||
float chest_rsrp(chest_t *q, uint32_t nslot, uint32_t port_id) {
|
||||
int nof_refs = q->refsignal[port_id][nslot].nof_refs;
|
||||
cf_t *ch_est = q->refsignal[port_id][nslot].ch_est;
|
||||
return crealf(vec_dot_prod_conj_ccc(ch_est, ch_est, nof_refs))/nof_refs;
|
||||
}
|
||||
|
||||
float chest_rsrp_sf(chest_t *q, uint32_t sf_idx) {
|
||||
int n,p;
|
||||
float rsrp=0;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
for (n=0;n<2;n++) {
|
||||
rsrp+=chest_rsrp(q, 2*sf_idx+n, p)/(2*q->nof_ports);
|
||||
}
|
||||
}
|
||||
return rsrp;
|
||||
}
|
||||
|
||||
float chest_rssi(chest_t *q, cf_t *input) {
|
||||
float rssi = 0;
|
||||
int i;
|
||||
int l[2];
|
||||
if (q->nof_symbols == CPNORM_NSYMB) {
|
||||
l[0] = 0; l[1] = 4;
|
||||
} else {
|
||||
l[0] = 0; l[1] = 3;
|
||||
}
|
||||
|
||||
for (i=0;i<2;i++) {
|
||||
cf_t *tmp = &input[l[i]*q->nof_re];
|
||||
rssi += crealf(vec_dot_prod_conj_ccc(tmp, tmp, q->nof_re));
|
||||
}
|
||||
return rssi;
|
||||
}
|
||||
|
||||
float chest_rssi_sf(chest_t *q, cf_t *input) {
|
||||
int n;
|
||||
int slotsz = q->nof_symbols*q->nof_re;
|
||||
float rssi=0;
|
||||
for (n=0;n<2;n++) {
|
||||
rssi += chest_rssi(q, &input[n*slotsz]);
|
||||
}
|
||||
return rssi;
|
||||
}
|
||||
|
||||
float chest_rsrq(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id) {
|
||||
return (q->nof_re/RE_X_RB) * chest_rsrp(q, nslot, port_id) / chest_rssi(q, input);
|
||||
}
|
||||
|
||||
float chest_rsrq_sf(chest_t *q, cf_t *input, uint32_t sf_idx) {
|
||||
return (4*q->nof_ports*q->nof_re/RE_X_RB) * chest_rsrp_sf(q, sf_idx) / chest_rssi_sf(q, input);
|
||||
}
|
||||
|
||||
int chest_measure_ref(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id, uint32_t nref) {
|
||||
int fidx, tidx;
|
||||
cf_t known_ref, channel_ref;
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
|
||||
if (q != NULL &&
|
||||
input != NULL &&
|
||||
nslot < NSLOTS_X_FRAME &&
|
||||
port_id < q->nof_ports)
|
||||
{
|
||||
if (nref < q->refsignal[port_id][nslot].nof_refs) {
|
||||
|
||||
fidx = q->refsignal[port_id][nslot].refs[nref].freq_idx; // reference frequency index
|
||||
tidx = q->refsignal[port_id][nslot].refs[nref].time_idx; // reference time index
|
||||
|
||||
known_ref = q->refsignal[port_id][nslot].refs[nref].symbol;
|
||||
channel_ref = input[tidx * q->nof_re + fidx];
|
||||
q->refsignal[port_id][nslot].recv_symbol[nref] = channel_ref;
|
||||
|
||||
DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, tidx * q->nof_re + fidx,
|
||||
10*log10f(cabsf(channel_ref/known_ref)),
|
||||
cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,
|
||||
cargf(channel_ref/known_ref)/M_PI);
|
||||
|
||||
|
||||
/* FIXME: compare with threshold */
|
||||
if (channel_ref != 0) {
|
||||
q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref;
|
||||
} else {
|
||||
q->refsignal[port_id][nslot].ch_est[nref] = 1e-6;
|
||||
}
|
||||
ret = LIBLTE_SUCCESS;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void chest_measure_slot_port(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id)
|
||||
{
|
||||
int i;
|
||||
refsignal_t *r = &q->refsignal[port_id][nslot];
|
||||
|
||||
DEBUG("Estimating channel slot=%d port=%d using %d reference signals\n",
|
||||
nslot, port_id, r->nof_refs);
|
||||
|
||||
for (i=0;i<r->nof_refs;i++) {
|
||||
chest_measure_ref(q, input, nslot, port_id, i);
|
||||
}
|
||||
}
|
||||
|
||||
void chest_measure_slot(chest_t *q, cf_t *input, uint32_t nslot) {
|
||||
int p;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
chest_measure_slot_port(q, input, nslot, p);
|
||||
}
|
||||
}
|
||||
|
||||
void chest_measure_sf(chest_t *q, cf_t *input, uint32_t sf_idx) {
|
||||
int p, n, slotsz;
|
||||
slotsz = q->nof_symbols*q->nof_re;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
for (n=0;n<2;n++) {
|
||||
chest_measure_slot_port(q, &input[n*slotsz], 2*sf_idx+n, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Computes channel estimates for each reference in a slot and port.
|
||||
* Saves the nof_prb * 12 * nof_symbols channel estimates in the array ce
|
||||
*/
|
||||
int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32_t port_id)
|
||||
{
|
||||
uint32_t i, j;
|
||||
cf_t x[2], y[MAX_NSYMB];
|
||||
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
|
||||
if (q != NULL &&
|
||||
input != NULL &&
|
||||
nslot < NSLOTS_X_FRAME &&
|
||||
port_id < q->nof_ports)
|
||||
{
|
||||
if (q->refsignal[port_id][nslot].nsymbols <= 2) {
|
||||
refsignal_t *r = &q->refsignal[port_id][nslot];
|
||||
|
||||
chest_measure_slot_port(q, input, nslot, port_id);
|
||||
|
||||
/* interpolate the symbols with references
|
||||
* in the freq domain */
|
||||
for (i=0;i<r->nsymbols;i++) {
|
||||
#ifdef VOLK_INTERP
|
||||
interp_run_offset(&q->interp_freq[port_id],
|
||||
&r->ch_est[i * r->nof_refs/2], &ce[r->symbols_ref[i] * q->nof_re],
|
||||
r->voffset, RE_X_RB/2-r->voffset);
|
||||
#else
|
||||
interp_linear_offset(&r->ch_est[i * r->nof_refs/2],
|
||||
&ce[r->symbols_ref[i] * q->nof_re], RE_X_RB/2,
|
||||
r->nof_refs/2, r->voffset, RE_X_RB/2-r->voffset);
|
||||
#endif
|
||||
}
|
||||
/* now interpolate in the time domain */
|
||||
for (i=0;i<q->nof_re; i++) {
|
||||
if (r->nsymbols > 1) {
|
||||
for (j=0;j<r->nsymbols;j++) {
|
||||
x[j] = ce[r->symbols_ref[j] * q->nof_re + i];
|
||||
}
|
||||
#ifdef VOLK_INTERP
|
||||
interp_run_offset(&q->interp_time[port_id], x, y,
|
||||
r->symbols_ref[0], 3);
|
||||
#else
|
||||
interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0],
|
||||
2, r->symbols_ref[0], 3);
|
||||
#endif
|
||||
} else {
|
||||
for (j=0;j<MAX_NSYMB;j++) {
|
||||
y[j] = ce[r->symbols_ref[0] * q->nof_re + i];
|
||||
}
|
||||
}
|
||||
for (j=0;j<q->nof_symbols;j++) {
|
||||
ce[j * q->nof_re + i] = y[j];
|
||||
}
|
||||
}
|
||||
ret = LIBLTE_SUCCESS;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Computes channel estimates for each reference in a subframe and port id.
|
||||
*/
|
||||
int chest_ce_sf_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id) {
|
||||
int n, slotsz, ret;
|
||||
slotsz = q->nof_symbols*q->nof_re;
|
||||
for (n=0;n<2;n++) {
|
||||
ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[n*slotsz], 2*sf_idx+n, port_id);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
/* Computes channel estimates for each reference in a slot for all ports.
|
||||
*/
|
||||
int chest_ce_slot(chest_t *q, cf_t *input, cf_t **ce, uint32_t nslot) {
|
||||
int p, ret;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
ret = chest_ce_slot_port(q, input, ce[p], nslot, p);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
/* Computes channel estimates for each reference in a subframe for all ports.
|
||||
*/
|
||||
int chest_ce_sf(chest_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t sf_idx) {
|
||||
int p, n, slotsz, ret;
|
||||
slotsz = q->nof_symbols*q->nof_re;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
for (n=0;n<2;n++) {
|
||||
ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[p][n*slotsz], 2*sf_idx+n, p);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int chest_init(chest_t *q, uint32_t nof_re, uint32_t nof_symbols, uint32_t nof_ports) {
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
|
||||
if (q != NULL &&
|
||||
nof_ports <= MAX_PORTS)
|
||||
{
|
||||
bzero(q, sizeof(chest_t));
|
||||
|
||||
q->nof_ports = nof_ports;
|
||||
q->nof_symbols = nof_symbols;
|
||||
q->nof_re = nof_re;
|
||||
|
||||
INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n",
|
||||
q->nof_symbols, q->nof_re, nof_ports);
|
||||
|
||||
ret = LIBLTE_SUCCESS;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void chest_free(chest_t *q) {
|
||||
int p, n;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
for (n=0;n<NSLOTS_X_FRAME;n++) {
|
||||
refsignal_free(&q->refsignal[p][n]);
|
||||
}
|
||||
}
|
||||
#ifdef VOLK_INTERP
|
||||
for (p=0;p<MAX_PORTS;p++) {
|
||||
interp_free(&q->interp_freq[p]);
|
||||
interp_free(&q->interp_time[p]);
|
||||
}
|
||||
#endif
|
||||
bzero(q, sizeof(chest_t));
|
||||
}
|
||||
|
||||
/* Fills l[2] with the symbols in the slot nslot that contain references.
|
||||
* returns the number of symbols with references (in the slot)
|
||||
*/
|
||||
int chest_ref_get_symbols(chest_t *q, uint32_t port_id, uint32_t nslot, uint32_t l[2]) {
|
||||
|
||||
if (q != NULL &&
|
||||
port_id < MAX_PORTS &&
|
||||
nslot < NSLOTS_X_FRAME)
|
||||
{
|
||||
memcpy(l, q->refsignal[port_id][nslot].symbols_ref, sizeof(uint32_t) * q->refsignal[port_id][nslot].nsymbols);
|
||||
return q->refsignal[port_id][nslot].nsymbols;
|
||||
} else {
|
||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Downlink Channel estimator
|
||||
*
|
||||
*********************************************************************/
|
||||
int chest_init_LTEDL(chest_t *q, lte_cell_t cell) {
|
||||
int ret;
|
||||
ret = chest_init(q, cell.nof_prb * RE_X_RB, CP_NSYMB(cell.cp), cell.nof_ports);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
return ret;
|
||||
} else {
|
||||
return chest_ref_set_LTEDL(q, cell);
|
||||
}
|
||||
}
|
||||
|
||||
int chest_ref_set_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_cell_t cell) {
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
|
||||
if (q != NULL &&
|
||||
port_id < MAX_PORTS &&
|
||||
nslot < NSLOTS_X_FRAME)
|
||||
{
|
||||
ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell);
|
||||
|
||||
#ifdef VOLK_INTERP
|
||||
if (ret == LIBLTE_SUCCESS) {
|
||||
if (nslot == 0) {
|
||||
ret = interp_init(&q->interp_freq[port_id], LINEAR, q->refsignal[port_id][nslot].nof_refs/2, RE_X_RB/2);
|
||||
if (ret == LIBLTE_SUCCESS) {
|
||||
ret = interp_init(&q->interp_time[port_id], LINEAR, 2,
|
||||
q->refsignal[port_id][nslot].symbols_ref[1] - q->refsignal[port_id][nslot].symbols_ref[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int chest_ref_set_LTEDL_slot(chest_t *q, uint32_t nslot, lte_cell_t cell) {
|
||||
int p, ret;
|
||||
for (p=0;p<q->nof_ports;p++) {
|
||||
ret = chest_ref_set_LTEDL_slot_port(q, nslot, p, cell);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int chest_ref_set_LTEDL(chest_t *q, lte_cell_t cell) {
|
||||
int n, ret;
|
||||
for (n=0;n<NSLOTS_X_FRAME;n++) {
|
||||
ret = chest_ref_set_LTEDL_slot(q, n, cell);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* TODO: Uplink Channel estimator
|
||||
*
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** High-level API
|
||||
*/
|
||||
int chest_initialize(chest_hl* h) {
|
||||
|
||||
lte_cell_t cell;
|
||||
|
||||
if (!h->init.nof_symbols) {
|
||||
h->init.nof_symbols = CPNORM_NSYMB; // Normal CP
|
||||
}
|
||||
if (!h->init.nof_prb) {
|
||||
h->init.nof_prb = 6;
|
||||
}
|
||||
|
||||
cell.id = h->init.cell_id;
|
||||
cell.nof_ports = h->init.nof_ports;
|
||||
cell.nof_prb = h->init.nof_prb;
|
||||
cell.cp = h->init.nof_symbols == CPNORM_NSYMB ? CPNORM : CPEXT;
|
||||
|
||||
if (chest_init_LTEDL(&h->obj, cell)) {
|
||||
fprintf(stderr, "Error initializing equalizer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** This function must be called in an subframe basis (1ms) for LTE */
|
||||
int chest_work(chest_hl* hl) {
|
||||
chest_t *q = &hl->obj;
|
||||
chest_ce_sf(q, hl->input, hl->output, hl->ctrl_in.sf_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chest_stop(chest_hl* hl) {
|
||||
chest_free(&hl->obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,382 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2014 The libLTE Developers. See the
|
||||
* COPYRIGHT file at the top-level directory of this distribution.
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the libLTE library.
|
||||
*
|
||||
* libLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* libLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Lesser General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <complex.h>
|
||||
|
||||
#include "liblte/phy/common/phy_common.h"
|
||||
#include "liblte/phy/ch_estimation/refsignal.h"
|
||||
#include "liblte/phy/utils/vector.h"
|
||||
#include "liblte/phy/utils/debug.h"
|
||||
#include "liblte/phy/common/sequence.h"
|
||||
|
||||
#include "ul_rs_tables.h"
|
||||
|
||||
#define idx(x, y) (l*nof_refs_x_symbol+i)
|
||||
|
||||
int refsignal_v(uint32_t port_id, uint32_t ns, uint32_t symbol_id)
|
||||
{
|
||||
int v = -1;
|
||||
switch (port_id) {
|
||||
case 0:
|
||||
if (symbol_id == 0) {
|
||||
v = 0;
|
||||
} else {
|
||||
v = 3;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (symbol_id == 0) {
|
||||
v = 3;
|
||||
} else {
|
||||
v = 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
v = 3 * (ns % 2);
|
||||
break;
|
||||
case 3:
|
||||
v = 3 + 3 * (ns % 2);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
uint32_t refsignal_k(uint32_t m, uint32_t v, uint32_t cell_id)
|
||||
{
|
||||
return 6 * m + ((v + (cell_id % 6)) % 6);
|
||||
}
|
||||
|
||||
int refsignal_put(refsignal_t * q, cf_t * slot_symbols)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t fidx, tidx;
|
||||
if (q != NULL && slot_symbols != NULL) {
|
||||
for (i = 0; i < q->nof_refs; i++) {
|
||||
fidx = q->refs[i].freq_idx; // reference frequency index
|
||||
tidx = q->refs[i].time_idx; // reference time index
|
||||
slot_symbols[SAMPLE_IDX(q->nof_prb, tidx, fidx)] = q->refs[i].symbol;
|
||||
}
|
||||
return LIBLTE_SUCCESS;
|
||||
} else {
|
||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes refsignal_t object according to 3GPP 36.211 6.10.1
|
||||
*
|
||||
*/
|
||||
int refsignal_init_LTEDL(refsignal_t * q, uint32_t port_id, uint32_t nslot,
|
||||
lte_cell_t cell)
|
||||
{
|
||||
|
||||
uint32_t c_init;
|
||||
uint32_t ns, l, lp[2];
|
||||
uint32_t N_cp;
|
||||
uint32_t i;
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
sequence_t seq;
|
||||
int v;
|
||||
uint32_t mp;
|
||||
uint32_t nof_refs_x_symbol, nof_ref_symbols;
|
||||
|
||||
if (q != NULL &&
|
||||
port_id < MAX_PORTS &&
|
||||
nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
|
||||
|
||||
bzero(q, sizeof(refsignal_t));
|
||||
bzero(&seq, sizeof(sequence_t));
|
||||
|
||||
if (CP_ISNORM(cell.cp)) {
|
||||
N_cp = 1;
|
||||
} else {
|
||||
N_cp = 0;
|
||||
}
|
||||
|
||||
if (port_id < 2) {
|
||||
nof_ref_symbols = 2;
|
||||
lp[0] = 0;
|
||||
lp[1] = CP_NSYMB(cell.cp) - 3;
|
||||
} else {
|
||||
nof_ref_symbols = 1;
|
||||
lp[0] = 1;
|
||||
}
|
||||
nof_refs_x_symbol = 2 * cell.nof_prb;
|
||||
|
||||
q->nof_refs = nof_refs_x_symbol * nof_ref_symbols;
|
||||
q->nsymbols = nof_ref_symbols;
|
||||
q->voffset = cell.id % 6;
|
||||
q->nof_prb = cell.nof_prb;
|
||||
|
||||
q->symbols_ref = malloc(sizeof(uint32_t) * nof_ref_symbols);
|
||||
if (!q->symbols_ref) {
|
||||
perror("malloc");
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
memcpy(q->symbols_ref, lp, sizeof(uint32_t) * nof_ref_symbols);
|
||||
|
||||
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
|
||||
if (!q->refs) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
|
||||
if (!q->ch_est) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
q->recv_symbol = vec_malloc(q->nof_refs * sizeof(cf_t));
|
||||
if (!q->recv_symbol) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
ns = nslot;
|
||||
for (l = 0; l < nof_ref_symbols; l++) {
|
||||
|
||||
c_init = 1024 * (7 * (ns + 1) + lp[l] + 1) * (2 * cell.id + 1)
|
||||
+ 2 * cell.id + N_cp;
|
||||
ret = sequence_LTE_pr(&seq, 2 * 2 * MAX_PRB, c_init);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
v = refsignal_v(port_id, ns, lp[l]);
|
||||
|
||||
for (i = 0; i < nof_refs_x_symbol; i++) {
|
||||
mp = i + MAX_PRB - cell.nof_prb;
|
||||
|
||||
/* generate signal */
|
||||
__real__ q->refs[idx(l, i)].symbol =
|
||||
(1 - 2 * (float) seq.c[2 * mp]) / sqrt(2);
|
||||
__imag__ q->refs[idx(l, i)].symbol =
|
||||
(1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2);
|
||||
|
||||
/* mapping to resource elements */
|
||||
q->refs[idx(l, i)].freq_idx = refsignal_k(i, (uint32_t) v, cell.id);
|
||||
q->refs[idx(l, i)].time_idx = lp[l];
|
||||
}
|
||||
}
|
||||
ret = LIBLTE_SUCCESS;
|
||||
}
|
||||
free_and_exit:
|
||||
if (ret != LIBLTE_ERROR_INVALID_INPUTS) {
|
||||
sequence_free(&seq);
|
||||
}
|
||||
if (ret == LIBLTE_ERROR) {
|
||||
refsignal_free(q);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// n_drms_2 table 5.5.2.1.1-1 from 36.211
|
||||
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
|
||||
|
||||
// n_drms_1 table 5.5.2.1.1-2 from 36.211
|
||||
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
|
||||
|
||||
|
||||
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */
|
||||
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id,
|
||||
refsignal_ul_cfg_t * cfg)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// Calculate u and v
|
||||
uint32_t u, v;
|
||||
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
|
||||
if (cfg->group_hopping_en) {
|
||||
sequence_t seq;
|
||||
sequence_LTE_pr(&seq, cell_id / 30, 160);
|
||||
uint32_t f_gh = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
f_gh += seq.c[8 * ns + i] << i;
|
||||
}
|
||||
sequence_free(&seq);
|
||||
u = ((f_gh%30) + f_ss) % 30;
|
||||
} else {
|
||||
u = f_ss % 30;
|
||||
}
|
||||
|
||||
if (len < 6 * RE_X_RB) {
|
||||
v = 0;
|
||||
} else {
|
||||
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) {
|
||||
sequence_t seq;
|
||||
sequence_LTE_pr(&seq, ((cell_id / 30) << 5) + f_ss, 20);
|
||||
v = seq.c[ns];
|
||||
sequence_free(&seq);
|
||||
} else {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
if (len >= 3 * RE_X_RB) {
|
||||
uint32_t n_sz=0;
|
||||
uint32_t q;
|
||||
float q_hat;
|
||||
/* get largest prime n_zc<len */
|
||||
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
|
||||
if (prime_numbers[i] < len) {
|
||||
n_sz = prime_numbers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
q_hat = (float) n_sz *(u + 1) / 31;
|
||||
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
|
||||
q = (uint32_t) (q_hat + 0.5) + v;
|
||||
} else {
|
||||
q = (uint32_t) (q_hat + 0.5) - v;
|
||||
}
|
||||
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
|
||||
if (!x_q) {
|
||||
perror("malloc");
|
||||
return LIBLTE_ERROR;
|
||||
}
|
||||
for (i = 0; i < n_sz; i++) {
|
||||
x_q[i] =
|
||||
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz);
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
refs[i].symbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz];
|
||||
}
|
||||
free(x_q);
|
||||
} else {
|
||||
if (len == RE_X_RB) {
|
||||
for (i = 0; i < len; i++) {
|
||||
refs[i].symbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
refs[i].symbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
|
||||
*
|
||||
*/
|
||||
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start,
|
||||
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
|
||||
{
|
||||
|
||||
uint32_t i;
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
uint32_t n_prs;
|
||||
uint32_t M_sc;
|
||||
float alpha;
|
||||
|
||||
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
|
||||
|
||||
bzero(q, sizeof(refsignal_t));
|
||||
|
||||
M_sc = nof_prb * RE_X_RB;
|
||||
|
||||
q->nof_refs = M_sc;
|
||||
q->nsymbols = 1;
|
||||
q->voffset = cell.id % 6;
|
||||
q->nof_prb = cell.nof_prb;
|
||||
|
||||
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
|
||||
if (!q->symbols_ref) {
|
||||
perror("malloc");
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
if (CP_ISNORM(cell.cp)) {
|
||||
q->symbols_ref[0] = 3;
|
||||
} else {
|
||||
q->symbols_ref[0] = 2;
|
||||
}
|
||||
|
||||
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
|
||||
if (!q->refs) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
|
||||
if (!q->ch_est) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
/* Calculate n_prs */
|
||||
uint32_t c_init;
|
||||
sequence_t seq;
|
||||
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
|
||||
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
n_prs = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
|
||||
}
|
||||
sequence_free(&seq);
|
||||
|
||||
// Calculate cyclic shift alpha
|
||||
uint32_t n_cs =
|
||||
(n_drms_1[cfg->cyclic_shift] +
|
||||
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
|
||||
alpha = 2 * M_PI * (n_cs) / 12;
|
||||
|
||||
if (rs_sequence(q->refs, M_sc, alpha, cell.id, nslot, cfg)) {
|
||||
fprintf(stderr, "Error generating RS sequence\n");
|
||||
goto free_and_exit;
|
||||
}
|
||||
/* mapping to resource elements */
|
||||
for (i=0;i<M_sc;i++) {
|
||||
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
|
||||
q->refs[i].time_idx = q->symbols_ref[0];
|
||||
}
|
||||
|
||||
ret = LIBLTE_SUCCESS;
|
||||
}
|
||||
free_and_exit:
|
||||
if (ret == LIBLTE_ERROR) {
|
||||
refsignal_free(q);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void refsignal_free(refsignal_t * q)
|
||||
{
|
||||
if (q->symbols_ref) {
|
||||
free(q->symbols_ref);
|
||||
}
|
||||
if (q->refs) {
|
||||
free(q->refs);
|
||||
}
|
||||
if (q->ch_est) {
|
||||
free(q->ch_est);
|
||||
}
|
||||
bzero(q, sizeof(refsignal_t));
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
|
||||
#ifdef nocompile
|
||||
// n_drms_2 table 5.5.2.1.1-1 from 36.211
|
||||
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
|
||||
|
||||
// n_drms_1 table 5.5.2.1.1-2 from 36.211
|
||||
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
|
||||
|
||||
|
||||
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */
|
||||
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id,
|
||||
refsignal_ul_cfg_t * cfg)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// Calculate u and v
|
||||
uint32_t u, v;
|
||||
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
|
||||
printf("f_ss: %d\n", f_ss);
|
||||
if (cfg->group_hopping_en) {
|
||||
sequence_t seq;
|
||||
bzero(&seq, sizeof(sequence_t));
|
||||
sequence_LTE_pr(&seq, 160, cell_id / 30);
|
||||
uint32_t f_gh = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
f_gh += (((uint32_t) seq.c[8 * ns + i]) << i);
|
||||
}
|
||||
printf("f_gh: %u\n", f_gh);
|
||||
sequence_free(&seq);
|
||||
u = ((f_gh%30) + f_ss) % 30;
|
||||
} else {
|
||||
u = f_ss % 30;
|
||||
}
|
||||
|
||||
if (len < 6 * RE_X_RB) {
|
||||
v = 0;
|
||||
} else {
|
||||
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) {
|
||||
sequence_t seq;
|
||||
bzero(&seq, sizeof(sequence_t));
|
||||
sequence_LTE_pr(&seq, 20, ((cell_id / 30) << 5) + f_ss);
|
||||
v = seq.c[ns];
|
||||
sequence_free(&seq);
|
||||
} else {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
printf("u: %d, v: %d\n", u, v);
|
||||
if (len >= 3 * RE_X_RB) {
|
||||
uint32_t n_sz;
|
||||
uint32_t q;
|
||||
float q_hat;
|
||||
/* get largest prime n_zc<len */
|
||||
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
|
||||
if (prime_numbers[i] < len) {
|
||||
n_sz = prime_numbers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("n_sz: %d\n", n_sz);
|
||||
q_hat = (float) n_sz *(u + 1) / 31;
|
||||
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
|
||||
q = (uint32_t) (q_hat + 0.5) + v;
|
||||
} else {
|
||||
q = (uint32_t) (q_hat + 0.5) - v;
|
||||
}
|
||||
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
|
||||
if (!x_q) {
|
||||
perror("malloc");
|
||||
return LIBLTE_ERROR;
|
||||
}
|
||||
for (i = 0; i < n_sz; i++) {
|
||||
x_q[i] =
|
||||
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
refs[i].simbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz];
|
||||
}
|
||||
free(x_q);
|
||||
} else {
|
||||
if (len == RE_X_RB) {
|
||||
for (i = 0; i < len; i++) {
|
||||
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LIBLTE_SUCCESS;
|
||||
}
|
||||
|
||||
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
|
||||
*
|
||||
*/
|
||||
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start,
|
||||
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
|
||||
{
|
||||
|
||||
uint32_t i;
|
||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||
uint32_t n_prs;
|
||||
uint32_t M_sc;
|
||||
float alpha;
|
||||
|
||||
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
|
||||
|
||||
bzero(q, sizeof(refsignal_t));
|
||||
|
||||
M_sc = nof_prb * RE_X_RB;
|
||||
|
||||
q->nof_refs = M_sc;
|
||||
q->nsymbols = 1;
|
||||
q->voffset = cell.id % 6;
|
||||
q->nof_prb = cell.nof_prb;
|
||||
|
||||
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
|
||||
if (!q->symbols_ref) {
|
||||
perror("malloc");
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
if (CP_ISNORM(cell.cp)) {
|
||||
q->symbols_ref[0] = 3;
|
||||
} else {
|
||||
q->symbols_ref[0] = 2;
|
||||
}
|
||||
|
||||
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
|
||||
if (!q->refs) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
|
||||
if (!q->ch_est) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
|
||||
/* Calculate n_prs */
|
||||
uint32_t c_init;
|
||||
sequence_t seq;
|
||||
bzero(&seq, sizeof(sequence_t));
|
||||
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
|
||||
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
|
||||
if (ret != LIBLTE_SUCCESS) {
|
||||
goto free_and_exit;
|
||||
}
|
||||
n_prs = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
|
||||
}
|
||||
sequence_free(&seq);
|
||||
|
||||
// Calculate cyclic shift alpha
|
||||
uint32_t n_cs =
|
||||
(n_drms_1[cfg->cyclic_shift] +
|
||||
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
|
||||
alpha = 2 * M_PI * (n_cs) / 12;
|
||||
|
||||
printf("alpha: %g\n", alpha);
|
||||
|
||||
if (rs_sequence(q->refs, M_sc, alpha, nslot, cell.id, cfg)) {
|
||||
fprintf(stderr, "Error generating RS sequence\n");
|
||||
goto free_and_exit;
|
||||
}
|
||||
/* mapping to resource elements */
|
||||
for (i=0;i<M_sc;i++) {
|
||||
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
|
||||
q->refs[i].time_idx = q->symbols_ref[0];
|
||||
}
|
||||
|
||||
ret = LIBLTE_SUCCESS;
|
||||
}
|
||||
free_and_exit:
|
||||
if (ret == LIBLTE_ERROR) {
|
||||
refsignal_free(q);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,92 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "liblte/phy/phy.h"
|
||||
|
||||
typedef _Complex float cf_t;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint32_t N = 1000; // Number of sinwave samples
|
||||
interp_t interp;
|
||||
struct timeval t[3];
|
||||
|
||||
if (argc < 3) {
|
||||
printf("usage: %s upsampling_rate nof_trials\n", argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
uint32_t M = atoi(argv[1]);
|
||||
uint32_t nof_trials = atoi(argv[2]);
|
||||
|
||||
if (interp_init(&interp, LINEAR, N, M)) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
cf_t *in = vec_malloc(N*sizeof(cf_t));
|
||||
if(!in) {
|
||||
perror("malloc");
|
||||
exit(-1);
|
||||
}
|
||||
cf_t *out = vec_malloc(M * N*sizeof(cf_t));
|
||||
if(!out) {
|
||||
perror("malloc");
|
||||
exit(-1);
|
||||
}
|
||||
cf_t *out_volk = vec_malloc(M * N*sizeof(cf_t));
|
||||
if(!out_volk) {
|
||||
perror("malloc");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
for(uint32_t i=0;i<N;i++) {
|
||||
//in[i] = sin((float) i*2*M_PI/N)+10*I*cos((float) i*2*M_PI/N);
|
||||
in[i] = (float) rand()/RAND_MAX-0.5 + ((float) rand()/RAND_MAX - 0.5) * I;
|
||||
}
|
||||
uint32_t exec_normal, exec_volk;
|
||||
float mean_normal=0, mean_volk=0;
|
||||
bzero(out_volk, M * N*sizeof(cf_t));
|
||||
bzero(out, M * N*sizeof(cf_t));
|
||||
|
||||
for (int n=0;n<nof_trials;n++) {
|
||||
|
||||
gettimeofday(&t[1], NULL);
|
||||
interp_linear_c(in, out, M, N);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
exec_normal = t[0].tv_usec;
|
||||
|
||||
gettimeofday(&t[1], NULL);
|
||||
interp_run(&interp, in, out_volk);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
exec_volk = t[0].tv_usec;
|
||||
|
||||
if (n == 0) {
|
||||
exec_volk = 0;
|
||||
}
|
||||
|
||||
mean_normal = (mean_normal + exec_normal * n) / (n+1);
|
||||
mean_volk = (mean_volk + exec_volk * n) / (n+1);
|
||||
}
|
||||
|
||||
// Check interp values
|
||||
float diff = 0.0;
|
||||
for(int i=0;i<N*M;i++){
|
||||
diff += cabsf(out_volk[i] - out[i])/N/M;
|
||||
}
|
||||
printf("%d: error=%f Exec: %f - %f\n", M, diff, mean_normal, mean_volk);
|
||||
|
||||
free(in);
|
||||
free(out);
|
||||
free(out_volk);
|
||||
|
||||
interp_free(&interp);
|
||||
|
||||
printf("Ok\n");
|
||||
exit(0);
|
||||
}
|
Loading…
Reference in New Issue