blob: bd4f182719b8b3d6f021f553b5e5d17b9c507016 [file] [log] [blame]
piotr437f5462014-02-04 17:57:25 +01001/* -*- c++ -*- */
piotrd0bf1492014-02-05 17:27:32 +01002/*
ptrkrysik529895b2014-12-02 18:07:38 +01003 * @file
Piotr Krysika6268a52017-08-23 16:02:19 +02004 * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
ptrkrysik529895b2014-12-02 18:07:38 +01005 * @section LICENSE
piotrd0bf1492014-02-05 17:27:32 +01006 *
ptrkrysik529895b2014-12-02 18:07:38 +01007 * Gr-gsm is free software; you can redistribute it and/or modify
piotr437f5462014-02-04 17:57:25 +01008 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
piotrd0bf1492014-02-05 17:27:32 +010011 *
ptrkrysik529895b2014-12-02 18:07:38 +010012 * Gr-gsm is distributed in the hope that it will be useful,
piotr437f5462014-02-04 17:57:25 +010013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
piotrd0bf1492014-02-05 17:27:32 +010016 *
piotr437f5462014-02-04 17:57:25 +010017 * You should have received a copy of the GNU General Public License
ptrkrysik529895b2014-12-02 18:07:38 +010018 * along with gr-gsm; see the file COPYING. If not, write to
piotr437f5462014-02-04 17:57:25 +010019 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <gnuradio/io_signature.h>
piotr437f5462014-02-04 17:57:25 +010028#include <gnuradio/math.h>
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070029
piotr437f5462014-02-04 17:57:25 +010030#include <algorithm>
piotr437f5462014-02-04 17:57:25 +010031#include <string.h>
piotr437f5462014-02-04 17:57:25 +010032#include <iostream>
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070033#include <numeric>
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070034#include <vector>
ptrkrysik3be74a72014-12-13 10:11:00 +010035
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070036#include <boost/circular_buffer.hpp>
37#include <boost/scoped_ptr.hpp>
ptrkrysik3be74a72014-12-13 10:11:00 +010038#include <grgsm/endian.h>
ptrkrysik58213792014-10-30 09:05:15 +010039
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070040#include "receiver_impl.h"
41#include "viterbi_detector.h"
42#include "sch.h"
43
44#if 0
45/* Files included for debuging */
Piotr Krysik467a7aa2019-07-17 09:43:03 +020046#include "grgsm/plotting.hpp"
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070047#include <pthread.h>
48#include <iomanip>
49#endif
piotr437f5462014-02-04 17:57:25 +010050
Piotr Krysik9e861d72019-07-17 10:11:36 +020051#define SYNC_SEARCH_RANGE 50
52#define SYNC_START (BURST_SIZE/2-(SYNC_SEARCH_RANGE/2)-(N_SYNC_BITS/2) + 8)
piotr437f5462014-02-04 17:57:25 +010053
piotrd0bf1492014-02-05 17:27:32 +010054namespace gr
55{
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070056 namespace gsm
57 {
piotrd0bf1492014-02-05 17:27:32 +010058
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070059 /* The public constructor */
60 receiver::sptr
61 receiver::make(
Piotr Krysikcd695772019-07-16 13:39:39 +020062 int osr,
63 const std::vector<int> &cell_allocation,
64 const std::vector<int> &tseq_nums,
65 double resamp_rate,
66 bool process_uplink
67 )
piotr437f5462014-02-04 17:57:25 +010068 {
Piotr Krysikcd695772019-07-16 13:39:39 +020069 return gnuradio::get_initial_sptr(
70 new receiver_impl(
71 osr,
72 cell_allocation,
73 tseq_nums,
74 resamp_rate,
75 process_uplink)
76 );
piotr437f5462014-02-04 17:57:25 +010077 }
78
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070079 /* The private constructor */
80 receiver_impl::receiver_impl(
Piotr Krysikcd695772019-07-16 13:39:39 +020081 int osr,
82 const std::vector<int> &cell_allocation,
83 const std::vector<int> &tseq_nums,
84 double resamp_rate,
85 bool process_uplink
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070086 ) : gr::sync_block("receiver",
87 gr::io_signature::make(1, -1, sizeof(gr_complex)),
88 gr::io_signature::make(0, 0, 0)),
Piotr Krysik96f2ea72017-10-16 15:47:08 +020089 d_samples_consumed(0),
Piotr Krysikdf978692017-09-27 21:58:24 +020090 d_rx_time_received(false),
Piotr Krysikc5ed0452019-07-16 16:17:43 +020091 d_time_samp_ref(osr*GSM_SYMBOL_RATE, resamp_rate),
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070092 d_OSR(osr),
93 d_process_uplink(process_uplink),
94 d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
Piotr Krysikdf978692017-09-27 21:58:24 +020095 d_counter(0), //TODO: use nitems_read instead of d_counter
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070096 d_fcch_start_pos(0),
97 d_freq_offset_setting(0),
98 d_state(fcch_search),
99 d_burst_nr(osr),
100 d_failed_sch(0),
101 d_signal_dbm(-120),
102 d_tseq_nums(tseq_nums),
103 d_cell_allocation(cell_allocation),
104 d_last_time(0.0)
ptrkrysik32c21162015-04-04 14:01:52 +0200105 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700106 /**
107 * Don't send samples to the receiver
108 * until there are at least samples for one
109 */
110 set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR));
111
112 /**
113 * Prepare SCH sequence bits
114 *
115 * (TS_BITS + 2 * GUARD_PERIOD)
116 * Burst and two guard periods
117 * (one guard period is an arbitrary overlap)
118 */
119 gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
120 d_sch_training_seq, gr_complex(0.0, -1.0));
121
122 /* Prepare bits of training sequences */
123 for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
124 /**
125 * If first bit of the sequence is 0
126 * => first symbol is 1, else -1
127 */
128 gr_complex startpoint = train_seq[i][0] == 0 ?
129 gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
130 gmsk_mapper(train_seq[i], N_TRAIN_BITS,
131 d_norm_training_seq[i], startpoint);
132 }
133
134 /* Register output ports */
135 message_port_register_out(pmt::mp("C0"));
136 message_port_register_out(pmt::mp("CX"));
137 message_port_register_out(pmt::mp("measurements"));
138
139 /**
140 * Configure the receiver,
141 * i.e. tell it where to find which burst type
142 */
143 configure_receiver();
144 }
145
146 /* Our virtual destructor */
147 receiver_impl::~receiver_impl() {}
148
149 int
150 receiver_impl::work(
151 int noutput_items,
152 gr_vector_const_void_star &input_items,
153 gr_vector_void_star &output_items)
154 {
155 gr_complex *input = (gr_complex *) input_items[0];
Piotr Krysikc5ed0452019-07-16 16:17:43 +0200156
157 /* Frequency correction loop */ //TODO: move this to FCCH burst processing
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700158 d_freq_offset_tag_in_fcch = false;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700159 std::vector<tag_t> freq_offset_tags;
160 pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset");
Piotr Krysikc5ed0452019-07-16 16:17:43 +0200161 get_tags_in_window(freq_offset_tags, 0, 0, noutput_items, key);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700162
163 if (!freq_offset_tags.empty()) {
piotr4089c1a2014-08-06 14:10:56 +0200164 tag_t freq_offset_tag = freq_offset_tags[0];
Piotr Krysikc5ed0452019-07-16 16:17:43 +0200165 uint64_t tag_offset = freq_offset_tag.offset - nitems_read(0);
Piotr Krysik43af70d2016-07-20 21:37:24 +0200166 d_freq_offset_setting = pmt::to_double(freq_offset_tag.value);
piotr4089c1a2014-08-06 14:10:56 +0200167
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700168 burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr);
169 if (d_state == synchronized && b_type == fcch_burst){
170 uint64_t last_sample_nr =
171 ceil((GUARD_PERIOD + 2.0 * TAIL_BITS + 156.25) * d_OSR) + 1;
172 d_freq_offset_tag_in_fcch = tag_offset < last_sample_nr;
piotrd0bf1492014-02-05 17:27:32 +0100173 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700174 }
175
176 /* Main state machine */
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200177 d_samples_consumed = 0;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700178 switch (d_state) {
179 case fcch_search:
180 fcch_search_handler(input, noutput_items);
piotrd0bf1492014-02-05 17:27:32 +0100181 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700182 case sch_search:
183 sch_search_handler(input, noutput_items);
piotrd0bf1492014-02-05 17:27:32 +0100184 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700185 case synchronized:
Piotr Krysik59257a12019-07-17 10:26:00 +0200186 synchronized_handler(input_items, noutput_items);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700187 break;
188 }
189
Piotr Krysikc5ed0452019-07-16 16:17:43 +0200190 /* Time synchronization */
191 /* Obtaining current time with use of rx_time tag provided i.e. by UHD devices */
192 /* and storing it in time_sample_ref for sample number to time conversion */
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200193
Piotr Krysikc5ed0452019-07-16 16:17:43 +0200194 typedef std::vector<tag_t>::iterator tag_iter;
195 std::vector<tag_t> all_tags;
196 get_tags_in_window(all_tags, 0, 0, d_samples_consumed);
197
198 for(tag_iter itag = all_tags.begin(); itag != all_tags.end(); itag++){
199 if(pmt::eqv(itag->key, pmt::mp("set_resamp_ratio"))){
200 double resamp_rate = pmt::to_double(itag->value);
201 uint64_t N = get_offset_before_resampler(itag->offset);
202 d_time_samp_ref.update(itag->offset, N, resamp_rate);
203 }
204 else if(pmt::eqv(itag->key, pmt::mp("rx_time"))){
205 d_rx_time_received = true;
206 uint64_t N = get_offset_before_resampler(itag->offset);
207 time_spec_t rx_time = time_spec_t(
208 pmt::to_uint64(tuple_ref(itag->value,0)),
209 pmt::to_double(tuple_ref(itag->value,1))
210 );
211 d_time_samp_ref.update(itag->offset, N, rx_time);
212 }
213// else if(pmt::eqv(itag->key, pmt::mp("rx_rate"))){ //to jest źle TODO extra: zamienić to na update samp_rate dal clock_offset_controllera, a on następnie ustawiałby resamp_rate w sterowanym resamplerze
214// d_rx_time_received = true;
215// d_time_samp_ref.set_samp_rate(pmt::to_double(itag->value));
216// }
217 }
218
219 /* Send updates of time for clock offset controller every 0.1 second */
220 time_spec_t current_time = d_time_samp_ref.convert_M_to_ideal_t(nitems_read(0));
221
222 if ((current_time - d_last_time).get_real_secs() > 0.1) {
223 pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"),
224 pmt::from_double(current_time.get_real_secs())); //TODO: przerobić to na parę pmt i w bloku controllera przerabiać to na time_spec_t
225 message_port_pub(pmt::mp("measurements"), msg);
226 d_last_time = current_time;
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200227 }
228
229 return d_samples_consumed;
piotrd0bf1492014-02-05 17:27:32 +0100230 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700231
232 void
233 receiver_impl::fcch_search_handler(gr_complex *input, int noutput_items)
piotrd0bf1492014-02-05 17:27:32 +0100234 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700235 double freq_offset_tmp;
ptrkrysik58213792014-10-30 09:05:15 +0100236
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700237 /* Check if received samples is a FCCN burst */
238 if (!find_fcch_burst(input, noutput_items, freq_offset_tmp))
239 return;
piotr437f5462014-02-04 17:57:25 +0100240
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700241 /* We found it, compose a message */
242 pmt::pmt_t msg = pmt::make_tuple(
243 pmt::mp("freq_offset"),
244 pmt::from_double(freq_offset_tmp - d_freq_offset_setting),
245 pmt::mp("fcch_search")
246 );
Piotr Krysik9bc0fc02017-01-18 21:53:17 +0100247
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700248 /* Notify FCCH loop */
249 message_port_pub(pmt::mp("measurements"), msg);
250
251 /* Update current state */
252 d_state = sch_search;
piotrd0bf1492014-02-05 17:27:32 +0100253 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700254
255 void
256 receiver_impl::sch_search_handler(gr_complex *input, int noutput_items)
257 {
258 std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
259 unsigned char burst_buf[BURST_SIZE];
260 int rc, t1, t2, t3;
261 int burst_start;
262
263 /* Wait until we get a SCH burst */
264 if (!reach_sch_burst(noutput_items))
265 return;
266
267 /* Get channel impulse response from it */
268 burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]);
269
270 /* Detect bits using MLSE detection */
271 detect_burst(input, &channel_imp_resp[0], burst_start, burst_buf);
272
273 /* Attempt to decode BSIC and frame number */
274 rc = decode_sch(&burst_buf[3], &t1, &t2, &t3, &d_ncc, &d_bcc);
275 if (rc) {
276 /**
277 * There is error in the SCH burst,
278 * go back to the FCCH search state
279 */
280 d_state = fcch_search;
281 return;
282 }
283
284 /* Set counter of bursts value */
285 d_burst_nr.set(t1, t2, t3, 0);
286 d_burst_nr++;
287
288 /* Consume samples up to the next guard period */
Piotr Krysik9e861d72019-07-17 10:11:36 +0200289 unsigned int to_consume = burst_start + BURST_SIZE * d_OSR + 4 * d_OSR + 2 * d_OSR + 1; //TODO: figure out where + 2 * d_OSR + 1 comes from
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200290// consume_each(to_consume);
291 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700292
293 /* Update current state */
294 d_state = synchronized;
piotr437f5462014-02-04 17:57:25 +0100295 }
piotr437f5462014-02-04 17:57:25 +0100296
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700297 void
Piotr Krysik59257a12019-07-17 10:26:00 +0200298 receiver_impl::synchronized_handler(gr_vector_const_void_star &input_items,
299 int noutput_items)
piotr437f5462014-02-04 17:57:25 +0100300 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700301 /**
302 * In this state receiver is synchronized and it processes
303 * bursts according to burst type for given burst number
304 */
Piotr Krysik59257a12019-07-17 10:26:00 +0200305 gr_complex *input = (gr_complex *) input_items[0];
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700306 std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
Piotr Krysik3b116892018-02-27 08:43:30 +0100307 size_t inputs_to_process = d_cell_allocation.size();
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700308 unsigned char output_binary[BURST_SIZE];
309 burst_type b_type;
310 int to_consume = 0;
311 int offset = 0;
piotr437f5462014-02-04 17:57:25 +0100312
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700313 if (d_process_uplink)
314 inputs_to_process *= 2;
piotr437f5462014-02-04 17:57:25 +0100315
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700316 /* Process all connected inputs */
Piotr Krysik3b116892018-02-27 08:43:30 +0100317 for (size_t input_nr = 0; input_nr < inputs_to_process; input_nr++) {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700318 input = (gr_complex *) input_items[input_nr];
319 double signal_pwr = 0;
320
321 for (int ii = GUARD_PERIOD; ii < TS_BITS; ii++)
322 signal_pwr += abs(input[ii]) * abs(input[ii]);
323
324 signal_pwr = signal_pwr / (TS_BITS);
325 d_signal_dbm = round(10 * log10(signal_pwr / 50));
326
327 if (input_nr == 0)
328 d_c0_signal_dbm = d_signal_dbm;
329
330 /* Get burst type for given burst number */
331 b_type = input_nr == 0 ?
332 d_channel_conf.get_burst_type(d_burst_nr) : normal_or_noise;
333
334 /* Process burst according to its type */
335 switch (b_type) {
336 case fcch_burst:
piotrd0bf1492014-02-05 17:27:32 +0100337 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700338 if (d_freq_offset_tag_in_fcch)
piotr437f5462014-02-04 17:57:25 +0100339 break;
340
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700341 /* Send all-zero sequence message */
342 send_burst(d_burst_nr, fc_fb, GSMTAP_BURST_FCCH, input_nr);
piotr437f5462014-02-04 17:57:25 +0100343
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700344 /* Extract frequency offset */
345 const unsigned first_sample =
Piotr Krysik9e861d72019-07-17 10:11:36 +0200346 ceil((GUARD_PERIOD/2 + TAIL_BITS) * d_OSR) + 1;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700347 const unsigned last_sample =
348 first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR;
349 double freq_offset_tmp =
350 compute_freq_offset(input, first_sample, last_sample);
piotr437f5462014-02-04 17:57:25 +0100351
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700352 /* Frequency correction loop */
353 pmt::pmt_t msg = pmt::make_tuple(
354 pmt::mp("freq_offset"),
355 pmt::from_double(freq_offset_tmp - d_freq_offset_setting),
356 pmt::mp("synchronized"));
357 message_port_pub(pmt::mp("measurements"), msg);
358
359 break;
360 }
361
362 case sch_burst:
363 {
Piotr Krysikbfb47dc2017-11-11 11:12:02 +0100364 int ncc, bcc;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700365 int t1, t2, t3;
366 int rc;
367
368 /* Get channel impulse response */
369 d_c0_burst_start = get_sch_chan_imp_resp(input,
370 &channel_imp_resp[0]);
371
372 /* Perform MLSE detection */
373 detect_burst(input, &channel_imp_resp[0],
374 d_c0_burst_start, output_binary);
375
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700376 /* Attempt to decode SCH burst */
Piotr Krysikbfb47dc2017-11-11 11:12:02 +0100377 rc = decode_sch(&output_binary[3], &t1, &t2, &t3, &ncc, &bcc);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700378 if (rc) {
379 if (++d_failed_sch >= MAX_SCH_ERRORS) {
380 /* We have to resynchronize, change state */
381 d_state = fcch_search;
382
383 /* Frequency correction loop */
384 pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),
385 pmt::from_double(0.0),pmt::mp("sync_loss"));
386 message_port_pub(pmt::mp("measurements"), msg);
piotr437f5462014-02-04 17:57:25 +0100387 }
Piotr Krysik9e861d72019-07-17 10:11:36 +0200388 } else {
389 /* Compose a message with GSMTAP header and bits */
390 send_burst(d_burst_nr, output_binary,
391 GSMTAP_BURST_SCH, input_nr, d_c0_burst_start);
piotr437f5462014-02-04 17:57:25 +0100392
Piotr Krysik9e861d72019-07-17 10:11:36 +0200393 /**
394 * Decoding was successful, now
395 * compute offset from burst_start,
396 * burst should start after a guard period.
397 */
398 offset = d_c0_burst_start - 2 * d_OSR; //TODO: figure out the -2 * d_OSR part
399 to_consume += offset;
400 d_failed_sch = 0;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700401 }
piotr437f5462014-02-04 17:57:25 +0100402
piotrd0bf1492014-02-05 17:27:32 +0100403 }
Piotr Krysik9e861d72019-07-17 10:11:36 +0200404 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700405
406 case normal_burst:
407 {
408 float normal_corr_max;
409 /**
410 * Get channel impulse response for given
411 * training sequence number - d_bcc
412 */
413 d_c0_burst_start = get_norm_chan_imp_resp(input,
414 &channel_imp_resp[0], &normal_corr_max, d_bcc);
415
416 /* Perform MLSE detection */
417 detect_burst(input, &channel_imp_resp[0],
418 d_c0_burst_start, output_binary);
419
420 /* Compose a message with GSMTAP header and bits */
421 send_burst(d_burst_nr, output_binary,
Piotr Krysikdf978692017-09-27 21:58:24 +0200422 GSMTAP_BURST_NORMAL, input_nr, d_c0_burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700423
424 break;
425 }
426
427 case dummy_or_normal:
428 {
429 unsigned int normal_burst_start, dummy_burst_start;
430 float dummy_corr_max, normal_corr_max;
431
432 dummy_burst_start = get_norm_chan_imp_resp(input,
433 &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
434 normal_burst_start = get_norm_chan_imp_resp(input,
435 &channel_imp_resp[0], &normal_corr_max, d_bcc);
436
437 if (normal_corr_max > dummy_corr_max) {
438 d_c0_burst_start = normal_burst_start;
439
440 /* Perform MLSE detection */
441 detect_burst(input, &channel_imp_resp[0],
442 normal_burst_start, output_binary);
443
444 /* Compose a message with GSMTAP header and bits */
445 send_burst(d_burst_nr, output_binary,
Piotr Krysikdf978692017-09-27 21:58:24 +0200446 GSMTAP_BURST_NORMAL, input_nr, normal_burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700447 } else {
448 d_c0_burst_start = dummy_burst_start;
449
450 /* Compose a message with GSMTAP header and bits */
451 send_burst(d_burst_nr, dummy_burst,
Piotr Krysikdf978692017-09-27 21:58:24 +0200452 GSMTAP_BURST_DUMMY, input_nr, dummy_burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700453 }
454
455 break;
456 }
457
458 case normal_or_noise:
459 {
460 std::vector<gr_complex> v(input, input + noutput_items);
461 float normal_corr_max = -1e6;
Piotr Krysik3b116892018-02-27 08:43:30 +0100462// float normal_corr_max_tmp;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700463 unsigned int burst_start;
464 int max_tn, tseq_num;
465
466 if (d_tseq_nums.size() == 0) {
467 /**
468 * There is no information about training sequence,
469 * however the receiver can detect it with use of a
470 * very simple algorithm based on finding
471 */
472 get_norm_chan_imp_resp(input, &channel_imp_resp[0],
473 &normal_corr_max, 0);
474
475 float ts_max = normal_corr_max;
476 int ts_max_num = 0;
477
478 for (int ss = 1; ss <= 7; ss++) {
479 get_norm_chan_imp_resp(input, &channel_imp_resp[0],
480 &normal_corr_max, ss);
481
482 if (ts_max < normal_corr_max) {
483 ts_max = normal_corr_max;
484 ts_max_num = ss;
485 }
486 }
487
488 d_tseq_nums.push_back(ts_max_num);
489 }
490
491 /* Choose proper training sequence number */
492 tseq_num = input_nr <= d_tseq_nums.size() ?
493 d_tseq_nums[input_nr - 1] : d_tseq_nums.back();
494
495 /* Get channel impulse response */
496 burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0],
497 &normal_corr_max, tseq_num);
498
499 /* Perform MLSE detection */
500 detect_burst(input, &channel_imp_resp[0],
501 burst_start, output_binary);
502
503 /* Compose a message with GSMTAP header and bits */
Piotr Krysikdf978692017-09-27 21:58:24 +0200504 send_burst(d_burst_nr, output_binary, GSMTAP_BURST_NORMAL, input_nr, burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700505
506 break;
507 }
508
509 case dummy:
510 send_burst(d_burst_nr, dummy_burst, GSMTAP_BURST_DUMMY, input_nr);
511 break;
512
513 case rach_burst:
514 case empty:
515 /* Do nothing */
516 break;
517 }
518
519 if (input_nr == input_items.size() - 1) {
520 /* Go to the next burst */
521 d_burst_nr++;
522
523 /* Consume samples of the burst up to next guard period */
524 to_consume += TS_BITS * d_OSR + d_burst_nr.get_offset();
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200525// consume_each(to_consume);
526 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700527 }
528 }
529 }
530
531 bool
532 receiver_impl::find_fcch_burst(const gr_complex *input,
533 const int nitems, double &computed_freq_offset)
534 {
535 /* Circular buffer used to scan through signal to find */
536 boost::circular_buffer<float>
537 phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR);
538 boost::circular_buffer<float>::iterator buffer_iter;
539
540 float lowest_max_min_diff;
541 float phase_diff; /* Best match for FCCH burst */
542 float min_phase_diff;
543 float max_phase_diff;
544 double best_sum = 0;
545 gr_complex conjprod;
546 int start_pos;
547 int hit_count;
548 int miss_count;
549
550 int sample_number = 0;
551 int to_consume = 0;
552 bool result = false;
553 bool end = false;
554
555 /* Possible states of FCCH search algorithm */
556 enum states
557 {
558 init, /* initialize variables */
559 search, /* search for positive samples */
560 found_something, /* search for FCCH and the best position of it */
561 fcch_found, /* when FCCH was found */
562 search_fail /* when there is no FCCH in the input vector */
563 } fcch_search_state;
564
565 /* Set initial state */
566 fcch_search_state = init;
567
568 while (!end)
569 {
570 switch (fcch_search_state) {
571 case init:
572 {
573 hit_count = 0;
574 miss_count = 0;
575 start_pos = -1;
576 lowest_max_min_diff = 99999;
577 phase_diff_buffer.clear();
578
579 /* Change current state */
580 fcch_search_state = search;
581
582 break;
583 }
584
585 case search:
586 {
587 sample_number++;
588
589 if (sample_number > nitems - FCCH_HITS_NEEDED * d_OSR) {
590 /**
591 * If it isn't possible to find FCCH, because
592 * there is too few samples left to look into,
593 * don't do anything with those samples which are left
594 * and consume only those which were checked
595 */
596 to_consume = sample_number;
597 fcch_search_state = search_fail;
598 break;
599 }
600
601 phase_diff = compute_phase_diff(input[sample_number],
602 input[sample_number - 1]);
603
604 /**
605 * If a positive phase difference was found
606 * switch to state in which searches for FCCH
607 */
608 if (phase_diff > 0) {
609 to_consume = sample_number;
610 fcch_search_state = found_something;
611 } else {
612 fcch_search_state = search;
613 }
614
615 break;
616 }
617
618 case found_something:
619 {
620 if (phase_diff > 0)
621 hit_count++;
622 else
623 miss_count++;
624
625 if ((miss_count >= FCCH_MAX_MISSES * d_OSR)
626 && (hit_count <= FCCH_HITS_NEEDED * d_OSR))
627 {
628 /* If miss_count exceeds limit before hit_count */
629 fcch_search_state = init;
630 continue;
631 }
632
633 if (((miss_count >= FCCH_MAX_MISSES * d_OSR)
634 && (hit_count > FCCH_HITS_NEEDED * d_OSR))
635 || (hit_count > 2 * FCCH_HITS_NEEDED * d_OSR))
636 {
637 /**
638 * If hit_count and miss_count exceeds
639 * limit then FCCH was found
640 */
641 fcch_search_state = fcch_found;
642 continue;
643 }
644
645 if ((miss_count < FCCH_MAX_MISSES * d_OSR)
646 && (hit_count > FCCH_HITS_NEEDED * d_OSR))
647 {
648 /**
649 * Find difference between minimal and maximal
650 * element in the buffer. For FCCH this value
651 * should be low. This part is searching for
652 * a region where this value is lowest.
653 */
654 min_phase_diff = *(min_element(phase_diff_buffer.begin(),
655 phase_diff_buffer.end()));
656 max_phase_diff = *(max_element(phase_diff_buffer.begin(),
657 phase_diff_buffer.end()));
658
659 if (lowest_max_min_diff > max_phase_diff - min_phase_diff) {
660 lowest_max_min_diff = max_phase_diff - min_phase_diff;
661 start_pos = sample_number - FCCH_HITS_NEEDED
662 * d_OSR - FCCH_MAX_MISSES * d_OSR;
663 best_sum = 0;
664
665 for (buffer_iter = phase_diff_buffer.begin();
666 buffer_iter != (phase_diff_buffer.end());
667 buffer_iter++) {
668 /* Store best value of phase offset sum */
669 best_sum += *buffer_iter - (M_PI / 2) / d_OSR;
670 }
671 }
672 }
673
674 /* If there is no single sample left to check */
675 if (++sample_number >= nitems) {
676 fcch_search_state = search_fail;
677 continue;
678 }
679
680 phase_diff = compute_phase_diff(input[sample_number],
681 input[sample_number-1]);
682 phase_diff_buffer.push_back(phase_diff);
683 fcch_search_state = found_something;
684
685 break;
686 }
piotrd0bf1492014-02-05 17:27:32 +0100687
688 case fcch_found:
689 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700690 /* Consume one FCCH burst */
691 to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1;
692 d_fcch_start_pos = d_counter + start_pos;
piotrd0bf1492014-02-05 17:27:32 +0100693
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700694 /**
695 * Compute frequency offset
696 *
697 * 1625000.0 / 6 - GMSK symbol rate in GSM
698 */
699 double phase_offset = best_sum / FCCH_HITS_NEEDED;
700 double freq_offset = phase_offset * 1625000.0 / 6 / (2 * M_PI);
701 computed_freq_offset = freq_offset;
piotrd0bf1492014-02-05 17:27:32 +0100702
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700703 end = true;
704 result = true;
piotrd0bf1492014-02-05 17:27:32 +0100705
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700706 break;
piotrd0bf1492014-02-05 17:27:32 +0100707 }
piotr437f5462014-02-04 17:57:25 +0100708
piotrd0bf1492014-02-05 17:27:32 +0100709 case search_fail:
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700710 end = true;
711 result = false;
712 break;
piotr437f5462014-02-04 17:57:25 +0100713 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700714 }
715
716 d_counter += to_consume;
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200717// consume_each(to_consume);
718 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700719
720 return result;
piotr437f5462014-02-04 17:57:25 +0100721 }
722
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700723 double
724 receiver_impl::compute_freq_offset(const gr_complex * input,
725 unsigned first_sample, unsigned last_sample)
Piotr Krysik654d6522017-01-23 21:53:48 +0100726 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700727 double phase_sum = 0;
728 unsigned ii;
729
730 for (ii = first_sample; ii < last_sample; ii++)
731 {
732 double phase_diff = compute_phase_diff(input[ii],
733 input[ii-1]) - (M_PI / 2) / d_OSR;
Piotr Krysik654d6522017-01-23 21:53:48 +0100734 phase_sum += phase_diff;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700735 }
736
737 double phase_offset = phase_sum / (last_sample - first_sample);
738 double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
739
740 return freq_offset;
Piotr Krysik654d6522017-01-23 21:53:48 +0100741 }
742
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700743 inline float
744 receiver_impl::compute_phase_diff(gr_complex val1, gr_complex val2)
piotrd0bf1492014-02-05 17:27:32 +0100745 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700746 gr_complex conjprod = val1 * conj(val2);
747 return fast_atan2f(imag(conjprod), real(conjprod));
piotrd0bf1492014-02-05 17:27:32 +0100748 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700749
750 bool
751 receiver_impl::reach_sch_burst(const int nitems)
piotrd0bf1492014-02-05 17:27:32 +0100752 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700753 /* It just consumes samples to get near to a SCH burst */
754 int to_consume = 0;
755 bool result = false;
756 unsigned sample_nr = d_fcch_start_pos
757 + (FRAME_BITS - SAFETY_MARGIN) * d_OSR;
758
759 /* Consume samples until d_counter will be equal to sample_nr */
760 if (d_counter < sample_nr) {
761 to_consume = d_counter + nitems >= sample_nr ?
762 sample_nr - d_counter : nitems;
763 } else {
piotr437f5462014-02-04 17:57:25 +0100764 to_consume = 0;
765 result = true;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700766 }
767
768 d_counter += to_consume;
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200769// consume_each(to_consume);
770 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700771
772 return result;
piotr437f5462014-02-04 17:57:25 +0100773 }
774
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700775 int
776 receiver_impl::get_sch_chan_imp_resp(const gr_complex *input,
777 gr_complex * chan_imp_resp)
piotr437f5462014-02-04 17:57:25 +0100778 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700779 std::vector<gr_complex> correlation_buffer;
780 std::vector<float> window_energy_buffer;
781 std::vector<float> power_buffer;
782
783 int chan_imp_resp_center = 0;
784 int strongest_window_nr;
785 int burst_start;
786 float energy = 0;
787
Piotr Krysik9e861d72019-07-17 10:11:36 +0200788 int len = (SYNC_SEARCH_RANGE) * d_OSR;
789 int start = (SYNC_START) * d_OSR;
790 int stop = start + len;
791 for (int ii = start; ii < stop; ii++) {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700792 gr_complex correlation = correlate_sequence(&d_sch_training_seq[5],
793 N_SYNC_BITS - 10, &input[ii]);
piotr437f5462014-02-04 17:57:25 +0100794 correlation_buffer.push_back(correlation);
795 power_buffer.push_back(std::pow(abs(correlation), 2));
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700796 }
797
798 /* Compute window energies */
799 std::vector<float>::iterator iter = power_buffer.begin();
800 while (iter != power_buffer.end()) {
ptrkrysikef5e2db2015-01-03 12:10:14 +0100801 std::vector<float>::iterator iter_ii = iter;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700802 bool loop_end = false;
piotr437f5462014-02-04 17:57:25 +0100803 energy = 0;
804
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700805 for (int ii = 0; ii < (d_chan_imp_length) * d_OSR; ii++, iter_ii++) {
806 if (iter_ii == power_buffer.end()) {
807 loop_end = true;
piotrd0bf1492014-02-05 17:27:32 +0100808 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700809 }
810
811 energy += (*iter_ii);
piotr437f5462014-02-04 17:57:25 +0100812 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700813
814 if (loop_end)
815 break;
816
piotr437f5462014-02-04 17:57:25 +0100817 window_energy_buffer.push_back(energy);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700818 iter++;
819 }
piotr437f5462014-02-04 17:57:25 +0100820
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700821 strongest_window_nr = max_element(window_energy_buffer.begin(),
822 window_energy_buffer.end()) - window_energy_buffer.begin();
piotr437f5462014-02-04 17:57:25 +0100823
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700824#if 0
825 d_channel_imp_resp.clear();
826#endif
827
828 float max_correlation = 0;
829 for (int ii = 0; ii < (d_chan_imp_length) * d_OSR; ii++) {
piotr437f5462014-02-04 17:57:25 +0100830 gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700831 if (abs(correlation) > max_correlation) {
832 chan_imp_resp_center = ii;
833 max_correlation = abs(correlation);
piotr437f5462014-02-04 17:57:25 +0100834 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700835
836#if 0
837 d_channel_imp_resp.push_back(correlation);
838#endif
839
piotr437f5462014-02-04 17:57:25 +0100840 chan_imp_resp[ii] = correlation;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700841 }
842
843 burst_start = strongest_window_nr + chan_imp_resp_center
Piotr Krysik9e861d72019-07-17 10:11:36 +0200844 - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_START * d_OSR;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700845 return burst_start;
piotr437f5462014-02-04 17:57:25 +0100846 }
847
848
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700849 void
850 receiver_impl::detect_burst(const gr_complex * input,
851 gr_complex * chan_imp_resp, int burst_start,
852 unsigned char * output_binary)
piotr437f5462014-02-04 17:57:25 +0100853 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700854 std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
855 unsigned int stop_states[2] = {4, 12};
856 gr_complex filtered_burst[BURST_SIZE];
857 gr_complex rhh[CHAN_IMP_RESP_LENGTH];
858 float output[BURST_SIZE];
859 int start_state = 3;
860
861 autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length*d_OSR);
862 for (int ii = 0; ii < d_chan_imp_length; ii++)
piotr437f5462014-02-04 17:57:25 +0100863 rhh[ii] = conj(rhh_temp[ii*d_OSR]);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700864
865 mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
866 d_chan_imp_length * d_OSR, filtered_burst);
867
868 viterbi_detector(filtered_burst, BURST_SIZE, rhh,
869 start_state, stop_states, 2, output);
870
871 for (int i = 0; i < BURST_SIZE; i++)
872 output_binary[i] = output[i] > 0;
piotr437f5462014-02-04 17:57:25 +0100873 }
874
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700875 void
876 receiver_impl::gmsk_mapper(const unsigned char * input,
877 int nitems, gr_complex * gmsk_output, gr_complex start_point)
piotr437f5462014-02-04 17:57:25 +0100878 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700879 gr_complex j = gr_complex(0.0, 1.0);
880 gmsk_output[0] = start_point;
piotr437f5462014-02-04 17:57:25 +0100881
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700882 int previous_symbol = 2 * input[0] - 1;
883 int current_symbol;
884 int encoded_symbol;
piotr437f5462014-02-04 17:57:25 +0100885
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700886 for (int i = 1; i < nitems; i++) {
887 /* Change bits representation to NRZ */
piotr437f5462014-02-04 17:57:25 +0100888 current_symbol = 2 * input[i] - 1;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700889
890 /* Differentially encode */
piotr437f5462014-02-04 17:57:25 +0100891 encoded_symbol = current_symbol * previous_symbol;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700892
893 /* And do GMSK mapping */
894 gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
895 * gmsk_output[i-1];
896
piotr437f5462014-02-04 17:57:25 +0100897 previous_symbol = current_symbol;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700898 }
piotr437f5462014-02-04 17:57:25 +0100899 }
900
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700901 gr_complex
902 receiver_impl::correlate_sequence(const gr_complex * sequence,
903 int length, const gr_complex * input)
piotr437f5462014-02-04 17:57:25 +0100904 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700905 gr_complex result(0.0, 0.0);
906
907 for (int ii = 0; ii < length; ii++)
908 result += sequence[ii] * conj(input[ii * d_OSR]);
909
910 return result / gr_complex(length, 0);
911 }
912
913 /* Computes autocorrelation for positive arguments */
914 inline void
915 receiver_impl::autocorrelation(const gr_complex * input,
916 gr_complex * out, int nitems)
917 {
918 for (int k = nitems - 1; k >= 0; k--) {
piotr437f5462014-02-04 17:57:25 +0100919 out[k] = gr_complex(0, 0);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700920 for (int i = k; i < nitems; i++)
921 out[k] += input[i] * conj(input[i - k]);
922 }
piotr437f5462014-02-04 17:57:25 +0100923 }
924
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700925 inline void
926 receiver_impl::mafi(const gr_complex * input, int nitems,
927 gr_complex * filter, int filter_length, gr_complex * output)
piotr437f5462014-02-04 17:57:25 +0100928 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700929 for (int n = 0; n < nitems; n++) {
930 int a = n * d_OSR;
piotr437f5462014-02-04 17:57:25 +0100931 output[n] = 0;
piotr437f5462014-02-04 17:57:25 +0100932
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700933 for (int ii = 0; ii < filter_length; ii++) {
934 if ((a + ii) >= nitems * d_OSR)
piotrd0bf1492014-02-05 17:27:32 +0100935 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700936
937 output[n] += input[a + ii] * filter[ii];
piotr437f5462014-02-04 17:57:25 +0100938 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700939 }
940 }
941
942 /* Especially computations of strongest_window_nr */
943 int
944 receiver_impl::get_norm_chan_imp_resp(const gr_complex *input,
945 gr_complex *chan_imp_resp, float *corr_max, int bcc)
946 {
947 std::vector<gr_complex> correlation_buffer;
948 std::vector<float> window_energy_buffer;
949 std::vector<float> power_buffer;
950
951 int search_center = (int) (TRAIN_POS + GUARD_PERIOD) * d_OSR;
952 int search_start_pos = search_center + 1 - 5 * d_OSR;
953 int search_stop_pos = search_center
954 + d_chan_imp_length * d_OSR + 5 * d_OSR;
955
956 for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
957 gr_complex correlation = correlate_sequence(
958 &d_norm_training_seq[bcc][TRAIN_BEGINNING],
959 N_TRAIN_BITS - 10, &input[ii]);
960 correlation_buffer.push_back(correlation);
961 power_buffer.push_back(std::pow(abs(correlation), 2));
962 }
963
964#if 0
965 plot(power_buffer);
966#endif
967
968 /* Compute window energies */
969 std::vector<float>::iterator iter = power_buffer.begin();
970 while (iter != power_buffer.end()) {
971 std::vector<float>::iterator iter_ii = iter;
972 bool loop_end = false;
973 float energy = 0;
974
975 int len = d_chan_imp_length * d_OSR;
976 for (int ii = 0; ii < len; ii++, iter_ii++) {
977 if (iter_ii == power_buffer.end()) {
978 loop_end = true;
979 break;
980 }
981
982 energy += (*iter_ii);
983 }
984
985 if (loop_end)
986 break;
piotr437f5462014-02-04 17:57:25 +0100987
988 window_energy_buffer.push_back(energy);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700989 iter++;
990 }
piotr437f5462014-02-04 17:57:25 +0100991
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700992 /* Calculate the strongest window number */
993 int strongest_window_nr = max_element(window_energy_buffer.begin(),
994 window_energy_buffer.end() - d_chan_imp_length * d_OSR)
995 - window_energy_buffer.begin();
996
997 if (strongest_window_nr < 0)
998 strongest_window_nr = 0;
999
1000 float max_correlation = 0;
1001 for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
piotr437f5462014-02-04 17:57:25 +01001002 gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
piotrd0bf1492014-02-05 17:27:32 +01001003 if (abs(correlation) > max_correlation)
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001004 max_correlation = abs(correlation);
1005
1006#if 0
1007 d_channel_imp_resp.push_back(correlation);
1008#endif
1009
piotr437f5462014-02-04 17:57:25 +01001010 chan_imp_resp[ii] = correlation;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001011 }
1012
1013 *corr_max = max_correlation;
1014
1015 /**
1016 * Compute first sample position, which corresponds
1017 * to the first sample of the impulse response
1018 */
1019 return search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR;
piotr437f5462014-02-04 17:57:25 +01001020 }
1021
1022
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001023 void
1024 receiver_impl::send_burst(burst_counter burst_nr,
1025 const unsigned char * burst_binary, uint8_t burst_type,
Piotr Krysik8a8d41a2018-04-16 22:21:29 +02001026 size_t input_nr, unsigned int burst_start)
ptrkrysik380dea82015-08-06 10:11:58 +02001027 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001028 /* Buffer for GSMTAP header and burst */
1029 uint8_t buf[sizeof(gsmtap_hdr) + BURST_SIZE];
1030 uint32_t frame_number;
1031 uint16_t arfcn;
1032 uint8_t tn;
ptrkrysik617ba032014-11-21 10:11:05 +01001033
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001034 /* Set pointers to GSMTAP header and burst inside buffer */
1035 struct gsmtap_hdr *tap_header = (struct gsmtap_hdr *) buf;
1036 uint8_t *burst = buf + sizeof(gsmtap_hdr);
1037
1038 tap_header->version = GSMTAP_VERSION;
1039 tap_header->hdr_len = sizeof(gsmtap_hdr) / 4;
1040 tap_header->type = GSMTAP_TYPE_UM_BURST;
1041 tap_header->sub_type = burst_type;
1042
1043 bool dl_burst = !(input_nr >= d_cell_allocation.size());
1044 if (dl_burst) {
1045 tn = static_cast<uint8_t>(d_burst_nr.get_timeslot_nr());
1046 frame_number = htobe32(d_burst_nr.get_frame_nr());
1047 arfcn = htobe16(d_cell_allocation[input_nr]);
1048 } else {
1049 input_nr -= d_cell_allocation.size();
1050 tn = static_cast<uint8_t>
1051 (d_burst_nr.subtract_timeslots(3).get_timeslot_nr());
1052 frame_number = htobe32(
1053 d_burst_nr.subtract_timeslots(3).get_frame_nr());
1054 arfcn = htobe16(
1055 d_cell_allocation[input_nr] | GSMTAP_ARFCN_F_UPLINK);
1056 }
1057
1058 tap_header->frame_number = frame_number;
1059 tap_header->timeslot = tn;
1060 tap_header->arfcn = arfcn;
1061
1062 tap_header->signal_dbm = static_cast<int8_t>(d_signal_dbm);
1063 tap_header->snr_db = 0; /* FIXME: Can we calculate this? */
1064
Piotr Krysikdf978692017-09-27 21:58:24 +02001065 pmt::pmt_t pdu_header = pmt::make_dict();
1066
1067 /* Add timestamp of the first sample - if available */
Piotr Krysikc5ed0452019-07-16 16:17:43 +02001068 if(d_rx_time_received && burst_start != -1) {
1069 time_spec_t time_spec_of_first_sample =
1070 d_time_samp_ref.convert_M_to_t(nitems_read(0) + burst_start);
Piotr Krysikdf978692017-09-27 21:58:24 +02001071 uint64_t full = time_spec_of_first_sample.get_full_secs();
1072 double frac = time_spec_of_first_sample.get_frac_secs();
Piotr Krysikc5ed0452019-07-16 16:17:43 +02001073 int64_t N = d_time_samp_ref.convert_M_to_N(nitems_read(0) + burst_start);
1074 time_spec_t ref_t = d_time_samp_ref.get_ref_time();
1075 pdu_header =
1076 pmt::dict_add(pdu_header, pmt::mp("fn_time"),
Piotr Krysik96f2ea72017-10-16 15:47:08 +02001077 pmt::cons(
1078 pmt::cons(pmt::from_uint64(be32toh(frame_number)), pmt::from_uint64(tn)),
1079 pmt::cons(pmt::from_uint64(full), pmt::from_double(frac))));
Piotr Krysikdf978692017-09-27 21:58:24 +02001080 }
1081
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001082 /* Copy burst to the buffer */
1083 memcpy(burst, burst_binary, BURST_SIZE);
1084
1085 /* Allocate a new message */
1086 pmt::pmt_t blob = pmt::make_blob(buf, sizeof(gsmtap_hdr) + BURST_SIZE);
Piotr Krysik96f2ea72017-10-16 15:47:08 +02001087 pmt::pmt_t msg = pmt::cons(pdu_header, blob);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001088
1089 /* Send message */
1090 if (input_nr == 0)
ptrkrysike518bbf2014-11-06 14:50:59 +01001091 message_port_pub(pmt::mp("C0"), msg);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001092 else
ptrkrysike518bbf2014-11-06 14:50:59 +01001093 message_port_pub(pmt::mp("CX"), msg);
1094 }
piotr6d152d92014-02-21 00:02:44 +01001095
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001096 void
1097 receiver_impl::configure_receiver(void)
1098 {
1099 d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51);
1100 d_channel_conf.set_burst_types(TIMESLOT0, TEST51,
1101 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
1102 d_channel_conf.set_burst_types(TIMESLOT0, TEST_CCH_FRAMES,
1103 sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), dummy_or_normal);
1104 d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES,
1105 sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst);
1106 d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES,
1107 sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst);
piotr437f5462014-02-04 17:57:25 +01001108
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001109 d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_51);
1110 d_channel_conf.set_burst_types(TIMESLOT1, TEST51,
1111 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotr437f5462014-02-04 17:57:25 +01001112
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001113 d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_51);
1114 d_channel_conf.set_burst_types(TIMESLOT2, TEST51,
1115 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotr437f5462014-02-04 17:57:25 +01001116
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001117 d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_51);
1118 d_channel_conf.set_burst_types(TIMESLOT3, TEST51,
1119 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
ptrkrysike518bbf2014-11-06 14:50:59 +01001120
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001121 d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_51);
1122 d_channel_conf.set_burst_types(TIMESLOT4, TEST51,
1123 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotrf2b6a1b2014-08-04 11:28:59 +02001124
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001125 d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_51);
1126 d_channel_conf.set_burst_types(TIMESLOT5, TEST51,
1127 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotr437f5462014-02-04 17:57:25 +01001128
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001129 d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_51);
1130 d_channel_conf.set_burst_types(TIMESLOT6, TEST51,
1131 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
1132
1133 d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_51);
1134 d_channel_conf.set_burst_types(TIMESLOT7, TEST51,
1135 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
1136 }
1137
1138 void
1139 receiver_impl::set_cell_allocation(
1140 const std::vector<int> &cell_allocation)
1141 {
1142 d_cell_allocation = cell_allocation;
1143 }
1144
1145 void
1146 receiver_impl::set_tseq_nums(const std::vector<int> &tseq_nums)
1147 {
1148 d_tseq_nums = tseq_nums;
1149 }
1150
1151 void
1152 receiver_impl::reset(void)
1153 {
1154 d_state = fcch_search;
1155 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001156 } /* namespace gsm */
piotr437f5462014-02-04 17:57:25 +01001157} /* namespace gr */