mirror of https://github.com/pvnis/srsRAN_4G.git
adding native lime, soapy, decimation filtering and neon optimizations
parent
783d26b40a
commit
ae94416395
@ -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)
|
@ -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)
|
@ -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 <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "srslte/config.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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
|
@ -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 <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include "parity.h"
|
||||||
|
|
||||||
|
//#define DEBUG
|
||||||
|
//#define HAVE_NEON
|
||||||
|
#ifdef HAVE_NEON
|
||||||
|
|
||||||
|
#include <arm_neon.h>
|
||||||
|
|
||||||
|
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;s<nbits;s++) {
|
||||||
|
memset(d+s,0,sizeof(decision_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
while(nbits--) {
|
||||||
|
uint8x16_t sym0v,sym1v,sym2v;
|
||||||
|
|
||||||
|
void *tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// printf("nbits=%d, syms=%d,%d,%d\n", nbits, syms[0], syms[1], syms[2]);fflush(stdout);
|
||||||
|
|
||||||
|
/* Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc */
|
||||||
|
sym0v = vld1q_dup_u8(syms); // passing a char as opposed to a pointer to a char
|
||||||
|
sym1v = vld1q_dup_u8(syms+1);
|
||||||
|
sym2v = vld1q_dup_u8(syms+2);
|
||||||
|
syms += 3;
|
||||||
|
|
||||||
|
for(i=0;i<2;i++){
|
||||||
|
uint8x16_t decision0, decision1, metric, m_metric, m0, m1, m2, m3, survivor0, survivor1;
|
||||||
|
|
||||||
|
/* Form branch metrics */
|
||||||
|
m0 = vrhaddq_u8(veorq_u8(Branchtab37_neon[0].v[i],sym0v),veorq_u8(Branchtab37_neon[1].v[i],sym1v));
|
||||||
|
metric = vrhaddq_u8(veorq_u8(Branchtab37_neon[2].v[i],sym2v),m0);
|
||||||
|
|
||||||
|
metric = vshrq_n_u8(metric,3);
|
||||||
|
m_metric = vsubq_u8(vld1q_dup_u8(&thirtyone),metric);
|
||||||
|
|
||||||
|
/* Add branch metrics to path metrics */
|
||||||
|
m0 = vaddq_u8(vp->old_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
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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 <sys/time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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 <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#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);
|
||||||
|
|
@ -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 <sys/time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "srslte/srslte.h"
|
||||||
|
#include "rf_soapy_imp.h"
|
||||||
|
#include "srslte/rf/rf.h"
|
||||||
|
|
||||||
|
#include <SoapySDR/Device.h>
|
||||||
|
#include <SoapySDR/Formats.h>
|
||||||
|
//#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;i<num_channels;i++)
|
||||||
|
{
|
||||||
|
cf_t *data_c = (cf_t*) data[i];
|
||||||
|
buffs_ptr[i] = &data_c[n];
|
||||||
|
} //(void*)(&data)
|
||||||
|
ret = SoapySDRDevice_readStream(handler->device, 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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 <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#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);
|
||||||
|
|
@ -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<size/M;i++) {
|
||||||
|
output[i] = input[i*M];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue