From ae94416395060cf2060bb088d01a2c63873f9c85 Mon Sep 17 00:00:00 2001 From: yagoda Date: Wed, 3 May 2017 16:48:40 +0100 Subject: [PATCH] adding native lime, soapy, decimation filtering and neon optimizations --- cmake/modules/FindLimeSDR.cmake | 28 ++ cmake/modules/FindSoapySDR.cmake | 31 ++ srslte/CMakeLists.txt | 21 +- srslte/examples/pdsch_ue.c | 11 +- srslte/include/srslte/fec/viterbi.h | 6 + srslte/include/srslte/sync/pss.h | 5 +- srslte/include/srslte/sync/sync.h | 2 +- srslte/include/srslte/ue/ue_sync.h | 2 +- srslte/include/srslte/utils/convolution.h | 8 + srslte/include/srslte/utils/filter.h | 60 +++ srslte/lib/CMakeLists.txt | 9 + srslte/lib/fec/viterbi.c | 87 ++++ srslte/lib/fec/viterbi37.h | 23 ++ srslte/lib/fec/viterbi37_neon.c | 354 ++++++++++++++++ srslte/lib/rf/CMakeLists.txt | 20 + srslte/lib/rf/rf_dev.h | 84 ++++ srslte/lib/rf/rf_limesdr_imp.c | 475 ++++++++++++++++++++++ srslte/lib/rf/rf_limesdr_imp.h | 118 ++++++ srslte/lib/rf/rf_soapy_imp.c | 457 +++++++++++++++++++++ srslte/lib/rf/rf_soapy_imp.h | 118 ++++++ srslte/lib/sync/pss.c | 59 ++- srslte/lib/sync/sync.c | 10 +- srslte/lib/ue/ue_sync.c | 15 +- srslte/lib/utils/convolution.c | 24 ++ srslte/lib/utils/filter.c | 126 ++++++ 25 files changed, 2134 insertions(+), 19 deletions(-) create mode 100644 cmake/modules/FindLimeSDR.cmake create mode 100644 cmake/modules/FindSoapySDR.cmake create mode 100644 srslte/include/srslte/utils/filter.h create mode 100644 srslte/lib/fec/viterbi37_neon.c create mode 100644 srslte/lib/rf/rf_limesdr_imp.c create mode 100644 srslte/lib/rf/rf_limesdr_imp.h create mode 100644 srslte/lib/rf/rf_soapy_imp.c create mode 100644 srslte/lib/rf/rf_soapy_imp.h create mode 100644 srslte/lib/utils/filter.c diff --git a/cmake/modules/FindLimeSDR.cmake b/cmake/modules/FindLimeSDR.cmake new file mode 100644 index 000000000..0cfec6f17 --- /dev/null +++ b/cmake/modules/FindLimeSDR.cmake @@ -0,0 +1,28 @@ +if(NOT LIMESDR_FOUND) + pkg_check_modules (LIMESDR_PKG LimeSuite) + + find_path(LIMESDR_INCLUDE_DIRS + NAMES LimeSuite.h + PATHS ${LIMESDR_PKG_INCLUDE_DIRS} + /usr/include/lime + /usr/local/include/lime + ) + + find_library(LIMESDR_LIBRARIES + NAMES LimeSuite + PATHS ${LIMESDR_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + +if(LIMESDR_INCLUDE_DIRS AND LIMESDR_LIBRARIES) + set(LIMESDR_FOUND TRUE CACHE INTERNAL "libLimeSuite found") + message(STATUS "Found libLimeSuite: ${LIMESDR_INCLUDE_DIRS}, ${LIMESDR_LIBRARIES}") +else(LIMESDR_INCLUDE_DIRS AND LIMESDR_LIBRARIES) + set(LIMESDR_FOUND FALSE CACHE INTERNAL "libLimeSuite found") + message(STATUS "libLimeSuite not found.") +endif(LIMESDR_INCLUDE_DIRS AND LIMESDR_LIBRARIES) + +mark_as_advanced(LIMESDR_LIBRARIES LIMESDR_INCLUDE_DIRS) + +endif(NOT LIMESDR_FOUND) diff --git a/cmake/modules/FindSoapySDR.cmake b/cmake/modules/FindSoapySDR.cmake new file mode 100644 index 000000000..d375a9564 --- /dev/null +++ b/cmake/modules/FindSoapySDR.cmake @@ -0,0 +1,31 @@ + +message(STATUS "FINDING SOAPY.") +if(NOT SOAPYSDR_FOUND) + pkg_check_modules (SOAPYSDR_PKG SoapySDR) + + find_path(SOAPYSDR_INCLUDE_DIRS + NAMES Device.h + PATHS ${SOAPYSDR_PKG_INCLUDE_DIRS} + /usr/include/SoapySDR + /usr/include/local/SoapySDR + ) + + find_library(SOAPYSDR_LIBRARIES + NAMES SoapySDR + PATHS ${LIMESDR_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + + ) + +if(SOAPYSDR_INCLUDE_DIRS AND SOAPYSDR_LIBRARIES) + set(SOAPYSDR_FOUND TRUE CACHE INTERNAL "libSOAPYSDR found") + message(STATUS "Found libSOAPYSDR: ${SOAPYSDR_INCLUDE_DIRS}, ${SOAPYSDR_LIBRARIES}") +else(SOAPYSDR_INCLUDE_DIRS AND SOAPYSDR_LIBRARIES) + set(SOAPYSDR_FOUND FALSE CACHE INTERNAL "libSOAPYSDR found") + message(STATUS "libSOAPYSDR not found.") +endif(SOAPYSDR_INCLUDE_DIRS AND SOAPYSDR_LIBRARIES) + +mark_as_advanced(SOAPYSDR_LIBRARIES SOAPYSDR_INCLUDE_DIRS) + +endif(NOT SOAPYSDR_FOUND) diff --git a/srslte/CMakeLists.txt b/srslte/CMakeLists.txt index d4f336d57..cf47d6381 100644 --- a/srslte/CMakeLists.txt +++ b/srslte/CMakeLists.txt @@ -69,12 +69,27 @@ if(NOT DisableBladeRF) endif(BLADERF_FOUND) endif(NOT DisableBladeRF) -if(BLADERF_FOUND OR UHD_FOUND) +find_package(SoapySDR) +if(SOAPYSDR_FOUND) + include_directories(${SOAPYSDR_INCLUDE_DIRS}) + link_directories(${SOAPYSDR_LIBRARY_DIRS}) +endif(SOAPYSDR_FOUND) + + +find_package(LimeSDR) +if(LIMESDR_FOUND) + include_directories(${LIMESDR_INCLUDE_DIRS}) + link_directories(${LIMESDR_LIBRARY_DIRS}) +endif(LIMESDR_FOUND) + + + +if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR LIMESDR_FOUND) set(RF_FOUND TRUE CACHE INTERNAL "RF frontend found") -else(BLADERF_FOUND OR UHD_FOUND) +else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR LIMESDR_FOUND) set(RF_FOUND FALSE CACHE INTERNAL "RF frontend found") add_definitions(-DDISABLE_RF) -endif(BLADERF_FOUND OR UHD_FOUND) +endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR LIMESDR_FOUND) include(CheckFunctionExistsMath) if(${DISABLE_VOLK}) diff --git a/srslte/examples/pdsch_ue.c b/srslte/examples/pdsch_ue.c index eb14959de..1c2b55976 100644 --- a/srslte/examples/pdsch_ue.c +++ b/srslte/examples/pdsch_ue.c @@ -95,7 +95,8 @@ typedef struct { int net_port; char *net_address; int net_port_signal; - char *net_address_signal; + char *net_address_signal; + int decimate; }prog_args_t; void args_default(prog_args_t *args) { @@ -124,6 +125,7 @@ void args_default(prog_args_t *args) { args->net_address = "127.0.0.1"; args->net_port_signal = -1; args->net_address_signal = "127.0.0.1"; + args->decimate = 0; } void usage(prog_args_t *args, char *prog) { @@ -166,7 +168,7 @@ void usage(prog_args_t *args, char *prog) { void parse_args(prog_args_t *args, int argc, char **argv) { int opt; args_default(args); - while ((opt = getopt(argc, argv, "aAoglipPcOCtdDnvrfuUsS")) != -1) { + while ((opt = getopt(argc, argv, "aAoglipPcOCtdDnvrfuUsSZ")) != -1) { switch (opt) { case 'i': args->input_file_name = argv[optind]; @@ -234,6 +236,9 @@ void parse_args(prog_args_t *args, int argc, char **argv) { case 'v': srslte_verbose++; break; + case 'Z': + args->decimate = atoi(argv[optind]); + break; default: usage(args, argv[0]); exit(-1); @@ -412,6 +417,8 @@ int main(int argc, char **argv) { } else { #ifndef DISABLE_RF + if(!prog_args.decimate) + ue_sync.decimate = prog_args.decimate; if (srslte_ue_sync_init_multi(&ue_sync, cell, srslte_rf_recv_wrapper, prog_args.rf_nof_rx_ant, (void*) &rf)) { fprintf(stderr, "Error initiating ue_sync\n"); exit(-1); diff --git a/srslte/include/srslte/fec/viterbi.h b/srslte/include/srslte/fec/viterbi.h index 1707daf8b..043a6f9f9 100644 --- a/srslte/include/srslte/fec/viterbi.h +++ b/srslte/include/srslte/fec/viterbi.h @@ -100,6 +100,12 @@ SRSLTE_API int srslte_viterbi_init_sse(srslte_viterbi_t *q, uint32_t max_frame_length, bool tail_bitting); +SRSLTE_API int srslte_viterbi_init_neon(srslte_viterbi_t *q, + srslte_viterbi_type_t type, + int poly[3], + uint32_t max_frame_length, + bool tail_bitting); + #endif diff --git a/srslte/include/srslte/sync/pss.h b/srslte/include/srslte/sync/pss.h index 0c7830792..c805870b9 100644 --- a/srslte/include/srslte/sync/pss.h +++ b/srslte/include/srslte/sync/pss.h @@ -51,6 +51,7 @@ #include "srslte/config.h" #include "srslte/common/phy_common.h" #include "srslte/utils/convolution.h" +#include "srslte/utils/filter.h" #define CONVOLUTION_FFT @@ -74,8 +75,10 @@ typedef struct SRSLTE_API { #ifdef CONVOLUTION_FFT srslte_conv_fft_cc_t conv_fft; -#endif + srslte_filt_cc_t filter; +#endif + int decimate; uint32_t frame_size; uint32_t N_id_2; uint32_t fft_size; diff --git a/srslte/include/srslte/sync/sync.h b/srslte/include/srslte/sync/sync.h index 364baed19..dcf64544d 100644 --- a/srslte/include/srslte/sync/sync.h +++ b/srslte/include/srslte/sync/sync.h @@ -65,7 +65,7 @@ typedef struct SRSLTE_API { srslte_sss_synch_t sss; srslte_cp_synch_t cp_synch; cf_t *cfo_i_corr[2]; - + int decimate; float threshold; float peak_value; uint32_t N_id_2; diff --git a/srslte/include/srslte/ue/ue_sync.h b/srslte/include/srslte/ue/ue_sync.h index 3764e4331..602fe84af 100644 --- a/srslte/include/srslte/ue/ue_sync.h +++ b/srslte/include/srslte/ue/ue_sync.h @@ -73,7 +73,7 @@ typedef struct SRSLTE_API { srslte_agc_t agc; bool do_agc; uint32_t agc_period; - + int decimate; void *stream; void *stream_single; int (*recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*); diff --git a/srslte/include/srslte/utils/convolution.h b/srslte/include/srslte/utils/convolution.h index 93fba38f8..fbbf06bce 100644 --- a/srslte/include/srslte/utils/convolution.h +++ b/srslte/include/srslte/utils/convolution.h @@ -49,6 +49,9 @@ typedef struct SRSLTE_API { srslte_dft_plan_t input_plan; srslte_dft_plan_t filter_plan; srslte_dft_plan_t output_plan; + cf_t *pss_signal_time_fft[3]; // One sequence for each N_id_2 + cf_t *pss_signal_time[3]; + }srslte_conv_fft_cc_t; SRSLTE_API int srslte_conv_fft_cc_init(srslte_conv_fft_cc_t *q, @@ -62,6 +65,11 @@ SRSLTE_API uint32_t srslte_conv_fft_cc_run(srslte_conv_fft_cc_t *q, cf_t *filter, cf_t *output); +SRSLTE_API uint32_t srslte_conv_fft_cc_run_opt(srslte_conv_fft_cc_t *q, + cf_t *input, + int N_id_2, + cf_t *output); + SRSLTE_API uint32_t srslte_conv_cc(cf_t *input, cf_t *filter, cf_t *output, diff --git a/srslte/include/srslte/utils/filter.h b/srslte/include/srslte/utils/filter.h new file mode 100644 index 000000000..87f7424c4 --- /dev/null +++ b/srslte/include/srslte/utils/filter.h @@ -0,0 +1,60 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: debug.h + * + * Description: Debug output utilities. + * + * Reference: + *****************************************************************************/ + +#ifndef FILTER_H +#define FILTER_H +#include +#include +#include "srslte/config.h" +#include +#include "srslte/utils/vector.h" +typedef struct SRSLTE_API{ + cf_t *filter_input; + cf_t *downsampled_input; + cf_t *filter_output; + bool is_decimator; + int factor; + int num_taps; + float *taps; + +}srslte_filt_cc_t; + +void srslte_filt_decim_cc_init(srslte_filt_cc_t *q, int factor, int order); + +void srslte_filt_decim_cc_free(srslte_filt_cc_t *q); + +void srslte_filt_decim_cc_execute(srslte_filt_cc_t *q, cf_t *input, cf_t *downsampled_input, cf_t *output, int size); + +void srslte_downsample_cc(cf_t *input, cf_t *output, int M, int size) ; +#endif // FILTER_H \ No newline at end of file diff --git a/srslte/lib/CMakeLists.txt b/srslte/lib/CMakeLists.txt index 88431bcde..8a2e86b17 100644 --- a/srslte/lib/CMakeLists.txt +++ b/srslte/lib/CMakeLists.txt @@ -95,6 +95,15 @@ if(RF_FOUND) if(BLADERF_FOUND) target_link_libraries(srslte ${BLADERF_LIBRARIES}) endif(BLADERF_FOUND) + + if(LIMESDR_FOUND) + target_link_libraries(srslte ${LIMESDR_LIBRARIES}) + endif(LIMESDR_FOUND) + + if(SOAPYSDR_FOUND) + target_link_libraries(srslte ${SOAPYSDR_LIBRARIES}) + endif(SOAPYSDR_FOUND) + endif(RF_FOUND) if(VOLK_FOUND) diff --git a/srslte/lib/fec/viterbi.c b/srslte/lib/fec/viterbi.c index 0eb2e6d0a..09ef4af8e 100644 --- a/srslte/lib/fec/viterbi.c +++ b/srslte/lib/fec/viterbi.c @@ -119,6 +119,51 @@ void free37_sse(void *o) { #endif + +#ifdef HAVE_NEON +int decode37_neon(void *o, uint8_t *symbols, uint8_t *data, uint32_t frame_length) { + srslte_viterbi_t *q = o; + + uint32_t best_state; + + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + + /* Initialize Viterbi decoder */ + init_viterbi37_neon(q->ptr, q->tail_biting?-1:0); + + /* Decode block */ + if (q->tail_biting) { + for (int i=0;itmp[i*3*frame_length], symbols, 3*frame_length*sizeof(uint8_t)); + } + update_viterbi37_blk_neon(q->ptr, q->tmp, TB_ITER*frame_length, &best_state); + chainback_viterbi37_neon(q->ptr, q->tmp, TB_ITER*frame_length, best_state); + memcpy(data, &q->tmp[((int) (TB_ITER/2))*frame_length], frame_length*sizeof(uint8_t)); + } else { + update_viterbi37_blk_neon(q->ptr, symbols, frame_length+q->K-1, NULL); + chainback_viterbi37_neon(q->ptr, data, frame_length, 0); + } + + return q->framebits; +} + +void free37_neon(void *o) { + srslte_viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->tmp) { + free(q->tmp); + } + delete_viterbi37_neon(q->ptr); +} + +#endif + void free37(void *o) { srslte_viterbi_t *q = o; if (q->symbols_uc) { @@ -203,6 +248,44 @@ int init37_sse(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_b } #endif +#ifdef HAVE_NEON +int init37_neon(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->gain_quant_s = 4; + q->gain_quant = DEFAULT_GAIN; + q->tail_biting = tail_biting; + q->decode = decode37_neon; + q->free = free37_neon; + q->decode_f = NULL; + printf("USING NEON VITERBI***************\n"); + q->symbols_uc = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = srslte_vec_malloc(TB_ITER*3*(q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + + if ((q->ptr = create_viterbi37_neon(poly, TB_ITER*framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} +#endif + void srslte_viterbi_set_gain_quant(srslte_viterbi_t *q, float gain_quant) { q->gain_quant = gain_quant; } @@ -218,7 +301,11 @@ int srslte_viterbi_init(srslte_viterbi_t *q, srslte_viterbi_type_t type, int pol #ifdef LV_HAVE_SSE return init37_sse(q, poly, max_frame_length, tail_bitting); #else + #ifdef HAVE_NEON + return init37_neon(q, poly, max_frame_length, tail_bitting); + #else return init37(q, poly, max_frame_length, tail_bitting); + #endif #endif default: fprintf(stderr, "Decoder not implemented\n"); diff --git a/srslte/lib/fec/viterbi37.h b/srslte/lib/fec/viterbi37.h index f5f304858..2c7f8c57f 100644 --- a/srslte/lib/fec/viterbi37.h +++ b/srslte/lib/fec/viterbi37.h @@ -65,3 +65,26 @@ int update_viterbi37_blk_sse(void *p, uint8_t *syms, uint32_t nbits, uint32_t *best_state); + +void *create_viterbi37_neon(int polys[3], + uint32_t len); + +int init_viterbi37_neon(void *p, + int starting_state); + + +void reset_blk_neon(void *p, int nbits); + +int chainback_viterbi37_neon(void *p, + uint8_t *data, + uint32_t nbits, + uint32_t endstate); + +void delete_viterbi37_neon(void *p); + +int update_viterbi37_blk_neon(void *p, + uint8_t *syms, + uint32_t nbits, + uint32_t *best_state); + + diff --git a/srslte/lib/fec/viterbi37_neon.c b/srslte/lib/fec/viterbi37_neon.c new file mode 100644 index 000000000..452dba567 --- /dev/null +++ b/srslte/lib/fec/viterbi37_neon.c @@ -0,0 +1,354 @@ +/* Adapted Phil Karn's r=1/3 k=9 viterbi decoder to r=1/3 k=7 + * + * K=15 r=1/6 Viterbi decoder for ARM NEON + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include "parity.h" + +//#define DEBUG +//#define HAVE_NEON +#ifdef HAVE_NEON + +#include + +typedef union { + unsigned char c[64]; + uint8x16_t v[4]; +} metric_t; + + +typedef union { + unsigned long w[2]; + unsigned char c[8]; + unsigned short s[4]; + uint8x8_t v[1]; +} decision_t; + + +union branchtab27{ + unsigned char c[32]; + uint8x16_t v[2]; +} Branchtab37_neon[3]; + + int8_t __attribute__((aligned(16))) xr[8]; + uint8x8_t mask_and; + int8x8_t mask_shift; + + +int firstGo; +/* State info for instance of Viterbi decoder */ +struct v37 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ + uint32_t len; +}; + +void set_viterbi37_polynomial_neon(int polys[3]) { + int state; + + for(state=0;state < 32;state++){ + Branchtab37_neon[0].c[state] = (polys[0] < 0) ^ parity((2*state) & polys[0]) ? 255:0; + Branchtab37_neon[1].c[state] = (polys[1] < 0) ^ parity((2*state) & polys[1]) ? 255:0; + Branchtab37_neon[2].c[state] = (polys[2] < 0) ^ parity((2*state) & polys[2]) ? 255:0; + } +} + +void clear_v37_neon(struct v37 *vp) { + bzero(vp->decisions, sizeof(decision_t)*vp->len); + vp->dp = NULL; + bzero(&vp->metrics1, sizeof(metric_t)); + bzero(&vp->metrics2, sizeof(metric_t)); + vp->old_metrics = NULL; + vp->new_metrics = NULL; +} + + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi37_neon(void *p, int starting_state) { + struct v37 *vp = p; + uint32_t i; + firstGo = 1; + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + clear_v37_neon(vp); + for(int i = 0; i <8;i++) + xr[i] = i-7; + + mask_and = vdup_n_u8(0x80); + mask_shift = vld1_s8(xr); + + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + if (starting_state != -1) { + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + } + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi37_neon(int polys[3], uint32_t len) { + void *p; + struct v37 *vp; + + set_viterbi37_polynomial_neon(polys); + + /* Ordinary malloc() only returns 8-byte alignment, we need 16 */ + if(posix_memalign(&p, sizeof(uint8x16_t),sizeof(struct v37))) + return NULL; + + vp = (struct v37 *)p; + if(posix_memalign(&p, sizeof(uint8x16_t),(len+6)*sizeof(decision_t))) { + free(vp); + return NULL; + } + vp->decisions = (decision_t *)p; + vp->len = len+6; + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi37_neon( + void *p, + uint8_t *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ + struct v37 *vp = p; + + if (p == NULL) + return -1; + + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits--) { + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; + //printf("nbits=%d, endstate=%3d, k=%d, w[0]=%d, w[1]=%d, c=%d\n", nbits, endstate, k, d[nbits].s[1]&1, d[nbits].s[2]&1, d[nbits].c[(endstate>>2)/8]&1); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi37_neon(void *p){ + struct v37 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +void print_uint8x16_t(char *s, uint8x16_t val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<16;i++) { + printf("%3d, ", x[i]); + } + printf("\n"); +} + +int movemask_neon(uint8x16_t movemask_low_in) +{ + uint8x8_t lo = vget_low_u8(movemask_low_in); + uint8x8_t hi = vget_high_u8(movemask_low_in); + lo = vand_u8(lo, mask_and); + lo = vshl_u8(lo, mask_shift); + hi = vand_u8(hi, mask_and); + hi = vshl_u8(hi, mask_shift); + + lo = vpadd_u8(lo, lo); + lo = vpadd_u8(lo, lo); + lo = vpadd_u8(lo, lo); + + hi = vpadd_u8(hi, hi); + hi = vpadd_u8(hi, hi); + hi = vpadd_u8(hi, hi); + + return ((hi[0] << 8) | (lo[0] & 0xFF)); +} + +void update_viterbi37_blk_neon(void *p,unsigned char *syms,int nbits, uint32_t *best_state) { + struct v37 *vp = p; + decision_t *d; + + uint8_t thirtyone; + thirtyone = 31; + if(p == NULL) + return; + +#ifdef DEBUG + printf("["); +#endif + + d = (decision_t *) vp->dp; + + for (int s=0;sold_metrics->v[i],metric); + m3 = vaddq_u8(vp->old_metrics->v[2+i],metric); + m1 = vaddq_u8(vp->old_metrics->v[2+i],m_metric); + m2 = vaddq_u8(vp->old_metrics->v[i],m_metric); + + /* Compare and select, using modulo arithmetic */ + + + decision0 = (uint8x16_t)vcgtq_s8(vsubq_s8((int8x16_t)m0,(int8x16_t)m1),vdupq_n_s8(0)); + decision1 = (uint8x16_t)vcgtq_s8(vsubq_s8((int8x16_t)m2,(int8x16_t)m3),vdupq_n_s8(0)); + survivor0 = vorrq_u8(vandq_u8(decision0,m1),vandq_u8(vmvnq_u8(decision0),m0)); + survivor1 = vorrq_u8 (vandq_u8(decision1,m3),vandq_u8(vmvnq_u8(decision1),m2) ); + + ////// equal to _mm_unpacklo_epi8 ////////// + uint8x8_t a1 = vget_low_u8(decision0); + uint8x8_t b1 = vget_low_u8(decision1); + uint8x8x2_t result = vzip_u8(a1, b1); + uint8x16_t movemask_low_in = vcombine_u8(result.val[0], result.val[1]); + ///////////////////////////////////////// + + + ////////equal to _mm_movemask_epi8 //////// + d->s[2*i] = movemask_neon(movemask_low_in); + + ///////equal to _mm_unpackhi_epi8//////////// + a1 = vget_high_u8(decision0); + b1 = vget_high_u8(decision1); + result = vzip_u8(a1, b1); + uint8x16_t movemask_hi_in = vcombine_u8(result.val[0], result.val[1]); + + + + ////////equal to _mm_movemask////////////// + d->s[2*i+1] = movemask_neon(movemask_hi_in); + + + a1 = vget_low_u8(survivor0); + b1 = vget_low_u8(survivor1); + result = vzip_u8(a1, b1); + vp->new_metrics->v[2*i] = vcombine_u8(result.val[0], result.val[1]); + + + a1 = vget_high_u8(survivor0); + b1 = vget_high_u8(survivor1); + result = vzip_u8(a1, b1); + vp->new_metrics->v[2*i+1] = vcombine_u8(result.val[0], result.val[1]); + + + + } + + // See if we need to normalize + if (vp->new_metrics->c[0] > 100) { + int i; + uint8_t adjust; + uint8x16_t adjustv; + + union { uint8x16_t v; signed short w[8]; } t; + + adjustv = vp->new_metrics->v[0]; + for(i=1;i<4;i++) + { + adjustv = vminq_u8(vp->new_metrics->v[i],adjustv); + } + + adjustv = vminq_u8(adjustv,vextq_u8(adjustv, vdupq_n_u8(0), (8))); + adjustv = vminq_u8(adjustv,vextq_u8(adjustv, vdupq_n_u8(0), (4))); + adjustv = vminq_u8(adjustv,vextq_u8(adjustv, vdupq_n_u8(0), (2))); + t.v = adjustv; + adjust = t.w[0]; + adjustv = vld1q_dup_u8(&adjust); + + /* We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + * This is okay since it can't overflow anyway + */ + for(i=0;i<4;i++) + { + vp->new_metrics->v[i] = vsubq_u8(vp->new_metrics->v[i],adjustv); + } + + } + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + //firstGo = 0; + } + + if (best_state) { + uint32_t i, bst=0; + uint8_t minmetric=UINT8_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->c[i] <= minmetric) { + bst = i; + minmetric = vp->old_metrics->c[i]; + } + } + *best_state = bst; + } + + #ifdef DEBUG + printf("];\n===========================================\n"); +#endif + + vp->dp = d; +} + +#endif + + + diff --git a/srslte/lib/rf/CMakeLists.txt b/srslte/lib/rf/CMakeLists.txt index 22b1fb1c3..aed202e66 100644 --- a/srslte/lib/rf/CMakeLists.txt +++ b/srslte/lib/rf/CMakeLists.txt @@ -33,6 +33,17 @@ if(RF_FOUND) list(APPEND SOURCES_RF rf_blade_imp.c) endif (BLADERF_FOUND) + if (LIMESDR_FOUND) + add_definitions(-DENABLE_LIMESDR) + list(APPEND SOURCES_RF rf_limesdr_imp.c) + endif (LIMESDR_FOUND) + + if (SOAPYSDR_FOUND) + add_definitions(-DENABLE_SOAPYSDR) + list(APPEND SOURCES_RF rf_soapy_imp.c) + endif (SOAPYSDR_FOUND) + + add_library(srslte_rf SHARED ${SOURCES_RF}) @@ -44,6 +55,15 @@ if(RF_FOUND) target_link_libraries(srslte_rf ${BLADERF_LIBRARIES}) endif (BLADERF_FOUND) + if (LIMESDR_FOUND) + target_link_libraries(srslte_rf ${LIMESDR_LIBRARIES}) + endif (LIMESDR_FOUND) + + if (SOAPYSDR_FOUND) + target_link_libraries(srslte_rf ${SOAPYSDR_LIBRARIES}) + endif (SOAPYSDR_FOUND) + + INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) SRSLTE_SET_PIC(srslte_rf) endif(RF_FOUND) diff --git a/srslte/lib/rf/rf_dev.h b/srslte/lib/rf/rf_dev.h index 293f158b1..c85541c1a 100644 --- a/srslte/lib/rf/rf_dev.h +++ b/srslte/lib/rf/rf_dev.h @@ -140,6 +140,83 @@ static rf_dev_t dev_blade = { }; #endif +/* Define implementation for LimeSDR */ +#ifdef ENABLE_LIMESDR + +#include "rf_limesdr_imp.h" + +static rf_dev_t dev_limesdr = { + "limesdr", + rf_limesdr_devname, + rf_limesdr_rx_wait_lo_locked, + rf_limesdr_start_rx_stream, + rf_limesdr_stop_rx_stream, + rf_limesdr_flush_buffer, + rf_limesdr_has_rssi, + rf_limesdr_get_rssi, + rf_limesdr_suppress_stdout, + rf_limesdr_register_error_handler, + rf_limesdr_open, + rf_limesdr_open_multi, + rf_limesdr_close, + rf_limesdr_set_master_clock_rate, + rf_limesdr_is_master_clock_dynamic, + rf_limesdr_set_rx_srate, + rf_limesdr_set_rx_gain, + rf_limesdr_set_tx_gain, + rf_limesdr_get_rx_gain, + rf_limesdr_get_tx_gain, + rf_limesdr_set_rx_freq, + rf_limesdr_set_tx_srate, + rf_limesdr_set_tx_freq, + rf_limesdr_get_time, + rf_limesdr_recv_with_time, + rf_limesdr_recv_with_time_multi, + rf_limesdr_send_timed, + rf_limesdr_set_tx_cal, + rf_limesdr_set_rx_cal +}; + +#endif + +#ifdef ENABLE_SOAPYSDR + +#include "rf_soapy_imp.h" + +static rf_dev_t dev_soapy = { + "soapy", + rf_soapy_devname, + rf_soapy_rx_wait_lo_locked, + rf_soapy_start_rx_stream, + rf_soapy_stop_rx_stream, + rf_soapy_flush_buffer, + rf_soapy_has_rssi, + rf_soapy_get_rssi, + rf_soapy_suppress_stdout, + rf_soapy_register_error_handler, + rf_soapy_open, + rf_soapy_open_multi, + rf_soapy_close, + rf_soapy_set_master_clock_rate, + rf_soapy_is_master_clock_dynamic, + rf_soapy_set_rx_srate, + rf_soapy_set_rx_gain, + rf_soapy_set_tx_gain, + rf_soapy_get_rx_gain, + rf_soapy_get_tx_gain, + rf_soapy_set_rx_freq, + rf_soapy_set_tx_srate, + rf_soapy_set_tx_freq, + rf_soapy_get_time, + rf_soapy_recv_with_time, + rf_soapy_recv_with_time_multi, + rf_soapy_send_timed, + rf_soapy_set_tx_cal, + rf_soapy_set_rx_cal +}; + +#endif + //#define ENABLE_DUMMY_DEV #ifdef ENABLE_DUMMY_DEV @@ -183,12 +260,19 @@ static rf_dev_t dev_dummy = { #endif static rf_dev_t *available_devices[] = { + #ifdef ENABLE_UHD &dev_uhd, #endif +#ifdef ENABLE_SOAPYSDR + &dev_soapy, +#endif #ifdef ENABLE_BLADERF &dev_blade, #endif +#ifdef ENABLE_LIMESDR + &dev_limesdr, +#endif #ifdef ENABLE_DUMMY_DEV &dev_dummy, #endif diff --git a/srslte/lib/rf/rf_limesdr_imp.c b/srslte/lib/rf/rf_limesdr_imp.c new file mode 100644 index 000000000..f3594c479 --- /dev/null +++ b/srslte/lib/rf/rf_limesdr_imp.c @@ -0,0 +1,475 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/srslte.h" +#include "rf_limesdr_imp.h" +#include "srslte/rf/rf.h" +#include "lime/LimeSuite.h" + +typedef struct { + char *devname; + lms_dev_info_t *dev_info; + lms_device_t *device; + lms_info_str_t list[8]; + lms_stream_t rx_stream; + lms_stream_t tx_stream; + int sampling_rate; + bool rx_is_streaming; + bool tx_is_streaming; + int channel; + + int buffer_size; + int num_buffers; + + lms_stream_meta_t tx_metadata; //Use metadata for additional control over sample receive function behaviour + lms_stream_meta_t rx_metadata; //Use metadata for additional control over sample receive function behaviour + + lms_range_t rx_range; + lms_range_t tx_range; + +} rf_limesdr_handler_t; + +int lime_error(void *h) +{ + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + + //print last error message + fprintf(stderr, "Error: %s\n", LMS_GetLastErrorMessage()); + if(handler->device != NULL) + LMS_Close(handler->device); + + return SRSLTE_ERROR; +} + +void rf_limesdr_get_freq_range(void *h) +{ + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + LMS_GetLOFrequencyRange(handler->device, LMS_CH_RX, &(handler->rx_range)); + LMS_GetLOFrequencyRange(handler->device, LMS_CH_TX, &(handler->tx_range)); +} + +void rf_limesdr_suppress_handler(const char *x) +{ + // not supported +} + +void rf_limesdr_msg_handler(const char *msg) +{ + // not supported +} + +void rf_limesdr_suppress_stdout(void *h) +{ + // not supported +} + +void rf_limesdr_register_error_handler(void *notused, srslte_rf_error_handler_t new_handler) +{ + // not supported +} + +static bool isLocked(rf_limesdr_handler_t *handler, char *sensor_name, void *value_h) +{ + // not supported + return true; +} + +char* rf_limesdr_devname(void* h) +{ + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + handler->dev_info = LMS_GetDeviceInfo(handler); + + return handler->dev_info->deviceName; +} + +bool rf_limesdr_rx_wait_lo_locked(void *h) +{ + // not supported + return true; +} + +void rf_limesdr_set_tx_cal(void *h, srslte_rf_cal_t *cal) +{ + // not supported +} + +void rf_limesdr_set_rx_cal(void *h, srslte_rf_cal_t *cal) +{ + // not supported +} + +int rf_limesdr_start_rx_stream(void *h) +{ + printf("Starting rx stream\n"); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if(LMS_StartStream(&(handler->rx_stream)) != 0){ + return lime_error(h); + } + return 0; +} + + +int rf_limesdr_start_tx_stream(void *h) +{ + printf("Starting tx stream\n"); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if(LMS_StartStream(&(handler->tx_stream)) != 0){ + return lime_error(h); + } + return 0; +} + +int rf_limesdr_stop_rx_stream(void *h) +{ + printf("Stopping rx stream\n"); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + //stream is stopped but can be started again with LMS_StartStream() + if(LMS_StopStream(&(handler->rx_stream)) != 0){ + return lime_error(h); + } + return 0; +} +int rf_limesdr_stop_tx_stream(void *h) +{ + printf("Stopping tx stream\n"); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + //stream is stopped but can be started again with LMS_StartStream() + if(LMS_StopStream(&(handler->tx_stream)) != 0){ + return lime_error(h); + } + return 0; +} + +void rf_limesdr_flush_buffer(void *h) +{ + int n; + cf_t tmp1[1024]; + cf_t tmp2[1024]; + void *data[2] = {tmp1, tmp2}; + do { + n = rf_limesdr_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + } while (n > 0); +} + +bool rf_limesdr_has_rssi(void *h) +{ + return false; +} + +float rf_limesdr_get_rssi(void *h) +{ + return 0.0; +} + +//TODO: add multi-channel support +int rf_limesdr_open_multi(char *args, void **h, uint32_t nof_rx_antennas) +{ + return rf_limesdr_open(args, h); +} + +int rf_limesdr_open(char *args, void **h) +{ + printf("Opening device\n"); + *h = NULL; + + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) malloc(sizeof(rf_limesdr_handler_t)); + if (!handler) { + perror("malloc"); + return -1; + } + *h = handler; + + handler->device = NULL; + + handler->buffer_size = 1024; + handler->num_buffers = 8; + handler->channel = 0; + + + int n; + if ((n = LMS_GetDeviceList(handler->list)) < 0) //NULL can be passed to only get number of devices + return SRSLTE_ERROR; + + if (LMS_Open(&(handler->device), handler->list[0], NULL)) + return SRSLTE_ERROR; + + if (LMS_Init(handler->device) != 0) + return SRSLTE_ERROR; + + if (LMS_EnableChannel(handler->device, LMS_CH_RX, handler->channel, true) != 0) + return lime_error(handler); + + if (LMS_EnableChannel(handler->device, LMS_CH_TX, handler->channel, true) != 0) + return lime_error(handler); + + rf_limesdr_get_freq_range(handler); + + handler->rx_is_streaming = false; + handler->rx_stream.channel = handler->channel; //channel number + handler->rx_stream.fifoSize = 1024 * 1024; //fifo size in samples + handler->rx_stream.throughputVsLatency = 1.0; //optimize for max throughput + handler->rx_stream.isTx = false; //RX channel + handler->rx_stream.dataFmt = LMS_FMT_F32; + handler->rx_metadata.flushPartialPacket = false; //Do not discard data remainder when read size differs from packet size + handler->rx_metadata.waitForTimestamp = false; //Do not wait for specific timestamps + + if (LMS_SetupStream(handler->device, &(handler->rx_stream)) != 0) + return lime_error(handler); + + handler->tx_is_streaming = false; + handler->tx_stream.channel = handler->channel; //channel number + handler->tx_stream.fifoSize = 1024 * 1024; //fifo size in samples + handler->tx_stream.throughputVsLatency = 1.0; //optimize for max throughput + handler->tx_stream.isTx = true; //TX channel + handler->rx_stream.dataFmt = LMS_FMT_F32; + handler->tx_metadata.flushPartialPacket = false; //Do not discard data remainder when read size differs from packet size + handler->tx_metadata.waitForTimestamp = false; //Do not wait for specific timestamps + + if (LMS_SetupStream(handler->device, &(handler->tx_stream)) != 0) + return lime_error(handler); + + return SRSLTE_SUCCESS; +} + + +int rf_limesdr_close(void *h) +{ + printf("Closing device\n"); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if(handler->rx_is_streaming) { + LMS_StopStream(&(handler->rx_stream)); + } + LMS_DestroyStream(handler->device, &(handler->rx_stream)); //stream is deallocated and can no longer be used + + if(handler->tx_is_streaming) { + LMS_StopStream(&(handler->tx_stream)); + } + LMS_DestroyStream(handler->device, &(handler->tx_stream)); //stream is deallocated and can no longer be used + + LMS_Close(handler->device); + return SRSLTE_SUCCESS; +} + +void rf_limesdr_set_master_clock_rate(void *h, double rate) +{ + // Allow the limesdr to automatically set the appropriate clock rate +} + +bool rf_limesdr_is_master_clock_dynamic(void *h) +{ + return true; +} + +double rf_limesdr_set_rx_srate(void *h, double rate) +{ + fprintf(stdout, "Setting rx rate: %f\n", rate); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if (LMS_SetSampleRate(handler->device, rate, 0) != 0) + return lime_error(handler); + + handler->sampling_rate = rate; + return rate; +} + +double rf_limesdr_set_tx_srate(void *h, double rate) +{ + fprintf(stdout, "Setting tx rate: %f\n", rate); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if (LMS_SetSampleRate(handler->device, rate, 0) != 0) + return lime_error(handler); + + handler->sampling_rate = rate; + return rate; +} + +double rf_limesdr_set_rx_gain(void *h, double gain) +{ + fprintf(stdout, "Setting rx gain: %f\n", gain); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if (LMS_SetNormalizedGain(handler->device, LMS_CH_RX, handler->channel, gain) != 0) + return lime_error(handler); + + return gain; +} + +double rf_limesdr_set_tx_gain(void *h, double gain) +{ + fprintf(stdout, "Setting tx gain: %f\n", gain); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if (LMS_SetNormalizedGain(handler->device, LMS_CH_TX, handler->channel, gain) != 0) + return lime_error(handler); + + return gain; +} + +double rf_limesdr_get_rx_gain(void *h) +{ + double gain; + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if(LMS_GetNormalizedGain(handler->device, LMS_CH_RX,handler->channel,&gain) != 0) + return lime_error(handler); + + return gain; +} + +double rf_limesdr_get_tx_gain(void *h) +{ + double gain; + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if(LMS_GetNormalizedGain(handler->device, LMS_CH_TX, handler->channel, &gain) != 0) + return lime_error(handler); + + return gain; +} + +double rf_limesdr_set_rx_freq(void *h, double freq) +{ + fprintf(stdout, "Setting rx freq: %f\n", freq); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + + if(freq > handler->rx_range.max || freq < handler->rx_range.min) { + fprintf(stderr, "Requested freq outside supported range. freq: %f, min: %f, max: %f\n", freq, handler->rx_range.min, handler->rx_range.max); + return SRSLTE_ERROR; + } + + if(LMS_SetLOFrequency(handler->device, LMS_CH_RX, handler->channel, freq) != 0) + return lime_error(handler); + + // Automatic antenna port selection doesn't work - so set manually + int ant_port = 1; // manually select antenna index 1 (LNA_H) + if(freq < 1.5e9) { + ant_port = 2; // manually select antenna index 2 (LNA_L) + } + if (LMS_SetAntenna(handler->device, LMS_CH_RX, handler->channel, ant_port) != 0) + return lime_error(handler); + + lms_name_t antenna_list[10]; //large enough list for antenna names. + //Alternatively, NULL can be passed to LMS_GetAntennaList() to find out number of available antennae + int n = 0; + if ((n = LMS_GetAntennaList(handler->device, LMS_CH_RX, 0, antenna_list)) < 0) + return lime_error(handler); + + fprintf(stdout, "Available antennae:\n"); //print available antennae names + for(int i = 0; i < n; i++) + fprintf(stdout, "%d : %s\n", i, antenna_list[i]); + + if((n = LMS_GetAntenna(handler->device, LMS_CH_RX, handler->channel)) < 0) //get currently selected antenna index + return lime_error(handler); + fprintf(stdout, "Selected antenna: %d : %s\n", n, antenna_list[n]); //print antenna index and name + + return freq; +} + +double rf_limesdr_set_tx_freq(void *h, double freq) +{ + fprintf(stdout, "Setting tx freq: %f\n", freq); + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + if(freq > handler->tx_range.max || freq < handler->tx_range.min) { + fprintf(stderr, "Requested freq outside supported range. freq: %f, min: %f, max: %f\n", freq, handler->rx_range.min, handler->rx_range.max); + return SRSLTE_ERROR; + } + + if(LMS_SetLOFrequency(handler->device, LMS_CH_TX, handler->channel, freq) != 0) + return lime_error(handler); + + return freq; +} + + +void rf_limesdr_get_time(void *h, time_t *secs, double *frac_secs) { + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + LMS_RecvStream(&(handler->rx_stream),NULL,0, &(handler->rx_metadata), 0); + if (secs && frac_secs) { + *secs = (handler->rx_metadata.timestamp) / (handler->sampling_rate); + int remainder = handler->rx_metadata.timestamp % handler->sampling_rate; + *frac_secs = remainder/(handler->sampling_rate); + } +} + +//TODO: add multi-channel support +int rf_limesdr_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return rf_limesdr_recv_with_time(h, *data, nsamples, blocking, secs, frac_secs); +} + +int rf_limesdr_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + int samples = LMS_RecvStream(&(handler->rx_stream),data,nsamples, &(handler->rx_metadata), blocking ? 1000:0); + if (secs && frac_secs) { + *secs = (handler->rx_metadata.timestamp) / (handler->sampling_rate); + int remainder = handler->rx_metadata.timestamp % handler->sampling_rate; + *frac_secs = remainder/(handler->sampling_rate); + } + + return samples; +} + + +int rf_limesdr_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_limesdr_handler_t *handler = (rf_limesdr_handler_t*) h; + + //float *data_in = (float*) data; + + if(!handler->tx_is_streaming) + rf_limesdr_start_tx_stream(h); + + handler->tx_metadata.timestamp = secs*handler->sampling_rate; + handler->tx_metadata.timestamp += frac_secs*handler->sampling_rate; + + LMS_SendStream(&(handler->rx_stream), data, nsamples, &(handler->tx_metadata), blocking ? 1000:0); + + return 1; +} + + + + diff --git a/srslte/lib/rf/rf_limesdr_imp.h b/srslte/lib/rf/rf_limesdr_imp.h new file mode 100644 index 000000000..5200f2987 --- /dev/null +++ b/srslte/lib/rf/rf_limesdr_imp.h @@ -0,0 +1,118 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/config.h" +#include "srslte/rf/rf.h" + + +SRSLTE_API int rf_limesdr_open( char *args, + void **handler); + +SRSLTE_API int rf_limesdr_open_multi( char *args, + void **handler, + uint32_t nof_rx_antennas); + +SRSLTE_API char* rf_limesdr_devname(void *h); + +SRSLTE_API int rf_limesdr_close(void *h); + +SRSLTE_API void rf_limesdr_set_tx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API void rf_limesdr_set_rx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API int rf_limesdr_start_rx_stream(void *h); + +SRSLTE_API int rf_limesdr_stop_rx_stream(void *h); + +SRSLTE_API void rf_limesdr_flush_buffer(void *h); + +SRSLTE_API bool rf_limesdr_has_rssi(void *h); + +SRSLTE_API float rf_limesdr_get_rssi(void *h); + +SRSLTE_API bool rf_limesdr_rx_wait_lo_locked(void *h); + +SRSLTE_API void rf_limesdr_set_master_clock_rate(void *h, + double rate); + +SRSLTE_API bool rf_limesdr_is_master_clock_dynamic(void *h); + +SRSLTE_API double rf_limesdr_set_rx_srate(void *h, + double freq); + +SRSLTE_API double rf_limesdr_set_rx_gain(void *h, + double gain); + +SRSLTE_API double rf_limesdr_get_rx_gain(void *h); + +SRSLTE_API double rf_limesdr_set_tx_gain(void *h, + double gain); + +SRSLTE_API double rf_limesdr_get_tx_gain(void *h); + +SRSLTE_API void rf_limesdr_suppress_stdout(void *h); + +SRSLTE_API void rf_limesdr_register_error_handler(void *h, srslte_rf_error_handler_t error_handler); + +SRSLTE_API double rf_limesdr_set_rx_freq(void *h, + double freq); + +SRSLTE_API int rf_limesdr_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_limesdr_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API double rf_limesdr_set_tx_srate(void *h, + double freq); + +SRSLTE_API double rf_limesdr_set_tx_freq(void *h, + double freq); + +SRSLTE_API void rf_limesdr_get_time(void *h, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_limesdr_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + diff --git a/srslte/lib/rf/rf_soapy_imp.c b/srslte/lib/rf/rf_soapy_imp.c new file mode 100644 index 000000000..261c4ab54 --- /dev/null +++ b/srslte/lib/rf/rf_soapy_imp.c @@ -0,0 +1,457 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/srslte.h" +#include "rf_soapy_imp.h" +#include "srslte/rf/rf.h" + +#include +#include +//#include "lime/LimeSuite.h" + +typedef struct { + + SoapySDRKwargs args; + SoapySDRDevice *device; + SoapySDRRange *ranges; + + SoapySDRStream *rxStream; + SoapySDRStream *txStream; + + +} rf_soapy_handler_t; + +int soapy_error(void *h) +{ + +} + +void rf_soapy_get_freq_range(void *h) +{ + +} + +void rf_soapy_suppress_handler(const char *x) +{ + // not supported +} + +void rf_soapy_msg_handler(const char *msg) +{ + // not supported +} + +void rf_soapy_suppress_stdout(void *h) +{ + // not supported +} + +void rf_soapy_register_error_handler(void *notused, srslte_rf_error_handler_t new_handler) +{ + // not supported +} + +static bool isLocked(rf_soapy_handler_t *handler, char *sensor_name, void *value_h) +{ + // not supported + return true; +} + +char* rf_soapy_devname(void* h) +{ + +} + +bool rf_soapy_rx_wait_lo_locked(void *h) +{ + // not supported + return true; +} + +void rf_soapy_set_tx_cal(void *h, srslte_rf_cal_t *cal) +{ + // not supported +} + +void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal) +{ + // not supported +} + +int rf_soapy_start_rx_stream(void *h) +{ + //printf("starting SOAPY rx stream \n"); + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + //SoapySDRStream *rxStream; + + if(SoapySDRDevice_activateStream(handler->device, handler->rxStream, 0, 0, 0)!=0)//start streaming + return SRSLTE_ERROR; + + + return SRSLTE_SUCCESS; +} + + +int rf_soapy_start_tx_stream(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + //SoapySDRStream *rxStream; + if (SoapySDRDevice_setupStream(handler->device, &(handler->txStream), SOAPY_SDR_TX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) + { + printf("setupStream fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + if(SoapySDRDevice_activateStream(handler->device, handler->txStream, 0, 0, 0) != 0) + return SRSLTE_ERROR; + + + return SRSLTE_SUCCESS; +} + +int rf_soapy_stop_rx_stream(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if(SoapySDRDevice_deactivateStream(handler->device, handler->rxStream, 0, 0) != 0) + return SRSLTE_ERROR; + + + + return SRSLTE_SUCCESS; +} +int rf_soapy_stop_tx_stream(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + + if(SoapySDRDevice_deactivateStream(handler->device, handler->txStream, 0, 0) != 0) + return SRSLTE_ERROR; + + + + return SRSLTE_SUCCESS; +} + +void rf_soapy_flush_buffer(void *h) +{ + int n; + cf_t tmp1[1024]; + cf_t tmp2[1024]; + void *data[2] = {tmp1, tmp2}; + do { + n = rf_soapy_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + } while (n > 0); +} + +bool rf_soapy_has_rssi(void *h) +{ + +} + +float rf_soapy_get_rssi(void *h) +{ + +} + +//TODO: add multi-channel support +int rf_soapy_open_multi(char *args, void **h, uint32_t nof_rx_antennas) +{//SoapySDRKwargs soapy_args = {}; + size_t length; + const SoapySDRKwargs *soapy_args = SoapySDRDevice_enumerate(NULL, &length); + + if(length == 0) + { + return SRSLTE_ERROR; + } + + for (size_t i = 0; i < length; i++) + { + printf("Soapy Has Found device #%d: ", (int)i); + for (size_t j = 0; j < soapy_args[i].size; j++) + { + printf("%s=%s, ", soapy_args[i].keys[j], soapy_args[i].vals[j]); + } + printf("\n"); + } + + // SoapySDRrgs_set(&soapy_args, "driver", "rtlsdr"); + SoapySDRDevice *sdr = SoapySDRDevice_make(&(soapy_args[0])); + + if(sdr == NULL) + { + printf("failed to create SOAPY object\n"); + return SRSLTE_ERROR; + + } + + //SoapySDRKwargs_clear(&soapy_args); + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) malloc(sizeof(rf_soapy_handler_t)); + *h = handler; + handler->device = sdr; + + + + //size_t channels[1]; + //channels[0] = 0; + + if (SoapySDRDevice_setupStream(handler->device, &(handler->rxStream), SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) + { + printf("setupStream fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + + + + return SRSLTE_SUCCESS; + +} + +int rf_soapy_open(char *args, void **h) +{ + return rf_soapy_open_multi(args, h, 1); +} + + +int rf_soapy_close(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + SoapySDRDevice_closeStream(handler->device, handler->txStream); + SoapySDRDevice_closeStream(handler->device, handler->rxStream); + SoapySDRDevice_unmake(handler->device); +} + +void rf_soapy_set_master_clock_rate(void *h, double rate) +{ + // Allow the soapy to automatically set the appropriate clock rate + + printf("SET MASTER CLOCK RATE\n"); +} + +bool rf_soapy_is_master_clock_dynamic(void *h) +{ + +} + +double rf_soapy_set_rx_srate(void *h, double rate) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setSampleRate(handler->device, SOAPY_SDR_RX, 0, rate) != 0) + { + printf("setSampleRate fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + + double ret = SoapySDRDevice_getSampleRate(handler->device, SOAPY_SDR_RX,0); + printf("Sampling rate is set to %f.3 : \n",ret); + return ret; +} + +double rf_soapy_set_tx_srate(void *h, double rate) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setSampleRate(handler->device, SOAPY_SDR_TX, 0, rate) != 0) + { + printf("setSampleRate fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + double ret = SoapySDRDevice_getSampleRate(handler->device, SOAPY_SDR_TX,0); + printf("Sampling rate is set to %f.3 : \n",ret); + return ret; +} + +double rf_soapy_set_rx_gain(void *h, double gain) +{ + + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setGain(handler->device, SOAPY_SDR_RX, 0, gain) != 0) + { + printf("setGain fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + double ret = rf_soapy_get_rx_gain(h); + printf("gain has been set to %f.2 \n",ret); + return ret; +} + +double rf_soapy_set_tx_gain(void *h, double gain) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setGain(handler->device, SOAPY_SDR_TX, 0, gain) != 0) + { + printf("setGain fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + double ret = rf_soapy_get_rx_gain(h); + printf("gain has been set to %f.2 \n",ret); + return ret; +} + +double rf_soapy_get_rx_gain(void *h) +{ + + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + return SoapySDRDevice_getGain(handler->device,SOAPY_SDR_RX,0); + +} + +double rf_soapy_get_tx_gain(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + return SoapySDRDevice_getGain(handler->device,SOAPY_SDR_TX,0); +} + +double rf_soapy_set_rx_freq(void *h, double freq) +{ + + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setFrequency(handler->device, SOAPY_SDR_RX, 0, freq, NULL) != 0) + { + printf("setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + + double ret = SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_RX, 0); + printf("Frequency has been set to %f : \n",ret); + return ret; + +} + +double rf_soapy_set_tx_freq(void *h, double freq) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setFrequency(handler->device, SOAPY_SDR_TX, 0, freq, NULL) != 0) + { + printf("setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + double ret = SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_RX, 0); + printf("Frequency has been set to %f : \n",ret); + return ret; + +} + + +void rf_soapy_get_time(void *h, time_t *secs, double *frac_secs) { + +} + +//TODO: add multi-channel support +int rf_soapy_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + //void *buffs[] = {buff}; //array of buffers + + int flags; //flags set by receive operation + + int num_channels = 1; // temp + + int trials = 0; + int ret = 0; + long long timeNs; //timestamp for receive buffer + int n = 0; + do{ + + size_t rx_samples = nsamples; + + if (rx_samples > nsamples - n) + { + rx_samples = nsamples - n; + } + void *buffs_ptr[4]; + for (int i=0;idevice, handler->rxStream,buffs_ptr , rx_samples, &flags, &timeNs, 1000000); + + if(ret < 0) + return SRSLTE_ERROR; + n += ret; + trials++; + }while (n < nsamples && trials < 100); + + + //*secs = timeNs / 1000000000; + //*frac_secs = (timeNs % 1000000000)/1000000000; + // printf("ret=%d, flags=%d, timeNs=%lld\n", ret, flags, timeNs); + return n; + + +} + +int rf_soapy_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return rf_soapy_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + + +int rf_soapy_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + + int flags; + long long timeNs; + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + timeNs = secs * 1000000000; + timeNs = timeNs + (frac_secs * 1000000000); + int ret = SoapySDRDevice_writeStream(handler->device, handler->txStream, &data, nsamples, &flags, timeNs, 100000); + + + + if(ret != nsamples) + return SRSLTE_ERROR; + + + + return ret; + +} + + + + diff --git a/srslte/lib/rf/rf_soapy_imp.h b/srslte/lib/rf/rf_soapy_imp.h new file mode 100644 index 000000000..145609267 --- /dev/null +++ b/srslte/lib/rf/rf_soapy_imp.h @@ -0,0 +1,118 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/config.h" +#include "srslte/rf/rf.h" + + +SRSLTE_API int rf_soapy_open( char *args, + void **handler); + +SRSLTE_API int rf_soapy_open_multi( char *args, + void **handler, + uint32_t nof_rx_antennas); + +SRSLTE_API char* rf_soapy_devname(void *h); + +SRSLTE_API int rf_soapy_close(void *h); + +SRSLTE_API void rf_soapy_set_tx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API int rf_soapy_start_rx_stream(void *h); + +SRSLTE_API int rf_soapy_stop_rx_stream(void *h); + +SRSLTE_API void rf_soapy_flush_buffer(void *h); + +SRSLTE_API bool rf_soapy_has_rssi(void *h); + +SRSLTE_API float rf_soapy_get_rssi(void *h); + +SRSLTE_API bool rf_soapy_rx_wait_lo_locked(void *h); + +SRSLTE_API void rf_soapy_set_master_clock_rate(void *h, + double rate); + +SRSLTE_API bool rf_soapy_is_master_clock_dynamic(void *h); + +SRSLTE_API double rf_soapy_set_rx_srate(void *h, + double freq); + +SRSLTE_API double rf_soapy_set_rx_gain(void *h, + double gain); + +SRSLTE_API double rf_soapy_get_rx_gain(void *h); + +SRSLTE_API double rf_soapy_set_tx_gain(void *h, + double gain); + +SRSLTE_API double rf_soapy_get_tx_gain(void *h); + +SRSLTE_API void rf_soapy_suppress_stdout(void *h); + +SRSLTE_API void rf_soapy_register_error_handler(void *h, srslte_rf_error_handler_t error_handler); + +SRSLTE_API double rf_soapy_set_rx_freq(void *h, + double freq); + +SRSLTE_API int rf_soapy_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_soapy_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API double rf_soapy_set_tx_srate(void *h, + double freq); + +SRSLTE_API double rf_soapy_set_tx_freq(void *h, + double freq); + +SRSLTE_API void rf_soapy_get_time(void *h, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_soapy_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + diff --git a/srslte/lib/sync/pss.c b/srslte/lib/sync/pss.c index 647baa838..c470c89ad 100644 --- a/srslte/lib/sync/pss.c +++ b/srslte/lib/sync/pss.c @@ -97,15 +97,33 @@ int srslte_pss_synch_init_fft_offset(srslte_pss_synch_t *q, uint32_t frame_size, ret = SRSLTE_ERROR; uint32_t N_id_2; - uint32_t buffer_size; + uint32_t buffer_size; + int decimation_factor = q->decimate; bzero(q, sizeof(srslte_pss_synch_t)); - q->N_id_2 = 10; + q->N_id_2 = 10; + q->ema_alpha = 0.2; + + q->decimate = decimation_factor; + fft_size = fft_size/q->decimate; + frame_size = frame_size/q->decimate; + q->fft_size = fft_size; q->frame_size = frame_size; - q->ema_alpha = 0.2; buffer_size = fft_size + frame_size + 1; + + if(q->decimate > 1) + { + int filter_order = 3; + srslte_filt_decim_cc_init(&q->filter,q->decimate,filter_order); + q->filter.filter_output = srslte_vec_malloc((buffer_size) * sizeof(cf_t)); + q->filter.downsampled_input = srslte_vec_malloc((buffer_size + filter_order) * sizeof(cf_t)); + } + + + printf("decimation in the PSS is %d \n",q->decimate); + if (srslte_dft_plan(&q->dftp_input, fft_size, SRSLTE_DFT_FORWARD, SRSLTE_DFT_COMPLEX)) { fprintf(stderr, "Error creating DFT plan \n"); @@ -115,7 +133,7 @@ int srslte_pss_synch_init_fft_offset(srslte_pss_synch_t *q, uint32_t frame_size, srslte_dft_plan_set_dc(&q->dftp_input, true); srslte_dft_plan_set_norm(&q->dftp_input, true); - q->tmp_input = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + q->tmp_input = srslte_vec_malloc((buffer_size + frame_size*(q->decimate - 1)) * sizeof(cf_t)); if (!q->tmp_input) { fprintf(stderr, "Error allocating memory\n"); goto clean_and_exit; @@ -159,6 +177,10 @@ int srslte_pss_synch_init_fft_offset(srslte_pss_synch_t *q, uint32_t frame_size, } #ifdef CONVOLUTION_FFT + + for(N_id_2 = 0; N_id_2<3; N_id_2++) + q->conv_fft.pss_signal_time[N_id_2] = q->pss_signal_time[N_id_2]; + if (srslte_conv_fft_cc_init(&q->conv_fft, frame_size, fft_size)) { fprintf(stderr, "Error initiating convolution FFT\n"); goto clean_and_exit; @@ -204,6 +226,14 @@ void srslte_pss_synch_free(srslte_pss_synch_t *q) { } srslte_dft_plan_free(&q->dftp_input); + + if(q->decimate > 1) + { + srslte_filt_decim_cc_free(&q->filter); + free(q->filter.filter_output); + free(q->filter.downsampled_input); + } + bzero(q, sizeof(srslte_pss_synch_t)); } @@ -314,8 +344,17 @@ int srslte_pss_synch_find_pss(srslte_pss_synch_t *q, cf_t *input, float *corr_pe */ if (q->frame_size >= q->fft_size) { #ifdef CONVOLUTION_FFT - memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); - conv_output_len = srslte_conv_fft_cc_run(&q->conv_fft, q->tmp_input, q->pss_signal_time[q->N_id_2], q->conv_output); + memcpy(q->tmp_input, input, (q->frame_size * q->decimate) * sizeof(cf_t)); + if(q->decimate > 1) + { + srslte_filt_decim_cc_execute(&(q->filter), q->tmp_input, q->filter.downsampled_input, q->filter.filter_output , (q->frame_size * q->decimate)); + conv_output_len = srslte_conv_fft_cc_run_opt(&q->conv_fft, q->filter.filter_output, q->N_id_2, q->conv_output); + } + else + { + conv_output_len = srslte_conv_fft_cc_run_opt(&q->conv_fft, q->tmp_input, q->N_id_2, q->conv_output); + } + #else conv_output_len = srslte_conv_cc(input, q->pss_signal_time[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); #endif @@ -387,6 +426,14 @@ int srslte_pss_synch_find_pss(srslte_pss_synch_t *q, cf_t *input, float *corr_pe *corr_peak_value = q->conv_output_avg[corr_peak_pos]; } #endif + + if(q->decimate >1) + { + int decimation_correction = (q->filter.num_taps -2); + corr_peak_pos = corr_peak_pos - decimation_correction; + corr_peak_pos = corr_peak_pos*q->decimate; + } + if (q->frame_size >= q->fft_size) { ret = (int) corr_peak_pos; diff --git a/srslte/lib/sync/sync.c b/srslte/lib/sync/sync.c index f7ddddd84..00c914276 100644 --- a/srslte/lib/sync/sync.c +++ b/srslte/lib/sync/sync.c @@ -56,7 +56,8 @@ int srslte_sync_init(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset, fft_size_isvalid(fft_size)) { ret = SRSLTE_ERROR; - + int decimate = q->decimate; + bzero(q, sizeof(srslte_sync_t)); q->detect_cp = true; q->sss_en = true; @@ -105,7 +106,12 @@ int srslte_sync_init(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset, } srslte_sync_set_cp(q, SRSLTE_CP_NORM); - + + if(!decimate) + decimate = 1; + + q->pss.decimate = decimate; + if (srslte_pss_synch_init_fft(&q->pss, max_offset, fft_size)) { fprintf(stderr, "Error initializing PSS object\n"); goto clean_exit; diff --git a/srslte/lib/ue/ue_sync.c b/srslte/lib/ue/ue_sync.c index 42aab49e2..22cfc09f0 100644 --- a/srslte/lib/ue/ue_sync.c +++ b/srslte/lib/ue/ue_sync.c @@ -138,9 +138,9 @@ int srslte_ue_sync_init_multi(srslte_ue_sync_t *q, recv_callback != NULL) { ret = SRSLTE_ERROR; - + int decimate = q->decimate; bzero(q, sizeof(srslte_ue_sync_t)); - + q->decimate = decimate; q->stream = stream_handler; q->recv_callback = recv_callback; q->nof_rx_antennas = nof_rx_antennas; @@ -169,7 +169,16 @@ int srslte_ue_sync_init_multi(srslte_ue_sync_t *q, } q->frame_len = q->nof_recv_sf*q->sf_len; - + + if(q->fft_size > 1000 && q->decimate) + { + q->sfind.decimate = q->decimate; + } + else + { + q->sfind.decimate = 1; + } + if(srslte_sync_init(&q->sfind, q->frame_len, q->frame_len, q->fft_size)) { fprintf(stderr, "Error initiating sync find\n"); goto clean_exit; diff --git a/srslte/lib/utils/convolution.c b/srslte/lib/utils/convolution.c index c3cd383ed..908e1b29a 100644 --- a/srslte/lib/utils/convolution.c +++ b/srslte/lib/utils/convolution.c @@ -58,6 +58,15 @@ int srslte_conv_fft_cc_init(srslte_conv_fft_cc_t *q, uint32_t input_len, uint32_ srslte_dft_plan_set_norm(&q->input_plan, true); srslte_dft_plan_set_norm(&q->filter_plan, true); srslte_dft_plan_set_norm(&q->output_plan, false); + + for(int i =0; i< 3; i++) + { + q->pss_signal_time_fft[i] = srslte_vec_malloc(sizeof(cf_t)*q->output_len); + + srslte_dft_run_c(&q->filter_plan, q->pss_signal_time[i], q->pss_signal_time_fft[i]); + + } + return SRSLTE_SUCCESS; } @@ -71,6 +80,11 @@ void srslte_conv_fft_cc_free(srslte_conv_fft_cc_t *q) { if (q->output_fft) { free(q->output_fft); } + for(int i = 0; i < 3;i++) + { + free(q->pss_signal_time_fft[i]); + } + srslte_dft_plan_free(&q->input_plan); srslte_dft_plan_free(&q->filter_plan); srslte_dft_plan_free(&q->output_plan); @@ -79,6 +93,16 @@ void srslte_conv_fft_cc_free(srslte_conv_fft_cc_t *q) { } +uint32_t srslte_conv_fft_cc_run_opt(srslte_conv_fft_cc_t *q, cf_t *input, int N_id_2, cf_t *output) +{ + srslte_dft_run_c(&q->input_plan, input, q->input_fft); + srslte_vec_prod_ccc(q->input_fft,q->pss_signal_time_fft[N_id_2],q->output_fft,q->output_len); + srslte_dft_run_c(&q->output_plan, q->output_fft, output); + + return (q->output_len-1); // divide output length by dec factor + +} + uint32_t srslte_conv_fft_cc_run(srslte_conv_fft_cc_t *q, cf_t *input, cf_t *filter, cf_t *output) { srslte_dft_run_c(&q->input_plan, input, q->input_fft); diff --git a/srslte/lib/utils/filter.c b/srslte/lib/utils/filter.c new file mode 100644 index 000000000..40dbe6ab3 --- /dev/null +++ b/srslte/lib/utils/filter.c @@ -0,0 +1,126 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/utils/filter.h" +#define SRSLTE_NUM_FILTERS 8 +#define SRSLTE_MAX_FILTER_SIZE 11 + +float srslte_filt_decim2[SRSLTE_NUM_FILTERS][SRSLTE_MAX_FILTER_SIZE] = +{ + {0.0167364016736, 0.48326359832636, 0.48326359832636, 0.01673640167364,0,0,0,0,0,0,0}, + {0.000000000000000, 0.203712369200737, 0.592575261598526, 0.203712369200737, 0.000000000000000,0,0,0,0,0,0}, + {-0.007776312719103, 0.064454645578710, 0.443321667140393, 0.443321667140393, 0.064454645578710, -0.007776312719103,0,0,0,0,0}, + {-0.008721828105097, 0.000000000000000, 0.251842786534672, 0.513758083140849, 0.251842786534672, 0.000000000000000, -0.008721828105097,0,0,0,0}, + {-0.005164298061200, -0.022882524920256, 0.096755650536968, 0.431291172444487, 0.431291172444487, 0.096755650536968, -0.022882524920256, -0.005164298061200,0,0,0}, + {-0.000000000000000, -0.022663985459553, 0.000000000000000, 0.273977082565524, 0.497373805788057, 0.273977082565524, 0.000000000000000, -0.022663985459553, -0.000000000000000,0,0}, + { 0.003971846362414, -0.011976365116169, -0.041119498116286, 0.114687063714704, 0.434436953155337, 0.434436953155337, 0.114687063714704, -0.041119498116286, -0.011976365116169, 0.003971846362414,0}, + {0.005060317124845, -0.000000000000000, -0.041942879431345, 0.000000000000000, 0.288484826302638, 0.496795472007725, 0.288484826302638, 0.000000000000000, -0.041942879431345, -0.000000000000000, 0.005060317124845} + }; + +float srslte_filt_decim3[SRSLTE_NUM_FILTERS][SRSLTE_MAX_FILTER_SIZE] = +{ + {0.032388663967611, 0.467611336032389, 0.467611336032389, 0.032388663967611,0,0,0,0,0,0,0}, + {0.016883339167609, 0.227925078762723, 0.510383164139335, 0.227925078762723, 0.016883339167609,0,0,0,0,0,0}, + {0.006703633822959, 0.111127306155495, 0.382169060021546, 0.382169060021546, 0.111127306155495, 0.006703633822959,0,0,0,0,0}, + {0.000000000000000, 0.050666848023938, 0.251699825667307, 0.395266652617510, 0.251699825667307, 0.050666848023938, 0.000000000000000,0,0,0,0}, + {-0.004018779518049, 0.017806838679915, 0.150587600493065, 0.335624340345069, 0.335624340345069, 0.150587600493065, 0.017806838679915, -0.004018779518049,0,0,0}, + {-0.005814396641997, 0.000000000000000, 0.078494354666956, 0.251550893097387, 0.351538297755307, 0.251550893097387, 0.078494354666956, 0.000000000000000, -0.005814396641997,0,0}, + { -0.005798226803038, -0.008741738083915, 0.030013771222565, 0.167423798937736, 0.317102394726653, 0.317102394726653, 0.167423798937736, 0.030013771222565, -0.008741738083915, -0.005798226803038,0}, + {-0.004444793932295, -0.011657318166992, 0.000000000000000, 0.094750202492597, 0.253394317761931, 0.335915183689516, 0.253394317761931, 0.094750202492597, 0.000000000000000, -0.011657318166992, -0.004444793932295}, + +}; + + +float srslte_filt_decim4[SRSLTE_NUM_FILTERS][SRSLTE_MAX_FILTER_SIZE] = +{ + { 0.038579006748772, 0.461420993251228, 0.461420993251228, 0.038579006748772,0,0,0,0,0,0,0}, + {0.024553834015017, 0.234389464237986, 0.482113403493995, 0.234389464237986, 0.024553834015017,0,0,0,0,0,0}, + {0.015196373491712, 0.125956465856097, 0.358847160652191, 0.358847160652191, 0.125956465856097, 0.015196373491712,0,0,0,0,0}, + {0.008485920061584, 0.069755250084282, 0.245030941778248, 0.353455776151771, 0.245030941778248, 0.069755250084282, 0.008485920061584,0,0,0,0}, + {0.003560172702629, 0.038083722795699, 0.161031852333115, 0.297324252168557, 0.297324252168557, 0.161031852333115, 0.038083722795699, 0.003560172702629,0,0,0}, + {0.000000000000000, 0.019096925170212, 0.101875313412667, 0.230856124287772, 0.296343274258697, 0.230856124287772, 0.101875313412667, 0.019096925170212, 0.000000000000000,0,0}, + {-0.002426023829880, 0.007315224335493, 0.060635381185575, 0.169119131895270, 0.265356286413542, 0.265356286413542, 0.169119131895270, 0.060635381185575 , 0.007315224335493, -0.002426023829880,0}, + {-0.003871323167475, 0.000000000000000, 0.032087799410030, 0.116708621643743, 0.220701186106900, 0.268747432013603, 0.220701186106900, 0.116708621643743 , 0.032087799410030, 0.000000000000000,-0.003871323167475} +}; + + +void srslte_filt_decim_cc_init(srslte_filt_cc_t *q, int factor, int order) +{ + q->factor = factor; + q->num_taps = order + 1; + q->is_decimator = true; + q->taps = malloc(q->num_taps * sizeof(float)); + + switch(q->factor) + { + case 2: + for(int i = 0; i <(q->num_taps); i++) + q->taps[i] = srslte_filt_decim2[(q->num_taps) - 4][i]; + break; + case 3: + for(int i = 0; i <(q->num_taps); i++) + q->taps[i] = srslte_filt_decim3[(q->num_taps) - 4][i]; + case 4: + for(int i = 0; i <(q->num_taps); i++) + q->taps[i] = srslte_filt_decim4[(q->num_taps) - 4][i]; + + break; + default: + + break; + } + + for(int x = 0; x<(q->num_taps);x++) + { + printf("tap : %f.9\n" ,q->taps[x]); + } +} + +void srslte_filt_decim_cc_free(srslte_filt_cc_t *q) +{ + free(q->taps); +} + +void srslte_filt_decim_cc_execute(srslte_filt_cc_t *q, cf_t *input, cf_t *downsampled_input, cf_t *output, int size) +{ + // we assume that "downsampled_input" made size (input/2 + order) so as to have prepended zeros // + srslte_downsample_cc(input, downsampled_input + (q->num_taps - 1), q->factor, size); + + for(int i = 0;i < size/q->factor;i++) + { + output[i] = srslte_vec_dot_prod_cfc(&(downsampled_input[i]), q->taps, q->num_taps); + } + + +} + +/* Performs integer linear downsamling by a factor of M */ +void srslte_downsample_cc(cf_t *input, cf_t *output, int M, int size) { + int i; + for (i=0;i