srsLTE/lib/src/phy/phch/npdcch.c

802 lines
25 KiB
C

/**
* Copyright 2013-2022 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsRAN 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 Affero General Public License for more details.
*
* A copy of the GNU Affero 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "prb_dl.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/phch/dci_nbiot.h"
#include "srsran/phy/phch/npdcch.h"
#include "srsran/phy/utils/bit.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#define DUMP_SIGNALS 0
#define RE_EXT_DEBUG 0
/** Initializes the NPDCCH transmitter and receiver */
int srsran_npdcch_init(srsran_npdcch_t* q)
{
int ret = SRSRAN_ERROR_INVALID_INPUTS;
if (q != NULL) {
ret = SRSRAN_ERROR;
bzero(q, sizeof(srsran_npdcch_t));
q->nof_cce = 2; // One Format1 NPDCCH occupying both NCCEs
// Allocate memory for the maximum number of NPDCCH bits, i.e. one full PRB
q->max_bits = SRSRAN_CP_NORM_SF_NSYMB * SRSRAN_NRE * 2;
q->ncce_bits = q->max_bits / 2;
INFO("Init NPDCCH: Max bits: %d, %d ports.", q->max_bits, q->cell.nof_ports);
if (srsran_modem_table_lte(&q->mod, SRSRAN_MOD_QPSK)) {
goto clean;
}
if (srsran_crc_init(&q->crc, SRSRAN_LTE_CRC16, 16)) {
goto clean;
}
int poly[3] = {0x6D, 0x4F, 0x57};
if (srsran_viterbi_init(&q->decoder, SRSRAN_VITERBI_37, poly, SRSRAN_NBIOT_DCI_MAX_SIZE + 16, true)) {
goto clean;
}
q->e = srsran_vec_u8_malloc(q->max_bits);
if (!q->e) {
goto clean;
}
for (int i = 0; i < 2; i++) {
q->llr[i] = srsran_vec_f_malloc(q->max_bits);
if (!q->llr[i]) {
goto clean;
}
srsran_vec_f_zero(q->llr[i], q->max_bits);
}
q->d = srsran_vec_cf_malloc(q->max_bits / 2);
if (!q->d) {
goto clean;
}
for (uint32_t i = 0; i < SRSRAN_MAX_PORTS; i++) {
q->ce[i] = srsran_vec_cf_malloc(q->max_bits / 2);
if (!q->ce[i]) {
goto clean;
}
for (uint32_t k = 0; k < q->max_bits / 2; k++) {
q->ce[i][k] = 1;
}
q->x[i] = srsran_vec_cf_malloc(q->max_bits / 2);
if (!q->x[i]) {
goto clean;
}
q->symbols[i] = srsran_vec_cf_malloc(q->max_bits / 2);
if (!q->symbols[i]) {
goto clean;
}
memset(q->symbols[i], 0, sizeof(cf_t) * q->max_bits / 2);
}
ret = SRSRAN_SUCCESS;
}
clean:
if (ret == SRSRAN_ERROR) {
srsran_npdcch_free(q);
}
return ret;
}
void srsran_npdcch_free(srsran_npdcch_t* q)
{
if (q->e) {
free(q->e);
}
for (uint32_t i = 0; i < 2; i++) {
if (q->llr[i]) {
free(q->llr[i]);
}
}
if (q->d) {
free(q->d);
}
for (uint32_t i = 0; i < SRSRAN_MAX_PORTS; i++) {
if (q->ce[i]) {
free(q->ce[i]);
}
if (q->x[i]) {
free(q->x[i]);
}
if (q->symbols[i]) {
free(q->symbols[i]);
}
}
for (uint32_t i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) {
srsran_sequence_free(&q->seq[i]);
}
srsran_modem_table_free(&q->mod);
srsran_viterbi_free(&q->decoder);
bzero(q, sizeof(srsran_npdcch_t));
}
int srsran_npdcch_set_cell(srsran_npdcch_t* q, srsran_nbiot_cell_t cell)
{
int ret = SRSRAN_ERROR_INVALID_INPUTS;
if (q != NULL && srsran_nbiot_cell_isvalid(&cell)) {
ret = SRSRAN_ERROR;
if (q->cell.n_id_ncell != cell.n_id_ncell || q->cell.base.nof_prb == 0) {
q->cell = cell;
if (q->cell.mode == SRSRAN_NBIOT_MODE_INBAND_SAME_PCI || q->cell.mode == SRSRAN_NBIOT_MODE_INBAND_DIFFERENT_PCI) {
q->i_n_start = 3;
} else {
q->i_n_start = 0;
}
// TODO: Add case for LTE cell with 4 ports
if (q->cell.nof_ports == 1) {
q->nof_nbiot_refs = 2;
} else {
q->nof_nbiot_refs = 4;
}
if (q->cell.base.nof_ports == 1) {
q->nof_lte_refs = 2;
} else {
q->nof_lte_refs = 4;
}
// Update the maximum number of NPDCCH bits, i.e. one PRB minus the starting offset minus the reference symbols
q->max_bits = srsran_ra_nbiot_dl_grant_nof_re(q->cell, q->i_n_start) * 2;
q->ncce_bits = q->max_bits / 2;
for (int i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) {
if (srsran_sequence_npdcch(&q->seq[i], 2 * i, q->cell.n_id_ncell, q->max_bits)) {
return SRSRAN_ERROR;
}
}
}
ret = SRSRAN_SUCCESS;
}
return ret;
}
int srsran_npdcch_dci_decode(srsran_npdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc)
{
uint16_t p_bits, crc_res;
uint8_t* x;
if (q != NULL) {
if (data != NULL && E <= q->max_bits && nof_bits <= SRSRAN_DCI_MAX_BITS) {
srsran_vec_f_zero(q->rm_f, 3 * (SRSRAN_DCI_MAX_BITS + 16));
uint32_t coded_len = 3 * (nof_bits + 16);
// unrate matching
srsran_rm_conv_rx(e, E, q->rm_f, coded_len);
// viterbi decoder
srsran_viterbi_decode_f(&q->decoder, q->rm_f, data, nof_bits + 16);
x = &data[nof_bits];
p_bits = (uint16_t)srsran_bit_pack(&x, 16);
crc_res = ((uint16_t)srsran_crc_checksum(&q->crc, data, nof_bits) & 0xffff);
if (crc) {
*crc = p_bits ^ crc_res;
}
return SRSRAN_SUCCESS;
} else {
fprintf(stderr, "Invalid parameters: E: %d, max_bits: %d, nof_bits: %d\n", E, q->max_bits, nof_bits);
return SRSRAN_ERROR_INVALID_INPUTS;
}
} else {
return SRSRAN_ERROR_INVALID_INPUTS;
}
}
/** Tries to decode a DCI message from the LLRs stored in the srsran_npdcch_t structure by the function
* srsran_npdcch_extract_llr(). This function can be called multiple times.
* The decoded message is stored in msg and the CRC remainder in crc_rem pointer
*
*/
int srsran_npdcch_decode_msg(srsran_npdcch_t* q,
srsran_dci_msg_t* msg,
srsran_dci_location_t* location,
srsran_dci_format_t format,
uint16_t* crc_rem)
{
int ret = SRSRAN_ERROR_INVALID_INPUTS;
int num_decoded_symbols = 0;
if (q != NULL && msg != NULL && srsran_nbiot_dci_location_isvalid(location)) {
uint32_t nof_bits = (format == SRSRAN_DCI_FORMATN2) ? 15 : 23;
uint32_t e_bits = q->ncce_bits * location->L;
// Get the right softbits for this aggregation level
float* llr = (location->L == 1) ? q->llr[0] : q->llr[1];
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("LLR:");
srsran_vec_fprint_f(stdout, llr, q->max_bits);
}
double mean = 0;
for (int i = 0; i < e_bits; i++) {
mean += fabsf(llr[location->ncce * q->ncce_bits + i]);
}
mean /= e_bits;
if (mean > 0.3) {
ret = srsran_npdcch_dci_decode(q, &llr[location->ncce * q->ncce_bits], msg->payload, e_bits, nof_bits, crc_rem);
if (ret == SRSRAN_SUCCESS) {
num_decoded_symbols = e_bits / 2;
msg->nof_bits = nof_bits;
// Check format differentiation
if (format == SRSRAN_DCI_FORMATN0 || format == SRSRAN_DCI_FORMATN1) {
msg->format = (msg->payload[0] == 0) ? SRSRAN_DCI_FORMATN0 : SRSRAN_DCI_FORMATN1;
} else {
msg->format = format;
}
} else {
fprintf(stderr, "Error calling npdcch_dci_decode\n");
}
if (crc_rem) {
DEBUG("Decoded DCI: nCCE=%d, L=%d, format=%s, msg_len=%d, mean=%f, crc_rem=0x%x",
location->ncce,
location->L,
srsran_dci_format_string(msg->format),
nof_bits,
mean,
*crc_rem);
}
} else {
DEBUG("Skipping DCI: nCCE=%d, L=%d, msg_len=%d, mean=%f", location->ncce, location->L, nof_bits, mean);
}
q->num_decoded_symbols = num_decoded_symbols;
ret = SRSRAN_SUCCESS;
} else {
fprintf(stderr, "Invalid parameters, location=%d,%d\n", location->ncce, location->L);
}
return ret;
}
/** Extracts the LLRs from srsran_dci_location_t location of the subframe and stores them in the srsran_npdcch_t
* structure. DCI messages can be extracted from this location calling the function srsran_npdcch_decode_msg(). Every
* time this function is called (with a different location), the last demodulated symbols are overwritten and new
* messages from other locations can be decoded
*/
int srsran_npdcch_extract_llr(srsran_npdcch_t* q,
cf_t* sf_symbols,
cf_t* ce[SRSRAN_MAX_PORTS],
float noise_estimate,
uint32_t sf_idx)
{
int ret = SRSRAN_ERROR_INVALID_INPUTS;
/* Set pointers for layermapping & precoding */
uint32_t num_symbols, e_bits;
float* llr;
cf_t* x[SRSRAN_MAX_LAYERS];
if (q != NULL && sf_idx < 10) {
ret = SRSRAN_ERROR;
for (int i = 0; i < SRSRAN_NPDCCH_FORMAT_NITEMS; i++) {
// set parameters according to NPDCCH format
switch (i) {
case SRSRAN_NPDCCH_FORMAT0_LOWER_HALF:
e_bits = q->ncce_bits;
llr = q->llr[0];
break;
case SRSRAN_NPDCCH_FORMAT0_UPPER_HALF:
e_bits = q->ncce_bits;
llr = &q->llr[0][q->ncce_bits];
break;
case SRSRAN_NPDCCH_FORMAT1:
e_bits = q->ncce_bits * 2;
llr = q->llr[1];
break;
default:
return ret;
}
num_symbols = e_bits / 2;
DEBUG("Extracting LLRs for NPDCCH %s: E: %d, SF: %d", srsran_npdcch_format_text[i], e_bits, sf_idx);
if (i != SRSRAN_NPDCCH_FORMAT0_UPPER_HALF) {
// don't overwrite lower half LLRs
srsran_vec_f_zero(llr, q->max_bits);
}
// number of layers equals number of ports
for (int f = 0; f < q->cell.nof_ports; f++) {
x[f] = q->x[f];
}
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSRAN_MAX_LAYERS - q->cell.nof_ports));
// extract symbols
int n = srsran_npdcch_get(q, sf_symbols, q->symbols[0], i);
if (num_symbols != n) {
fprintf(stderr, "Expected %d NPDCCH symbols but got %d symbols\n", num_symbols, n);
return ret;
}
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_rx_mapping_output.bin: NPDCCH after extracting symbols");
srsran_vec_save_file("npdcch_rx_mapping_output.bin", q->symbols[0], n * sizeof(cf_t));
}
#endif
// extract channel estimates
for (int p = 0; p < q->cell.nof_ports; p++) {
n = srsran_npdcch_get(q, ce[p], q->ce[p], i);
if (num_symbols != n) {
fprintf(stderr, "Expected %d NPDCCH symbols but got %d symbols\n", num_symbols, n);
return ret;
}
}
// in control channels, only diversity is supported
if (q->cell.nof_ports == 1) {
// no need for layer demapping
srsran_predecoding_single(q->symbols[0], q->ce[0], q->d, NULL, num_symbols, 1.0, noise_estimate);
} else {
srsran_predecoding_diversity(q->symbols[0], q->ce, x, q->cell.nof_ports, num_symbols, 1.0);
srsran_layerdemap_diversity(x, q->d, q->cell.nof_ports, num_symbols / q->cell.nof_ports);
}
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_rx_predecode_output.bin: NPDCCH after predecoding symbols");
srsran_vec_save_file("npdcch_rx_predecode_output.bin", q->d, q->num_decoded_symbols * sizeof(cf_t));
}
#endif
// demodulate symbols
srsran_demod_soft_demodulate(SRSRAN_MOD_QPSK, q->d, llr, num_symbols);
// descramble
srsran_scrambling_f_offset(&q->seq[sf_idx], llr, 0, e_bits);
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_rx_descramble_output.bin: NPDCCH after de-scrambling");
srsran_vec_save_file("npdcch_rx_descramble_output.bin", llr, e_bits);
}
#endif
}
ret = SRSRAN_SUCCESS;
}
return ret;
}
static void crc_set_mask_rnti(uint8_t* crc, uint16_t rnti)
{
uint8_t mask[16] = {};
uint8_t* r = mask;
DEBUG("Mask CRC with RNTI 0x%x", rnti);
srsran_bit_unpack(rnti, &r, 16);
for (uint32_t i = 0; i < 16; i++) {
crc[i] = (crc[i] + mask[i]) % 2;
}
}
void srsran_npdcch_dci_encode_conv(srsran_npdcch_t* q,
uint8_t* data,
uint32_t nof_bits,
uint8_t* coded_data,
uint16_t rnti)
{
srsran_convcoder_t encoder;
int poly[3] = {0x6D, 0x4F, 0x57};
encoder.K = 7;
encoder.R = 3;
encoder.tail_biting = true;
memcpy(encoder.poly, poly, 3 * sizeof(int));
srsran_crc_attach(&q->crc, data, nof_bits);
crc_set_mask_rnti(&data[nof_bits], rnti);
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_tx_convcoder_input.bin: NPDCCH before convolution coding");
srsran_vec_save_file("npdcch_tx_convcoder_input.bin", data, nof_bits + 16);
}
#endif
srsran_convcoder_encode(&encoder, data, coded_data, nof_bits + 16);
}
/** 36.212 5.3.3.2 to 5.3.3.4
* TODO: UE transmit antenna selection CRC mask
*/
int srsran_npdcch_dci_encode(srsran_npdcch_t* q,
uint8_t* data,
uint8_t* e,
uint32_t nof_bits,
uint32_t E,
uint16_t rnti)
{
uint8_t tmp[3 * (SRSRAN_DCI_MAX_BITS + 16)];
if (q != NULL && data != NULL && e != NULL && nof_bits < SRSRAN_DCI_MAX_BITS && E <= q->max_bits) {
srsran_npdcch_dci_encode_conv(q, data, nof_bits, tmp, rnti);
DEBUG("CConv output: ");
if (SRSRAN_VERBOSE_ISDEBUG()) {
srsran_vec_fprint_b(stdout, tmp, 3 * (nof_bits + 16));
}
srsran_rm_conv_tx(tmp, 3 * (nof_bits + 16), e, E);
return SRSRAN_SUCCESS;
} else {
return SRSRAN_ERROR_INVALID_INPUTS;
}
}
int srsran_npdcch_encode(srsran_npdcch_t* q,
srsran_dci_msg_t* msg,
srsran_dci_location_t location,
uint16_t rnti,
cf_t* sf_symbols[SRSRAN_MAX_PORTS],
uint32_t nsubframe)
{
int ret = SRSRAN_ERROR_INVALID_INPUTS;
if (q != NULL && sf_symbols != NULL && nsubframe < 10 && srsran_nbiot_dci_location_isvalid(&location)) {
ret = SRSRAN_ERROR;
uint32_t e_bits = q->nof_cce * q->ncce_bits;
uint32_t nof_symbols = e_bits / 2;
if (msg->nof_bits < SRSRAN_DCI_MAX_BITS - 16) {
DEBUG("Encoding DCI: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x, sf_idx: %d",
msg->nof_bits,
e_bits,
location.ncce,
location.L,
rnti,
nsubframe);
if (srsran_npdcch_dci_encode(q, msg->payload, q->e, msg->nof_bits, e_bits, rnti) != SRSRAN_SUCCESS) {
fprintf(stderr, "Error encoding DCI\n");
return SRSRAN_ERROR;
}
// number of layers equals number of ports
cf_t* x[SRSRAN_MAX_LAYERS] = {NULL};
for (int i = 0; i < q->cell.nof_ports; i++) {
x[i] = q->x[i];
}
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSRAN_MAX_LAYERS - q->cell.nof_ports));
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_tx_scramble_input.bin: NPDCCH before scrambling");
srsran_vec_save_file("npdcch_tx_scramble_input.bin", q->e, e_bits);
}
#endif
srsran_scrambling_b_offset(&q->seq[nsubframe], q->e, 72 * location.ncce, e_bits);
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_tx_mod_input.bin: NPDCCH before modulation");
srsran_vec_save_file("npdcch_tx_mod_input.bin", q->e, e_bits);
}
#endif
srsran_mod_modulate(&q->mod, q->e, q->d, e_bits);
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_tx_precode_input.bin: NPDCCH before precoding symbols");
srsran_vec_save_file("npdcch_tx_precode_input.bin", q->d, nof_symbols * sizeof(cf_t));
}
#endif
// layer mapping & precoding
if (q->cell.nof_ports > 1) {
srsran_layermap_diversity(q->d, x, q->cell.nof_ports, nof_symbols);
srsran_precoding_diversity(x, q->symbols, q->cell.nof_ports, nof_symbols / q->cell.nof_ports, 1.0);
} else {
memcpy(q->symbols[0], q->d, nof_symbols * sizeof(cf_t));
}
#if DUMP_SIGNALS
if (SRSRAN_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdcch_tx_mapping_input.bin: NPDCCH before mapping to resource elements");
srsran_vec_save_file("npdcch_tx_mapping_input.bin", q->symbols[0], nof_symbols * sizeof(cf_t));
}
#endif
// mapping to resource elements
for (int i = 0; i < q->cell.nof_ports; i++) {
srsran_npdcch_put(q, q->symbols[i], sf_symbols[i], SRSRAN_NPDCCH_FORMAT1);
}
ret = SRSRAN_SUCCESS;
} else {
fprintf(stderr, "Illegal DCI message nCCE: %d, L: %d, nof_cce: %d\n", location.ncce, location.L, q->nof_cce);
}
} else {
fprintf(stderr, "Invalid parameters: L=%d, nCCE=%d\n", location.L, location.ncce);
}
return ret;
}
/** 36.213 v9.1.1
* Computes up to max_candidates UE-specific candidates for DCI messages and saves them
* in the structure pointed by c.
* Returns the number of candidates saved in the array c.
*/
uint32_t srsran_npdcch_ue_locations(srsran_dci_location_t* c, uint32_t max_candidates)
{
// NPDCCH format 1 takes both NCCE
c[0].L = 2;
c[0].ncce = 0;
// NPDCCH format 0 only takes one NCCE so two of them may be transmitted in one subframe
c[1].L = 1;
c[1].ncce = 0;
c[2].L = 1;
c[2].ncce = 1;
return max_candidates;
}
uint32_t srsran_npdcch_common_locations(srsran_dci_location_t* c, uint32_t max_candidates)
{
return srsran_npdcch_ue_locations(c, max_candidates);
}
int srsran_npdcch_cp(srsran_npdcch_t* q, cf_t* input, cf_t* output, bool put, srsran_npdcch_format_t format)
{
// sanity check
if (q == NULL || input == NULL || output == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
#if RE_EXT_DEBUG
int num_extracted = 0;
#endif
cf_t *in_ptr = input, *out_ptr = output;
bool skip_crs = false;
if (put) {
out_ptr += (q->i_n_start * q->cell.base.nof_prb * SRSRAN_NRE) + q->cell.nbiot_prb * SRSRAN_NRE;
} else {
in_ptr += (q->i_n_start * q->cell.base.nof_prb * SRSRAN_NRE) + q->cell.nbiot_prb * SRSRAN_NRE;
}
if (q->cell.mode == SRSRAN_NBIOT_MODE_INBAND_SAME_PCI || q->cell.mode == SRSRAN_NBIOT_MODE_INBAND_DIFFERENT_PCI) {
skip_crs = true;
}
// start mapping at specified OFDM symbol
for (int l = q->i_n_start; l < SRSRAN_CP_NORM_SF_NSYMB; l++) {
uint32_t delta = (q->cell.base.nof_prb - 1) * SRSRAN_NRE; // the number of REs skipped in each OFDM symbol
uint32_t offset = 0; // the number of REs left out before start of the REF signal RE
if (l == 5 || l == 6 || l == 12 || l == 13) {
// always skip NRS
if (q->nof_nbiot_refs == 2) {
if (l == 5 || l == 12) {
offset = q->cell.n_id_ncell % 6;
delta = q->cell.n_id_ncell % 6 == 5 ? 1 : 0;
} else {
offset = (q->cell.n_id_ncell + 3) % 6;
delta = (q->cell.n_id_ncell + 3) % 6 == 5 ? 1 : 0;
}
} else if (q->nof_nbiot_refs == 4) {
offset = q->cell.n_id_ncell % 3;
delta = (q->cell.n_id_ncell + ((q->cell.n_id_ncell >= 5) ? 0 : 3)) % 6 == 5 ? 1 : 0;
} else {
// TODO: not handled right now
return SRSRAN_ERROR;
}
switch (format) {
case SRSRAN_NPDCCH_FORMAT0_LOWER_HALF:
prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_nbiot_refs, q->nof_nbiot_refs, put);
// we have copied too much, rewind ptr
if (put) {
in_ptr -= (SRSRAN_NRE - q->nof_nbiot_refs) / 2;
} else {
out_ptr -= (SRSRAN_NRE - q->nof_nbiot_refs) / 2;
}
break;
case SRSRAN_NPDCCH_FORMAT0_UPPER_HALF:
// TODO: this causes valgrind to detect an invalid memory access
#if 0
// skip lower half
if (put) {
out_ptr += SRSRAN_NRE / 2;
} else {
in_ptr += SRSRAN_NRE / 2;
}
#endif
// copy REs
prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_nbiot_refs, q->nof_nbiot_refs, put);
// we have copied too much, rewind ptr
if (put) {
in_ptr -= (SRSRAN_NRE - q->nof_nbiot_refs) / 2;
} else {
out_ptr -= (SRSRAN_NRE - q->nof_nbiot_refs) / 2;
}
break;
case SRSRAN_NPDCCH_FORMAT1:
prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_nbiot_refs, q->nof_nbiot_refs, put);
break;
default:
printf("Wrong NPDCCH format!\n");
return SRSRAN_ERROR;
}
} else if ((l == 0 || l == 4 || l == 7 || l == 11) && skip_crs) {
// skip LTE's CRS (TODO: use base cell ID?)
if (q->nof_lte_refs == 2) {
if (l == 0 || l == 7) {
offset = q->cell.base.id % 6;
delta = (q->cell.base.id + 3) % 6 == 2 ? 1 : 0;
} else if (l == 4 || l == 11) {
offset = (q->cell.base.id + 3) % 6;
delta = (q->cell.base.id + ((q->cell.base.id <= 5) ? 3 : 0)) % 6 == 5 ? 1 : 0;
}
} else {
offset = q->cell.base.id % 3;
delta = q->cell.base.id % 3 == 2 ? 1 : 0;
}
switch (format) {
case SRSRAN_NPDCCH_FORMAT0_LOWER_HALF:
prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_lte_refs, q->nof_lte_refs, put);
// we have copied too much, rewind ptr
if (put) {
in_ptr -= (SRSRAN_NRE - q->nof_lte_refs) / 2;
} else {
out_ptr -= (SRSRAN_NRE - q->nof_lte_refs) / 2;
}
break;
case SRSRAN_NPDCCH_FORMAT0_UPPER_HALF:
// skip lower half
if (put) {
out_ptr += SRSRAN_NRE / 2;
} else {
in_ptr += SRSRAN_NRE / 2;
}
// copy REs
prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_lte_refs, q->nof_lte_refs, put);
// we have copied too much, rewind ptr
if (put) {
in_ptr -= (SRSRAN_NRE - q->nof_lte_refs) / 2;
} else {
out_ptr -= (SRSRAN_NRE - q->nof_lte_refs) / 2;
}
break;
case SRSRAN_NPDCCH_FORMAT1:
prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_lte_refs, q->nof_lte_refs, put);
break;
default:
printf("Wrong NPDCCH format!\n");
return SRSRAN_ERROR;
}
} else {
switch (format) {
case SRSRAN_NPDCCH_FORMAT0_LOWER_HALF:
prb_cp_half(&in_ptr, &out_ptr, 1);
// skip upper half
if (put) {
out_ptr += SRSRAN_NRE / 2;
} else {
in_ptr += SRSRAN_NRE / 2;
}
break;
case SRSRAN_NPDCCH_FORMAT0_UPPER_HALF:
// skip lower half
if (put) {
out_ptr += SRSRAN_NRE / 2;
} else {
in_ptr += SRSRAN_NRE / 2;
}
prb_cp_half(&in_ptr, &out_ptr, 1);
break;
case SRSRAN_NPDCCH_FORMAT1:
// occupy entire symbol
prb_cp(&in_ptr, &out_ptr, 1);
break;
default:
printf("Wrong NPDCCH format!\n");
return SRSRAN_ERROR;
}
}
if (put) {
out_ptr += delta;
} else {
in_ptr += delta;
}
#if RE_EXT_DEBUG
printf("\nl=%d, delta=%d offset=%d\n", l, delta, offset);
uint32_t num_extracted_this_sym = abs((int)(output - out_ptr)) - num_extracted;
printf(" - extracted total of %d RE after symbol %d (this symbol=%d)\n",
abs((int)(output - out_ptr)),
l,
num_extracted_this_sym);
srsran_vec_fprint_c(stdout, &output[num_extracted], num_extracted_this_sym);
num_extracted = abs((int)(output - out_ptr));
#endif
}
int r;
if (put) {
r = abs((int)(input - in_ptr));
} else {
r = abs((int)(output - out_ptr));
}
return r;
}
/**
* Puts NPDCCH in the subframe
*
* Returns the number of symbols written to sf_symbols
*
* 36.211 10.3 section 6.3.5
*/
int srsran_npdcch_put(srsran_npdcch_t* q, cf_t* symbols, cf_t* sf_symbols, srsran_npdcch_format_t format)
{
return srsran_npdcch_cp(q, symbols, sf_symbols, true, format);
}
/**
* Extracts NPDCCH from the subframe
*
* Returns the number of symbols read
*
* 36.211 10.3 section 6.3.5
*/
int srsran_npdcch_get(srsran_npdcch_t* q, cf_t* sf_symbols, cf_t* symbols, srsran_npdcch_format_t format)
{
return srsran_npdcch_cp(q, sf_symbols, symbols, false, format);
}