mirror of https://github.com/pvnis/srsRAN_4G.git
Initial implementation of the 5G NR PDCCH DMRS encoding
parent
579526f1fe
commit
29ad2427d9
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE 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.
|
||||
*
|
||||
* srsLTE 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/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSLTE_DMRS_PDCCH_H
|
||||
#define SRSLTE_DMRS_PDCCH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "srslte/phy/common/phy_common_nr.h"
|
||||
#include "srslte/srslte.h"
|
||||
|
||||
SRSLTE_API int
|
||||
srslte_dmrs_pdcch_put(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_cfg_t* dl_sf, cf_t* sf_symbols);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SRSLTE_DMRS_PDCCH_H
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE 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.
|
||||
*
|
||||
* srsLTE 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/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSLTE_PHY_COMMON_NR_H
|
||||
#define SRSLTE_PHY_COMMON_NR_H
|
||||
|
||||
#include "phy_common.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Defines the number of symbols per slot. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.
|
||||
*/
|
||||
#define SRSLTE_NR_NSYMB_PER_SLOT 14
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum numerology supported. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.
|
||||
*/
|
||||
#define SRSLTE_NR_MAX_NUMEROLOGY 4
|
||||
|
||||
/**
|
||||
* @brief Defines the number of slots per SF. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.
|
||||
*/
|
||||
#define SRSLTE_NR_NSLOTS_PER_SF(NUM) (1U << (NUM))
|
||||
|
||||
/**
|
||||
* @brief Defines the number of slots per frame. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.
|
||||
*/
|
||||
#define SRSLTE_NR_NSLOTS_PER_FRAME(NUM) (SRSLTE_NR_NSLOTS_PER_SF(NUM) * SRSLTE_NOF_SF_X_FRAME)
|
||||
|
||||
/**
|
||||
* @brief Maximum Carrier identification value. Defined by TS 38.331 v15.10.0 as PhysCellId from 0 to 1007.
|
||||
*/
|
||||
#define SRSLTE_NR_MAX_ID 1007
|
||||
|
||||
/**
|
||||
* @brief Maximum number of physical resource blocks (PRB) that a 5G NR can have. This is defined by TS 38.331 v15.10.0
|
||||
* as maxNrofPhysicalResourceBlocks
|
||||
*/
|
||||
#define SRSLTE_NR_MAX_PRB 275
|
||||
|
||||
#define SRSLTE_NR_MAX_START 2199
|
||||
|
||||
/**
|
||||
* Common carrier parameters
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t numerology;
|
||||
uint32_t nof_prb;
|
||||
uint32_t start;
|
||||
} srslte_nr_carrier_t;
|
||||
|
||||
/**
|
||||
* CORESET related constants
|
||||
*/
|
||||
#define SRSLTE_CORESET_DURATION_MIN 1
|
||||
#define SRSLTE_CORESET_DURATION_MAX 3
|
||||
#define SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE 45
|
||||
#define SRSLTE_CORESET_SHIFT_INDEX_MAX (SRSLTE_CORESET_NOF_PRB_MAX - 1)
|
||||
|
||||
typedef enum {
|
||||
srslte_coreset_mapping_type_interleaved = 0,
|
||||
srslte_coreset_mapping_type_non_interleaved,
|
||||
} srslte_coreset_mapping_type_t;
|
||||
|
||||
typedef enum {
|
||||
srslte_coreset_bundle_size_n2 = 0,
|
||||
srslte_coreset_bundle_size_n3,
|
||||
srslte_coreset_bundle_size_n6,
|
||||
} srslte_coreset_bundle_size_t;
|
||||
|
||||
typedef enum {
|
||||
srslte_coreset_precoder_granularity_contiguous = 0,
|
||||
srslte_coreset_precoder_granularity_reg_bundle
|
||||
} srslte_coreset_precoder_granularity_t;
|
||||
|
||||
/**
|
||||
* CORESET structure
|
||||
*
|
||||
* Fields follow the same order than described in 3GPP 38.331 R15 - ControlResourceSet
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
srslte_coreset_mapping_type_t mapping_type;
|
||||
uint32_t id;
|
||||
uint32_t duration;
|
||||
bool freq_domain_resources[SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE];
|
||||
srslte_coreset_bundle_size_t interleaver_size;
|
||||
|
||||
bool dmrs_scrambling_id_present;
|
||||
uint32_t dmrs_scrambling_id;
|
||||
srslte_coreset_precoder_granularity_t precoder_granularity;
|
||||
srslte_coreset_bundle_size_t reg_bundle_size;
|
||||
uint32_t shift_index;
|
||||
/** Missing TCI parameters */
|
||||
} srslte_coreset_t;
|
||||
|
||||
typedef enum {
|
||||
srslte_search_space_type_common = 0,
|
||||
srslte_search_space_type_ue,
|
||||
} srslte_search_space_type_t;
|
||||
|
||||
#define SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS 5
|
||||
|
||||
typedef struct {
|
||||
uint32_t start; // start symbol within slot
|
||||
uint32_t duration; // in slots
|
||||
srslte_search_space_type_t type;
|
||||
uint32_t nof_candidates[SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS];
|
||||
} srslte_search_space_t;
|
||||
|
||||
typedef struct {
|
||||
srslte_nr_carrier_t carrier;
|
||||
uint16_t rnti;
|
||||
srslte_coreset_t coreset;
|
||||
srslte_search_space_t search_space;
|
||||
uint32_t candidate;
|
||||
uint32_t aggregation_level;
|
||||
} srslte_nr_pdcch_cfg_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SRSLTE_PHY_COMMON_NR_H
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE 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.
|
||||
*
|
||||
* srsLTE 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 "srslte/phy/ch_estimation/dmrs_pdcch.h"
|
||||
|
||||
uint32_t srslte_pdcch_calculate_Y_p_n(uint32_t coreset_id, uint16_t rnti, int n)
|
||||
{
|
||||
const uint32_t A_p[3] = {39827, 39829, 39839};
|
||||
const uint32_t D = 65537;
|
||||
|
||||
if (n < 0) {
|
||||
return rnti;
|
||||
}
|
||||
|
||||
return (A_p[coreset_id % 3] * srslte_pdcch_calculate_Y_p_n(coreset_id, rnti, n - 1)) % D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the Control Channnel Element As described in 3GPP 38.213 R15 10.1 UE procedure for determining physical
|
||||
* downlink control channel assignment
|
||||
*
|
||||
*/
|
||||
int srslte_pdcch_get_ncce(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_cfg_t* dl_sf)
|
||||
{
|
||||
if (cfg->aggregation_level >= SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS) {
|
||||
ERROR("Invalid aggregation level %d;\n", cfg->aggregation_level);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
uint32_t L = 1U << cfg->aggregation_level; // Aggregation level
|
||||
uint32_t n_ci = 0; // Carrier indicator field
|
||||
uint32_t m = cfg->candidate; // Selected PDDCH candidate
|
||||
uint32_t M = cfg->search_space.nof_candidates[cfg->aggregation_level]; // Number of aggregation levels
|
||||
|
||||
if (M == 0) {
|
||||
ERROR("Invalid number of candidates %d for aggregation level %d\n", M, cfg->aggregation_level);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// Count number of REG
|
||||
uint32_t N_cce = 0;
|
||||
for (uint32_t i = 0; i < SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
|
||||
N_cce += cfg->coreset.freq_domain_resources[i] ? cfg->coreset.duration : 0;
|
||||
}
|
||||
|
||||
if (N_cce < L) {
|
||||
ERROR("Error number of CCE %d is lower than the aggregation level %d\n", N_cce, L);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// Calculate Y_p_n
|
||||
uint32_t Y_p_n = 0;
|
||||
if (cfg->search_space.type == srslte_search_space_type_ue) {
|
||||
Y_p_n = srslte_pdcch_calculate_Y_p_n(
|
||||
cfg->coreset.id, cfg->rnti, dl_sf->tti % SRSLTE_NR_NSLOTS_PER_FRAME(cfg->carrier.numerology));
|
||||
}
|
||||
|
||||
return (int)(L * ((Y_p_n + (m * N_cce) / (L * M) + n_ci) % (N_cce / L)));
|
||||
}
|
||||
|
||||
static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uint32_t n_id)
|
||||
{
|
||||
return (uint32_t)((((SRSLTE_NR_NSYMB_PER_SLOT * slot_idx + symbol_idx + 1UL) << 17UL) * (2 * n_id + 1) + 2 * n_id) &
|
||||
(uint64_t)INT32_MAX);
|
||||
}
|
||||
|
||||
static void
|
||||
dmrs_pdcch_put_symbol_noninterleaved(const srslte_nr_pdcch_cfg_t* cfg, uint32_t cinit, uint32_t ncce, cf_t* sf_symbol)
|
||||
{
|
||||
uint32_t L = 1U << cfg->aggregation_level;
|
||||
uint32_t nof_freq_res = SRSLTE_MIN(cfg->carrier.nof_prb / 6, SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE);
|
||||
|
||||
// Initialise sequence for this symbol
|
||||
srslte_sequence_state_t sequence_state = {};
|
||||
srslte_sequence_state_init(&sequence_state, cinit);
|
||||
uint32_t sequence_skip = 0; // Accumulates pilot locations to skip
|
||||
|
||||
// Calculate Resource block indexes range, every CCE is 6 REG, 1 REG is 6 RE in resource blocks
|
||||
uint32_t rb_coreset_idx_begin = (ncce * 6) / cfg->coreset.duration;
|
||||
uint32_t rb_coreset_idx_end = ((ncce + L) * 6) / cfg->coreset.duration;
|
||||
|
||||
// CORESET Resource Block counter
|
||||
uint32_t rb_coreset_idx = 0;
|
||||
for (uint32_t i = 0; i < nof_freq_res; i++) {
|
||||
// Every frequency resource is 6 Resource blocks, every resource block carries 3 pilots. So 18 possible pilots per
|
||||
// frequency resource.
|
||||
const uint32_t nof_pilots_x_resource = 18;
|
||||
|
||||
// Skip frequency resource if outside of the CORESET
|
||||
if (!cfg->coreset.freq_domain_resources[i]) {
|
||||
// Skip possible DMRS locations in this region
|
||||
sequence_skip += nof_pilots_x_resource;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if the frequency resource highest RB is lower than the first CCE resource block.
|
||||
if ((rb_coreset_idx + 6) <= rb_coreset_idx_begin) {
|
||||
// Skip possible DMRS locations in this region
|
||||
sequence_skip += nof_pilots_x_resource;
|
||||
|
||||
// Since this is part of the CORESET, count the RB as CORESET
|
||||
rb_coreset_idx += 6;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return if the first RB of the frequency resource is greater than the last CCE resource block
|
||||
if (rb_coreset_idx > rb_coreset_idx_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip all discarded possible pilot locations
|
||||
srslte_sequence_state_advance(&sequence_state, 2 * sequence_skip);
|
||||
sequence_skip = 0;
|
||||
|
||||
// Generate pilots
|
||||
cf_t rl[nof_pilots_x_resource];
|
||||
srslte_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, nof_pilots_x_resource * 2);
|
||||
|
||||
// For each RB in the frequency resource
|
||||
for (uint32_t j = 0; j < 6; j++) {
|
||||
// Calculate absolute RB index
|
||||
uint32_t n = i * 6 + j;
|
||||
|
||||
// Skip if lower than begin
|
||||
if (rb_coreset_idx < rb_coreset_idx_begin) {
|
||||
rb_coreset_idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return if greater than end
|
||||
if (rb_coreset_idx >= rb_coreset_idx_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write pilots in the symbol
|
||||
for (uint32_t k_prime = 0; k_prime < 3; k_prime++) {
|
||||
// Calculate sub-carrier index
|
||||
uint32_t k = n * SRSLTE_NRE + 4 * k_prime + 1;
|
||||
|
||||
sf_symbol[k] = rl[(3 * n + k_prime) % nof_pilots_x_resource];
|
||||
}
|
||||
rb_coreset_idx++;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE 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.
|
||||
*
|
||||
* srsLTE 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 "srslte/common/test_common.h"
|
||||
#include "srslte/srslte.h"
|
||||
#include <complex.h>
|
||||
#include <srslte/phy/ch_estimation/dmrs_pdcch.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static srslte_nr_carrier_t carrier = {
|
||||
.nof_prb = 50,
|
||||
};
|
||||
|
||||
static uint16_t rnti = 0x1234;
|
||||
|
||||
void usage(char* prog)
|
||||
{
|
||||
printf("Usage: %s [recov]\n", prog);
|
||||
|
||||
printf("\t-r nof_prb [Default %d]\n", carrier.nof_prb);
|
||||
printf("\t-e extended cyclic prefix [Default normal]\n");
|
||||
|
||||
printf("\t-c cell_id [Default %d]\n", carrier.id);
|
||||
|
||||
printf("\t-v increase verbosity\n");
|
||||
}
|
||||
|
||||
void parse_args(int argc, char** argv)
|
||||
{
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "recov")) != -1) {
|
||||
switch (opt) {
|
||||
case 'r':
|
||||
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
|
||||
break;
|
||||
case 'c':
|
||||
carrier.id = (uint32_t)strtol(argv[optind], NULL, 10);
|
||||
break;
|
||||
case 'v':
|
||||
srslte_verbose++;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int run_test(const srslte_nr_pdcch_cfg_t* cfg, cf_t* sf_symbols, cf_t* h)
|
||||
{
|
||||
srslte_dl_sf_cfg_t dl_sf = {};
|
||||
for (dl_sf.tti = 0; dl_sf.tti < SRSLTE_NOF_SF_X_FRAME; dl_sf.tti++) {
|
||||
TESTASSERT(srslte_dmrs_pdcch_put(cfg, &dl_sf, sf_symbols) == SRSLTE_SUCCESS);
|
||||
|
||||
/*srslte_dmrs_pdsch_get_sf(cfg, &dl_sf, sf_symbols, h);
|
||||
|
||||
float mse = 0.0f;
|
||||
for (uint32_t i = 0; i < dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc * SRSLTE_NRE; i++) {
|
||||
cf_t err = h[i] - 1.0f;
|
||||
mse += cabsf(err);
|
||||
}
|
||||
mse /= (float)dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc;
|
||||
|
||||
TESTASSERT(!isnan(mse));
|
||||
TESTASSERT(mse < 1e-6f);*/
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = SRSLTE_ERROR;
|
||||
|
||||
parse_args(argc, argv);
|
||||
|
||||
srslte_nr_pdcch_cfg_t cfg = {};
|
||||
|
||||
uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_MAX_NSYMB;
|
||||
cf_t* sf_symbols = srslte_vec_cf_malloc(nof_re);
|
||||
cf_t* h = srslte_vec_cf_malloc(nof_re);
|
||||
|
||||
uint32_t test_counter = 0;
|
||||
uint32_t test_passed = 0;
|
||||
|
||||
cfg.carrier = carrier;
|
||||
cfg.rnti = rnti;
|
||||
cfg.coreset.mapping_type = srslte_coreset_mapping_type_non_interleaved;
|
||||
|
||||
uint32_t nof_frequency_resource = SRSLTE_MIN(SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6);
|
||||
for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource); frequency_resources++) {
|
||||
uint32_t nof_freq_resources = 0;
|
||||
for (uint32_t i = 0; i < nof_frequency_resource; i++) {
|
||||
uint32_t mask = ((frequency_resources >> i) & 1U);
|
||||
cfg.coreset.freq_domain_resources[i] = (mask == 1);
|
||||
nof_freq_resources += mask;
|
||||
}
|
||||
|
||||
for (cfg.coreset.duration = 1; cfg.coreset.duration <= 3; cfg.coreset.duration++) {
|
||||
|
||||
for (cfg.search_space.type = srslte_search_space_type_common;
|
||||
cfg.search_space.type <= srslte_search_space_type_ue;
|
||||
cfg.search_space.type++) {
|
||||
|
||||
for (uint32_t i = 0; i < SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS; i++) {
|
||||
uint32_t L = 1 << i;
|
||||
uint32_t nof_reg = cfg.coreset.duration * nof_freq_resources * 6;
|
||||
uint32_t nof_cce = nof_reg / 6;
|
||||
cfg.search_space.nof_candidates[i] = nof_cce / L;
|
||||
}
|
||||
|
||||
for (cfg.aggregation_level = 0; cfg.aggregation_level < SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS;
|
||||
cfg.aggregation_level++) {
|
||||
|
||||
for (cfg.candidate = 0; cfg.candidate < cfg.search_space.nof_candidates[cfg.aggregation_level];
|
||||
cfg.candidate++) {
|
||||
|
||||
if (run_test(&cfg, sf_symbols, h)) {
|
||||
ERROR("Test %d failed\n", test_counter);
|
||||
} else {
|
||||
test_passed++;
|
||||
}
|
||||
test_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sf_symbols) {
|
||||
free(sf_symbols);
|
||||
}
|
||||
|
||||
if (h) {
|
||||
free(h);
|
||||
}
|
||||
|
||||
ret = test_passed == test_counter ? SRSLTE_SUCCESS : SRSLTE_ERROR;
|
||||
printf("%s, %d of %d test passed successfully.\n", ret ? "Failed" : "Passed", test_passed, test_counter);
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue