blob: cf080cb3a34de04f2018fd110686044e8665a140 [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>
34#include <math.h>
35#include <vector>
ptrkrysik3be74a72014-12-13 10:11:00 +010036
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070037#include <boost/circular_buffer.hpp>
38#include <boost/scoped_ptr.hpp>
ptrkrysik3be74a72014-12-13 10:11:00 +010039#include <grgsm/endian.h>
ptrkrysik58213792014-10-30 09:05:15 +010040
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070041#include "receiver_impl.h"
42#include "viterbi_detector.h"
43#include "sch.h"
44
45#if 0
46/* Files included for debuging */
47#include "plotting/plotting.hpp"
48#include <pthread.h>
49#include <iomanip>
50#endif
piotr437f5462014-02-04 17:57:25 +010051
52#define SYNC_SEARCH_RANGE 30
53
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(
62 int osr, const std::vector<int> &cell_allocation,
63 const std::vector<int> &tseq_nums, bool process_uplink)
piotr437f5462014-02-04 17:57:25 +010064 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070065 return gnuradio::get_initial_sptr
66 (new receiver_impl(osr, cell_allocation,
67 tseq_nums, process_uplink));
piotr437f5462014-02-04 17:57:25 +010068 }
69
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070070 /* The private constructor */
71 receiver_impl::receiver_impl(
72 int osr, const std::vector<int> &cell_allocation,
73 const std::vector<int> &tseq_nums, bool process_uplink
74 ) : gr::sync_block("receiver",
75 gr::io_signature::make(1, -1, sizeof(gr_complex)),
76 gr::io_signature::make(0, 0, 0)),
Piotr Krysik96f2ea72017-10-16 15:47:08 +020077 d_samples_consumed(0),
Piotr Krysikdf978692017-09-27 21:58:24 +020078 d_rx_time_received(false),
79 d_time_samp_ref(GSM_SYMBOL_RATE * osr),
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070080 d_OSR(osr),
81 d_process_uplink(process_uplink),
82 d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
Piotr Krysikdf978692017-09-27 21:58:24 +020083 d_counter(0), //TODO: use nitems_read instead of d_counter
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070084 d_fcch_start_pos(0),
85 d_freq_offset_setting(0),
86 d_state(fcch_search),
87 d_burst_nr(osr),
88 d_failed_sch(0),
89 d_signal_dbm(-120),
90 d_tseq_nums(tseq_nums),
91 d_cell_allocation(cell_allocation),
92 d_last_time(0.0)
ptrkrysik32c21162015-04-04 14:01:52 +020093 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +070094 /**
95 * Don't send samples to the receiver
96 * until there are at least samples for one
97 */
98 set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR));
99
100 /**
101 * Prepare SCH sequence bits
102 *
103 * (TS_BITS + 2 * GUARD_PERIOD)
104 * Burst and two guard periods
105 * (one guard period is an arbitrary overlap)
106 */
107 gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
108 d_sch_training_seq, gr_complex(0.0, -1.0));
109
110 /* Prepare bits of training sequences */
111 for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
112 /**
113 * If first bit of the sequence is 0
114 * => first symbol is 1, else -1
115 */
116 gr_complex startpoint = train_seq[i][0] == 0 ?
117 gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
118 gmsk_mapper(train_seq[i], N_TRAIN_BITS,
119 d_norm_training_seq[i], startpoint);
120 }
121
122 /* Register output ports */
123 message_port_register_out(pmt::mp("C0"));
124 message_port_register_out(pmt::mp("CX"));
125 message_port_register_out(pmt::mp("measurements"));
126
127 /**
128 * Configure the receiver,
129 * i.e. tell it where to find which burst type
130 */
131 configure_receiver();
132 }
133
134 /* Our virtual destructor */
135 receiver_impl::~receiver_impl() {}
136
137 int
138 receiver_impl::work(
139 int noutput_items,
140 gr_vector_const_void_star &input_items,
141 gr_vector_void_star &output_items)
142 {
143 gr_complex *input = (gr_complex *) input_items[0];
144 uint64_t start = nitems_read(0);
145 uint64_t stop = start + noutput_items;
146 d_freq_offset_tag_in_fcch = false;
147
148#if 0
149 /* FIXME: jak zrobić to rzutowanie poprawnie */
150 std::vector<const gr_complex *> iii =
151 (std::vector<const gr_complex *>) input_items;
152#endif
Piotr Krysikdf978692017-09-27 21:58:24 +0200153
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700154
155 /* Time synchronization loop */
156 float current_time =
157 static_cast<float>(start / (GSM_SYMBOL_RATE * d_OSR));
158 if ((current_time - d_last_time) > 0.1) {
159 pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"),
160 pmt::from_double(current_time));
ptrkrysik32c21162015-04-04 14:01:52 +0200161 message_port_pub(pmt::mp("measurements"), msg);
162 d_last_time = current_time;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700163 }
ptrkrysik32c21162015-04-04 14:01:52 +0200164
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700165 /* Frequency correction loop */
166 std::vector<tag_t> freq_offset_tags;
167 pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset");
168 get_tags_in_range(freq_offset_tags, 0, start, stop, key);
169
170 if (!freq_offset_tags.empty()) {
piotr4089c1a2014-08-06 14:10:56 +0200171 tag_t freq_offset_tag = freq_offset_tags[0];
Piotr Krysik43af70d2016-07-20 21:37:24 +0200172 uint64_t tag_offset = freq_offset_tag.offset - start;
Piotr Krysik43af70d2016-07-20 21:37:24 +0200173 d_freq_offset_setting = pmt::to_double(freq_offset_tag.value);
piotr4089c1a2014-08-06 14:10:56 +0200174
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700175 burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr);
176 if (d_state == synchronized && b_type == fcch_burst){
177 uint64_t last_sample_nr =
178 ceil((GUARD_PERIOD + 2.0 * TAIL_BITS + 156.25) * d_OSR) + 1;
179 d_freq_offset_tag_in_fcch = tag_offset < last_sample_nr;
piotrd0bf1492014-02-05 17:27:32 +0100180 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700181 }
Piotr Krysikdf978692017-09-27 21:58:24 +0200182
183 /* Obtaining current time with use of rx_time tag provided i.e. by UHD devices */
184 /* And storing it in time_sample_ref for sample number to time conversion */
185 std::vector<tag_t> rx_time_tags;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700186
187 /* Main state machine */
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200188 d_samples_consumed = 0;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700189 switch (d_state) {
190 case fcch_search:
191 fcch_search_handler(input, noutput_items);
piotrd0bf1492014-02-05 17:27:32 +0100192 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700193 case sch_search:
194 sch_search_handler(input, noutput_items);
piotrd0bf1492014-02-05 17:27:32 +0100195 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700196 case synchronized:
197 synchronized_handler(input, input_items, noutput_items);
198 break;
199 }
200
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200201 get_tags_in_window(rx_time_tags, 0, 0, d_samples_consumed, pmt::string_to_symbol("rx_time"));
202 if(!rx_time_tags.empty()){
203 d_rx_time_received = true;
204 tag_t rx_time_tag = *(rx_time_tags.begin());
205
206 uint64_t rx_time_full_part = to_uint64(tuple_ref(rx_time_tag.value,0));
207 double rx_time_frac_part = to_double(tuple_ref(rx_time_tag.value,1));
208
209 time_spec_t current_rx_time = time_spec_t(rx_time_full_part, rx_time_frac_part);
210 uint64_t current_start_offset = rx_time_tag.offset;
211 d_time_samp_ref.update(current_rx_time, current_start_offset);
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200212 }
213
214 return d_samples_consumed;
piotrd0bf1492014-02-05 17:27:32 +0100215 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700216
217 void
218 receiver_impl::fcch_search_handler(gr_complex *input, int noutput_items)
piotrd0bf1492014-02-05 17:27:32 +0100219 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700220 double freq_offset_tmp;
ptrkrysik58213792014-10-30 09:05:15 +0100221
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700222 /* Check if received samples is a FCCN burst */
223 if (!find_fcch_burst(input, noutput_items, freq_offset_tmp))
224 return;
piotr437f5462014-02-04 17:57:25 +0100225
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700226 /* We found it, compose a message */
227 pmt::pmt_t msg = pmt::make_tuple(
228 pmt::mp("freq_offset"),
229 pmt::from_double(freq_offset_tmp - d_freq_offset_setting),
230 pmt::mp("fcch_search")
231 );
Piotr Krysik9bc0fc02017-01-18 21:53:17 +0100232
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700233 /* Notify FCCH loop */
234 message_port_pub(pmt::mp("measurements"), msg);
235
236 /* Update current state */
237 d_state = sch_search;
piotrd0bf1492014-02-05 17:27:32 +0100238 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700239
240 void
241 receiver_impl::sch_search_handler(gr_complex *input, int noutput_items)
242 {
243 std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
244 unsigned char burst_buf[BURST_SIZE];
245 int rc, t1, t2, t3;
246 int burst_start;
247
248 /* Wait until we get a SCH burst */
249 if (!reach_sch_burst(noutput_items))
250 return;
251
252 /* Get channel impulse response from it */
253 burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]);
254
255 /* Detect bits using MLSE detection */
256 detect_burst(input, &channel_imp_resp[0], burst_start, burst_buf);
257
258 /* Attempt to decode BSIC and frame number */
259 rc = decode_sch(&burst_buf[3], &t1, &t2, &t3, &d_ncc, &d_bcc);
260 if (rc) {
261 /**
262 * There is error in the SCH burst,
263 * go back to the FCCH search state
264 */
265 d_state = fcch_search;
266 return;
267 }
268
269 /* Set counter of bursts value */
270 d_burst_nr.set(t1, t2, t3, 0);
271 d_burst_nr++;
272
273 /* Consume samples up to the next guard period */
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200274 unsigned int to_consume = burst_start + BURST_SIZE * d_OSR + 4 * d_OSR;
275// consume_each(to_consume);
276 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700277
278 /* Update current state */
279 d_state = synchronized;
piotr437f5462014-02-04 17:57:25 +0100280 }
piotr437f5462014-02-04 17:57:25 +0100281
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700282 void
283 receiver_impl::synchronized_handler(gr_complex *input,
284 gr_vector_const_void_star &input_items, int noutput_items)
piotr437f5462014-02-04 17:57:25 +0100285 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700286 /**
287 * In this state receiver is synchronized and it processes
288 * bursts according to burst type for given burst number
289 */
290 std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
291 unsigned int inputs_to_process = d_cell_allocation.size();
292 unsigned char output_binary[BURST_SIZE];
293 burst_type b_type;
294 int to_consume = 0;
295 int offset = 0;
piotr437f5462014-02-04 17:57:25 +0100296
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700297 if (d_process_uplink)
298 inputs_to_process *= 2;
piotr437f5462014-02-04 17:57:25 +0100299
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700300 /* Process all connected inputs */
301 for (int input_nr = 0; input_nr < inputs_to_process; input_nr++) {
302 input = (gr_complex *) input_items[input_nr];
303 double signal_pwr = 0;
304
305 for (int ii = GUARD_PERIOD; ii < TS_BITS; ii++)
306 signal_pwr += abs(input[ii]) * abs(input[ii]);
307
308 signal_pwr = signal_pwr / (TS_BITS);
309 d_signal_dbm = round(10 * log10(signal_pwr / 50));
310
311 if (input_nr == 0)
312 d_c0_signal_dbm = d_signal_dbm;
313
314 /* Get burst type for given burst number */
315 b_type = input_nr == 0 ?
316 d_channel_conf.get_burst_type(d_burst_nr) : normal_or_noise;
317
318 /* Process burst according to its type */
319 switch (b_type) {
320 case fcch_burst:
piotrd0bf1492014-02-05 17:27:32 +0100321 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700322 if (d_freq_offset_tag_in_fcch)
piotr437f5462014-02-04 17:57:25 +0100323 break;
324
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700325 /* Send all-zero sequence message */
326 send_burst(d_burst_nr, fc_fb, GSMTAP_BURST_FCCH, input_nr);
piotr437f5462014-02-04 17:57:25 +0100327
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700328 /* Extract frequency offset */
329 const unsigned first_sample =
330 ceil((GUARD_PERIOD + 2 * TAIL_BITS) * d_OSR) + 1;
331 const unsigned last_sample =
332 first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR;
333 double freq_offset_tmp =
334 compute_freq_offset(input, first_sample, last_sample);
piotr437f5462014-02-04 17:57:25 +0100335
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700336 /* Frequency correction loop */
337 pmt::pmt_t msg = pmt::make_tuple(
338 pmt::mp("freq_offset"),
339 pmt::from_double(freq_offset_tmp - d_freq_offset_setting),
340 pmt::mp("synchronized"));
341 message_port_pub(pmt::mp("measurements"), msg);
342
343 break;
344 }
345
346 case sch_burst:
347 {
Piotr Krysikbfb47dc2017-11-11 11:12:02 +0100348 int ncc, bcc;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700349 int t1, t2, t3;
350 int rc;
351
352 /* Get channel impulse response */
353 d_c0_burst_start = get_sch_chan_imp_resp(input,
354 &channel_imp_resp[0]);
355
356 /* Perform MLSE detection */
357 detect_burst(input, &channel_imp_resp[0],
358 d_c0_burst_start, output_binary);
359
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700360 /* Attempt to decode SCH burst */
Piotr Krysikbfb47dc2017-11-11 11:12:02 +0100361 rc = decode_sch(&output_binary[3], &t1, &t2, &t3, &ncc, &bcc);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700362 if (rc) {
363 if (++d_failed_sch >= MAX_SCH_ERRORS) {
364 /* We have to resynchronize, change state */
365 d_state = fcch_search;
366
367 /* Frequency correction loop */
368 pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),
369 pmt::from_double(0.0),pmt::mp("sync_loss"));
370 message_port_pub(pmt::mp("measurements"), msg);
piotr437f5462014-02-04 17:57:25 +0100371 }
piotr437f5462014-02-04 17:57:25 +0100372 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700373 }
piotr437f5462014-02-04 17:57:25 +0100374
Piotr Krysikbfb47dc2017-11-11 11:12:02 +0100375 /* Compose a message with GSMTAP header and bits */
376 send_burst(d_burst_nr, output_binary,
377 GSMTAP_BURST_SCH, input_nr, d_c0_burst_start);
378
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700379 /**
380 * Decoding was successful, now
381 * compute offset from burst_start,
382 * burst should start after a guard period.
383 */
384 offset = d_c0_burst_start - floor((GUARD_PERIOD) * d_OSR);
385 to_consume += offset;
386 d_failed_sch = 0;
piotr437f5462014-02-04 17:57:25 +0100387
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700388 break;
piotrd0bf1492014-02-05 17:27:32 +0100389 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700390
391 case normal_burst:
392 {
393 float normal_corr_max;
394 /**
395 * Get channel impulse response for given
396 * training sequence number - d_bcc
397 */
398 d_c0_burst_start = get_norm_chan_imp_resp(input,
399 &channel_imp_resp[0], &normal_corr_max, d_bcc);
400
401 /* Perform MLSE detection */
402 detect_burst(input, &channel_imp_resp[0],
403 d_c0_burst_start, output_binary);
404
405 /* Compose a message with GSMTAP header and bits */
406 send_burst(d_burst_nr, output_binary,
Piotr Krysikdf978692017-09-27 21:58:24 +0200407 GSMTAP_BURST_NORMAL, input_nr, d_c0_burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700408
409 break;
410 }
411
412 case dummy_or_normal:
413 {
414 unsigned int normal_burst_start, dummy_burst_start;
415 float dummy_corr_max, normal_corr_max;
416
417 dummy_burst_start = get_norm_chan_imp_resp(input,
418 &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
419 normal_burst_start = get_norm_chan_imp_resp(input,
420 &channel_imp_resp[0], &normal_corr_max, d_bcc);
421
422 if (normal_corr_max > dummy_corr_max) {
423 d_c0_burst_start = normal_burst_start;
424
425 /* Perform MLSE detection */
426 detect_burst(input, &channel_imp_resp[0],
427 normal_burst_start, output_binary);
428
429 /* Compose a message with GSMTAP header and bits */
430 send_burst(d_burst_nr, output_binary,
Piotr Krysikdf978692017-09-27 21:58:24 +0200431 GSMTAP_BURST_NORMAL, input_nr, normal_burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700432 } else {
433 d_c0_burst_start = dummy_burst_start;
434
435 /* Compose a message with GSMTAP header and bits */
436 send_burst(d_burst_nr, dummy_burst,
Piotr Krysikdf978692017-09-27 21:58:24 +0200437 GSMTAP_BURST_DUMMY, input_nr, dummy_burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700438 }
439
440 break;
441 }
442
443 case normal_or_noise:
444 {
445 std::vector<gr_complex> v(input, input + noutput_items);
446 float normal_corr_max = -1e6;
447 float normal_corr_max_tmp;
448 unsigned int burst_start;
449 int max_tn, tseq_num;
450
451 if (d_tseq_nums.size() == 0) {
452 /**
453 * There is no information about training sequence,
454 * however the receiver can detect it with use of a
455 * very simple algorithm based on finding
456 */
457 get_norm_chan_imp_resp(input, &channel_imp_resp[0],
458 &normal_corr_max, 0);
459
460 float ts_max = normal_corr_max;
461 int ts_max_num = 0;
462
463 for (int ss = 1; ss <= 7; ss++) {
464 get_norm_chan_imp_resp(input, &channel_imp_resp[0],
465 &normal_corr_max, ss);
466
467 if (ts_max < normal_corr_max) {
468 ts_max = normal_corr_max;
469 ts_max_num = ss;
470 }
471 }
472
473 d_tseq_nums.push_back(ts_max_num);
474 }
475
476 /* Choose proper training sequence number */
477 tseq_num = input_nr <= d_tseq_nums.size() ?
478 d_tseq_nums[input_nr - 1] : d_tseq_nums.back();
479
480 /* Get channel impulse response */
481 burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0],
482 &normal_corr_max, tseq_num);
483
484 /* Perform MLSE detection */
485 detect_burst(input, &channel_imp_resp[0],
486 burst_start, output_binary);
487
488 /* Compose a message with GSMTAP header and bits */
Piotr Krysikdf978692017-09-27 21:58:24 +0200489 send_burst(d_burst_nr, output_binary, GSMTAP_BURST_NORMAL, input_nr, burst_start);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700490
491 break;
492 }
493
494 case dummy:
495 send_burst(d_burst_nr, dummy_burst, GSMTAP_BURST_DUMMY, input_nr);
496 break;
497
498 case rach_burst:
499 case empty:
500 /* Do nothing */
501 break;
502 }
503
504 if (input_nr == input_items.size() - 1) {
505 /* Go to the next burst */
506 d_burst_nr++;
507
508 /* Consume samples of the burst up to next guard period */
509 to_consume += TS_BITS * d_OSR + d_burst_nr.get_offset();
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200510// consume_each(to_consume);
511 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700512 }
513 }
514 }
515
516 bool
517 receiver_impl::find_fcch_burst(const gr_complex *input,
518 const int nitems, double &computed_freq_offset)
519 {
520 /* Circular buffer used to scan through signal to find */
521 boost::circular_buffer<float>
522 phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR);
523 boost::circular_buffer<float>::iterator buffer_iter;
524
525 float lowest_max_min_diff;
526 float phase_diff; /* Best match for FCCH burst */
527 float min_phase_diff;
528 float max_phase_diff;
529 double best_sum = 0;
530 gr_complex conjprod;
531 int start_pos;
532 int hit_count;
533 int miss_count;
534
535 int sample_number = 0;
536 int to_consume = 0;
537 bool result = false;
538 bool end = false;
539
540 /* Possible states of FCCH search algorithm */
541 enum states
542 {
543 init, /* initialize variables */
544 search, /* search for positive samples */
545 found_something, /* search for FCCH and the best position of it */
546 fcch_found, /* when FCCH was found */
547 search_fail /* when there is no FCCH in the input vector */
548 } fcch_search_state;
549
550 /* Set initial state */
551 fcch_search_state = init;
552
553 while (!end)
554 {
555 switch (fcch_search_state) {
556 case init:
557 {
558 hit_count = 0;
559 miss_count = 0;
560 start_pos = -1;
561 lowest_max_min_diff = 99999;
562 phase_diff_buffer.clear();
563
564 /* Change current state */
565 fcch_search_state = search;
566
567 break;
568 }
569
570 case search:
571 {
572 sample_number++;
573
574 if (sample_number > nitems - FCCH_HITS_NEEDED * d_OSR) {
575 /**
576 * If it isn't possible to find FCCH, because
577 * there is too few samples left to look into,
578 * don't do anything with those samples which are left
579 * and consume only those which were checked
580 */
581 to_consume = sample_number;
582 fcch_search_state = search_fail;
583 break;
584 }
585
586 phase_diff = compute_phase_diff(input[sample_number],
587 input[sample_number - 1]);
588
589 /**
590 * If a positive phase difference was found
591 * switch to state in which searches for FCCH
592 */
593 if (phase_diff > 0) {
594 to_consume = sample_number;
595 fcch_search_state = found_something;
596 } else {
597 fcch_search_state = search;
598 }
599
600 break;
601 }
602
603 case found_something:
604 {
605 if (phase_diff > 0)
606 hit_count++;
607 else
608 miss_count++;
609
610 if ((miss_count >= FCCH_MAX_MISSES * d_OSR)
611 && (hit_count <= FCCH_HITS_NEEDED * d_OSR))
612 {
613 /* If miss_count exceeds limit before hit_count */
614 fcch_search_state = init;
615 continue;
616 }
617
618 if (((miss_count >= FCCH_MAX_MISSES * d_OSR)
619 && (hit_count > FCCH_HITS_NEEDED * d_OSR))
620 || (hit_count > 2 * FCCH_HITS_NEEDED * d_OSR))
621 {
622 /**
623 * If hit_count and miss_count exceeds
624 * limit then FCCH was found
625 */
626 fcch_search_state = fcch_found;
627 continue;
628 }
629
630 if ((miss_count < FCCH_MAX_MISSES * d_OSR)
631 && (hit_count > FCCH_HITS_NEEDED * d_OSR))
632 {
633 /**
634 * Find difference between minimal and maximal
635 * element in the buffer. For FCCH this value
636 * should be low. This part is searching for
637 * a region where this value is lowest.
638 */
639 min_phase_diff = *(min_element(phase_diff_buffer.begin(),
640 phase_diff_buffer.end()));
641 max_phase_diff = *(max_element(phase_diff_buffer.begin(),
642 phase_diff_buffer.end()));
643
644 if (lowest_max_min_diff > max_phase_diff - min_phase_diff) {
645 lowest_max_min_diff = max_phase_diff - min_phase_diff;
646 start_pos = sample_number - FCCH_HITS_NEEDED
647 * d_OSR - FCCH_MAX_MISSES * d_OSR;
648 best_sum = 0;
649
650 for (buffer_iter = phase_diff_buffer.begin();
651 buffer_iter != (phase_diff_buffer.end());
652 buffer_iter++) {
653 /* Store best value of phase offset sum */
654 best_sum += *buffer_iter - (M_PI / 2) / d_OSR;
655 }
656 }
657 }
658
659 /* If there is no single sample left to check */
660 if (++sample_number >= nitems) {
661 fcch_search_state = search_fail;
662 continue;
663 }
664
665 phase_diff = compute_phase_diff(input[sample_number],
666 input[sample_number-1]);
667 phase_diff_buffer.push_back(phase_diff);
668 fcch_search_state = found_something;
669
670 break;
671 }
piotrd0bf1492014-02-05 17:27:32 +0100672
673 case fcch_found:
674 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700675 /* Consume one FCCH burst */
676 to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1;
677 d_fcch_start_pos = d_counter + start_pos;
piotrd0bf1492014-02-05 17:27:32 +0100678
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700679 /**
680 * Compute frequency offset
681 *
682 * 1625000.0 / 6 - GMSK symbol rate in GSM
683 */
684 double phase_offset = best_sum / FCCH_HITS_NEEDED;
685 double freq_offset = phase_offset * 1625000.0 / 6 / (2 * M_PI);
686 computed_freq_offset = freq_offset;
piotrd0bf1492014-02-05 17:27:32 +0100687
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700688 end = true;
689 result = true;
piotrd0bf1492014-02-05 17:27:32 +0100690
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700691 break;
piotrd0bf1492014-02-05 17:27:32 +0100692 }
piotr437f5462014-02-04 17:57:25 +0100693
piotrd0bf1492014-02-05 17:27:32 +0100694 case search_fail:
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700695 end = true;
696 result = false;
697 break;
piotr437f5462014-02-04 17:57:25 +0100698 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700699 }
700
701 d_counter += to_consume;
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200702// consume_each(to_consume);
703 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700704
705 return result;
piotr437f5462014-02-04 17:57:25 +0100706 }
707
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700708 double
709 receiver_impl::compute_freq_offset(const gr_complex * input,
710 unsigned first_sample, unsigned last_sample)
Piotr Krysik654d6522017-01-23 21:53:48 +0100711 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700712 double phase_sum = 0;
713 unsigned ii;
714
715 for (ii = first_sample; ii < last_sample; ii++)
716 {
717 double phase_diff = compute_phase_diff(input[ii],
718 input[ii-1]) - (M_PI / 2) / d_OSR;
Piotr Krysik654d6522017-01-23 21:53:48 +0100719 phase_sum += phase_diff;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700720 }
721
722 double phase_offset = phase_sum / (last_sample - first_sample);
723 double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
724
725 return freq_offset;
Piotr Krysik654d6522017-01-23 21:53:48 +0100726 }
727
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700728 inline float
729 receiver_impl::compute_phase_diff(gr_complex val1, gr_complex val2)
piotrd0bf1492014-02-05 17:27:32 +0100730 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700731 gr_complex conjprod = val1 * conj(val2);
732 return fast_atan2f(imag(conjprod), real(conjprod));
piotrd0bf1492014-02-05 17:27:32 +0100733 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700734
735 bool
736 receiver_impl::reach_sch_burst(const int nitems)
piotrd0bf1492014-02-05 17:27:32 +0100737 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700738 /* It just consumes samples to get near to a SCH burst */
739 int to_consume = 0;
740 bool result = false;
741 unsigned sample_nr = d_fcch_start_pos
742 + (FRAME_BITS - SAFETY_MARGIN) * d_OSR;
743
744 /* Consume samples until d_counter will be equal to sample_nr */
745 if (d_counter < sample_nr) {
746 to_consume = d_counter + nitems >= sample_nr ?
747 sample_nr - d_counter : nitems;
748 } else {
piotr437f5462014-02-04 17:57:25 +0100749 to_consume = 0;
750 result = true;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700751 }
752
753 d_counter += to_consume;
Piotr Krysik96f2ea72017-10-16 15:47:08 +0200754// consume_each(to_consume);
755 d_samples_consumed += to_consume;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700756
757 return result;
piotr437f5462014-02-04 17:57:25 +0100758 }
759
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700760 int
761 receiver_impl::get_sch_chan_imp_resp(const gr_complex *input,
762 gr_complex * chan_imp_resp)
piotr437f5462014-02-04 17:57:25 +0100763 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700764 std::vector<gr_complex> correlation_buffer;
765 std::vector<float> window_energy_buffer;
766 std::vector<float> power_buffer;
767
768 int chan_imp_resp_center = 0;
769 int strongest_window_nr;
770 int burst_start;
771 float energy = 0;
772
773 int len = (SYNC_POS + SYNC_SEARCH_RANGE) * d_OSR;
774 for (int ii = SYNC_POS * d_OSR; ii < len; ii++) {
775 gr_complex correlation = correlate_sequence(&d_sch_training_seq[5],
776 N_SYNC_BITS - 10, &input[ii]);
piotr437f5462014-02-04 17:57:25 +0100777 correlation_buffer.push_back(correlation);
778 power_buffer.push_back(std::pow(abs(correlation), 2));
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700779 }
780
781 /* Compute window energies */
782 std::vector<float>::iterator iter = power_buffer.begin();
783 while (iter != power_buffer.end()) {
ptrkrysikef5e2db2015-01-03 12:10:14 +0100784 std::vector<float>::iterator iter_ii = iter;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700785 bool loop_end = false;
piotr437f5462014-02-04 17:57:25 +0100786 energy = 0;
787
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700788 for (int ii = 0; ii < (d_chan_imp_length) * d_OSR; ii++, iter_ii++) {
789 if (iter_ii == power_buffer.end()) {
790 loop_end = true;
piotrd0bf1492014-02-05 17:27:32 +0100791 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700792 }
793
794 energy += (*iter_ii);
piotr437f5462014-02-04 17:57:25 +0100795 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700796
797 if (loop_end)
798 break;
799
piotr437f5462014-02-04 17:57:25 +0100800 window_energy_buffer.push_back(energy);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700801 iter++;
802 }
piotr437f5462014-02-04 17:57:25 +0100803
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700804 strongest_window_nr = max_element(window_energy_buffer.begin(),
805 window_energy_buffer.end()) - window_energy_buffer.begin();
piotr437f5462014-02-04 17:57:25 +0100806
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700807#if 0
808 d_channel_imp_resp.clear();
809#endif
810
811 float max_correlation = 0;
812 for (int ii = 0; ii < (d_chan_imp_length) * d_OSR; ii++) {
piotr437f5462014-02-04 17:57:25 +0100813 gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700814 if (abs(correlation) > max_correlation) {
815 chan_imp_resp_center = ii;
816 max_correlation = abs(correlation);
piotr437f5462014-02-04 17:57:25 +0100817 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700818
819#if 0
820 d_channel_imp_resp.push_back(correlation);
821#endif
822
piotr437f5462014-02-04 17:57:25 +0100823 chan_imp_resp[ii] = correlation;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700824 }
825
826 burst_start = strongest_window_nr + chan_imp_resp_center
827 - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR;
828 return burst_start;
piotr437f5462014-02-04 17:57:25 +0100829 }
830
831
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700832 void
833 receiver_impl::detect_burst(const gr_complex * input,
834 gr_complex * chan_imp_resp, int burst_start,
835 unsigned char * output_binary)
piotr437f5462014-02-04 17:57:25 +0100836 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700837 std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
838 unsigned int stop_states[2] = {4, 12};
839 gr_complex filtered_burst[BURST_SIZE];
840 gr_complex rhh[CHAN_IMP_RESP_LENGTH];
841 float output[BURST_SIZE];
842 int start_state = 3;
843
844 autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length*d_OSR);
845 for (int ii = 0; ii < d_chan_imp_length; ii++)
piotr437f5462014-02-04 17:57:25 +0100846 rhh[ii] = conj(rhh_temp[ii*d_OSR]);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700847
848 mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
849 d_chan_imp_length * d_OSR, filtered_burst);
850
851 viterbi_detector(filtered_burst, BURST_SIZE, rhh,
852 start_state, stop_states, 2, output);
853
854 for (int i = 0; i < BURST_SIZE; i++)
855 output_binary[i] = output[i] > 0;
piotr437f5462014-02-04 17:57:25 +0100856 }
857
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700858 void
859 receiver_impl::gmsk_mapper(const unsigned char * input,
860 int nitems, gr_complex * gmsk_output, gr_complex start_point)
piotr437f5462014-02-04 17:57:25 +0100861 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700862 gr_complex j = gr_complex(0.0, 1.0);
863 gmsk_output[0] = start_point;
piotr437f5462014-02-04 17:57:25 +0100864
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700865 int previous_symbol = 2 * input[0] - 1;
866 int current_symbol;
867 int encoded_symbol;
piotr437f5462014-02-04 17:57:25 +0100868
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700869 for (int i = 1; i < nitems; i++) {
870 /* Change bits representation to NRZ */
piotr437f5462014-02-04 17:57:25 +0100871 current_symbol = 2 * input[i] - 1;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700872
873 /* Differentially encode */
piotr437f5462014-02-04 17:57:25 +0100874 encoded_symbol = current_symbol * previous_symbol;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700875
876 /* And do GMSK mapping */
877 gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
878 * gmsk_output[i-1];
879
piotr437f5462014-02-04 17:57:25 +0100880 previous_symbol = current_symbol;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700881 }
piotr437f5462014-02-04 17:57:25 +0100882 }
883
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700884 gr_complex
885 receiver_impl::correlate_sequence(const gr_complex * sequence,
886 int length, const gr_complex * input)
piotr437f5462014-02-04 17:57:25 +0100887 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700888 gr_complex result(0.0, 0.0);
889
890 for (int ii = 0; ii < length; ii++)
891 result += sequence[ii] * conj(input[ii * d_OSR]);
892
893 return result / gr_complex(length, 0);
894 }
895
896 /* Computes autocorrelation for positive arguments */
897 inline void
898 receiver_impl::autocorrelation(const gr_complex * input,
899 gr_complex * out, int nitems)
900 {
901 for (int k = nitems - 1; k >= 0; k--) {
piotr437f5462014-02-04 17:57:25 +0100902 out[k] = gr_complex(0, 0);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700903 for (int i = k; i < nitems; i++)
904 out[k] += input[i] * conj(input[i - k]);
905 }
piotr437f5462014-02-04 17:57:25 +0100906 }
907
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700908 inline void
909 receiver_impl::mafi(const gr_complex * input, int nitems,
910 gr_complex * filter, int filter_length, gr_complex * output)
piotr437f5462014-02-04 17:57:25 +0100911 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700912 for (int n = 0; n < nitems; n++) {
913 int a = n * d_OSR;
piotr437f5462014-02-04 17:57:25 +0100914 output[n] = 0;
piotr437f5462014-02-04 17:57:25 +0100915
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700916 for (int ii = 0; ii < filter_length; ii++) {
917 if ((a + ii) >= nitems * d_OSR)
piotrd0bf1492014-02-05 17:27:32 +0100918 break;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700919
920 output[n] += input[a + ii] * filter[ii];
piotr437f5462014-02-04 17:57:25 +0100921 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700922 }
923 }
924
925 /* Especially computations of strongest_window_nr */
926 int
927 receiver_impl::get_norm_chan_imp_resp(const gr_complex *input,
928 gr_complex *chan_imp_resp, float *corr_max, int bcc)
929 {
930 std::vector<gr_complex> correlation_buffer;
931 std::vector<float> window_energy_buffer;
932 std::vector<float> power_buffer;
933
934 int search_center = (int) (TRAIN_POS + GUARD_PERIOD) * d_OSR;
935 int search_start_pos = search_center + 1 - 5 * d_OSR;
936 int search_stop_pos = search_center
937 + d_chan_imp_length * d_OSR + 5 * d_OSR;
938
939 for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
940 gr_complex correlation = correlate_sequence(
941 &d_norm_training_seq[bcc][TRAIN_BEGINNING],
942 N_TRAIN_BITS - 10, &input[ii]);
943 correlation_buffer.push_back(correlation);
944 power_buffer.push_back(std::pow(abs(correlation), 2));
945 }
946
947#if 0
948 plot(power_buffer);
949#endif
950
951 /* Compute window energies */
952 std::vector<float>::iterator iter = power_buffer.begin();
953 while (iter != power_buffer.end()) {
954 std::vector<float>::iterator iter_ii = iter;
955 bool loop_end = false;
956 float energy = 0;
957
958 int len = d_chan_imp_length * d_OSR;
959 for (int ii = 0; ii < len; ii++, iter_ii++) {
960 if (iter_ii == power_buffer.end()) {
961 loop_end = true;
962 break;
963 }
964
965 energy += (*iter_ii);
966 }
967
968 if (loop_end)
969 break;
piotr437f5462014-02-04 17:57:25 +0100970
971 window_energy_buffer.push_back(energy);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700972 iter++;
973 }
piotr437f5462014-02-04 17:57:25 +0100974
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700975 /* Calculate the strongest window number */
976 int strongest_window_nr = max_element(window_energy_buffer.begin(),
977 window_energy_buffer.end() - d_chan_imp_length * d_OSR)
978 - window_energy_buffer.begin();
979
980 if (strongest_window_nr < 0)
981 strongest_window_nr = 0;
982
983 float max_correlation = 0;
984 for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
piotr437f5462014-02-04 17:57:25 +0100985 gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
piotrd0bf1492014-02-05 17:27:32 +0100986 if (abs(correlation) > max_correlation)
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700987 max_correlation = abs(correlation);
988
989#if 0
990 d_channel_imp_resp.push_back(correlation);
991#endif
992
piotr437f5462014-02-04 17:57:25 +0100993 chan_imp_resp[ii] = correlation;
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +0700994 }
995
996 *corr_max = max_correlation;
997
998 /**
999 * Compute first sample position, which corresponds
1000 * to the first sample of the impulse response
1001 */
1002 return search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR;
piotr437f5462014-02-04 17:57:25 +01001003 }
1004
1005
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001006 void
1007 receiver_impl::send_burst(burst_counter burst_nr,
1008 const unsigned char * burst_binary, uint8_t burst_type,
Piotr Krysikdf978692017-09-27 21:58:24 +02001009 unsigned int input_nr, unsigned int burst_start)
ptrkrysik380dea82015-08-06 10:11:58 +02001010 {
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001011 /* Buffer for GSMTAP header and burst */
1012 uint8_t buf[sizeof(gsmtap_hdr) + BURST_SIZE];
1013 uint32_t frame_number;
1014 uint16_t arfcn;
1015 uint8_t tn;
ptrkrysik617ba032014-11-21 10:11:05 +01001016
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001017 /* Set pointers to GSMTAP header and burst inside buffer */
1018 struct gsmtap_hdr *tap_header = (struct gsmtap_hdr *) buf;
1019 uint8_t *burst = buf + sizeof(gsmtap_hdr);
1020
1021 tap_header->version = GSMTAP_VERSION;
1022 tap_header->hdr_len = sizeof(gsmtap_hdr) / 4;
1023 tap_header->type = GSMTAP_TYPE_UM_BURST;
1024 tap_header->sub_type = burst_type;
1025
1026 bool dl_burst = !(input_nr >= d_cell_allocation.size());
1027 if (dl_burst) {
1028 tn = static_cast<uint8_t>(d_burst_nr.get_timeslot_nr());
1029 frame_number = htobe32(d_burst_nr.get_frame_nr());
1030 arfcn = htobe16(d_cell_allocation[input_nr]);
1031 } else {
1032 input_nr -= d_cell_allocation.size();
1033 tn = static_cast<uint8_t>
1034 (d_burst_nr.subtract_timeslots(3).get_timeslot_nr());
1035 frame_number = htobe32(
1036 d_burst_nr.subtract_timeslots(3).get_frame_nr());
1037 arfcn = htobe16(
1038 d_cell_allocation[input_nr] | GSMTAP_ARFCN_F_UPLINK);
1039 }
1040
1041 tap_header->frame_number = frame_number;
1042 tap_header->timeslot = tn;
1043 tap_header->arfcn = arfcn;
1044
1045 tap_header->signal_dbm = static_cast<int8_t>(d_signal_dbm);
1046 tap_header->snr_db = 0; /* FIXME: Can we calculate this? */
1047
Piotr Krysikdf978692017-09-27 21:58:24 +02001048 pmt::pmt_t pdu_header = pmt::make_dict();
1049
1050 /* Add timestamp of the first sample - if available */
1051 if(d_rx_time_received) {
1052 time_spec_t time_spec_of_first_sample = d_time_samp_ref.offset_to_time(nitems_read(0)+burst_start);
1053 uint64_t full = time_spec_of_first_sample.get_full_secs();
1054 double frac = time_spec_of_first_sample.get_frac_secs();
Piotr Krysik96f2ea72017-10-16 15:47:08 +02001055 pdu_header =
1056 pmt::dict_add(pdu_header, pmt::mp("fn_time"),
1057 pmt::cons(
1058 pmt::cons(pmt::from_uint64(be32toh(frame_number)), pmt::from_uint64(tn)),
1059 pmt::cons(pmt::from_uint64(full), pmt::from_double(frac))));
Piotr Krysikdf978692017-09-27 21:58:24 +02001060 }
1061
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001062 /* Copy burst to the buffer */
1063 memcpy(burst, burst_binary, BURST_SIZE);
1064
1065 /* Allocate a new message */
1066 pmt::pmt_t blob = pmt::make_blob(buf, sizeof(gsmtap_hdr) + BURST_SIZE);
Piotr Krysik96f2ea72017-10-16 15:47:08 +02001067 pmt::pmt_t msg = pmt::cons(pdu_header, blob);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001068
1069 /* Send message */
1070 if (input_nr == 0)
ptrkrysike518bbf2014-11-06 14:50:59 +01001071 message_port_pub(pmt::mp("C0"), msg);
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001072 else
ptrkrysike518bbf2014-11-06 14:50:59 +01001073 message_port_pub(pmt::mp("CX"), msg);
Piotr Krysikbfb47dc2017-11-11 11:12:02 +01001074
ptrkrysike518bbf2014-11-06 14:50:59 +01001075 }
piotr6d152d92014-02-21 00:02:44 +01001076
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001077 void
1078 receiver_impl::configure_receiver(void)
1079 {
1080 d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51);
1081 d_channel_conf.set_burst_types(TIMESLOT0, TEST51,
1082 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
1083 d_channel_conf.set_burst_types(TIMESLOT0, TEST_CCH_FRAMES,
1084 sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), dummy_or_normal);
1085 d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES,
1086 sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst);
1087 d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES,
1088 sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst);
piotr437f5462014-02-04 17:57:25 +01001089
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001090 d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_51);
1091 d_channel_conf.set_burst_types(TIMESLOT1, TEST51,
1092 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotr437f5462014-02-04 17:57:25 +01001093
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001094 d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_51);
1095 d_channel_conf.set_burst_types(TIMESLOT2, TEST51,
1096 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotr437f5462014-02-04 17:57:25 +01001097
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001098 d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_51);
1099 d_channel_conf.set_burst_types(TIMESLOT3, TEST51,
1100 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
ptrkrysike518bbf2014-11-06 14:50:59 +01001101
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001102 d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_51);
1103 d_channel_conf.set_burst_types(TIMESLOT4, TEST51,
1104 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotrf2b6a1b2014-08-04 11:28:59 +02001105
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001106 d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_51);
1107 d_channel_conf.set_burst_types(TIMESLOT5, TEST51,
1108 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
piotr437f5462014-02-04 17:57:25 +01001109
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001110 d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_51);
1111 d_channel_conf.set_burst_types(TIMESLOT6, TEST51,
1112 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
1113
1114 d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_51);
1115 d_channel_conf.set_burst_types(TIMESLOT7, TEST51,
1116 sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
1117 }
1118
1119 void
1120 receiver_impl::set_cell_allocation(
1121 const std::vector<int> &cell_allocation)
1122 {
1123 d_cell_allocation = cell_allocation;
1124 }
1125
1126 void
1127 receiver_impl::set_tseq_nums(const std::vector<int> &tseq_nums)
1128 {
1129 d_tseq_nums = tseq_nums;
1130 }
1131
1132 void
1133 receiver_impl::reset(void)
1134 {
1135 d_state = fcch_search;
1136 }
Vadim Yanitskiycc82cf02017-07-24 19:21:02 +07001137 } /* namespace gsm */
piotr437f5462014-02-04 17:57:25 +01001138} /* namespace gr */