Improved SFBC decoder and added SSE version. Fixed bug in MIB decoding. Improved calibration of synchronization constants

This commit is contained in:
ismagom 2016-04-08 17:04:01 +02:00
parent 799af37bed
commit c3268a93e2
45 changed files with 823 additions and 651 deletions

View File

@ -0,0 +1,46 @@
clear
addpath('../../build/srslte/lib/mimo/test')
enb = lteRMCDL('R.10');
cec = struct('FreqWindow',9,'TimeWindow',9,'InterpType','cubic');
cec.PilotAverage = 'UserDefined';
cec.InterpWinSize = 1;
cec.InterpWindow = 'Causal';
cfg.Seed = 1; % Random channel seed
cfg.NRxAnts = 1; % 1 receive antenna
cfg.DelayProfile = 'ETU'; % EVA delay spread
cfg.DopplerFreq = 100; % 120Hz Doppler frequency
cfg.MIMOCorrelation = 'Low'; % Low (no) MIMO correlation
cfg.InitTime = 0; % Initialize at time zero
cfg.NTerms = 16; % Oscillators used in fading model
cfg.ModelType = 'GMEDS'; % Rayleigh fading model type
cfg.InitPhase = 'Random'; % Random initial phases
cfg.NormalizePathGains = 'On'; % Normalize delay profile power
cfg.NormalizeTxAnts = 'On'; % Normalize for transmit antennas
[txWaveform, ~, info] = lteRMCDLTool(enb,[1;0;0;1]);
n = length(txWaveform);
cfg.SamplingRate = info.SamplingRate;
txWaveform = txWaveform+complex(randn(n,2),randn(n,2))*1e-3;
rxWaveform = lteFadingChannel(cfg,txWaveform);
rxGrid = lteOFDMDemodulate(enb,sum(rxWaveform,2));
[h,n0] = lteDLChannelEstimate(enb,cec,rxGrid);
signal=rxGrid(:,1);
hest(:,1,1)=reshape(h(:,1,1,1),[],1);
hest(:,1,2)=reshape(h(:,1,1,1),[],1);
output_mat = lteTransmitDiversityDecode(signal(, hest(1:598,1,:));
output_srs = srslte_diversitydecode(signal(1:598), hest(1:598,1,:));
plot(abs(output_mat-output_srs))
mean(abs(output_mat-output_srs).^2)

View File

@ -1,18 +1,10 @@
%clear
% R.1 10 MHz 1 port
% R.10 10 MHz 2 ports
% R.4 1.4 MHz 1 port
% R.11-2 5 MHz 2 ports
rmc = lteRMCDL('R.10');
clear
NofPortsTx=1;
SNR_values_db=1;%linspace(-8,-2,4);
Nrealizations=5;
enb = struct('NCellID',1,'NDLRB',25,'CellRefP',NofPortsTx,'CyclicPrefix','Normal','DuplexMode','FDD','NSubframe',0);
griddims = lteResourceGridSize(enb); % Resource grid dimensions
L = griddims(2);
Nblock=[3];
SNR_values_db=100;%linspace(-4,0,6);
Nrealizations=1;
enb = struct('NCellID',62,'NDLRB',50,'CellRefP',2,'CyclicPrefix','Normal','DuplexMode','FDD',...
'NSubframe',0,'PHICHDuration','Normal','Ng','One','NFrame',101,'TotSubframes',40);
cfg.Seed = 8; % Random channel seed
cfg.NRxAnts = 1; % 1 receive antenna
@ -33,59 +25,91 @@ cec.InterpType = 'linear'; % 2D interpolation type
cec.InterpWindow = 'Centered'; % Interpolation window type
cec.InterpWinSize = 1; % Interpolation window size
rmc.PDSCH.Modulation = '16QAM';
[waveform,rgrid,info] = lteRMCDLTool(rmc,[1;0;0;1]);
griddims = lteResourceGridSize(enb); % Resource grid dimensions
L = griddims(2);
cfg.SamplingRate = info.SamplingRate;
% Generate signal
mib = lteMIB(enb);
bchCoded = lteBCH(enb,mib);
mibCRC = lteCRCEncode(mib,'16');
mibCoded = lteConvolutionalEncode(mibCRC);
pbchSymbolsTx = ltePBCH(enb,bchCoded);
pbchIndtx = ltePBCHIndices(enb);
subframe_tx = lteDLResourceGrid(enb);
rs = lteCellRS(enb);
rsind = lteCellRSIndices(enb);
subframe_tx(rsind)=rs;
addpath('../../debug/lte/phy/lib/phch/test')
NofPortsTx=enb.CellRefP;
addpath('../../build/srslte/lib/phch/test')
txWaveform=cell(length(Nblock));
rxWaveform=cell(length(Nblock));
for n=1:length(Nblock)
subframe_tx2=subframe_tx;
subframe_tx2(pbchIndtx)=pbchSymbolsTx(Nblock(n)*240+1:(Nblock(n)+1)*240,:);
[txWaveform{n},info] = lteOFDMModulate(enb, subframe_tx2, 0);
cfg.SamplingRate = info.SamplingRate;
end
error=zeros(length(SNR_values_db),2);
for snr_idx=1:length(SNR_values_db)
SNRdB = SNR_values_db(snr_idx); % Desired SNR in dB
SNR = 10^(SNRdB/20); % Linear SNR
SNR = 10^(SNRdB/10); % Linear SNR
errorReal = zeros(Nrealizations,2);
for i=1:Nrealizations
rxWaveform = lteFadingChannel(cfg,sum(waveform,2));
for n=1:length(Nblock)
%% Additive Noise
N0 = 1/(sqrt(2.0*double(enb.CellRefP)*double(info.Nfft))*SNR);
%rxWaveform = lteFadingChannel(cfg,sum(txWaveform,2));
rxWaveform{n} = sum(txWaveform{n},2);
% Create additive white Gaussian noise
noise = N0*complex(randn(size(rxWaveform)),randn(size(rxWaveform)));
%% Additive Noise
N0 = 1/(sqrt(2.0*double(enb.CellRefP)*double(info.Nfft))*SNR);
rxWaveform = noise + rxWaveform;
rxWaveform = x((i-1)*76800+1:i*76800);
% Number of OFDM symbols in a subframe
% OFDM demodulate signal
rxgrid = lteOFDMDemodulate(enb, rxWaveform);
% Create additive white Gaussian noise
noise = N0*complex(randn(size(rxWaveform{n})),randn(size(rxWaveform{n})));
% Perform channel estimation
[hest, nest] = lteDLChannelEstimate(enb, cec, rxgrid(:,1:L,:));
pbchIndices = ltePBCHIndices(enb);
[pbchRx, pbchHest] = lteExtractResources( ...
pbchIndices, rxgrid(:,1:L,:), hest(:,1:L,:,:));
rxWaveform{n} = noise + rxWaveform{n};
% Decode PBCH
[bchBits, pbchSymbols, nfmod4, mib, nof_ports] = ltePBCHDecode(enb, pbchRx, pbchHest, nest);
% Number of OFDM symbols in a subframe
% OFDM demodulate signal
rxgrid = lteOFDMDemodulate(enb, rxWaveform{n}, 0);
if (nof_ports ~= NofPortsTx)
errorReal(i,1)=1;
% Perform channel estimation
%enb.CellRefP=2;
[hest, nest] = lteDLChannelEstimate(enb, cec, rxgrid(:,1:L,:));
pbchIndices = ltePBCHIndices(enb);
[pbchRx, pbchHest] = lteExtractResources(pbchIndices, rxgrid(:,1:L,:), ...
hest(:,1:L,:,:));
% Decode PBCH
[bchBits, pbchSymbols, nfmod4, mib, nof_ports] = ltePBCHDecode(enb, pbchRx, pbchHest, nest);
if (nof_ports ~= NofPortsTx)
errorReal(i,1)=1;
end
end
[nof_ports2, pbchSymbols2, pbchBits, ce, ce2, pbchRx2, pbchHest2]= srslte_pbch(enb, rxWaveform, hest, nest);
%enb.CellRefP=NofPortsTx;
[nof_ports2, pbchSymbols2, pbchBits, ce, ce2, pbchRx2, pbchHest2, mod2, codedbits]= ...
srslte_pbch(enb, rxWaveform);
subplot(2,1,1)
plot(abs((bchCoded(1:960)>0)-(pbchBits(1:960)>0)))
subplot(2,1,2)
codedbits2 = reshape(reshape(codedbits,3,[])',1,[]);
plot(abs((codedbits2'>0)-(mibCoded>0)))
%decodedData = lteConvolutionalDecode(noisysymbols);
%[decodedData2, quant] = srslte_viterbi(interleavedSymbols);
if (nof_ports2 ~= NofPortsTx)
errorReal(i,2)=1;
end
% if (errorReal(i,1) ~= errorReal(i,2))
% i=1;
% end
end
error(snr_idx,:) = sum(errorReal);
fprintf('SNR: %.2f dB\n', SNR_values_db(snr_idx));
@ -100,5 +124,7 @@ if (length(SNR_values_db) > 1)
axis([min(SNR_values_db) max(SNR_values_db) 1/Nrealizations/10 1])
else
disp(error)
disp(nfmod4)
disp(mod2)
end

View File

@ -5,25 +5,25 @@
% A structure |enbConfig| is used to configure the eNodeB.
clear
Npackets = 1;
SNR_values = 100;%linspace(-5,0,8);
Npackets = 60;
SNR_values = linspace(2,6,6);
txCFI = 1;
enbConfig.NDLRB = 50; % No of Downlink RBs in total BW
txCFI = 3;
enbConfig.NDLRB = 15; % No of Downlink RBs in total BW
enbConfig.CyclicPrefix = 'Normal'; % CP length
enbConfig.CFI = txCFI; ; % 4 PDCCH symbols as NDLRB <= 10
enbConfig.Ng = 'One'; % HICH groups
enbConfig.CellRefP = 2; % 1-antenna ports
enbConfig.NCellID = 424; % Physical layer cell identity
enbConfig.CellRefP = 1; % 1-antenna ports
enbConfig.NCellID = 0; % Physical layer cell identity
enbConfig.NSubframe = 5; % Subframe number 0
enbConfig.DuplexMode = 'FDD'; % Frame structure
enbConfig.PHICHDuration = 'Normal';
C_RNTI = 65535; % 16-bit UE-specific mask
C_RNTI = 1; % 16-bit UE-specific mask
%% Setup Fading channel model
cfg.Seed = 8; % Random channel seed
cfg.NRxAnts = 1; % 1 receive antenna
cfg.DelayProfile = 'EPA'; % EVA delay spread
cfg.DelayProfile = 'EVA'; % EVA delay spread
cfg.DopplerFreq = 5; % 120Hz Doppler frequency
cfg.MIMOCorrelation = 'Low'; % Low (no) MIMO correlation
cfg.InitTime = 0; % Initialize at time zero
@ -56,7 +56,7 @@ dciConfig.Allocation.RIV = 26; % Resource indication value
if C_RNTI<65535
pdcchConfig.RNTI = C_RNTI; % Radio network temporary identifier
end
pdcchConfig.PDCCHFormat = 2; % PDCCH format
pdcchConfig.PDCCHFormat = 0; % PDCCH format
ueConfig.RNTI = C_RNTI;
candidates = ltePDCCHSpace(enbConfig, pdcchConfig, {'bits', '1based'});
@ -153,7 +153,7 @@ for snr_idx=1:length(SNR_values)
%% Same with srsLTE
[rxCFI_srslte, pcfichRx2, pcfichSymbols2] = srslte_pcfich(enbConfigRx, subframe_rx);
decoded_cfi_srslte(snr_idx) = decoded_cfi_srslte(snr_idx) + (rxCFI_srslte == txCFI);
enbConfigRx.CFI = rxCFI_srslte;
enbConfigRx.CFI = rxCFI;
[found_srslte, pdcchBits2, pdcchRx2, pdcchSymbols2, hest2] = srslte_pdcch(enbConfigRx, ueConfig.RNTI, subframe_rx, hest, nest);
decoded_srslte(snr_idx) = decoded_srslte(snr_idx)+found_srslte;
end
@ -172,12 +172,6 @@ if (Npackets>1)
axis([min(SNR_values) max(SNR_values) 1/Npackets/10 1])
else
%scatter(real(pdcchSymbols2),imag(pdcchSymbols2))
%hold on
%scatter(real(pdcchSymbols),imag(pdcchSymbols))
%axis([-1.5 1.5 -1.5 1.5])
%hold off
n=min(length(pdcchSymbols),length(pdcchSymbols2));
subplot(2,1,1)
plot(abs(pdcchSymbols(1:n)-pdcchSymbols2(1:n)))
@ -185,6 +179,9 @@ else
subplot(2,1,2)
pdcchBitsTx(pdcchBitsTx==-1)=0;
plot(abs((pdcchBitsTx(1:n)>0.1)-(pdcchBits2(1:n)>0.1)))
subplot(1,1,1)
plot(1:180,real(hest(:,1,1,1)),1:180,real(hest2(1:180)))
disp(decoded)
disp(decoded_srslte)

View File

@ -53,7 +53,7 @@ char *mexutils_get_char_struct(const mxArray *ptr, const char *field_name) {
mxArray *p;
p = mxGetField(ptr, 0, field_name);
if (!p) {
return NULL;
return "";
}
if (mxIsCell(p)) {

View File

@ -40,10 +40,10 @@
#include "srslte/rf/rf_utils.h"
cell_search_cfg_t cell_detect_config = {
5000, // maximum number of frames to receive for MIB decoding
50, // maximum number of frames to receive for PSS correlation
10.0,
50.0
500,
50,
10,
0
};
/**********************************************************************
@ -153,6 +153,7 @@ int main(int argc, char **argv) {
int sfn_offset;
float rssi_utra=0,rssi=0, rsrp=0, rsrq=0, snr=0;
cf_t *ce[SRSLTE_MAX_PORTS];
float cfo = 0;
if (parse_args(&prog_args, argc, argv)) {
exit(-1);
@ -191,7 +192,7 @@ int main(int argc, char **argv) {
uint32_t ntrial=0;
do {
ret = rf_search_and_decode_mib(&rf, &cell_detect_config, prog_args.force_N_id_2, &cell);
ret = rf_search_and_decode_mib(&rf, &cell_detect_config, prog_args.force_N_id_2, &cell, &cfo);
if (ret < 0) {
fprintf(stderr, "Error searching for cell\n");
exit(-1);
@ -267,6 +268,9 @@ int main(int argc, char **argv) {
float rx_gain_offset = 0;
// Set initial CFO for ue_sync
srslte_ue_sync_set_cfo(&ue_sync, cfo);
/* Main loop */
while ((sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1) && !go_exit) {

View File

@ -55,12 +55,13 @@
int band = -1;
int earfcn_start=-1, earfcn_end = -1;
cell_search_cfg_t config = {
50, // maximum number of 5ms frames to capture for MIB decoding
50, // maximum number of 5ms frames to capture for PSS correlation
4.0, // early-stops cell detection if mean PSR is above this value
0 // 0 or negative to disable AGC
};
cell_search_cfg_t cell_detect_config = {
500,
50,
10,
0
};
struct cells {
srslte_cell_t cell;
@ -80,13 +81,12 @@ void usage(char *prog) {
printf("\t-s earfcn_start [Default All]\n");
printf("\t-e earfcn_end [Default All]\n");
printf("\t-n nof_frames_total [Default 100]\n");
printf("\t-t threshold [Default %.2f]\n",config.threshold);
printf("\t-v [set srslte_verbose to debug, default none]\n");
}
void parse_args(int argc, char **argv) {
int opt;
while ((opt = getopt(argc, argv, "agsendtvb")) != -1) {
while ((opt = getopt(argc, argv, "agsendvb")) != -1) {
switch(opt) {
case 'a':
rf_args = argv[optind];
@ -101,10 +101,7 @@ void parse_args(int argc, char **argv) {
earfcn_end = atoi(argv[optind]);
break;
case 'n':
config.max_frames_pss = atoi(argv[optind]);
break;
case 't':
config.threshold = atof(argv[optind]);
cell_detect_config.max_frames_pss = atoi(argv[optind]);
break;
case 'g':
rf_gain = atof(argv[optind]);
@ -159,7 +156,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
}
if (!config.init_agc) {
if (!cell_detect_config.init_agc) {
srslte_rf_set_rx_gain(&rf, rf_gain);
} else {
printf("Starting AGC thread...\n");
@ -203,19 +200,16 @@ int main(int argc, char **argv) {
bzero(found_cells, 3*sizeof(srslte_ue_cellsearch_result_t));
if (srslte_ue_cellsearch_init(&cs, srslte_rf_recv_wrapper, (void*) &rf)) {
if (srslte_ue_cellsearch_init(&cs, cell_detect_config.max_frames_pss, srslte_rf_recv_wrapper, (void*) &rf)) {
fprintf(stderr, "Error initiating UE cell detect\n");
exit(-1);
}
if (config.max_frames_pss) {
srslte_ue_cellsearch_set_nof_frames_to_scan(&cs, config.max_frames_pss);
if (cell_detect_config.max_frames_pss) {
srslte_ue_cellsearch_set_nof_valid_frames(&cs, cell_detect_config.nof_valid_pss_frames);
}
if (config.threshold) {
srslte_ue_cellsearch_set_threshold(&cs, config.threshold);
}
if (config.init_agc) {
srslte_ue_sync_start_agc(&cs.ue_sync, srslte_rf_set_rx_gain_wrapper, config.init_agc);
if (cell_detect_config.init_agc) {
srslte_ue_sync_start_agc(&cs.ue_sync, srslte_rf_set_rx_gain_wrapper, cell_detect_config.init_agc);
}
INFO("Setting sampling frequency %.2f MHz for PSS search\n", SRSLTE_CS_SAMP_FREQ/1000000);
@ -229,11 +223,11 @@ int main(int argc, char **argv) {
exit(-1);
} else if (n > 0) {
for (int i=0;i<3;i++) {
if (found_cells[i].psr > config.threshold/2) {
if (found_cells[i].psr > 10.0) {
srslte_cell_t cell;
cell.id = found_cells[i].cell_id;
cell.cp = found_cells[i].cp;
int ret = rf_mib_decoder(&rf, &config, &cell);
int ret = rf_mib_decoder(&rf, &cell_detect_config, &cell, NULL);
if (ret < 0) {
fprintf(stderr, "Error decoding MIB\n");
exit(-1);

View File

@ -39,15 +39,20 @@
#include "srslte/srslte.h"
// From srsLTE 1.2, AGC is disabled by default
//#define ENABLE_AGC_DEFAULT
#ifndef DISABLE_RF
#include "srslte/rf/rf.h"
#include "srslte/rf/rf_utils.h"
cell_search_cfg_t cell_detect_config = {
5000,
200, // nof_frames_total
10.0 // threshold
500,
50,
10,
0
};
#else
#warning Compiling pdsch_ue with no RF support
#endif
@ -110,7 +115,11 @@ void args_default(prog_args_t *args) {
args->file_offset_freq = 0;
args->rf_args = "";
args->rf_freq = -1.0;
#ifdef ENABLE_AGC_DEFAULT
args->rf_gain = -1.0;
#else
args->rf_gain = 50.0;
#endif
args->net_port = -1;
args->net_address = "127.0.0.1";
args->net_port_signal = -1;
@ -277,6 +286,7 @@ int main(int argc, char **argv) {
int n;
uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN];
int sfn_offset;
float cfo = 0;
parse_args(&prog_args, argc, argv);
@ -332,7 +342,7 @@ int main(int argc, char **argv) {
uint32_t ntrial=0;
do {
ret = rf_search_and_decode_mib(&rf, &cell_detect_config, prog_args.force_N_id_2, &cell);
ret = rf_search_and_decode_mib(&rf, &cell_detect_config, prog_args.force_N_id_2, &cell, &cfo);
if (ret < 0) {
fprintf(stderr, "Error searching for cell\n");
exit(-1);
@ -439,7 +449,10 @@ int main(int argc, char **argv) {
#endif
ue_sync.correct_cfo = !prog_args.disable_cfo;
// Set initial CFO for ue_sync
srslte_ue_sync_set_cfo(&ue_sync, cfo);
INFO("\nEntering main loop...\n\n", 0);
/* Main loop */
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
@ -478,7 +491,7 @@ int main(int argc, char **argv) {
decode_pdsch = true;
} else {
/* We are looking for SIB1 Blocks, search only in appropiate places */
if ((srslte_ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%2)==0)) {
if ((srslte_ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%8)==0)) {
decode_pdsch = true;
} else {
decode_pdsch = false;
@ -486,6 +499,7 @@ int main(int argc, char **argv) {
}
if (decode_pdsch) {
INFO("Attempting DL decode SFN=%d\n", sfn);
if (prog_args.rnti != SRSLTE_SIRNTI) {
n = srslte_ue_dl_decode(&ue_dl, &sf_buffer[prog_args.time_offset], data, srslte_ue_sync_get_sfidx(&ue_sync));
} else {
@ -495,7 +509,14 @@ int main(int argc, char **argv) {
n = srslte_ue_dl_decode_rnti_rv(&ue_dl, &sf_buffer[prog_args.time_offset], data,
srslte_ue_sync_get_sfidx(&ue_sync),
SRSLTE_SIRNTI, rv);
srslte_ue_dl_save_signal(&ue_dl, &ue_dl.softbuffer, sfn*10+srslte_ue_sync_get_sfidx(&ue_sync), rv);
/*
if (!n) {
printf("Saving signal...\n");
srslte_ue_dl_save_signal(&ue_dl, &ue_dl.softbuffer, sfn*10+srslte_ue_sync_get_sfidx(&ue_sync), rv);
exit(-1);
}
*/
}
if (n < 0) {
// fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);

View File

@ -45,37 +45,17 @@
*/
typedef struct {
cf_t *h_mod;
cf_t *tmp1;
cf_t *tmp2;
cf_t *tmp3;
float *y_mod;
float *z_real;
float *z_imag;
uint32_t max_frame_len;
} srslte_precoding_t;
SRSLTE_API int srslte_precoding_init(srslte_precoding_t *q,
uint32_t max_frame_len);
SRSLTE_API void srslte_precoding_free(srslte_precoding_t *q);
/* Generates the vector "y" from the input vector "x"
*/
SRSLTE_API int srslte_precoding_single(srslte_precoding_t *q,
cf_t *x,
SRSLTE_API int srslte_precoding_single(cf_t *x,
cf_t *y,
int nof_symbols);
SRSLTE_API int srslte_precoding_diversity(srslte_precoding_t *q,
cf_t *x[SRSLTE_MAX_LAYERS],
SRSLTE_API int srslte_precoding_diversity(cf_t *x[SRSLTE_MAX_LAYERS],
cf_t *y[SRSLTE_MAX_PORTS],
int nof_ports, int nof_symbols);
SRSLTE_API int srslte_precoding_type(srslte_precoding_t *q,
cf_t *x[SRSLTE_MAX_LAYERS],
SRSLTE_API int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS],
cf_t *y[SRSLTE_MAX_PORTS],
int nof_layers,
int nof_ports,
@ -90,16 +70,13 @@ SRSLTE_API int srslte_predecoding_single(cf_t *y,
int nof_symbols,
float noise_estimate);
SRSLTE_API int srslte_predecoding_diversity(srslte_precoding_t *q,
cf_t *y,
SRSLTE_API int srslte_predecoding_diversity(cf_t *y,
cf_t *h[SRSLTE_MAX_PORTS],
cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports,
int nof_symbols,
float noise_estimate);
int nof_symbols);
SRSLTE_API int srslte_predecoding_type(srslte_precoding_t *q,
cf_t *y,
SRSLTE_API int srslte_predecoding_type(cf_t *y,
cf_t *h[SRSLTE_MAX_PORTS],
cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports,

View File

@ -79,7 +79,6 @@ typedef struct SRSLTE_API {
srslte_viterbi_t decoder;
srslte_crc_t crc;
srslte_convcoder_t encoder;
srslte_precoding_t precoding;
bool search_all_ports;
} srslte_pbch_t;

View File

@ -73,8 +73,7 @@ typedef struct SRSLTE_API {
/* tx & rx objects */
srslte_modem_table_t mod;
srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME];
srslte_precoding_t precoding;
} srslte_pcfich_t;
SRSLTE_API int srslte_pcfich_init(srslte_pcfich_t *q,

View File

@ -80,8 +80,7 @@ typedef struct SRSLTE_API {
srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME];
srslte_viterbi_t decoder;
srslte_crc_t crc;
srslte_precoding_t precoding;
} srslte_pdcch_t;
SRSLTE_API int srslte_pdcch_init(srslte_pdcch_t *q,

View File

@ -67,8 +67,7 @@ typedef struct SRSLTE_API {
srslte_modem_table_t mod[4];
srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME];
srslte_precoding_t precoding;
srslte_sch_t dl_sch;
} srslte_pdsch_t;

View File

@ -80,8 +80,7 @@ typedef struct SRSLTE_API {
/* tx & rx objects */
srslte_modem_table_t mod;
srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME];
srslte_precoding_t precoding;
} srslte_phich_t;
SRSLTE_API int srslte_phich_init(srslte_phich_t *q,

View File

@ -73,8 +73,6 @@ typedef struct SRSLTE_API {
srslte_dft_precoding_t dft_precoding;
srslte_precoding_t equalizer;
/* buffers */
// void buffers are shared for tx and rx
cf_t *ce;

View File

@ -29,31 +29,34 @@
#include "srslte/rf/rf.h"
typedef struct SRSLTE_API {
uint32_t max_frames_pbch; // maximum number of 5ms frames to capture for MIB decoding
uint32_t max_frames_pss; // maximum number of 5ms frames to capture for PSS correlation
float threshold; // early-stops cell detection if mean PSR is above this value
uint32_t max_frames_pbch; // timeout in number of 5ms frames for MIB decoding
uint32_t max_frames_pss; // timeout in number of 5ms frames for synchronization
uint32_t nof_valid_pss_frames; // number of required synchronized frames
float init_agc; // 0 or negative to disable AGC
} cell_search_cfg_t;
SRSLTE_API int rf_rssi_scan(srslte_rf_t *rf,
float *freqs,
float *rssi,
int nof_bands,
double fs,
int nsamp);
float *freqs,
float *rssi,
int nof_bands,
double fs,
int nsamp);
SRSLTE_API int rf_mib_decoder(srslte_rf_t *rf,
cell_search_cfg_t *config,
srslte_cell_t *cell);
cell_search_cfg_t *config,
srslte_cell_t *cell,
float *cfo);
SRSLTE_API int rf_cell_search(srslte_rf_t *rf,
cell_search_cfg_t *config,
int force_N_id_2,
srslte_cell_t *cell);
cell_search_cfg_t *config,
int force_N_id_2,
srslte_cell_t *cell,
float *cfo);
SRSLTE_API int rf_search_and_decode_mib(srslte_rf_t *rf,
cell_search_cfg_t *config,
int force_N_id_2,
srslte_cell_t *cell);
cell_search_cfg_t *config,
int force_N_id_2,
srslte_cell_t *cell,
float *cfo);

View File

@ -68,7 +68,6 @@ typedef struct SRSLTE_API {
float threshold;
float peak_value;
float mean_peak_value;
uint32_t N_id_2;
uint32_t N_id_1;
uint32_t sf_idx;
@ -97,6 +96,13 @@ typedef struct SRSLTE_API {
}srslte_sync_t;
typedef enum {
SRSLTE_SYNC_FOUND = 1,
SRSLTE_SYNC_FOUND_NOSPACE = 2,
SRSLTE_SYNC_NOFOUND = 0,
SRSLTE_SYNC_ERROR = -1
} srslte_sync_find_ret_t;
SRSLTE_API int srslte_sync_init(srslte_sync_t *q,
uint32_t frame_size,
@ -108,10 +114,10 @@ SRSLTE_API void srslte_sync_free(srslte_sync_t *q);
SRSLTE_API void srslte_sync_reset(srslte_sync_t *q);
/* Finds a correlation peak in the input signal around position find_offset */
SRSLTE_API int srslte_sync_find(srslte_sync_t *q,
cf_t *input,
uint32_t find_offset,
uint32_t *peak_position);
SRSLTE_API srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q,
cf_t *input,
uint32_t find_offset,
uint32_t *peak_position);
/* Estimates the CP length */
SRSLTE_API srslte_cp_t srslte_sync_detect_cp(srslte_sync_t *q,

View File

@ -57,12 +57,6 @@
* TODO: Check also peak offset
*/
#define SRSLTE_CS_DEFAULT_MAXFRAMES_TOTAL 500
#define SRSLTE_CS_DEFAULT_MAXFRAMES_DETECTED 50
#define SRSLTE_CS_DEFAULT_NOFFRAMES_TOTAL 50
#define SRSLTE_CS_DEFAULT_NOFFRAMES_DETECTED 10
#define SRSLTE_CS_NOF_PRB 6
#define SRSLTE_CS_SAMP_FREQ 1920000.0
@ -80,8 +74,7 @@ typedef struct SRSLTE_API {
srslte_ue_sync_t ue_sync;
uint32_t max_frames;
uint32_t nof_frames_to_scan; // number of 5 ms frames to scan
float detect_threshold; // early-stops scan if mean PSR above this threshold
uint32_t nof_valid_frames; // number of 5 ms frames to scan
uint32_t *mode_ntimes;
uint8_t *mode_counted;
@ -89,16 +82,11 @@ typedef struct SRSLTE_API {
srslte_ue_cellsearch_result_t *candidates;
} srslte_ue_cellsearch_t;
SRSLTE_API int srslte_ue_cellsearch_init(srslte_ue_cellsearch_t *q,
uint32_t max_frames_total,
int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*),
void *stream_handler);
SRSLTE_API int srslte_ue_cellsearch_init_max(srslte_ue_cellsearch_t *q,
uint32_t max_frames_total,
int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*),
void *stream_handler);
SRSLTE_API void srslte_ue_cellsearch_free(srslte_ue_cellsearch_t *q);
SRSLTE_API int srslte_ue_cellsearch_scan_N_id_2(srslte_ue_cellsearch_t *q,
@ -109,11 +97,8 @@ SRSLTE_API int srslte_ue_cellsearch_scan(srslte_ue_cellsearch_t * q,
srslte_ue_cellsearch_result_t found_cells[3],
uint32_t *max_N_id_2);
SRSLTE_API int srslte_ue_cellsearch_set_nof_frames_to_scan(srslte_ue_cellsearch_t *q,
uint32_t nof_frames);
SRSLTE_API void srslte_ue_cellsearch_set_threshold(srslte_ue_cellsearch_t *q,
float threshold);
SRSLTE_API int srslte_ue_cellsearch_set_nof_valid_frames(srslte_ue_cellsearch_t *q,
uint32_t nof_frames);

View File

@ -41,8 +41,9 @@
#define ESTIMATE_NOISE_LS_PSS
#define DEFAULT_FILTER_LEN 3
//#define DEFAULT_FILTER_LEN 3
#ifdef DEFAULT_FILTER_LEN
static void set_default_filter(srslte_chest_dl_t *q, int filter_len) {
float fil[SRSLTE_CHEST_DL_MAX_SMOOTH_FIL_LEN];
@ -63,6 +64,7 @@ static void set_default_filter(srslte_chest_dl_t *q, int filter_len) {
srslte_chest_dl_set_smooth_filter(q, fil, filter_len);
}
#endif
/** 3GPP LTE Downlink channel estimator and equalizer.
* Estimates the channel in the resource elements transmitting references and interpolates for the rest
@ -124,9 +126,13 @@ int srslte_chest_dl_init(srslte_chest_dl_t *q, srslte_cell_t cell)
}
q->smooth_filter_len = 0;
#ifdef DEFAULT_FILTER_LEN
set_default_filter(q, DEFAULT_FILTER_LEN);
#else
float fil[3] = {0.1, 0.8, 0.1};
srslte_chest_dl_set_smooth_filter(q, fil, 3);
//set_default_filter(q, DEFAULT_FILTER_LEN);
#endif
q->cell = cell;
}

View File

@ -82,7 +82,6 @@ void parse_args(int argc, char **argv) {
int main(int argc, char **argv) {
srslte_chest_dl_t est;
srslte_precoding_t cheq;
cf_t *input = NULL, *ce = NULL, *h = NULL, *output = NULL;
int i, j, n_port=0, sf_idx=0, cid=0, num_re;
int ret = -1;
@ -129,8 +128,6 @@ int main(int argc, char **argv) {
cid = cell.id;
max_cid = cell.id;
}
srslte_precoding_init(&cheq, num_re);
while(cid <= max_cid) {
cell.id = cid;
@ -227,8 +224,6 @@ int main(int argc, char **argv) {
do_exit:
srslte_precoding_free(&cheq);
if (output) {
free(output);
}

View File

@ -57,7 +57,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
int i;
srslte_cell_t cell;
srslte_chest_dl_t chest;
srslte_precoding_t cheq;
cf_t *input_signal = NULL, *output_signal[SRSLTE_MAX_LAYERS];
cf_t *output_signal2 = NULL;
cf_t *ce[SRSLTE_MAX_PORTS];
@ -114,8 +114,6 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
}
output_signal2 = srslte_vec_malloc(nof_re * sizeof(cf_t));
srslte_precoding_init(&cheq, nof_re);
/* Create output values */
if (nlhs >= 1) {
plhs[0] = mxCreateDoubleMatrix(nof_re * nsubframes, cell.nof_ports, mxCOMPLEX);
@ -161,7 +159,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
if (cell.nof_ports == 1) {
srslte_predecoding_single(input_signal, ce[0], output_signal2, nof_re, srslte_chest_dl_get_noise_estimate(&chest));
} else {
srslte_predecoding_diversity(&cheq, input_signal, ce, output_signal, cell.nof_ports, nof_re, srslte_chest_dl_get_noise_estimate(&chest));
srslte_predecoding_diversity(input_signal, ce, output_signal, cell.nof_ports, nof_re);
srslte_layerdemap_diversity(output_signal, output_signal2, cell.nof_ports, nof_re/cell.nof_ports);
}
@ -205,7 +203,6 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
}
srslte_chest_dl_free(&chest);
srslte_precoding_free(&cheq);
return;
}

View File

@ -34,7 +34,6 @@
#include "srslte/common/phy_common.h"
#include "srslte/common/sequence.h"
#define FORCE_STANDARD_RATE
#ifdef FORCE_STANDARD_RATE
static bool use_standard_rates = true;
#else

View File

@ -204,11 +204,11 @@ int main(int argc, char **argv) {
for (j = 0; j < coded_length; j++) {
llr[j] = symbols[j] ? sqrt(2) : -sqrt(2);
}
srslte_ch_awgn_f(llr, llr, var[i], coded_length);
srslte_vec_quant_fuc(llr, llr_c, Gain, 127.5, 255, coded_length);
struct timeval t[3];
gettimeofday(&t[1], NULL);
int M = 1;
@ -264,8 +264,7 @@ int main(int argc, char **argv) {
free(data_rx);
if (snr_points == 1) {
int expected_errors = get_expected_errors(nof_frames,
seed, frame_length, tail_biting, ebno_db);
int expected_errors = get_expected_errors(nof_frames, seed, frame_length, tail_biting, ebno_db);
if (expected_errors == -1) {
fprintf(stderr, "Test parameters not defined in test_results.h\n");
exit(-1);

View File

@ -38,6 +38,7 @@
#include <xmmintrin.h>
#include <pmmintrin.h>
int srslte_predecoding_single_sse(cf_t *y, cf_t *h, cf_t *x, int nof_symbols, float noise_estimate);
int srslte_predecoding_diversity2_sse(cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS], int nof_symbols);
#endif
#ifdef LV_HAVE_AVX
@ -53,81 +54,6 @@ int srslte_predecoding_single_avx(cf_t *y, cf_t *h, cf_t *x, int nof_symbols, fl
*
**************************************************/
int srslte_precoding_init(srslte_precoding_t *q, uint32_t max_frame_len) {
if (q) {
bzero(q, sizeof(srslte_precoding_t));
q->h_mod = srslte_vec_malloc(sizeof(cf_t) * max_frame_len);
if (!q->h_mod) {
perror("malloc");
goto clean_exit;
}
q->tmp1 = srslte_vec_malloc(sizeof(cf_t) * max_frame_len);
if (!q->tmp1) {
perror("malloc");
goto clean_exit;
}
q->tmp2 = srslte_vec_malloc(sizeof(cf_t) * max_frame_len);
if (!q->tmp2) {
perror("malloc");
goto clean_exit;
}
q->tmp3 = srslte_vec_malloc(sizeof(cf_t) * max_frame_len);
if (!q->tmp3) {
perror("malloc");
goto clean_exit;
}
q->y_mod = srslte_vec_malloc(sizeof(float) * max_frame_len);
if (!q->y_mod) {
perror("malloc");
goto clean_exit;
}
q->z_real = srslte_vec_malloc(sizeof(float) * max_frame_len);
if (!q->z_real) {
perror("malloc");
goto clean_exit;
}
q->z_imag = srslte_vec_malloc(sizeof(float) * max_frame_len);
if (!q->z_imag) {
perror("malloc");
goto clean_exit;
}
q->max_frame_len = max_frame_len;
return SRSLTE_SUCCESS;
} else {
return SRSLTE_ERROR_INVALID_INPUTS;
}
clean_exit:
srslte_precoding_free(q);
return SRSLTE_ERROR;
}
void srslte_precoding_free(srslte_precoding_t *q) {
if (q->tmp1) {
free(q->tmp1);
}
if (q->tmp2) {
free(q->tmp2);
}
if (q->tmp3) {
free(q->tmp3);
}
if (q->h_mod) {
free(q->h_mod);
}
if (q->y_mod) {
free(q->y_mod);
}
if (q->z_real) {
free(q->z_real);
}
if (q->z_imag) {
free(q->z_imag);
}
bzero(q, sizeof(srslte_precoding_t));
}
#ifdef LV_HAVE_SSE
#define PROD(a,b) _mm_addsub_ps(_mm_mul_ps(a,_mm_moveldup_ps(b)),_mm_mul_ps(_mm_shuffle_ps(a,a,0xB1),_mm_movehdup_ps(b)))
@ -262,83 +188,37 @@ int srslte_predecoding_single(cf_t *y, cf_t *h, cf_t *x, int nof_symbols, float
#endif
}
/* ZF/MMSE STBC equalizer x=y(H'H+n0·I)^(-1)H' (ZF is n0=0.0)
*/
int srslte_predecoding_diversity(srslte_precoding_t *q, cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports, int nof_symbols, float noise_estimate)
/* C implementatino of the SFBC equalizer */
int srslte_predecoding_diversity_gen_(cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports, int nof_symbols, int symbol_start)
{
int i;
if (nof_ports == 2) {
#define new
#ifdef new
// reuse buffers
cf_t *r0 = q->tmp3;
cf_t *r1 = &q->tmp3[nof_symbols/2];
cf_t *h0 = q->h_mod;
cf_t *h1 = &q->h_mod[nof_symbols/2];
float *modhh = q->y_mod;
float *modh0 = q->z_real;
float *modh1 = q->z_imag;
// prepare buffers
for (i=0;i<nof_symbols/2;i++) {
h0[i] = h[0][2*i]; // h0
h1[i] = h[1][2*i+1]; // h1
r0[i] = y[2*i]; // r0
r1[i] = y[2*i+1]; // r1
}
// Compute common dividend and store in y_mod
srslte_vec_abs_square_cf(h0, modh0, nof_symbols/2);
srslte_vec_abs_square_cf(h1, modh1, nof_symbols/2);
srslte_vec_sum_fff(modh0, modh1, modhh, nof_symbols/2);
//if (noise_estimate > 0.0) {
// (H'H + n0)
//srslte_vec_sc_add_fff(modhh, noise_estimate, modhh, nof_symbols/2);
//}
srslte_vec_sc_prod_fff(modhh, 1/sqrt(2), modhh, nof_symbols/2);
// x[0] = r0·h0*/(|h0|+|h1|)+r1*·h1/(|h0|+|h1|)
srslte_vec_prod_conj_ccc(r0,h0,q->tmp1, nof_symbols/2);
srslte_vec_prod_conj_ccc(h1,r1,q->tmp2, nof_symbols/2);
srslte_vec_sum_ccc(q->tmp1, q->tmp2, x[0], nof_symbols/2);
srslte_vec_div_cfc(x[0], modhh, x[0], q->z_real, q->z_imag, nof_symbols/2);
cf_t h00, h01, h10, h11, r0, r1;
float hh;
// x[1] = r1·h0*/(|h0|+|h1|)-r0*·h1/(|h0|+|h1|)
srslte_vec_prod_conj_ccc(r1,h0,q->tmp1, nof_symbols/2);
srslte_vec_prod_conj_ccc(h1,r0,q->tmp2, nof_symbols/2);
srslte_vec_sub_ccc(q->tmp1, q->tmp2, x[1], nof_symbols/2);
srslte_vec_div_cfc(x[1], modhh, x[1], q->z_real, q->z_imag, nof_symbols/2);
#else
cf_t h0, h1, h2, h3, r0, r1, r2, r3;
float hh, hh02, hh13;
for (i = 0; i < nof_symbols / 2; i++) {
h0 = h[0][2 * i];
h1 = h[1][2 * i];
hh = crealf(h0) * crealf(h0) + cimagf(h0) * cimagf(h0)
+ crealf(h1) * crealf(h1) + cimagf(h1) * cimagf(h1) + noise_estimate;
for (i = symbol_start/2; i < nof_symbols / 2; i++) {
h00 = h[0][2 * i];
h01 = h[0][2 * i+1];
h10 = h[1][2 * i];
h11 = h[1][2 * i+1];
hh = crealf(h00) * crealf(h00) + cimagf(h00) * cimagf(h00)
+ crealf(h11) * crealf(h11) + cimagf(h11) * cimagf(h11);
r0 = y[2 * i];
r1 = y[2 * i + 1];
if (hh == 0) {
hh = 1e-2;
hh = 1e-4;
}
x[0][i] = (conjf(h0) * r0 + h1 * conjf(r1)) / hh * sqrt(2);
x[1][i] = (-h1 * conj(r0) + conj(h0) * r1) / hh * sqrt(2);
x[0][i] = (conjf(h00) * r0 + h11 * conjf(r1)) / hh * sqrt(2);
x[1][i] = (-h10 * conj(r0) + conj(h01) * r1) / hh * sqrt(2);
}
#endif
return i;
} else if (nof_ports == 4) {
cf_t h0, h1, h2, h3, r0, r1, r2, r3;
float hh02, hh13;
int m_ap = (nof_symbols % 4) ? ((nof_symbols - 2) / 4) : nof_symbols / 4;
for (i = 0; i < m_ap; i++) {
for (i = symbol_start; i < m_ap; i++) {
h0 = h[0][4 * i];
h1 = h[1][4 * i + 2];
h2 = h[2][4 * i];
@ -365,8 +245,88 @@ int srslte_predecoding_diversity(srslte_precoding_t *q, cf_t *y, cf_t *h[SRSLTE_
}
}
int srslte_predecoding_diversity_gen(cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports, int nof_symbols) {
return srslte_predecoding_diversity_gen_(y, h, x, nof_ports, nof_symbols, 0);
}
/* SSE implementation of the 2-port SFBC equalizer */
#ifdef LV_HAVE_SSE
int srslte_predecoding_diversity2_sse(cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS], int nof_symbols)
{
float *x0Ptr = (float*) x[0];
float *x1Ptr = (float*) x[1];
const float *h0Ptr = (const float*) h[0];
const float *h1Ptr = (const float*) h[1];
const float *yPtr = (const float*) y;
__m128 conjugator = _mm_setr_ps(0, -0.f, 0, -0.f);
__m128 sqrt2 = _mm_setr_ps(sqrt(2), sqrt(2), sqrt(2), sqrt(2));
__m128 h0Val_0, h0Val_1, h1Val_0, h1Val_1, h00, h00conj, h01, h01conj, h10, h11, hh, hhshuf, hhsum, hhadd;
__m128 r0Val, r1Val, r0, r1, r0conj, r1conj;
__m128 x0, x1;
for (int i=0;i<nof_symbols/4;i++) {
h0Val_0 = _mm_load_ps(h0Ptr); h0Ptr+=4; h0Val_1 = _mm_load_ps(h0Ptr); h0Ptr+=4;
h1Val_0 = _mm_load_ps(h1Ptr); h1Ptr+=4; h1Val_1 = _mm_load_ps(h1Ptr); h1Ptr+=4;
h00 = _mm_shuffle_ps(h0Val_0, h0Val_1, _MM_SHUFFLE(1, 0, 1, 0));
h01 = _mm_shuffle_ps(h0Val_0, h0Val_1, _MM_SHUFFLE(3, 2, 3, 2));
h10 = _mm_shuffle_ps(h1Val_0, h1Val_1, _MM_SHUFFLE(1, 0, 1, 0));
h11 = _mm_shuffle_ps(h1Val_0, h1Val_1, _MM_SHUFFLE(3, 2, 3, 2));
r0Val = _mm_load_ps(yPtr); yPtr+=4;
r1Val = _mm_load_ps(yPtr); yPtr+=4;
r0 = _mm_shuffle_ps(r0Val, r1Val, _MM_SHUFFLE(1, 0, 1, 0));
r1 = _mm_shuffle_ps(r0Val, r1Val, _MM_SHUFFLE(3, 2, 3, 2));
/* Compute channel gain */
hhadd = _mm_hadd_ps(_mm_mul_ps(h00, h00), _mm_mul_ps(h11, h11));
hhshuf = _mm_shuffle_ps(hhadd, hhadd, _MM_SHUFFLE(3, 1, 2, 0));
hhsum = _mm_hadd_ps(hhshuf, hhshuf);
hh = _mm_shuffle_ps(hhsum, hhsum, _MM_SHUFFLE(1, 1, 0, 0)); // h00^2+h11^2
// Conjugate value
h00conj = _mm_xor_ps(h00, conjugator);
h01conj = _mm_xor_ps(h01, conjugator);
r0conj = _mm_xor_ps(r0, conjugator);
r1conj = _mm_xor_ps(r1, conjugator);
// Multiply by channel matrix
x0 = _mm_add_ps(PROD(h00conj, r0), PROD(h11, r1conj));
x1 = _mm_sub_ps(PROD(h01conj, r1), PROD(h10, r0conj));
x0 = _mm_mul_ps(_mm_div_ps(x0, hh), sqrt2);
x1 = _mm_mul_ps(_mm_div_ps(x1, hh), sqrt2);
_mm_store_ps(x0Ptr, x0); x0Ptr+=4;
_mm_store_ps(x1Ptr, x1); x1Ptr+=4;
}
// Compute remaining symbols using generic implementation
srslte_predecoding_diversity_gen_(y, h, x, 2, nof_symbols, 4*(nof_symbols/4));
return nof_symbols;
}
#endif
int srslte_predecoding_diversity(cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports, int nof_symbols)
{
#ifdef LV_HAVE_SSE
if (nof_symbols > 32 && nof_ports == 2) {
return srslte_predecoding_diversity2_sse(y, h, x, nof_symbols);
} else {
return srslte_predecoding_diversity_gen(y, h, x, nof_ports, nof_symbols);
}
#else
return srslte_predecoding_diversity_gen(y, h, x, nof_ports, nof_symbols);
#endif
}
/* 36.211 v10.3.0 Section 6.3.4 */
int srslte_predecoding_type(srslte_precoding_t *q, cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS],
int srslte_predecoding_type(cf_t *y, cf_t *h[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS],
int nof_ports, int nof_layers, int nof_symbols, srslte_mimo_type_t type, float noise_estimate) {
if (nof_ports > SRSLTE_MAX_PORTS) {
@ -392,7 +352,7 @@ int srslte_predecoding_type(srslte_precoding_t *q, cf_t *y, cf_t *h[SRSLTE_MAX_P
break;
case SRSLTE_MIMO_TYPE_TX_DIVERSITY:
if (nof_ports == nof_layers) {
return srslte_predecoding_diversity(q, y, h, x, nof_ports, nof_symbols, noise_estimate);
return srslte_predecoding_diversity(y, h, x, nof_ports, nof_symbols);
} else {
fprintf(stderr,
"Error number of layers must equal number of ports in transmit diversity\n");
@ -417,11 +377,11 @@ int srslte_predecoding_type(srslte_precoding_t *q, cf_t *y, cf_t *h[SRSLTE_MAX_P
*
**************************************************/
int srslte_precoding_single(srslte_precoding_t *q, cf_t *x, cf_t *y, int nof_symbols) {
int srslte_precoding_single(cf_t *x, cf_t *y, int nof_symbols) {
memcpy(y, x, nof_symbols * sizeof(cf_t));
return nof_symbols;
}
int srslte_precoding_diversity(srslte_precoding_t *q, cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_ports,
int srslte_precoding_diversity(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_ports,
int nof_symbols) {
int i;
if (nof_ports == 2) {
@ -467,7 +427,7 @@ int srslte_precoding_diversity(srslte_precoding_t *q, cf_t *x[SRSLTE_MAX_LAYERS]
}
/* 36.211 v10.3.0 Section 6.3.4 */
int srslte_precoding_type(srslte_precoding_t *q, cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers,
int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers,
int nof_ports, int nof_symbols, srslte_mimo_type_t type) {
if (nof_ports > SRSLTE_MAX_PORTS) {
@ -484,7 +444,7 @@ int srslte_precoding_type(srslte_precoding_t *q, cf_t *x[SRSLTE_MAX_LAYERS], cf_
switch (type) {
case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA:
if (nof_ports == 1 && nof_layers == 1) {
return srslte_precoding_single(q, x[0], y[0], nof_symbols);
return srslte_precoding_single(x[0], y[0], nof_symbols);
} else {
fprintf(stderr,
"Number of ports and layers must be 1 for transmission on single antenna ports\n");
@ -493,7 +453,7 @@ int srslte_precoding_type(srslte_precoding_t *q, cf_t *x[SRSLTE_MAX_LAYERS], cf_
break;
case SRSLTE_MIMO_TYPE_TX_DIVERSITY:
if (nof_ports == nof_layers) {
return srslte_precoding_diversity(q, x, y, nof_ports, nof_symbols);
return srslte_precoding_diversity(x, y, nof_ports, nof_symbols);
} else {
fprintf(stderr,
"Error number of layers must equal number of ports in transmit diversity\n");

View File

@ -61,6 +61,8 @@ ADD_TEST(precoding_diversity2 precoding_test -n 1000 -m diversity -l 2 -p 2)
ADD_TEST(precoding_diversity4 precoding_test -n 1024 -m diversity -l 4 -p 4)
# MEX file for predecoding and layer demapping test
BuildMex(MEXNAME diversitydecode SOURCES diversitydecode_mex.c LIBRARIES srslte srslte_mex)

View File

@ -0,0 +1,116 @@
/**
*
* \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 <string.h>
#include "srslte/srslte.h"
#include "srslte/mex/mexutils.h"
/** MEX function to be called from MATLAB to test the predecoder
*/
#define INPUT prhs[0]
#define HEST prhs[1]
#define NOF_INPUTS 2
void help()
{
mexErrMsgTxt
("[output] = srslte_predecoder(input, hest, nest)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
cf_t *input = NULL;
cf_t *hest = NULL;
cf_t *output = NULL;
uint32_t nof_symbols = 0;
if (nrhs < NOF_INPUTS) {
help();
return;
}
// Read input symbols
nof_symbols = mexutils_read_cf(INPUT, &input);
if (nof_symbols < 0) {
mexErrMsgTxt("Error reading input\n");
return;
}
// Read channel estimates
uint32_t nof_symbols2 = mexutils_read_cf(HEST, &hest);
if (nof_symbols < 0) {
mexErrMsgTxt("Error reading hest\n");
return;
}
if ((nof_symbols2 % nof_symbols) != 0) {
mexErrMsgTxt("Hest size must be multiple of input size\n");
return;
}
// Calculate number of ports
uint32_t nof_ports = nof_symbols2/nof_symbols;
cf_t *x[8];
cf_t *h[4];
/* Allocate memory */
output = srslte_vec_malloc(sizeof(cf_t)*nof_symbols);
int i;
for (i = 0; i < nof_ports; i++) {
x[i] = srslte_vec_malloc(sizeof(cf_t)*nof_symbols);
h[i] = &hest[i*nof_symbols];
}
for (;i<8;i++) {
x[i] = NULL;
}
for (i=nof_ports;i<4;i++) {
h[i] = NULL;
}
srslte_predecoding_diversity(input, h, x, nof_ports, nof_symbols);
srslte_layerdemap_diversity(x, output, nof_ports, nof_symbols / nof_ports);
if (nlhs >= 1) {
mexutils_write_cf(output, &plhs[0], nof_symbols, 1);
}
if (input) {
free(input);
}
if (output) {
free(output);
}
for (i=0;i<8;i++) {
if (x[i]) {
free(x[i]);
}
}
return;
}

View File

@ -81,7 +81,6 @@ int main(int argc, char **argv) {
cf_t *x[SRSLTE_MAX_LAYERS], *r[SRSLTE_MAX_PORTS], *y[SRSLTE_MAX_PORTS], *h[SRSLTE_MAX_PORTS],
*xr[SRSLTE_MAX_LAYERS];
srslte_mimo_type_t type;
srslte_precoding_t precoding;
parse_args(argc, argv);
@ -135,13 +134,8 @@ int main(int argc, char **argv) {
}
}
if (srslte_precoding_init(&precoding, nof_symbols * nof_layers)) {
fprintf(stderr, "Error initializing precoding\n");
exit(-1);
}
/* precoding */
if (srslte_precoding_type(&precoding, x, y, nof_layers, nof_ports, nof_symbols, type) < 0) {
if (srslte_precoding_type(x, y, nof_layers, nof_ports, nof_symbols, type) < 0) {
fprintf(stderr, "Error layer mapper encoder\n");
exit(-1);
}
@ -152,7 +146,7 @@ int main(int argc, char **argv) {
h[i][nof_layers*j] = (float) rand()/RAND_MAX+((float) rand()/RAND_MAX)*_Complex_I;
// assume the channel is time-invariant in nlayer consecutive symbols
for (int k=0;k<nof_layers;k++) {
h[i][nof_layers*j+k] = h[i][nof_layers*j];
h[i][nof_layers*j+k] = h[i][nof_layers*j];
}
}
}
@ -168,11 +162,11 @@ int main(int argc, char **argv) {
r[0][j] += y[i][j] * h[i][j];
}
}
/* predecoding / equalization */
struct timeval t[3];
gettimeofday(&t[1], NULL);
if (srslte_predecoding_type(&precoding, r[0], h, xr, nof_ports, nof_layers,
if (srslte_predecoding_type(r[0], h, xr, nof_ports, nof_layers,
nof_symbols * nof_layers, type, 0) < 0) {
fprintf(stderr, "Error layer mapper encoder\n");
exit(-1);
@ -204,8 +198,6 @@ int main(int argc, char **argv) {
free(r[0]);
srslte_precoding_free(&precoding);
printf("Ok\n");
exit(0);
}

View File

@ -151,10 +151,6 @@ int srslte_pbch_init(srslte_pbch_t *q, srslte_cell_t cell) {
q->cell = cell;
q->nof_symbols = (SRSLTE_CP_ISNORM(q->cell.cp)) ? PBCH_RE_CP_NORM : PBCH_RE_CP_EXT;
if (srslte_precoding_init(&q->precoding, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
}
if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) {
goto clean;
}
@ -239,7 +235,6 @@ void srslte_pbch_free(srslte_pbch_t *q) {
if (q->rm_b) {
free(q->rm_b);
}
srslte_precoding_free(&q->precoding);
srslte_sequence_free(&q->seq);
srslte_modem_table_free(&q->mod);
srslte_viterbi_free(&q->decoder);
@ -373,8 +368,6 @@ int decode_frame(srslte_pbch_t *q, uint32_t src, uint32_t dst, uint32_t n,
uint32_t nof_bits, uint32_t nof_ports) {
int j;
DEBUG("Trying to decode PBCH %d bits, %d ports, src: %d, dst: %d, n=%d\n", nof_bits, nof_ports, src, dst, n);
memcpy(&q->temp[dst * nof_bits], &q->llr[src * nof_bits],
n * nof_bits * sizeof(float));
@ -392,6 +385,9 @@ int decode_frame(srslte_pbch_t *q, uint32_t src, uint32_t dst, uint32_t n,
/* unrate matching */
srslte_rm_conv_rx(q->temp, 4 * nof_bits, q->rm_f, SRSLTE_BCH_ENCODED_LEN);
/* Normalize LLR */
srslte_vec_sc_prod_fff(q->rm_f, 1.0/((float) 2*n), q->rm_f, SRSLTE_BCH_ENCODED_LEN);
/* decode */
srslte_viterbi_decode_f(&q->decoder, q->rm_f, q->data, SRSLTE_BCH_PAYLOADCRC_LEN);
@ -471,8 +467,8 @@ int srslte_pbch_decode(srslte_pbch_t *q, cf_t *slot1_symbols, cf_t *ce_slot1[SRS
/* no need for layer demapping */
srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, q->nof_symbols, noise_estimate);
} else {
srslte_predecoding_diversity(&q->precoding, q->symbols[0], q->ce, x, nant,
q->nof_symbols, noise_estimate);
srslte_predecoding_diversity(q->symbols[0], q->ce, x, nant,
q->nof_symbols);
srslte_layerdemap_diversity(x, q->d, nant, q->nof_symbols / nant);
}
@ -482,17 +478,14 @@ int srslte_pbch_decode(srslte_pbch_t *q, cf_t *slot1_symbols, cf_t *ce_slot1[SRS
/* We don't know where the 40 ms begin, so we try all combinations. E.g. if we received
* 4 frames, try 1,2,3,4 individually, 12, 23, 34 in pairs, 123, 234 and finally 1234.
* We know they are ordered.
*
* FIXME: There are unnecessary checks because 2,3,4 have already been processed in the previous
* calls.
*/
for (nb = 0; nb < q->frame_idx && !ret; nb++) {
for (dst = 0; (dst < 4 - nb) && !ret; dst++) {
for (src = 0; src < q->frame_idx - nb && !ret; src++) {
for (nb = 0; nb < q->frame_idx; nb++) {
for (dst = 0; (dst < 4 - nb); dst++) {
for (src = 0; src < q->frame_idx - nb; src++) {
ret = decode_frame(q, src, dst, nb + 1, nof_bits, nant);
if (ret == 1) {
if (sfn_offset) {
*sfn_offset = (int) dst - src;
*sfn_offset = (int) dst - src + q->frame_idx - 1;
}
if (nof_tx_ports) {
*nof_tx_ports = nant;
@ -500,13 +493,15 @@ int srslte_pbch_decode(srslte_pbch_t *q, cf_t *slot1_symbols, cf_t *ce_slot1[SRS
if (bch_payload) {
memcpy(bch_payload, q->data, sizeof(uint8_t) * SRSLTE_BCH_PAYLOAD_LEN);
}
INFO("Decoded PBCH: src=%d, dst=%d, nb=%d, sfn_offset=%d\n", src, dst, nb+1, (int) dst - src + q->frame_idx - 1);
return 1;
}
}
}
}
}
nant++;
} while(nant <= q->cell.nof_ports && !ret);
} while(nant <= q->cell.nof_ports);
/* If not found, make room for the next packet of radio frame symbols */
if (q->frame_idx == 4) {
@ -561,7 +556,7 @@ int srslte_pbch_encode(srslte_pbch_t *q, uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_
/* layer mapping & precoding */
if (q->cell.nof_ports > 1) {
srslte_layermap_diversity(q->d, x, q->cell.nof_ports, q->nof_symbols);
srslte_precoding_diversity(&q->precoding, x, q->symbols, q->cell.nof_ports,
srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports,
q->nof_symbols / q->cell.nof_ports);
} else {
memcpy(q->symbols[0], q->d, q->nof_symbols * sizeof(cf_t));

View File

@ -74,10 +74,6 @@ int srslte_pcfich_init(srslte_pcfich_t *q, srslte_regs_t *regs, srslte_cell_t ce
q->regs = regs;
q->nof_symbols = PCFICH_RE;
if (srslte_precoding_init(&q->precoding, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
}
if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) {
goto clean;
}
@ -110,7 +106,6 @@ void srslte_pcfich_free(srslte_pcfich_t *q) {
srslte_sequence_free(&q->seq[ns]);
}
srslte_modem_table_free(&q->mod);
srslte_precoding_free(&q->precoding);
bzero(q, sizeof(srslte_pcfich_t));
}
@ -194,8 +189,8 @@ int srslte_pcfich_decode(srslte_pcfich_t *q, cf_t *slot_symbols, cf_t *ce[SRSLTE
/* no need for layer demapping */
srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, q->nof_symbols, noise_estimate);
} else {
srslte_predecoding_diversity(&q->precoding, q->symbols[0], ce_precoding, x,
q->cell.nof_ports, q->nof_symbols, noise_estimate);
srslte_predecoding_diversity(q->symbols[0], ce_precoding, x,
q->cell.nof_ports, q->nof_symbols);
srslte_layerdemap_diversity(x, q->d, q->cell.nof_ports,
q->nof_symbols / q->cell.nof_ports);
}
@ -253,7 +248,7 @@ int srslte_pcfich_encode(srslte_pcfich_t *q, uint32_t cfi, cf_t *slot_symbols[SR
/* layer mapping & precoding */
if (q->cell.nof_ports > 1) {
srslte_layermap_diversity(q->d, x, q->cell.nof_ports, q->nof_symbols);
srslte_precoding_diversity(&q->precoding, x, symbols_precoding, q->cell.nof_ports,
srslte_precoding_diversity(x, symbols_precoding, q->cell.nof_ports,
q->nof_symbols / q->cell.nof_ports);
} else {
memcpy(q->symbols[0], q->d, q->nof_symbols * sizeof(cf_t));

View File

@ -67,10 +67,6 @@ int srslte_pdcch_init(srslte_pdcch_t *q, srslte_regs_t *regs, srslte_cell_t cell
q->cell = cell;
q->regs = regs;
if (srslte_precoding_init(&q->precoding, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
}
/* Allocate memory for the maximum number of PDCCH bits (CFI=3) */
q->max_bits = (srslte_regs_pdcch_nregs(q->regs, 3) / 9) * 72;
@ -166,7 +162,6 @@ void srslte_pdcch_free(srslte_pdcch_t *q) {
srslte_sequence_free(&q->seq[i]);
}
srslte_precoding_free(&q->precoding);
srslte_modem_table_free(&q->mod);
srslte_viterbi_free(&q->decoder);
@ -280,7 +275,10 @@ static int dci_decode(srslte_pdcch_t *q, float *e, uint8_t *data, uint32_t E, ui
/* unrate matching */
srslte_rm_conv_rx(e, E, q->rm_f, 3 * (nof_bits + 16));
/* Normalize LLR */
srslte_vec_sc_prod_fff(q->rm_f, (float) 3 * nof_bits/E, q->rm_f, 3*(nof_bits+16));
/* viterbi decoder */
srslte_viterbi_decode_f(&q->decoder, q->rm_f, data, nof_bits + 16);
@ -406,7 +404,7 @@ int srslte_pdcch_extract_llr(srslte_pdcch_t *q, cf_t *sf_symbols, cf_t *ce[SRSLT
/* no need for layer demapping */
srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, nof_symbols, noise_estimate);
} else {
srslte_predecoding_diversity(&q->precoding, q->symbols[0], q->ce, x, q->cell.nof_ports, nof_symbols, noise_estimate);
srslte_predecoding_diversity(q->symbols[0], q->ce, x, q->cell.nof_ports, nof_symbols);
srslte_layerdemap_diversity(x, q->d, q->cell.nof_ports, nof_symbols / q->cell.nof_ports);
}
@ -532,7 +530,7 @@ int srslte_pdcch_encode(srslte_pdcch_t *q, srslte_dci_msg_t *msg, srslte_dci_loc
/* layer mapping & precoding */
if (q->cell.nof_ports > 1) {
srslte_layermap_diversity(q->d, x, q->cell.nof_ports, nof_symbols);
srslte_precoding_diversity(&q->precoding, x, q->symbols, q->cell.nof_ports, nof_symbols / q->cell.nof_ports);
srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports, nof_symbols / q->cell.nof_ports);
} else {
memcpy(q->symbols[0], q->d, nof_symbols * sizeof(cf_t));
}

View File

@ -216,11 +216,6 @@ int srslte_pdsch_init(srslte_pdsch_t *q, srslte_cell_t cell) {
INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
q->cell.nof_prb, q->max_re);
if (srslte_precoding_init(&q->precoding, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
goto clean;
}
for (i = 0; i < 4; i++) {
if (srslte_modem_table_lte(&q->mod[i], modulations[i])) {
goto clean;
@ -295,7 +290,7 @@ void srslte_pdsch_free(srslte_pdsch_t *q) {
for (i = 0; i < 4; i++) {
srslte_modem_table_free(&q->mod[i]);
}
srslte_precoding_free(&q->precoding);
srslte_sch_free(&q->dl_sch);
bzero(q, sizeof(srslte_pdsch_t));
@ -409,8 +404,8 @@ int srslte_pdsch_decode_rnti(srslte_pdsch_t *q,
/* no need for layer demapping */
srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, cfg->nbits.nof_re, noise_estimate);
} else {
srslte_predecoding_diversity(&q->precoding, q->symbols[0], q->ce, x, q->cell.nof_ports,
cfg->nbits.nof_re, noise_estimate);
srslte_predecoding_diversity(q->symbols[0], q->ce, x, q->cell.nof_ports,
cfg->nbits.nof_re);
srslte_layerdemap_diversity(x, q->d, q->cell.nof_ports,
cfg->nbits.nof_re / q->cell.nof_ports);
}
@ -541,7 +536,7 @@ int srslte_pdsch_encode_rnti(srslte_pdsch_t *q,
/* TODO: only diversity supported */
if (q->cell.nof_ports > 1) {
srslte_layermap_diversity(q->d, x, q->cell.nof_ports, cfg->nbits.nof_re);
srslte_precoding_diversity(&q->precoding, x, q->symbols, q->cell.nof_ports,
srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports,
cfg->nbits.nof_re / q->cell.nof_ports);
} else {
memcpy(q->symbols[0], q->d, cfg->nbits.nof_re * sizeof(cf_t));

View File

@ -82,10 +82,6 @@ int srslte_phich_init(srslte_phich_t *q, srslte_regs_t *regs, srslte_cell_t cell
q->cell = cell;
q->regs = regs;
if (srslte_precoding_init(&q->precoding, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
}
if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_BPSK)) {
goto clean;
}
@ -109,7 +105,6 @@ void srslte_phich_free(srslte_phich_t *q) {
srslte_sequence_free(&q->seq[ns]);
}
srslte_modem_table_free(&q->mod);
srslte_precoding_free(&q->precoding);
bzero(q, sizeof(srslte_phich_t));
@ -217,8 +212,8 @@ int srslte_phich_decode(srslte_phich_t *q, cf_t *slot_symbols, cf_t *ce[SRSLTE_M
/* no need for layer demapping */
srslte_predecoding_single(q->symbols[0], q->ce[0], q->d0, SRSLTE_PHICH_MAX_NSYMB, noise_estimate);
} else {
srslte_predecoding_diversity(&q->precoding, q->symbols[0], ce_precoding, x,
q->cell.nof_ports, SRSLTE_PHICH_MAX_NSYMB, noise_estimate);
srslte_predecoding_diversity(q->symbols[0], ce_precoding, x,
q->cell.nof_ports, SRSLTE_PHICH_MAX_NSYMB);
srslte_layerdemap_diversity(x, q->d0, q->cell.nof_ports,
SRSLTE_PHICH_MAX_NSYMB / q->cell.nof_ports);
}
@ -382,7 +377,7 @@ int srslte_phich_encode(srslte_phich_t *q, uint8_t ack, uint32_t ngroup, uint32_
/* layer mapping & precoding */
if (q->cell.nof_ports > 1) {
srslte_layermap_diversity(q->d0, x, q->cell.nof_ports, SRSLTE_PHICH_MAX_NSYMB);
srslte_precoding_diversity(&q->precoding, x, symbols_precoding, q->cell.nof_ports,
srslte_precoding_diversity(x, symbols_precoding, q->cell.nof_ports,
SRSLTE_PHICH_MAX_NSYMB / q->cell.nof_ports);
/**FIXME: According to 6.9.2, Precoding for 4 tx ports is different! */
} else {

View File

@ -218,12 +218,6 @@ int srslte_pusch_init(srslte_pusch_t *q, srslte_cell_t cell) {
goto clean;
}
/* This is for equalization at receiver */
if (srslte_precoding_init(&q->equalizer, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
goto clean;
}
q->rnti_is_set = false;
// Allocate floats for reception (LLRs). Buffer casted to uint8_t for transmission
@ -281,8 +275,6 @@ void srslte_pusch_free(srslte_pusch_t *q) {
srslte_dft_precoding_free(&q->dft_precoding);
srslte_precoding_free(&q->equalizer);
for (i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) {
srslte_sequence_free(&q->seq[i]);
}

View File

@ -50,9 +50,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
srslte_pbch_t pbch;
srslte_chest_dl_t chest;
srslte_ofdm_t ofdm_rx;
cf_t *input_fft;
cf_t *input_fft = NULL;
cf_t *ce[SRSLTE_MAX_PORTS], *ce_slot[SRSLTE_MAX_PORTS];
srslte_verbose = SRSLTE_VERBOSE_DEBUG;
if (nrhs < NOF_INPUTS) {
help();
return;
@ -62,7 +64,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
help();
return;
}
// Allocate memory
for (i=0;i<SRSLTE_MAX_PORTS;i++) {
ce[i] = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
@ -78,59 +80,80 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
return;
}
uint32_t nof_ports = cell.nof_ports;
//cell.nof_ports = 0;
if (srslte_pbch_init(&pbch, cell)) {
fprintf(stderr, "Error initiating PBCH\n");
return;
}
cell.nof_ports = nof_ports;
// Read input signal
cf_t *input_signal = NULL;
int insignal_len = mexutils_read_cf(INPUT, &input_signal);
if (insignal_len < 0) {
mexErrMsgTxt("Error reading input signal\n");
return;
}
if (insignal_len == SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)) {
input_fft = input_signal;
} else {
input_fft = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
srslte_ofdm_rx_sf(&ofdm_rx, input_signal, input_fft);
free(input_signal);
}
if (nrhs > NOF_INPUTS) {
cf_t *cearray = NULL;
mexutils_read_cf(prhs[NOF_INPUTS], &cearray);
cf_t *cearray_ptr = cearray;
for (i=0;i<cell.nof_ports;i++) {
for (int j=0;j<SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp);j++) {
ce[i][j] = *cearray_ptr;
cearray_ptr++;
}
}
if (cearray) {
free(cearray);
}
} else {
srslte_chest_dl_estimate(&chest, input_fft, ce, 0);
}
float noise_power;
if (nrhs > NOF_INPUTS + 1) {
noise_power = mxGetScalar(prhs[NOF_INPUTS+1]);
} else if (nrhs > NOF_INPUTS) {
noise_power = 0;
} else {
noise_power = srslte_chest_dl_get_noise_estimate(&chest);
}
for (int i=0;i<SRSLTE_MAX_PORTS;i++) {
ce_slot[i] = &ce[i][SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp)];
}
uint32_t nof_ports;
int n = srslte_pbch_decode(&pbch, &input_fft[SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp)],
ce_slot, noise_power,
NULL, &nof_ports, NULL);
int nof_subframes=1;
if (mexutils_isCell(INPUT)) {
nof_subframes = mexutils_getLength(INPUT);
mexPrintf("input is cell. %d subframes\n", nof_subframes);
}
nof_ports = 0;
int sfn_offset = 0;
int n = -1;
for (int s=0;s<nof_subframes;s++) {
mxArray *tmp = (mxArray*) INPUT;
if (mexutils_isCell(INPUT)) {
tmp = mexutils_getCellArray(INPUT, s);
}
// Read input signal
cf_t *input_signal = NULL;
int insignal_len = mexutils_read_cf(tmp, &input_signal);
if (insignal_len < 0) {
mexErrMsgTxt("Error reading input signal\n");
return;
}
if (insignal_len == SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)) {
input_fft = input_signal;
} else {
input_fft = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
srslte_ofdm_rx_sf(&ofdm_rx, input_signal, input_fft);
free(input_signal);
}
if (nrhs > NOF_INPUTS) {
cf_t *cearray = NULL;
mexutils_read_cf(prhs[NOF_INPUTS], &cearray);
cf_t *cearray_ptr = cearray;
for (i=0;i<cell.nof_ports;i++) {
for (int j=0;j<SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp);j++) {
ce[i][j] = *cearray_ptr;
cearray_ptr++;
}
}
if (cearray) {
free(cearray);
}
} else {
srslte_chest_dl_estimate(&chest, input_fft, ce, 0);
}
float noise_power;
if (nrhs > NOF_INPUTS + 1) {
noise_power = mxGetScalar(prhs[NOF_INPUTS+1]);
} else if (nrhs > NOF_INPUTS) {
noise_power = 0;
} else {
noise_power = srslte_chest_dl_get_noise_estimate(&chest);
}
n = srslte_pbch_decode(&pbch, &input_fft[SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp)],
ce_slot, noise_power,
NULL, &nof_ports, &sfn_offset);
}
if (nlhs >= 1) {
if (n == 1) {
@ -143,7 +166,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
mexutils_write_cf(pbch.d, &plhs[1], pbch.nof_symbols, 1);
}
if (nlhs >= 3) {
mexutils_write_f(pbch.llr, &plhs[2], 2*pbch.nof_symbols, 1);
mexutils_write_f(pbch.temp, &plhs[2], 4*2*pbch.nof_symbols, 1);
}
if (nlhs >= 4) {
mexutils_write_cf(ce[0], &plhs[3], SRSLTE_SF_LEN_RE(cell.nof_prb,cell.cp)/14, 14);
@ -157,6 +180,12 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
if (nlhs >= 7) {
mexutils_write_cf(pbch.ce[0], &plhs[6], pbch.nof_symbols, 1);
}
if (nlhs >= 8) {
plhs[7] = mxCreateDoubleScalar(sfn_offset);
}
if (nlhs >= 9) {
mexutils_write_f(pbch.rm_f, &plhs[8], 120, 1);
}
srslte_chest_dl_free(&chest);
srslte_ofdm_rx_free(&ofdm_rx);
@ -165,8 +194,9 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
for (i=0;i<cell.nof_ports;i++) {
free(ce[i]);
}
free(input_fft);
if(input_fft) {
free(input_fft);
}
return;
}

View File

@ -152,6 +152,9 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
if (nlhs >= 3) {
mexutils_write_cf(pcfich.symbols[0], &plhs[2], 16, 1);
}
if (nlhs >= 4) {
mexutils_write_cf(ce[0], &plhs[3], SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp), 1);
}
srslte_chest_dl_free(&chest);
srslte_ofdm_rx_free(&ofdm_rx);

View File

@ -95,7 +95,7 @@ double srslte_rf_set_rx_gain_th_wrapper(void *h, double f) {
/** This function is simply a wrapper to the ue_cell_search module for rf devices
* Return 1 if the MIB is decoded, 0 if not or -1 on error.
*/
int rf_mib_decoder(srslte_rf_t *rf, cell_search_cfg_t *config, srslte_cell_t *cell) {
int rf_mib_decoder(srslte_rf_t *rf, cell_search_cfg_t *config, srslte_cell_t *cell, float *cfo) {
int ret = SRSLTE_ERROR;
srslte_ue_mib_sync_t ue_mib;
uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN];
@ -116,6 +116,11 @@ int rf_mib_decoder(srslte_rf_t *rf, cell_search_cfg_t *config, srslte_cell_t *ce
INFO("Starting receiver...\n", 0);
srslte_rf_start_rx_stream(rf);
// Set CFO if available
if (cfo) {
srslte_ue_sync_set_cfo(&ue_mib.ue_sync, *cfo);
}
/* Find and decody MIB */
ret = srslte_ue_mib_sync_decode(&ue_mib, config->max_frames_pss, bch_payload, &cell->nof_ports, NULL);
if (ret < 0) {
@ -131,6 +136,11 @@ int rf_mib_decoder(srslte_rf_t *rf, cell_search_cfg_t *config, srslte_cell_t *ce
config->init_agc = srslte_agc_get_gain(&ue_mib.ue_sync.agc);
}
// Save CFO
if (cfo) {
*cfo = srslte_ue_sync_get_cfo(&ue_mib.ue_sync);
}
clean_exit:
srslte_rf_stop_rx_stream(rf);
@ -142,7 +152,7 @@ clean_exit:
/** This function is simply a wrapper to the ue_cell_search module for rf devices
*/
int rf_cell_search(srslte_rf_t *rf, cell_search_cfg_t *config,
int force_N_id_2, srslte_cell_t *cell)
int force_N_id_2, srslte_cell_t *cell, float *cfo)
{
int ret = SRSLTE_ERROR;
srslte_ue_cellsearch_t cs;
@ -150,18 +160,14 @@ int rf_cell_search(srslte_rf_t *rf, cell_search_cfg_t *config,
bzero(found_cells, 3*sizeof(srslte_ue_cellsearch_result_t));
if (srslte_ue_cellsearch_init(&cs, srslte_rf_recv_wrapper_cs, (void*) rf)) {
if (srslte_ue_cellsearch_init(&cs, config->max_frames_pss, srslte_rf_recv_wrapper_cs, (void*) rf)) {
fprintf(stderr, "Error initiating UE cell detect\n");
return SRSLTE_ERROR;
}
if (config->max_frames_pss) {
srslte_ue_cellsearch_set_nof_frames_to_scan(&cs, config->max_frames_pss);
if (config->nof_valid_pss_frames) {
srslte_ue_cellsearch_set_nof_valid_frames(&cs, config->nof_valid_pss_frames);
}
if (config->threshold) {
srslte_ue_cellsearch_set_threshold(&cs, config->threshold);
}
if (config->init_agc > 0) {
srslte_ue_sync_start_agc(&cs.ue_sync, srslte_rf_set_rx_gain_th_wrapper, config->init_agc);
}
@ -206,6 +212,11 @@ int rf_cell_search(srslte_rf_t *rf, cell_search_cfg_t *config,
cell->cp = found_cells[max_peak_cell].cp;
}
// Save CFO
if (cfo) {
*cfo = found_cells[max_peak_cell].cfo;
}
// Save AGC value for MIB decoding
if (config->init_agc > 0) {
config->init_agc = srslte_agc_get_gain(&cs.ue_sync.agc);
@ -223,15 +234,15 @@ int rf_cell_search(srslte_rf_t *rf, cell_search_cfg_t *config,
* 0 if no cell was found or MIB could not be decoded,
* -1 on error
*/
int rf_search_and_decode_mib(srslte_rf_t *rf, cell_search_cfg_t *config, int force_N_id_2, srslte_cell_t *cell)
int rf_search_and_decode_mib(srslte_rf_t *rf, cell_search_cfg_t *config, int force_N_id_2, srslte_cell_t *cell, float *cfo)
{
int ret = SRSLTE_ERROR;
printf("Searching for cell...\n");
ret = rf_cell_search(rf, config, force_N_id_2, cell);
ret = rf_cell_search(rf, config, force_N_id_2, cell, cfo);
if (ret > 0) {
printf("Decoding PBCH for cell %d (N_id_2=%d)\n", cell->id, cell->id%3);
ret = rf_mib_decoder(rf, config, cell);
ret = rf_mib_decoder(rf, config, cell, cfo);
if (ret < 0) {
fprintf(stderr, "Could not decode PBCH from CELL ID %d\n", cell->id);
return SRSLTE_ERROR;

View File

@ -51,21 +51,20 @@ void srslte_cp_synch_free(srslte_cp_synch_t *q)
uint32_t srslte_cp_synch(srslte_cp_synch_t *q, cf_t *input, uint32_t max_offset, uint32_t nof_symbols, uint32_t cp_len)
{
if (max_offset <= q->symbol_sz) {
for (int i=0;i<max_offset;i++) {
q->corr[i] = 0;
cf_t *inputPtr = input;
for (int n=0;n<nof_symbols;n++) {
uint32_t cplen = (n%7)?cp_len:cp_len+1;
q->corr[i] += srslte_vec_dot_prod_conj_ccc(&inputPtr[i], &inputPtr[i+q->symbol_sz], cplen)/nof_symbols;
inputPtr += q->symbol_sz+cplen;
}
}
uint32_t max_idx = srslte_vec_max_abs_ci(q->corr, max_offset);
return max_idx;
} else {
return 0;
if (max_offset > q->symbol_sz) {
max_offset = q->symbol_sz;
}
for (int i=0;i<max_offset;i++) {
q->corr[i] = 0;
cf_t *inputPtr = input;
for (int n=0;n<nof_symbols;n++) {
uint32_t cplen = (n%7)?cp_len:cp_len+1;
q->corr[i] += srslte_vec_dot_prod_conj_ccc(&inputPtr[i], &inputPtr[i+q->symbol_sz], cplen)/nof_symbols;
inputPtr += q->symbol_sz+cplen;
}
}
uint32_t max_idx = srslte_vec_max_abs_ci(q->corr, max_offset);
return max_idx;
}
cf_t srslte_cp_synch_corr_output(srslte_cp_synch_t *q, uint32_t offset)

View File

@ -70,11 +70,14 @@ static void corr_all_sz_partial(cf_t z[SRSLTE_SSS_N], float s[SRSLTE_SSS_N][SRSL
static void extract_pair_sss(srslte_sss_synch_t *q, cf_t *input, cf_t *ce, cf_t y[2][SRSLTE_SSS_N]) {
cf_t input_fft[SRSLTE_SYMBOL_SZ_MAX];
float ce_mod[2*SRSLTE_SSS_N], z_real[2*SRSLTE_SSS_N], z_imag[2*SRSLTE_SSS_N];
srslte_dft_run_c(&q->dftp_input, input, input_fft);
if (ce) {
srslte_vec_prod_conj_ccc(&input_fft[q->fft_size/2-SRSLTE_SSS_N], ce, &input_fft[q->fft_size/2-SRSLTE_SSS_N], 2*SRSLTE_SSS_N);
srslte_vec_div_ccc(&input_fft[q->fft_size/2-SRSLTE_SSS_N], ce, ce_mod,
&input_fft[q->fft_size/2-SRSLTE_SSS_N], z_real, z_imag,
2*SRSLTE_SSS_N);
}
for (int i = 0; i < SRSLTE_SSS_N; i++) {

View File

@ -101,7 +101,7 @@ int srslte_pss_synch_init_fft_offset(srslte_pss_synch_t *q, uint32_t frame_size,
q->N_id_2 = 10;
q->fft_size = fft_size;
q->frame_size = frame_size;
q->ema_alpha = 0.1;
q->ema_alpha = 0.2;
buffer_size = fft_size + frame_size + 1;

View File

@ -35,9 +35,9 @@
#include "srslte/utils/vector.h"
#include "srslte/sync/cfo.h"
#define MEANPEAK_EMA_ALPHA 0.2
#define CFO_EMA_ALPHA 0.9
#define CP_EMA_ALPHA 0.2
#define MEANPEAK_EMA_ALPHA 0.1
#define CFO_EMA_ALPHA 0.1
#define CP_EMA_ALPHA 0.1
static bool fft_size_isvalid(uint32_t fft_size) {
if (fft_size >= SRSLTE_SYNC_FFT_SZ_MIN && fft_size <= SRSLTE_SYNC_FFT_SZ_MAX && (fft_size%64) == 0) {
@ -59,7 +59,6 @@ int srslte_sync_init(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset,
bzero(q, sizeof(srslte_sync_t));
q->detect_cp = true;
q->mean_peak_value = 0.0;
q->sss_en = true;
q->mean_cfo = 0;
q->N_id_2 = 1000;
@ -72,7 +71,15 @@ int srslte_sync_init(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset,
q->frame_size = frame_size;
q->max_offset = max_offset;
q->sss_alg = SSS_FULL;
q->enable_cfo_corr = true;
if (srslte_cfo_init(&q->cfocorr, q->frame_size)) {
fprintf(stderr, "Error initiating CFO\n");
goto clean_exit;
}
// Set a CFO tolerance of approx 100 Hz
srslte_cfo_set_tol(&q->cfocorr, 100/(15000*q->fft_size));
for (int i=0;i<2;i++) {
q->cfo_i_corr[i] = srslte_vec_malloc(sizeof(cf_t)*q->frame_size);
@ -93,11 +100,6 @@ int srslte_sync_init(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset,
goto clean_exit;
}
if (srslte_cfo_init(&q->cfocorr, frame_size)) {
fprintf(stderr, "Error initiating CFO\n");
goto clean_exit;
}
if (srslte_cp_synch_init(&q->cp_synch, fft_size)) {
fprintf(stderr, "Error initiating CFO\n");
goto clean_exit;
@ -207,7 +209,7 @@ float srslte_sync_get_last_peak_value(srslte_sync_t *q) {
}
float srslte_sync_get_peak_value(srslte_sync_t *q) {
return q->mean_peak_value;
return q->peak_value;
}
void srslte_sync_cp_en(srslte_sync_t *q, bool enabled) {
@ -245,45 +247,58 @@ void srslte_sync_set_sss_algorithm(srslte_sync_t *q, sss_alg_t alg) {
*/
srslte_cp_t srslte_sync_detect_cp(srslte_sync_t *q, cf_t *input, uint32_t peak_pos)
{
float R_norm, R_ext, C_norm, C_ext;
float R_norm=0, R_ext=0, C_norm=0, C_ext=0;
float M_norm=0, M_ext=0;
uint32_t cp_norm_len = SRSLTE_CP_LEN_NORM(7, q->fft_size);
uint32_t cp_ext_len = SRSLTE_CP_LEN_EXT(q->fft_size);
uint32_t nof_symbols = peak_pos/(q->fft_size+cp_ext_len);
if (nof_symbols > 3) {
nof_symbols = 3;
}
if (nof_symbols > 0) {
cf_t *input_cp_norm = &input[peak_pos-2*(q->fft_size+cp_norm_len)];
cf_t *input_cp_ext = &input[peak_pos-2*(q->fft_size+cp_ext_len)];
cf_t *input_cp_norm = &input[peak_pos-nof_symbols*(q->fft_size+cp_norm_len)];
cf_t *input_cp_ext = &input[peak_pos-nof_symbols*(q->fft_size+cp_ext_len)];
for (int i=0;i<2;i++) {
R_norm = crealf(srslte_vec_dot_prod_conj_ccc(&input_cp_norm[q->fft_size], input_cp_norm, cp_norm_len));
C_norm = cp_norm_len * srslte_vec_avg_power_cf(input_cp_norm, cp_norm_len);
input_cp_norm += q->fft_size+cp_norm_len;
M_norm += R_norm/C_norm;
}
q->M_norm_avg = SRSLTE_VEC_EMA(M_norm/2, q->M_norm_avg, CP_EMA_ALPHA);
for (int i=0;i<2;i++) {
R_ext = crealf(srslte_vec_dot_prod_conj_ccc(&input_cp_ext[q->fft_size], input_cp_ext, cp_ext_len));
C_ext = cp_ext_len * srslte_vec_avg_power_cf(input_cp_ext, cp_ext_len);
input_cp_ext += q->fft_size+cp_ext_len;
for (int i=0;i<nof_symbols;i++) {
R_norm += crealf(srslte_vec_dot_prod_conj_ccc(&input_cp_norm[q->fft_size], input_cp_norm, cp_norm_len));
C_norm += cp_norm_len * srslte_vec_avg_power_cf(input_cp_norm, cp_norm_len);
input_cp_norm += q->fft_size+cp_norm_len;
}
if (C_norm > 0) {
M_norm = R_norm/C_norm;
}
q->M_norm_avg = SRSLTE_VEC_EMA(M_norm/nof_symbols, q->M_norm_avg, CP_EMA_ALPHA);
for (int i=0;i<nof_symbols;i++) {
R_ext += crealf(srslte_vec_dot_prod_conj_ccc(&input_cp_ext[q->fft_size], input_cp_ext, cp_ext_len));
C_ext += cp_ext_len * srslte_vec_avg_power_cf(input_cp_ext, cp_ext_len);
input_cp_ext += q->fft_size+cp_ext_len;
}
if (C_ext > 0) {
M_ext += R_ext/C_ext;
M_ext = R_ext/C_ext;
}
}
q->M_ext_avg = SRSLTE_VEC_EMA(M_ext/2, q->M_ext_avg, CP_EMA_ALPHA);
q->M_ext_avg = SRSLTE_VEC_EMA(M_ext/nof_symbols, q->M_ext_avg, CP_EMA_ALPHA);
if (q->M_norm_avg > q->M_ext_avg) {
return SRSLTE_CP_NORM;
} else if (q->M_norm_avg < q->M_ext_avg) {
return SRSLTE_CP_EXT;
} else {
if (R_norm > R_ext) {
return SRSLTE_CP_NORM;
} else {
if (q->M_norm_avg > q->M_ext_avg) {
return SRSLTE_CP_NORM;
} else if (q->M_norm_avg < q->M_ext_avg) {
return SRSLTE_CP_EXT;
} else {
if (R_norm > R_ext) {
return SRSLTE_CP_NORM;
} else {
return SRSLTE_CP_EXT;
}
}
} else {
return SRSLTE_CP_NORM;
}
}
@ -333,6 +348,44 @@ srslte_pss_synch_t* srslte_sync_get_cur_pss_obj(srslte_sync_t *q) {
return pss_obj[q->cfo_i+1];
}
static float cfo_estimate(srslte_sync_t *q, cf_t *input) {
uint32_t cp_offset = 0;
cp_offset = srslte_cp_synch(&q->cp_synch, input, q->max_offset, q->nof_symbols, SRSLTE_CP_LEN_NORM(1,q->fft_size));
cf_t cp_corr_max = srslte_cp_synch_corr_output(&q->cp_synch, cp_offset);
float cfo = -carg(cp_corr_max) / M_PI / 2;
return cfo;
}
static int cfo_i_estimate(srslte_sync_t *q, cf_t *input, int find_offset, int *peak_pos) {
float peak_value;
float max_peak_value = -99;
int max_cfo_i = 0;
srslte_pss_synch_t *pss_obj[3] = {&q->pss_i[0], &q->pss, &q->pss_i[1]};
for (int cfo_i=0;cfo_i<3;cfo_i++) {
srslte_pss_synch_set_N_id_2(pss_obj[cfo_i], q->N_id_2);
int p = srslte_pss_synch_find_pss(pss_obj[cfo_i], &input[find_offset], &peak_value);
if (peak_value > max_peak_value) {
max_peak_value = peak_value;
if (peak_pos) {
*peak_pos = p;
}
q->peak_value = peak_value;
max_cfo_i = cfo_i-1;
}
}
return max_cfo_i;
}
float srslte_sync_cfo_estimate(srslte_sync_t *q, cf_t *input, int find_offset) {
float cfo_f = cfo_estimate(q, input);
int cfo_i = 0;
if (q->find_cfo_i) {
cfo_i = cfo_i_estimate(q, input, find_offset, NULL);
}
return (float) cfo_i + cfo_f;
}
/** Finds the PSS sequence previously defined by a call to srslte_sync_set_N_id_2()
* around the position find_offset in the buffer input.
* Returns 1 if the correlation peak exceeds the threshold set by srslte_sync_set_threshold()
@ -340,17 +393,17 @@ srslte_pss_synch_t* srslte_sync_get_cur_pss_obj(srslte_sync_t *q) {
*
* The maximum of the correlation peak is always stored in *peak_position
*/
int srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_position)
srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_position)
{
int ret = SRSLTE_ERROR_INVALID_INPUTS;
srslte_sync_find_ret_t ret = SRSLTE_SYNC_ERROR;
if (q != NULL &&
input != NULL &&
srslte_N_id_2_isvalid(q->N_id_2) &&
fft_size_isvalid(q->fft_size))
{
int peak_pos;
int peak_pos = 0;
ret = SRSLTE_SUCCESS;
@ -358,44 +411,20 @@ int srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32
*peak_position = 0;
}
/* Estimate CFO using CP before computing the PSS correlation as in:
* Shoujun Huang, et al, "Joint Time and Frequency Offset Estimation in LTE Downlink"
*/
uint32_t cp_offset = 0;
if (q->enable_cfo_corr) {
cp_offset = srslte_cp_synch(&q->cp_synch, input, q->max_offset, q->nof_symbols, SRSLTE_CP_LEN_NORM(1,q->fft_size));
cf_t cp_corr_max = srslte_cp_synch_corr_output(&q->cp_synch, cp_offset);
float cfo = -carg(cp_corr_max) / M_PI / 2;
float cfo = cfo_estimate(q, input);
/* compute cumulative moving average CFO */
DEBUG("cp_offset_pos=%d, abs=%f, cfo=%f, mean_cfo=%f, nof_symb=%d\n",
cp_offset, cabs(cp_corr_max), cfo, q->mean_cfo, q->nof_symbols);
if (q->mean_cfo) {
q->mean_cfo = SRSLTE_VEC_EMA(cfo, q->mean_cfo, q->cfo_ema_alpha);
} else {
q->mean_cfo = cfo;
}
/* compute exponential moving average CFO */
q->mean_cfo = SRSLTE_VEC_EMA(cfo, q->mean_cfo, q->cfo_ema_alpha);
/* Correct CFO with the averaged CFO estimation */
srslte_cfo_correct(&q->cfocorr, input, input, -q->mean_cfo / q->fft_size);
}
/* If integer CFO is enabled, find max PSS correlation for shifted +1/0/-1 integer versions */
if (q->find_cfo_i && q->enable_cfo_corr) {
float peak_value;
float max_peak_value = -99;
peak_pos = 0;
srslte_pss_synch_t *pss_obj[3] = {&q->pss_i[0], &q->pss, &q->pss_i[1]};
for (int cfo_i=0;cfo_i<3;cfo_i++) {
srslte_pss_synch_set_N_id_2(pss_obj[cfo_i], q->N_id_2);
int p = srslte_pss_synch_find_pss(pss_obj[cfo_i], &input[find_offset], &peak_value);
if (peak_value > max_peak_value) {
max_peak_value = peak_value;
peak_pos = p;
q->peak_value = peak_value;
q->cfo_i = cfo_i-1;
}
}
q->cfo_i = cfo_i_estimate(q, input, find_offset, &peak_pos);
if (q->cfo_i != 0) {
srslte_vec_prod_ccc(input, q->cfo_i_corr[q->cfo_i<0?0:1], input, q->frame_size);
INFO("Compensating cfo_i=%d\n", q->cfo_i);
@ -409,8 +438,6 @@ int srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32
}
}
q->mean_peak_value = SRSLTE_VEC_EMA(q->peak_value, q->mean_peak_value, MEANPEAK_EMA_ALPHA);
if (peak_position) {
*peak_position = (uint32_t) peak_pos;
}
@ -436,13 +463,16 @@ int srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32
}
}
// Return 1 (peak detected) even if we couldn't estimate CFO and SSS
ret = 1;
if (peak_pos + find_offset >= 2*(q->fft_size + SRSLTE_CP_LEN_EXT(q->fft_size))) {
ret = SRSLTE_SYNC_FOUND;
} else {
ret = SRSLTE_SYNC_FOUND_NOSPACE;
}
} else {
ret = 0;
ret = SRSLTE_SYNC_NOFOUND;
}
DEBUG("SYNC ret=%d N_id_2=%d find_offset=%d frame_len=%d, pos=%d peak=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f KHz\n",
INFO("SYNC ret=%d N_id_2=%d find_offset=%d frame_len=%d, pos=%d peak=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f KHz\n",
ret, q->N_id_2, find_offset, q->frame_size, peak_pos, q->peak_value,
q->threshold, q->sf_idx, 15*(q->cfo_i+q->mean_cfo));

View File

@ -259,23 +259,23 @@ int main(int argc, char **argv) {
// Find SSS
int sss_idx = peak_idx-2*fft_size-(SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN(fft_size, SRSLTE_CP_NORM_LEN):SRSLTE_CP_LEN(fft_size, SRSLTE_CP_EXT_LEN));
if (sss_idx >= 0 && sss_idx < flen-fft_size) {
INFO("Full N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1));
srslte_sss_synch_m0m1_partial(&sss, &buffer[sss_idx], 1, ce, &m0, &m0_value, &m1, &m1_value);
if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) {
sss_error2++;
}
INFO("Partial N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1));
srslte_sss_synch_m0m1_diff(&sss, &buffer[sss_idx], &m0, &m0_value, &m1, &m1_value);
srslte_sss_synch_m0m1_diff_coh(&sss, &buffer[sss_idx], ce, &m0, &m0_value, &m1, &m1_value);
if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) {
sss_error3++;
}
INFO("Diff N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1));
srslte_sss_synch_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value);
if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) {
sss_error1++;
}
INFO("Full N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1));
}
srslte_sss_synch_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value);
if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) {
sss_error1++;
}
// Estimate CP
if (peak_idx > 2*(fft_size + SRSLTE_CP_LEN_EXT(fft_size))) {
srslte_cp_t cp = srslte_sync_detect_cp(&ssync, buffer, peak_idx);
@ -300,7 +300,6 @@ int main(int argc, char **argv) {
nof_nodet++;
}
printf("[%5d]: Pos: %5d (%d-%d), PSR: %4.1f (~%4.1f) Pdet: %4.2f, "
"FA: %4.2f, CFO: %+4.1f KHz, SFO: %+.2f Hz SSSmiss: %4.2f/%4.2f/%4.2f CPNorm: %.0f%%\r",
frame_cnt,
@ -310,7 +309,6 @@ int main(int argc, char **argv) {
(float) nof_nopeakdet/frame_cnt, mean_cfo*15, sfo,
(float) sss_error1/nof_det,(float) sss_error2/nof_det,(float) sss_error3/nof_det,
(float) cp_is_norm/nof_det * 100);
if (frame_cnt > 100) {
if (abs(last_peak-peak_idx) > 4) {

View File

@ -35,12 +35,7 @@
#include "srslte/utils/debug.h"
#include "srslte/utils/vector.h"
int srslte_ue_cellsearch_init(srslte_ue_cellsearch_t * q, int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*), void *stream_handler)
{
return srslte_ue_cellsearch_init_max(q, SRSLTE_CS_DEFAULT_MAXFRAMES_TOTAL, recv_callback, stream_handler);
}
int srslte_ue_cellsearch_init_max(srslte_ue_cellsearch_t * q, uint32_t max_frames,
int srslte_ue_cellsearch_init(srslte_ue_cellsearch_t * q, uint32_t max_frames,
int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*), void *stream_handler)
{
int ret = SRSLTE_ERROR_INVALID_INPUTS;
@ -77,8 +72,7 @@ int srslte_ue_cellsearch_init_max(srslte_ue_cellsearch_t * q, uint32_t max_frame
}
q->max_frames = max_frames;
q->nof_frames_to_scan = SRSLTE_CS_DEFAULT_NOFFRAMES_TOTAL;
q->detect_threshold = 1.0;
q->nof_valid_frames = max_frames;
ret = SRSLTE_SUCCESS;
}
@ -107,15 +101,10 @@ void srslte_ue_cellsearch_free(srslte_ue_cellsearch_t * q)
}
void srslte_ue_cellsearch_set_threshold(srslte_ue_cellsearch_t * q, float threshold)
{
q->detect_threshold = threshold;
}
int srslte_ue_cellsearch_set_nof_frames_to_scan(srslte_ue_cellsearch_t * q, uint32_t nof_frames)
int srslte_ue_cellsearch_set_nof_valid_frames(srslte_ue_cellsearch_t * q, uint32_t nof_frames)
{
if (nof_frames <= q->max_frames) {
q->nof_frames_to_scan = nof_frames;
q->nof_valid_frames = nof_frames;
return SRSLTE_SUCCESS;
} else {
return SRSLTE_ERROR;
@ -233,23 +222,20 @@ int srslte_ue_cellsearch_scan_N_id_2(srslte_ue_cellsearch_t * q,
} else if (ret == 1) {
/* This means a peak was found and ue_sync is now in tracking state */
ret = srslte_sync_get_cell_id(&q->ue_sync.strack);
if (ret >= 0) {
if (srslte_sync_get_peak_value(&q->ue_sync.strack) > q->detect_threshold) {
/* Save cell id, cp and peak */
q->candidates[nof_detected_frames].cell_id = (uint32_t) ret;
q->candidates[nof_detected_frames].cp = srslte_sync_get_cp(&q->ue_sync.strack);
q->candidates[nof_detected_frames].peak = q->ue_sync.strack.pss.peak_value;
q->candidates[nof_detected_frames].psr = srslte_sync_get_peak_value(&q->ue_sync.strack);
q->candidates[nof_detected_frames].cfo = srslte_ue_sync_get_cfo(&q->ue_sync);
DEBUG
("CELL SEARCH: [%3d/%3d/%d]: Found peak PSR=%.3f, Cell_id: %d CP: %s\n",
nof_detected_frames, nof_scanned_frames, q->nof_frames_to_scan,
q->candidates[nof_detected_frames].psr, q->candidates[nof_detected_frames].cell_id,
srslte_cp_string(q->candidates[nof_detected_frames].cp));
nof_detected_frames++;
if (ret >= 0) {
/* Save cell id, cp and peak */
q->candidates[nof_detected_frames].cell_id = (uint32_t) ret;
q->candidates[nof_detected_frames].cp = srslte_sync_get_cp(&q->ue_sync.strack);
q->candidates[nof_detected_frames].peak = q->ue_sync.strack.pss.peak_value;
q->candidates[nof_detected_frames].psr = srslte_sync_get_peak_value(&q->ue_sync.strack);
q->candidates[nof_detected_frames].cfo = srslte_ue_sync_get_cfo(&q->ue_sync);
DEBUG
("CELL SEARCH: [%3d/%3d/%d]: Found peak PSR=%.3f, Cell_id: %d CP: %s\n",
nof_detected_frames, nof_scanned_frames, q->nof_valid_frames,
q->candidates[nof_detected_frames].psr, q->candidates[nof_detected_frames].cell_id,
srslte_cp_string(q->candidates[nof_detected_frames].cp));
}
nof_detected_frames++;
}
} else if (ret == 0) {
/* This means a peak is not yet found and ue_sync is in find state
@ -259,7 +245,7 @@ int srslte_ue_cellsearch_scan_N_id_2(srslte_ue_cellsearch_t * q,
nof_scanned_frames++;
} while (nof_scanned_frames < q->nof_frames_to_scan);
} while (nof_scanned_frames < q->max_frames && nof_detected_frames < q->nof_valid_frames);
/* In either case, check if the mean PSR is above the minimum threshold */
if (nof_detected_frames > 0) {

View File

@ -102,6 +102,8 @@ int srslte_ue_dl_init(srslte_ue_dl_t *q,
fprintf(stderr, "Error initiating SFO correct\n");
goto clean_exit;
}
srslte_cfo_set_tol(&q->sfo_correct, 0);
q->sf_symbols = srslte_vec_malloc(CURRENT_SFLEN_RE * sizeof(cf_t));
if (!q->sf_symbols) {
perror("malloc");

View File

@ -142,10 +142,10 @@ int srslte_ue_mib_decode(srslte_ue_mib_t * q, cf_t *input,
/* Decode PBCH */
ret = srslte_pbch_decode(&q->pbch, &q->sf_symbols[SRSLTE_SLOT_LEN_RE(q->chest.cell.nof_prb, q->chest.cell.cp)],
ce_slot1, 0,
ce_slot1, srslte_chest_dl_get_noise_estimate(&q->chest),
bch_payload, nof_tx_ports, sfn_offset);
if (ret < 0) {
fprintf(stderr, "Error decoding PBCH (%d)\n", ret);
} else if (ret == 1) {
@ -153,10 +153,11 @@ int srslte_ue_mib_decode(srslte_ue_mib_t * q, cf_t *input,
srslte_ue_mib_reset(q);
ret = SRSLTE_UE_MIB_FOUND;
} else {
ret = SRSLTE_UE_MIB_NOTFOUND;
INFO("MIB not decoded: %u\n", q->frame_cnt);
q->frame_cnt++;
ret = SRSLTE_UE_MIB_NOTFOUND;
}
return ret;
}

View File

@ -43,9 +43,9 @@ cf_t dummy[MAX_TIME_OFFSET];
#define TRACK_MAX_LOST 4
#define TRACK_FRAME_SIZE 32
#define FIND_NOF_AVG_FRAMES 2
#define DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD 20
#define DEFAULT_SFO_EMA_COEFF 0.1
#define FIND_NOF_AVG_FRAMES 4
#define DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD 10
#define DEFAULT_SFO_EMA_COEFF 0.1
cf_t dummy_offset_buffer[1024*1024];
@ -171,17 +171,17 @@ int srslte_ue_sync_init(srslte_ue_sync_t *q,
if (cell.id == 1000) {
/* If the cell id is unknown, enable CP detection on find */
srslte_sync_cp_en(&q->sfind, true);
srslte_sync_cp_en(&q->strack, true);
// FIXME: CP detection not working very well. Not supporting Extended CP right now
srslte_sync_cp_en(&q->sfind, false);
srslte_sync_cp_en(&q->strack, false);
srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.9);
srslte_sync_set_cfo_ema_alpha(&q->strack, 0.4);
srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.8);
srslte_sync_set_cfo_ema_alpha(&q->strack, 0.1);
srslte_sync_cfo_i_detec_en(&q->sfind, true);
srslte_sync_cfo_i_detec_en(&q->strack, true);
srslte_sync_cfo_i_detec_en(&q->sfind, false);
srslte_sync_set_threshold(&q->sfind, 1.5);
q->nof_avg_find_frames = FIND_NOF_AVG_FRAMES;
srslte_sync_set_threshold(&q->sfind, 2.0);
srslte_sync_set_threshold(&q->strack, 1.0);
} else {
@ -192,10 +192,9 @@ int srslte_ue_sync_init(srslte_ue_sync_t *q,
srslte_sync_cp_en(&q->sfind, false);
srslte_sync_cp_en(&q->strack, false);
srslte_sync_cfo_i_detec_en(&q->sfind, true);
srslte_sync_cfo_i_detec_en(&q->strack, true);
srslte_sync_cfo_i_detec_en(&q->sfind, false);
srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.9);
srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.01);
srslte_sync_set_cfo_ema_alpha(&q->strack, 0.01);
/* In find phase and if the cell is known, do not average pss correlation
@ -203,10 +202,10 @@ int srslte_ue_sync_init(srslte_ue_sync_t *q,
*/
q->nof_avg_find_frames = 1;
srslte_sync_set_em_alpha(&q->sfind, 1);
srslte_sync_set_threshold(&q->sfind, 4.0);
srslte_sync_set_threshold(&q->sfind, 3.0);
srslte_sync_set_em_alpha(&q->strack, 0.2);
srslte_sync_set_threshold(&q->strack, 1.0);
srslte_sync_set_threshold(&q->strack, 1.2);
}
@ -273,7 +272,8 @@ float srslte_ue_sync_get_cfo(srslte_ue_sync_t *q) {
return 15000 * srslte_sync_get_cfo(&q->strack);
}
void srslte_ue_sync_set_cfo(srslte_ue_sync_t *q, float cfo) {
void srslte_ue_sync_set_cfo(srslte_ue_sync_t *q, float cfo) {
srslte_sync_set_cfo(&q->sfind, cfo/15000);
srslte_sync_set_cfo(&q->strack, cfo/15000);
}
@ -330,7 +330,7 @@ static int find_peak_ok(srslte_ue_sync_t *q, cf_t *input_buffer) {
if (q->recv_callback(q->stream, input_buffer, q->peak_idx+q->sf_len/2, &q->last_timestamp) < 0) {
return SRSLTE_ERROR;
}
/* Reset variables */
q->frame_ok_cnt = 0;
q->frame_no_cnt = 0;
@ -346,7 +346,6 @@ static int find_peak_ok(srslte_ue_sync_t *q, cf_t *input_buffer) {
q->strack.cfo_i = q->sfind.cfo_i;
}
return 0;
}
@ -397,7 +396,7 @@ static int track_peak_ok(srslte_ue_sync_t *q, uint32_t track_idx) {
}
q->mean_sfo = SRSLTE_VEC_EMA(q->mean_sample_offset, q->mean_sfo, q->sfo_ema);
INFO("\n\nTime offset adjustment: %d samples (%.2f), mean SFO: %.2f Hz, %.5f samples/5-sf, ema=%f, length=%d\n",
INFO("Time offset adjustment: %d samples (%.2f), mean SFO: %.2f Hz, %.5f samples/5-sf, ema=%f, length=%d\n",
q->next_rf_sample_offset, q->mean_sample_offset,
srslte_ue_sync_get_sfo(q),
q->mean_sfo, q->sfo_ema, q->sample_offset_correct_period);
@ -433,6 +432,12 @@ static int track_peak_no(srslte_ue_sync_t *q) {
} else {
INFO("Tracking peak not found. Peak %.3f, %d lost\n",
srslte_sync_get_last_peak_value(&q->strack), (int) q->frame_no_cnt);
/*
printf("Saving files: pss_corr (%d), input (%d)\n", q->strack.pss.frame_size, SRSLTE_SF_LEN_PRB(q->cell.nof_prb));
srslte_vec_save_file("pss_corr", q->strack.pss.conv_output_avg, q->strack.pss.frame_size*sizeof(float));
srslte_vec_save_file("input", q->input_buffer, SRSLTE_SF_LEN_PRB(q->cell.nof_prb)*sizeof(cf_t));
exit(-1);
*/
return 1;
}
@ -512,19 +517,28 @@ int srslte_ue_sync_zerocopy(srslte_ue_sync_t *q, cf_t *input_buffer) {
return SRSLTE_ERROR;
}
switch (q->state) {
case SF_FIND:
ret = srslte_sync_find(&q->sfind, input_buffer, 0, &q->peak_idx);
if (ret < 0) {
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
return SRSLTE_ERROR;
}
case SF_FIND:
switch(srslte_sync_find(&q->sfind, input_buffer, 0, &q->peak_idx)) {
case SRSLTE_SYNC_ERROR:
ret = SRSLTE_ERROR;
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
return SRSLTE_ERROR;
case SRSLTE_SYNC_FOUND:
ret = find_peak_ok(q, input_buffer);
break;
case SRSLTE_SYNC_FOUND_NOSPACE:
/* If a peak was found but there is not enough space for SSS/CP detection, discard a few samples */
q->recv_callback(q->stream, dummy_offset_buffer, q->frame_len/2, NULL);
srslte_sync_reset(&q->sfind);
ret = SRSLTE_SUCCESS;
break;
default:
ret = SRSLTE_SUCCESS;
break;
}
if (q->do_agc) {
srslte_agc_process(&q->agc, input_buffer, q->sf_len);
}
if (ret == 1) {
ret = find_peak_ok(q, input_buffer);
}
break;
case SF_TRACK:
ret = 1;
@ -552,12 +566,26 @@ int srslte_ue_sync_zerocopy(srslte_ue_sync_t *q, cf_t *input_buffer) {
/* Track PSS/SSS around the expected PSS position
* In tracking phase, the subframe carrying the PSS is always the last one of the frame
*/
ret = srslte_sync_find(&q->strack, input_buffer,
q->frame_len - q->sf_len/2 - q->fft_size - q->strack.max_offset/2,
&track_idx);
if (ret < 0) {
fprintf(stderr, "Error tracking correlation peak\n");
return SRSLTE_ERROR;
switch(srslte_sync_find(&q->strack, input_buffer,
q->frame_len - q->sf_len/2 - q->fft_size - q->strack.max_offset/2,
&track_idx))
{
case SRSLTE_SYNC_ERROR:
ret = SRSLTE_ERROR;
fprintf(stderr, "Error tracking correlation peak\n");
return SRSLTE_ERROR;
case SRSLTE_SYNC_FOUND:
ret = track_peak_ok(q, track_idx);
break;
case SRSLTE_SYNC_FOUND_NOSPACE:
// It's very very unlikely that we fall here because this event should happen at FIND phase only
ret = 0;
q->state = SF_FIND;
printf("Warning: No space for SSS/CP while in tracking phase\n");
break;
case SRSLTE_SYNC_NOFOUND:
ret = track_peak_no(q);
break;
}
#ifdef MEASURE_EXEC_TIME
@ -566,11 +594,6 @@ int srslte_ue_sync_zerocopy(srslte_ue_sync_t *q, cf_t *input_buffer) {
q->mean_exec_time = (float) SRSLTE_VEC_CMA((float) t[0].tv_usec, q->mean_exec_time, q->frame_total_cnt);
#endif
if (ret == 1) {
ret = track_peak_ok(q, track_idx);
} else {
ret = track_peak_no(q);
}
if (ret == SRSLTE_ERROR) {
fprintf(stderr, "Error processing tracking peak\n");
q->state = SF_FIND;
@ -579,16 +602,14 @@ int srslte_ue_sync_zerocopy(srslte_ue_sync_t *q, cf_t *input_buffer) {
q->frame_total_cnt++;
} else {
/* Do CFO Correction if not in 0 or 5 subframes */
if (q->correct_cfo) {
srslte_cfo_correct(&q->sfind.cfocorr,
input_buffer,
input_buffer,
-srslte_sync_get_cfo(&q->strack) / q->fft_size);
-srslte_sync_get_cfo(&q->sfind) / q->fft_size);
}
}
}
break;
}

View File

@ -704,14 +704,14 @@ uint32_t srslte_vec_max_abs_ci(cf_t *x, uint32_t len) {
void srslte_vec_quant_fuc(float *in, uint8_t *out, float gain, float offset, float clip, uint32_t len) {
int i;
int tmp;
for (i=0;i<len;i++) {
tmp = (int) (offset + gain * in[i]);
if (tmp < 0)
tmp = 0;
if (tmp > clip)
tmp = clip;
out[i] = (uint8_t) tmp;
out[i] = (uint8_t) tmp;
}
}