/** * * \section COPYRIGHT * * Copyright 2013-2014 The libLTE Developers. See the * COPYRIGHT file at the top-level directory of this distribution. * * \section LICENSE * * This file is part of the libLTE library. * * libLTE is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * libLTE 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 Lesser General Public License for more details. * * A copy of the GNU Lesser General Public License can be found in * the LICENSE file in the top-level directory of this distribution * and at http://www.gnu.org/licenses/. * */ #include #include #include #include #include #include #include #include #include "liblte/phy/phy.h" //#define DISABLE_UHD #ifndef DISABLE_UHD #include "liblte/cuhd/cuhd.h" #endif #define MHZ 1000000 #define SAMP_FREQ 1920000 #define RSSI_FS 1000000 #define FLEN 9600 #define FLEN_PERIOD 0.005 #define RSSI_DECIM 20 #define IS_SIGNAL(i) (10*log10f(rssi[i]) + 30 > rssi_threshold) int band, earfcn=-1; float find_threshold = 10.0; int earfcn_start=-1, earfcn_end = -1; float rssi_threshold = -45.0; int max_track_lost=9; int nof_frames_find=20, nof_frames_track=100, nof_samples_rssi=50000; int track_len=500; cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; pbch_t pbch; lte_fft_t fft; chest_t chest; sync_t ssync; cfo_t cfocorr; float *cfo_v; int *idx_v, *idx_valid, *t; float *p2a_v; void *uhd; int nof_bands; float uhd_gain = 20.0; #define MAX_EARFCN 1000 lte_earfcn_t channels[MAX_EARFCN]; float rssi[MAX_EARFCN]; float rssi_d[MAX_EARFCN/RSSI_DECIM]; float freqs[MAX_EARFCN]; float cfo[MAX_EARFCN]; float p2a[MAX_EARFCN]; enum sync_state {INIT, FIND, TRACK, MIB, DONE}; void usage(char *prog) { printf("Usage: %s [seRrFfTgv] -b band\n", prog); printf("\t-s earfcn_start [Default All]\n"); printf("\t-e earfcn_end [Default All]\n"); printf("\t-R rssi_nof_samples [Default %d]\n", nof_samples_rssi); printf("\t-r rssi_threshold [Default %.2f dBm]\n", rssi_threshold); printf("\t-F pss_find_nof_frames [Default %d]\n", nof_frames_find); printf("\t-f pss_find_threshold [Default %.2f]\n", find_threshold); printf("\t-T pss_track_nof_frames [Default %d]\n", nof_frames_track); printf("\t-l pss_track_len [Default %d]\n", track_len); printf("\t-g gain [Default %.2f dB]\n", uhd_gain); printf("\t-v [set verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "bseRrFfTgv")) != -1) { switch(opt) { case 'b': band = atoi(argv[optind]); break; case 's': earfcn_start = atoi(argv[optind]); break; case 'e': earfcn_end = atoi(argv[optind]); break; case 'R': nof_samples_rssi = atoi(argv[optind]); break; case 'r': rssi_threshold = -atof(argv[optind]); break; case 'F': nof_frames_find = atoi(argv[optind]); break; case 'f': find_threshold = atof(argv[optind]); break; case 'T': nof_frames_track = atoi(argv[optind]); break; case 'g': uhd_gain = atof(argv[optind]); break; case 'v': verbose++; break; default: usage(argv[0]); exit(-1); } } } int base_init(int frame_length) { int i; input_buffer = malloc(2 * frame_length * sizeof(cf_t)); if (!input_buffer) { perror("malloc"); return -1; } fft_buffer = malloc(CPNORM_NSYMB * 72 * sizeof(cf_t)); if (!fft_buffer) { perror("malloc"); return -1; } for (i=0;i 0) { return mean/n; } else { return 0.0; } } int preprocess_idx(int *in, int *out, int *period, int len) { int i, n; n=0; for (i=0;i 100) { /* scan every Mhz, that is 10 freqs */ for (i=0;i= nof_frames_find) { state = INIT; freq++; } } break; case TRACK: INFO("Tracking PSS find_idx %d offset %d\n", find_idx, find_idx - track_len); ret = sync_track(&ssync, input_buffer, FLEN + find_idx - track_len, &track_idx); p2a_v[frame_cnt] = sync_get_peak_to_avg(&ssync); /* save cell id for the best peak-to-avg */ if (p2a_v[frame_cnt] > max_peak_to_avg) { max_peak_to_avg = p2a_v[frame_cnt]; cell_id = sync_get_cell_id(&ssync); } if (ret == 1) { cfo_v[frame_cnt] = sync_get_cfo(&ssync); last_found = frame_cnt; find_idx += track_idx - track_len; idx_v[frame_cnt] = find_idx; nslot = sync_get_slot_id(&ssync); } else { idx_v[frame_cnt] = -1; cfo_v[frame_cnt] = 0.0; } /* if we missed to many PSS it is not a cell, next freq */ if (frame_cnt - last_found > max_track_lost) { INFO("\n[%3d/%d]: EARFCN %d Freq. %.2f MHz %d frames lost\n", freq, nof_bands, channels[freq].id, channels[freq].fd, frame_cnt - last_found); state = INIT; freq++; } else if (frame_cnt >= nof_frames_track) { mib_decoder_init(cell_id); cfo[freq] = mean_valid(idx_v, cfo_v, frame_cnt); p2a[freq] = mean_valid(idx_v, p2a_v, frame_cnt); valid_frames = preprocess_idx(idx_v, idx_valid, t, frame_cnt); sfo = sfo_estimate_period(idx_valid, t, valid_frames, FLEN_PERIOD); state = MIB; nslot=(nslot+10)%20; } break; case MIB: INFO("Finding MIB at freq %.2f Mhz offset=%d, cell_id=%d, slot_idx=%d\n", channels[freq].fd, find_idx, cell_id, nslot); // TODO: Correct SFO // Correct CFO INFO("Correcting CFO=%.4f\n", cfo[freq]); cfo_correct(&cfocorr, &input_buffer[FLEN], &input_buffer[FLEN], (-cfo[freq])/128); if (nslot == 0) { if (mib_decoder_run(&input_buffer[FLEN+find_idx], &mib)) { INFO("MIB detected attempt=%d\n", mib_attempts); state = DONE; } else { INFO("MIB not detected attempt=%d\n", mib_attempts); if (mib_attempts == 0) { freq++; state = INIT; } } mib_attempts++; } nslot = (nslot+10)%20; break; case DONE: printf("\n[%3d/%d]: FOUND EARFCN %d Freq. %.2f MHz. " "PAR %2.2f dB, CFO=%+.2f KHz, SFO=%+2.3f KHz, CELL_ID=%3d\n", freq, nof_bands, channels[freq].id, channels[freq].fd, 10*log10f(p2a[freq]), cfo[freq] * 15, sfo / 1000, cell_id); pbch_mib_fprint(stdout, &mib); state = INIT; freq++; break; } /** FIXME: This is not necessary at all */ if (state == TRACK || state == FIND) { memcpy(input_buffer, &input_buffer[FLEN], FLEN * sizeof(cf_t)); } frame_cnt++; } } base_free(); printf("\n\nDone\n"); exit(0); }