diff options
author | piotr <Piotr Krysik pkrysik@elka.pw.edu.pl> | 2014-07-08 16:38:42 +0200 |
---|---|---|
committer | piotr <Piotr Krysik pkrysik@elka.pw.edu.pl> | 2014-07-08 16:38:42 +0200 |
commit | 7af92cadd52b373a3a874991f9e4a100e4b2c97a (patch) | |
tree | 989225c8bf632d45096963f46140d044b1209fc1 /lib/receiver | |
parent | f0039b49273f4dc14c9352ff77ccf57511d07247 (diff) |
Correction in plotting.h
Diffstat (limited to 'lib/receiver')
-rw-r--r-- | lib/receiver/assert.h | 67 | ||||
-rw-r--r-- | lib/receiver/gsm_constants.h | 154 | ||||
-rw-r--r-- | lib/receiver/receiver_config.cc | 84 | ||||
-rw-r--r-- | lib/receiver/receiver_config.h | 164 | ||||
-rw-r--r-- | lib/receiver/receiver_impl.cc | 842 | ||||
-rw-r--r-- | lib/receiver/receiver_impl.h | 214 |
6 files changed, 1525 insertions, 0 deletions
diff --git a/lib/receiver/assert.h b/lib/receiver/assert.h new file mode 100644 index 0000000..a80ae73 --- /dev/null +++ b/lib/receiver/assert.h @@ -0,0 +1,67 @@ +/* +* Copyright 2007 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#ifndef ASSERT_H +#define ASSERT_H + +#include "stdio.h" +#include <iostream> + +//#define NDEBUG + +/**@name Macros for standard messages. */ +//@{ +#define COUT(text) { std::cout << text << std::endl; } +#define CERR(text) { std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; } +#ifdef NDEBUG +#define DCOUT(text) {} +#define OBJDCOUT(text) {} +#else +#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); } +#define OBJDCOUT(text) { DCOUT(this << " " << text); } +#endif +//@} + + +/** This is thrown by assert() so that gdb can catch it. */ + +class assertion +{ + + public: + + assertion() { + fprintf( stderr,"Try setting a breakpoint @ %s:%u.\n",__FILE__,__LINE__ ); + return; + } + +}; + +#ifdef NDEBUG +#define assert(EXPR) {}; +#else +/** This replaces the regular assert() with a C++ exception. */ +#include "stdio.h" +#define assert(E) { if (!(E)) { fprintf(stderr,"%s:%u failed assertion '%s'\n",__FILE__,__LINE__,#E); throw Assertion(); } } +#endif + +#endif diff --git a/lib/receiver/gsm_constants.h b/lib/receiver/gsm_constants.h new file mode 100644 index 0000000..964c1cf --- /dev/null +++ b/lib/receiver/gsm_constants.h @@ -0,0 +1,154 @@ +#ifndef INCLUDED_GSM_CONSTANTS_H +#define INCLUDED_GSM_CONSTANTS_H + +#define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second +#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol + +//Burst timing +#define TAIL_BITS 3 +#define GUARD_BITS 8 +#define GUARD_FRACTIONAL 0.25 //fractional part of guard period +#define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL +#define DATA_BITS 57 //size of 1 data block in normal burst +#define STEALING_BIT 1 +#define N_TRAIN_BITS 26 +#define N_SYNC_BITS 64 +#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS ) +#define FCCH_BITS USEFUL_BITS +#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS) + +#define SCH_DATA_LEN 39 +#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits) +#define TS_PER_FRAME 8 +#define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8 +#define FCCH_POS TAIL_BITS +#define SYNC_POS 39 +#define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence + //aren't used for channel impulse response estimation +#define TRAIN_BEGINNING 5 +#define SAFETY_MARGIN 6 // + +#define FCCH_HITS_NEEDED (USEFUL_BITS - 4) +#define FCCH_MAX_MISSES 1 +#define FCCH_MAX_FREQ_OFFSET 100 + +#define CHAN_IMP_RESP_LENGTH 5 + +#define MAX_SCH_ERRORS 10 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state + +typedef enum {empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal} burst_type; +typedef enum {unknown, multiframe_26, multiframe_51} multiframe_type; + +static const unsigned char SYNC_BITS[] = { + 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 +}; + +const unsigned FCCH_FRAMES[] = {0, 10, 20, 30, 40}; +const unsigned SCH_FRAMES[] = {1, 11, 21, 31, 41}; + +const unsigned BCCH_FRAMES[] = {2, 3, 4, 5}; //!!the receiver shouldn't care about logical + //!!channels so this will be removed from this header +const unsigned TEST_CCH_FRAMES[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49}; +const unsigned TRAFFIC_CHANNEL_F[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; +const unsigned TEST51[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}; + + +#define TSC0 0 +#define TSC1 1 +#define TSC2 2 +#define TSC3 3 +#define TSC4 4 +#define TSC5 5 +#define TSC6 6 +#define TSC7 7 +#define TS_DUMMY 8 + +#define TRAIN_SEQ_NUM 9 + +#define TIMESLOT0 0 +#define TIMESLOT1 1 +#define TIMESLOT2 2 +#define TIMESLOT3 3 +#define TIMESLOT4 4 +#define TIMESLOT5 5 +#define TIMESLOT6 6 +#define TIMESLOT7 7 + + +static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = { + {0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1}, + {0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1}, + {0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0}, + {0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0}, + {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1}, + {0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0}, + {1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0}, + {0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY +}; + + +//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8 +static const unsigned char dummy_burst[] = { + 0, 0, 0, + 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, + 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, + + 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 1, 0, 1, + + 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, + 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0 +}; + + +/* + * The frequency correction burst is used for frequency synchronization + * of the mobile. This is broadcast in TS0 together with the SCH and + * BCCH. + * + * Modulating the bits below causes a spike at 62.5kHz above (below for + * COMPACT) the center frequency. One can use this spike with a narrow + * band filter to accurately determine the center of the channel. + */ +static const unsigned char fc_fb[] = { + 0, 0, 0, //I don't use this tables, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //I copied this here from burst_types.h because + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //the description is very informative - p.krysik + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const unsigned char fc_compact_fb[] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 +}; + + +#endif /* INCLUDED_GSM_CONSTANTS_H */ diff --git a/lib/receiver/receiver_config.cc b/lib/receiver/receiver_config.cc new file mode 100644 index 0000000..c7c996e --- /dev/null +++ b/lib/receiver/receiver_config.cc @@ -0,0 +1,84 @@ +/* -*- c++ -*- */ +/* + * @file + * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl> + * @section LICENSE + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + * + * @section DESCRIPTION + * This file contains classes which define gsm_receiver configuration + * and the burst_counter which is used to store internal state of the receiver + * when it's synchronized + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <receiver_config.h> + +burst_counter & burst_counter::operator++(int) +{ + d_timeslot_nr++; + if (d_timeslot_nr == TS_PER_FRAME) { + d_timeslot_nr = 0; + + if ((d_t2 == 25) && (d_t3 == 50)) { + d_t1 = (d_t1 + 1) % (1 << 11); + } + + d_t2 = (d_t2 + 1) % 26; + d_t3 = (d_t3 + 1) % 51; + } + + //update offset - this is integer for d_OSR which is multiple of four + d_offset_fractional += GUARD_FRACTIONAL * d_OSR; + d_offset_integer = floor(d_offset_fractional); + d_offset_fractional = d_offset_fractional - d_offset_integer; + return (*this); +} + +void burst_counter::set(uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr) +{ + d_t1 = t1; + d_t2 = t2; + d_t3 = t3; + d_timeslot_nr = timeslot_nr; + double first_sample_position = (get_frame_nr() * 8 + timeslot_nr) * TS_BITS; + d_offset_fractional = first_sample_position - floor(first_sample_position); + d_offset_integer = 0; +} + +burst_type channel_configuration::get_burst_type(burst_counter burst_nr) +{ + uint32_t timeslot_nr = burst_nr.get_timeslot_nr(); + multiframe_type m_type = d_timeslots_descriptions[timeslot_nr].get_type(); + uint32_t nr; + + switch (m_type) { + case multiframe_26: + nr = burst_nr.get_t2(); + break; + case multiframe_51: + nr = burst_nr.get_t3(); + break; + default: + nr = 0; + break; + } + + return d_timeslots_descriptions[timeslot_nr].get_burst_type(nr); +} diff --git a/lib/receiver/receiver_config.h b/lib/receiver/receiver_config.h new file mode 100644 index 0000000..b7ba43a --- /dev/null +++ b/lib/receiver/receiver_config.h @@ -0,0 +1,164 @@ +/* -*- c++ -*- */ +/* + * @file + * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl> + * @section LICENSE + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + * + * @section DESCRIPTION + * This file contains classes which define gsm_receiver configuration + * and the burst_counter which is used to store internal state of the receiver + * when it's synchronized + */ +#ifndef INCLUDED_GSM_RECEIVER_CONFIG_H +#define INCLUDED_GSM_RECEIVER_CONFIG_H + +#include <vector> +#include <algorithm> +#include <math.h> +#include <stdint.h> +#include <gsm_constants.h> + +class multiframe_configuration +{ + private: + multiframe_type d_type; + std::vector<burst_type> d_burst_types; + public: + multiframe_configuration() { + d_type = unknown; + fill(d_burst_types.begin(), d_burst_types.end(), empty); + } + + ~multiframe_configuration() {} + + void set_type(multiframe_type type) { + if (type == multiframe_26) { + d_burst_types.resize(26); + } else { + d_burst_types.resize(51); + } + + d_type = type; + } + + void set_burst_type(int nr, burst_type type) { + d_burst_types[nr] = type; + } + + multiframe_type get_type() { + return d_type; + } + + burst_type get_burst_type(int nr) { + return d_burst_types[nr]; + } +}; + +class burst_counter +{ + private: + const int d_OSR; + uint32_t d_t1, d_t2, d_t3, d_timeslot_nr; + double d_offset_fractional; + double d_offset_integer; + public: + burst_counter(int osr): + d_OSR(osr), + d_t1(0), + d_t2(0), + d_t3(0), + d_timeslot_nr(0), + d_offset_fractional(0.0), + d_offset_integer(0.0) { + } + + burst_counter(int osr, uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr): + d_OSR(osr), + d_t1(t1), + d_t2(t2), + d_t3(t3), + d_timeslot_nr(timeslot_nr), + d_offset_fractional(0.0), + d_offset_integer(0.0) { + double first_sample_position = (get_frame_nr() * 8 + timeslot_nr) * TS_BITS; + d_offset_integer = floor(first_sample_position); + d_offset_fractional = first_sample_position - floor(first_sample_position); + } + + burst_counter & operator++(int); + void set(uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr); + + uint32_t get_t1() { + return d_t1; + } + + uint32_t get_t2() { + return d_t2; + } + + uint32_t get_t3() { + return d_t3; + } + + uint32_t get_timeslot_nr() { + return d_timeslot_nr; + } + + uint32_t get_frame_nr() { + return (51 * 26 * d_t1) + (51 * (((d_t3 + 26) - d_t2) % 26)) + d_t3; + } + + uint32_t get_frame_nr_mod() { + return (d_t1 << 11) + (d_t3 << 5) + d_t2; + } + + unsigned get_offset() { + return (unsigned)d_offset_integer; + } +}; + +class channel_configuration +{ + private: + multiframe_configuration d_timeslots_descriptions[TS_PER_FRAME]; + public: + channel_configuration() { + for (int i = 0; i < TS_PER_FRAME; i++) { + d_timeslots_descriptions[i].set_type(unknown); + } + } + + void set_multiframe_type(int timeslot_nr, multiframe_type type) { + d_timeslots_descriptions[timeslot_nr].set_type(type); + } + + void set_burst_types(int timeslot_nr, const unsigned mapping[], unsigned mapping_size, burst_type b_type) { + unsigned i; + for (i = 0; i < mapping_size; i++) { + d_timeslots_descriptions[timeslot_nr].set_burst_type(mapping[i], b_type); + } + } + + void set_single_burst_type(int timeslot_nr, int burst_nr, burst_type b_type) { + d_timeslots_descriptions[timeslot_nr].set_burst_type(burst_nr, b_type); + } + + burst_type get_burst_type(burst_counter burst_nr); +}; + +#endif /* INCLUDED_GSM_RECEIVER_CONFIG_H */ diff --git a/lib/receiver/receiver_impl.cc b/lib/receiver/receiver_impl.cc new file mode 100644 index 0000000..6e92c2d --- /dev/null +++ b/lib/receiver/receiver_impl.cc @@ -0,0 +1,842 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 Piotr Krysik <pkrysik@elka.pw.edu.pl>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gnuradio/io_signature.h> +#include "receiver_impl.h" + +#include <gnuradio/io_signature.h> +#include <gnuradio/math.h> +#include <math.h> +#include <boost/circular_buffer.hpp> +#include <algorithm> +#include <numeric> +#include <viterbi_detector.h> +#include <string.h> +#include <sch.h> +#include <iostream> +#include <iomanip> +#include <assert.h> +#include <boost/scoped_ptr.hpp> + +#define SYNC_SEARCH_RANGE 30 + +namespace gr +{ +namespace gsm +{ + +typedef std::list<float> list_float; +typedef std::vector<float> vector_float; + +typedef boost::circular_buffer<float> circular_buffer_float; + +receiver::sptr +receiver::make(feval_dd * tuner, int osr, int arfcn) +{ + return gnuradio::get_initial_sptr + (new receiver_impl(tuner, osr, arfcn)); +} + +/* + * The private constructor + */ +receiver_impl::receiver_impl(feval_dd * tuner, int osr, int arfcn) + : gr::sync_block("receiver", + gr::io_signature::make(1, 1, sizeof(gr_complex)), + gr::io_signature::make(0, 0, 0)), + d_OSR(osr), + d_chan_imp_length(CHAN_IMP_RESP_LENGTH), + d_tuner(tuner), + d_counter(0), + d_fcch_start_pos(0), + d_freq_offset(0), + d_state(first_fcch_search), + d_burst_nr(osr), + d_failed_sch(0), + d_arfcn((int)(arfcn)), + d_signal_dbm(-120) +{ + int i; + set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR)); //don't send samples to the receiver until there are at least samples for one + // burst and two gurad periods (one gurard period is an arbitrary overlap) + gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0)); + for (i = 0; i < TRAIN_SEQ_NUM; i++) + { + gr_complex startpoint = (train_seq[i][0]==0) ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0); //if first bit of the seqeunce ==0 first symbol ==1 + gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint); + } + message_port_register_out(pmt::mp("bursts")); + configure_receiver(); //configure the receiver - tell it where to find which burst type +} + +/* + * Our virtual destructor. + */ +receiver_impl::~receiver_impl() +{ +} + +int +receiver_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + //std::cout << noutput_items << std::endl; + const gr_complex *input = (const gr_complex *) input_items[0]; + + switch (d_state) + { + //bootstrapping + case first_fcch_search: + DCOUT("FCCH search"); + if (find_fcch_burst(input, noutput_items)) //find frequency correction burst in the input buffer + { + //set_frequency(d_freq_offset); //if fcch search is successful set frequency offset + COUT("Freq offset " << d_freq_offset); + d_state = next_fcch_search; + } + else + { + d_state = first_fcch_search; + } + break; + + case next_fcch_search: //this state is used because it takes some time (a bunch of buffered samples) + { + DCOUT("NEXT FCCH search"); + float prev_freq_offset = d_freq_offset; //before previous set_frequqency cause change + if (find_fcch_burst(input, noutput_items)) + { + if (abs(prev_freq_offset - d_freq_offset) > FCCH_MAX_FREQ_OFFSET) + { + //set_frequency(d_freq_offset); //call set_frequncy only frequency offset change is greater than some value + DCOUT("Freq offset " << d_freq_offset); + } + d_state = sch_search; + } + else + { + d_state = next_fcch_search; + } + break; + } + + + case sch_search: + { + DCOUT("SCH search"); + vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR); + int t1, t2, t3; + int burst_start = 0; + unsigned char output_binary[BURST_SIZE]; + + if (reach_sch_burst(noutput_items)) //wait for a SCH burst + { + burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response from it + detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //detect bits using MLSE detection + if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) //decode SCH burst + { + DCOUT("sch burst_start: " << burst_start); + DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3); + d_burst_nr.set(t1, t2, t3, 0); //set counter of bursts value + d_burst_nr++; + + consume_each(burst_start + BURST_SIZE * d_OSR); //consume samples up to next guard period + d_state = synchronized; + } + else + { + d_state = next_fcch_search; //if there is error in the sch burst go back to fcch search phase + } + } + else + { + d_state = sch_search; + } + break; + } + //in this state receiver is synchronized and it processes bursts according to burst type for given burst number + case synchronized: + { + DCOUT("Synchronized"); + vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR); + int burst_start; + int offset = 0; + int to_consume = 0; + unsigned char output_binary[BURST_SIZE]; + + burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr); //get burst type for given burst number + double signal_pwr=0; + for(int ii=0;ii<noutput_items;ii++) + { + signal_pwr += abs(input[ii])*abs(input[ii]); + } + d_signal_dbm=static_cast<int8_t>(round(20*log10(signal_pwr))); + + switch (b_type) + { + case fcch_burst: //if it's FCCH burst + { + const unsigned first_sample = ceil((GUARD_PERIOD + 2 * TAIL_BITS) * d_OSR) + 1; + const unsigned last_sample = first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR; + double freq_offset = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it + + d_freq_offset_vals.push_front(freq_offset); + send_burst(d_burst_nr, fc_fb, b_type); + + if (d_freq_offset_vals.size() >= 10) + { + double sum = std::accumulate(d_freq_offset_vals.begin(), d_freq_offset_vals.end(), 0); + double mean_offset = sum / d_freq_offset_vals.size(); //compute mean + d_freq_offset_vals.clear(); + DCOUT("mean offset" << mean_offset); + if (abs(mean_offset) > FCCH_MAX_FREQ_OFFSET) + { + //d_freq_offset -= mean_offset; //and adjust frequency if it have changed beyond + //set_frequency(d_freq_offset); //some limit + DCOUT("Adjusting frequency, new frequency offset: " << d_freq_offset << "\n"); + } + } + } + break; + case sch_burst: //if it's SCH burst + { + int t1, t2, t3, d_ncc, d_bcc; + burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response + detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits + send_burst(d_burst_nr, output_binary, b_type); + if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) //and decode SCH data + { + // d_burst_nr.set(t1, t2, t3, 0); //but only to check if burst_start value is correct + d_failed_sch = 0; + DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3); + offset = burst_start - floor((GUARD_PERIOD) * d_OSR); //compute offset from burst_start - burst should start after a guard period + DCOUT("offset: "<<offset); + to_consume += offset; //adjust with offset number of samples to be consumed + } + else + { + d_failed_sch++; + if (d_failed_sch >= MAX_SCH_ERRORS) + { + d_state = next_fcch_search; + d_freq_offset_vals.clear(); + d_freq_offset=0; + //set_frequency(0); + DCOUT("Re-Synchronization"); + } + } + } + break; + + case normal_burst: + { + float normal_corr_max; //if it's normal burst + burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, d_bcc); //get channel impulse response for given training sequence number - d_bcc + detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits + send_burst(d_burst_nr, output_binary, b_type); + break; + } + case dummy_or_normal: + { + unsigned int normal_burst_start; + float dummy_corr_max, normal_corr_max; + DCOUT("Dummy"); + get_norm_chan_imp_resp(input, &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY); + DCOUT("Normal"); + normal_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, d_bcc); + + DCOUT("normal_corr_max: " << normal_corr_max << " dummy_corr_max:" << dummy_corr_max); + if (normal_corr_max > dummy_corr_max) + { + detect_burst(input, &channel_imp_resp[0], normal_burst_start, output_binary); + send_burst(d_burst_nr, output_binary, b_type); + } + else + { + send_burst(d_burst_nr, dummy_burst, b_type); + } + } + case rach_burst: + break; + case dummy: + send_burst(d_burst_nr, dummy_burst, b_type); + break; + case empty: //if it's empty burst + break; //do nothing + } + + d_burst_nr++; //go to next burst + + to_consume += TS_BITS * d_OSR + d_burst_nr.get_offset(); //consume samples of the burst up to next guard period + //and add offset which is introduced by + //0.25 fractional part of a guard period + consume_each(to_consume); + } + break; + } + + return 0; +} + + +bool receiver_impl::find_fcch_burst(const gr_complex *input, const int nitems) +{ + circular_buffer_float phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR); //circular buffer used to scan throug signal to find + //best match for FCCH burst + float phase_diff = 0; + gr_complex conjprod; + int start_pos = -1; + int hit_count = 0; + int miss_count = 0; + float min_phase_diff; + float max_phase_diff; + double best_sum = 0; + float lowest_max_min_diff = 99999; + + int to_consume = 0; + int sample_number = 0; + bool end = false; + bool result = false; + circular_buffer_float::iterator buffer_iter; + + /**@name Possible states of FCCH search algorithm*/ + //@{ + enum states + { + init, ///< initialize variables + search, ///< search for positive samples + found_something, ///< search for FCCH and the best position of it + fcch_found, ///< when FCCH was found + search_fail ///< when there is no FCCH in the input vector + } fcch_search_state; + //@} + + fcch_search_state = init; + + while (!end) + { + switch (fcch_search_state) + { + + case init: //initialize variables + hit_count = 0; + miss_count = 0; + start_pos = -1; + lowest_max_min_diff = 99999; + phase_diff_buffer.clear(); + fcch_search_state = search; + + break; + + case search: // search for positive samples + sample_number++; + + if (sample_number > nitems - FCCH_HITS_NEEDED * d_OSR) //if it isn't possible to find FCCH because + { + //there's too few samples left to look into, + to_consume = sample_number; //don't do anything with those samples which are left + //and consume only those which were checked + fcch_search_state = search_fail; + } + else + { + phase_diff = compute_phase_diff(input[sample_number], input[sample_number-1]); + + if (phase_diff > 0) //if a positive phase difference was found + { + to_consume = sample_number; + fcch_search_state = found_something; //switch to state in which searches for FCCH + } + else + { + fcch_search_state = search; + } + } + + break; + + case found_something: // search for FCCH and the best position of it + { + if (phase_diff > 0) + { + hit_count++; //positive phase differencies increases hits_count + } + else + { + miss_count++; //negative increases miss_count + } + + if ((miss_count >= FCCH_MAX_MISSES * d_OSR) && (hit_count <= FCCH_HITS_NEEDED * d_OSR)) + { + //if miss_count exceeds limit before hit_count + fcch_search_state = init; //go to init + continue; + } + else if (((miss_count >= FCCH_MAX_MISSES * d_OSR) && (hit_count > FCCH_HITS_NEEDED * d_OSR)) || (hit_count > 2 * FCCH_HITS_NEEDED * d_OSR)) + { + //if hit_count and miss_count exceeds limit then FCCH was found + fcch_search_state = fcch_found; + continue; + } + else if ((miss_count < FCCH_MAX_MISSES * d_OSR) && (hit_count > FCCH_HITS_NEEDED * d_OSR)) + { + //find difference between minimal and maximal element in the buffer + //for FCCH this value should be low + //this part is searching for a region where this value is lowest + min_phase_diff = * (min_element(phase_diff_buffer.begin(), phase_diff_buffer.end())); + max_phase_diff = * (max_element(phase_diff_buffer.begin(), phase_diff_buffer.end())); + + if (lowest_max_min_diff > max_phase_diff - min_phase_diff) + { + lowest_max_min_diff = max_phase_diff - min_phase_diff; + start_pos = sample_number - FCCH_HITS_NEEDED * d_OSR - FCCH_MAX_MISSES * d_OSR; //store start pos + best_sum = 0; + + for (buffer_iter = phase_diff_buffer.begin(); + buffer_iter != (phase_diff_buffer.end()); + buffer_iter++) + { + best_sum += *buffer_iter - (M_PI / 2) / d_OSR; //store best value of phase offset sum + } + } + } + + sample_number++; + + if (sample_number >= nitems) //if there's no single sample left to check + { + fcch_search_state = search_fail;//FCCH search failed + continue; + } + + phase_diff = compute_phase_diff(input[sample_number], input[sample_number-1]); + phase_diff_buffer.push_back(phase_diff); + fcch_search_state = found_something; + } + break; + + case fcch_found: + { + DCOUT("fcch found on position: " << d_counter + start_pos); + to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1; //consume one FCCH burst + + d_fcch_start_pos = d_counter + start_pos; + + //compute frequency offset + double phase_offset = best_sum / FCCH_HITS_NEEDED; + double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI); + d_freq_offset -= freq_offset; + DCOUT("freq_offset: " << d_freq_offset); + + end = true; + result = true; + break; + } + + case search_fail: + end = true; + result = false; + break; + } + } + + d_counter += to_consume; + consume_each(to_consume); + + return result; +} + +double receiver_impl::compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample) +{ + double phase_sum = 0; + unsigned ii; + + for (ii = first_sample; ii < last_sample; ii++) + { + double phase_diff = compute_phase_diff(input[ii], input[ii-1]) - (M_PI / 2) / d_OSR; + phase_sum += phase_diff; + } + + double phase_offset = phase_sum / (last_sample - first_sample); + double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI); + return freq_offset; +} + +void receiver_impl::set_frequency(double freq_offset) +{ + d_tuner->calleval(freq_offset); +} + +inline float receiver_impl::compute_phase_diff(gr_complex val1, gr_complex val2) +{ + gr_complex conjprod = val1 * conj(val2); + return fast_atan2f(imag(conjprod), real(conjprod)); +} + +bool receiver_impl::reach_sch_burst(const int nitems) +{ + //it just consumes samples to get near to a SCH burst + int to_consume = 0; + bool result = false; + unsigned sample_nr_near_sch_start = d_fcch_start_pos + (FRAME_BITS - SAFETY_MARGIN) * d_OSR; + + //consume samples until d_counter will be equal to sample_nr_near_sch_start + if (d_counter < sample_nr_near_sch_start) + { + if (d_counter + nitems >= sample_nr_near_sch_start) + { + to_consume = sample_nr_near_sch_start - d_counter; + } + else + { + to_consume = nitems; + } + result = false; + } + else + { + to_consume = 0; + result = true; + } + + d_counter += to_consume; + consume_each(to_consume); + return result; +} + +int receiver_impl::get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp) +{ + vector_complex correlation_buffer; + vector_float power_buffer; + vector_float window_energy_buffer; + + int strongest_window_nr; + int burst_start = 0; + int chan_imp_resp_center = 0; + float max_correlation = 0; + float energy = 0; + + for (int ii = SYNC_POS * d_OSR; ii < (SYNC_POS + SYNC_SEARCH_RANGE) *d_OSR; ii++) + { + gr_complex correlation = correlate_sequence(&d_sch_training_seq[5], N_SYNC_BITS - 10, &input[ii]); + correlation_buffer.push_back(correlation); + power_buffer.push_back(std::pow(abs(correlation), 2)); + } + + //compute window energies + vector_float::iterator iter = power_buffer.begin(); + bool loop_end = false; + while (iter != power_buffer.end()) + { + vector_float::iterator iter_ii = iter; + energy = 0; + + for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++, iter_ii++) + { + if (iter_ii == power_buffer.end()) + { + loop_end = true; + break; + } + energy += (*iter_ii); + } + if (loop_end) + { + break; + } + iter++; + window_energy_buffer.push_back(energy); + } + + strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - window_energy_buffer.begin(); + // d_channel_imp_resp.clear(); + + max_correlation = 0; + for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++) + { + gr_complex correlation = correlation_buffer[strongest_window_nr + ii]; + if (abs(correlation) > max_correlation) + { + chan_imp_resp_center = ii; + max_correlation = abs(correlation); + } + // d_channel_imp_resp.push_back(correlation); + chan_imp_resp[ii] = correlation; + } + + burst_start = strongest_window_nr + chan_imp_resp_center - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR; + return burst_start; +} + + + +void receiver_impl::detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary) +{ + float output[BURST_SIZE]; + gr_complex rhh_temp[CHAN_IMP_RESP_LENGTH*d_OSR]; + gr_complex rhh[CHAN_IMP_RESP_LENGTH]; + gr_complex filtered_burst[BURST_SIZE]; + int start_state = 3; + unsigned int stop_states[2] = {4, 12}; + + autocorrelation(chan_imp_resp, rhh_temp, d_chan_imp_length*d_OSR); + for (int ii = 0; ii < (d_chan_imp_length); ii++) + { + rhh[ii] = conj(rhh_temp[ii*d_OSR]); + } + + mafi(&input[burst_start], BURST_SIZE, chan_imp_resp, d_chan_imp_length*d_OSR, filtered_burst); + + viterbi_detector(filtered_burst, BURST_SIZE, rhh, start_state, stop_states, 2, output); + + for (int i = 0; i < BURST_SIZE ; i++) + { + output_binary[i] = (output[i] > 0); + } +} + +//TODO consider placing this funtion in a separate class for signal processing +void receiver_impl::gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point) +{ + gr_complex j = gr_complex(0.0, 1.0); + + int current_symbol; + int encoded_symbol; + int previous_symbol = 2 * input[0] - 1; + gmsk_output[0] = start_point; + + for (int i = 1; i < nitems; i++) + { + //change bits representation to NRZ + current_symbol = 2 * input[i] - 1; + //differentially encode + encoded_symbol = current_symbol * previous_symbol; + //and do gmsk mapping + gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i-1]; + previous_symbol = current_symbol; + } +} + +//TODO consider use of some generalized function for correlation and placing it in a separate class for signal processing +gr_complex receiver_impl::correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input) +{ + gr_complex result(0.0, 0.0); + int sample_number = 0; + + for (int ii = 0; ii < length; ii++) + { + sample_number = (ii * d_OSR) ; + result += sequence[ii] * conj(input[sample_number]); + } + + result = result / gr_complex(length, 0); + return result; +} + +//computes autocorrelation for positive arguments +//TODO consider placing this funtion in a separate class for signal processing +inline void receiver_impl::autocorrelation(const gr_complex * input, gr_complex * out, int nitems) +{ + int i, k; + for (k = nitems - 1; k >= 0; k--) + { + out[k] = gr_complex(0, 0); + for (i = k; i < nitems; i++) + { + out[k] += input[i] * conj(input[i-k]); + } + } +} + +//TODO consider use of some generalized function for filtering and placing it in a separate class for signal processing +inline void receiver_impl::mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output) +{ + int ii = 0, n, a; + + for (n = 0; n < nitems; n++) + { + a = n * d_OSR; + output[n] = 0; + ii = 0; + + while (ii < filter_length) + { + if ((a + ii) >= nitems*d_OSR){ + break; + } + output[n] += input[a+ii] * filter[ii]; + ii++; + } + } +} + +//TODO: get_norm_chan_imp_resp is similar to get_sch_chan_imp_resp - consider joining this two functions +//especially computations of strongest_window_nr +int receiver_impl::get_norm_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp, float *corr_max, int bcc) +{ + vector_complex correlation_buffer; + vector_float power_buffer; + vector_float window_energy_buffer; + + int strongest_window_nr; + int burst_start = 0; + int chan_imp_resp_center = 0; + float max_correlation = 0; + float energy = 0; + + int search_center = (int)((TRAIN_POS + GUARD_PERIOD) * d_OSR); + int search_start_pos = search_center + 1 - 5*d_OSR; + // int search_start_pos = search_center - d_chan_imp_length * d_OSR; + int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 5 * d_OSR; + + for (int ii = search_start_pos; ii < search_stop_pos; ii++) + { + gr_complex correlation = correlate_sequence(&d_norm_training_seq[bcc][TRAIN_BEGINNING], N_TRAIN_BITS - 10, &input[ii]); + + correlation_buffer.push_back(correlation); + power_buffer.push_back(std::pow(abs(correlation), 2)); + } + + //compute window energies + vector_float::iterator iter = power_buffer.begin(); + bool loop_end = false; + while (iter != power_buffer.end()) + { + vector_float::iterator iter_ii = iter; + energy = 0; + + for (int ii = 0; ii < (d_chan_imp_length - 2)*d_OSR; ii++, iter_ii++) + { + if (iter_ii == power_buffer.end()) + { + loop_end = true; + break; + } + energy += (*iter_ii); + } + if (loop_end) + { + break; + } + iter++; + + window_energy_buffer.push_back(energy); + } + + strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()-((d_chan_imp_length)*d_OSR)) - window_energy_buffer.begin(); + //strongest_window_nr = strongest_window_nr-d_OSR; + if(strongest_window_nr<0){ + strongest_window_nr = 0; + } + + max_correlation = 0; + for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++) + { + gr_complex correlation = correlation_buffer[strongest_window_nr + ii]; + if (abs(correlation) > max_correlation) + { + chan_imp_resp_center = ii; + max_correlation = abs(correlation); + } + // d_channel_imp_resp.push_back(correlation); + chan_imp_resp[ii] = correlation; + } + + *corr_max = max_correlation; + + DCOUT("strongest_window_nr_new: " << strongest_window_nr); + burst_start = search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR; //compute first sample posiiton which corresponds to the first sample of the impulse response + //TRAIN_POS=3+57+1+6 + //TODO: describe this part in detail in documentation as this is crucial part for synchronization + + DCOUT("burst_start: " << burst_start); + return burst_start; +} + + +void receiver_impl::send_burst(burst_counter burst_nr, const unsigned char * burst_binary, burst_type b_type) +{ + + boost::scoped_ptr<gsmtap_hdr> tap_header(new gsmtap_hdr()); + + tap_header->version = GSMTAP_VERSION; + tap_header->hdr_len = BURST_SIZE/4; + tap_header->type = GSMTAP_TYPE_UM_BURST; + tap_header->timeslot = static_cast<uint8_t>(d_burst_nr.get_timeslot_nr()); + tap_header->frame_number = d_burst_nr.get_frame_nr(); + tap_header->sub_type = static_cast<uint8_t>(b_type); + tap_header->arfcn = d_arfcn; + tap_header->signal_dbm = static_cast<int8_t>(d_signal_dbm); + pmt::pmt_t header_blob=pmt::make_blob(tap_header.get(),sizeof(gsmtap_hdr)); + pmt::pmt_t burst_binary_blob=pmt::make_blob(burst_binary,BURST_SIZE); + pmt::pmt_t msg = pmt::cons(header_blob, burst_binary_blob); + + message_port_pub(pmt::mp("bursts"), msg); +} + +void receiver_impl::configure_receiver() +{ + d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT0, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + + d_channel_conf.set_burst_types(TIMESLOT0, TEST_CCH_FRAMES, sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES, sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst); + d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES, sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst); + + // d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT1, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + // d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT2, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + // d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT3, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + // d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT4, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + // d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT5, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + // d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT6, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + // d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_26); + // d_channel_conf.set_burst_types(TIMESLOT7, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal); + + d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT1, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT2, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT3, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT4, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT5, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT6, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); + d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_51); + d_channel_conf.set_burst_types(TIMESLOT7, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal); +} + + +} /* namespace gsm */ +} /* namespace gr */ + diff --git a/lib/receiver/receiver_impl.h b/lib/receiver/receiver_impl.h new file mode 100644 index 0000000..b8b8b68 --- /dev/null +++ b/lib/receiver/receiver_impl.h @@ -0,0 +1,214 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GSM_RECEIVER_IMPL_H +#define INCLUDED_GSM_RECEIVER_IMPL_H + +#include <gsm/receiver.h> +#include <gsm_constants.h> +#include <receiver_config.h> +#include <gsmtap.h> + +namespace gr { + namespace gsm { + + typedef std::vector<gr_complex> vector_complex; + + class receiver_impl : public receiver + { + private: + /**@name Configuration of the receiver */ + //@{ + const int d_OSR; ///< oversampling ratio + const int d_chan_imp_length; ///< channel impulse length + uint16_t d_arfcn; + int8_t d_signal_dbm; + //@} + + gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst + gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal bursts and dummy bursts + + feval_dd *d_tuner; ///<callback to a python object which is used for frequency tunning + + /** Counts samples consumed by the receiver + * + * It is used in beetween find_fcch_burst and reach_sch_burst calls. + * My intention was to synchronize this counter with some internal sample + * counter of the USRP. Simple access to such USRP's counter isn't possible + * so this variable isn't used in the "synchronized" state of the receiver yet. + */ + unsigned d_counter; + + /**@name Variables used to store result of the find_fcch_burst fuction */ + //@{ + unsigned d_fcch_start_pos; ///< position of the first sample of the fcch burst + float d_freq_offset; ///< frequency offset of the received signal + //@} + std::list<double> d_freq_offset_vals; + + /**@name Identifiers of the BTS extracted from the SCH burst */ + //@{ + int d_ncc; ///< network color code + int d_bcc; ///< base station color code + //@} + + /**@name Internal state of the gsm receiver */ + //@{ + enum states { + first_fcch_search, next_fcch_search, sch_search, // synchronization search part + synchronized // receiver is synchronized in this state + } d_state; + //@} + + /**@name Variables which make internal state in the "synchronized" state */ + //@{ + burst_counter d_burst_nr; ///< frame number and timeslot number + channel_configuration d_channel_conf; ///< mapping of burst_counter to burst_type + //@} + + unsigned d_failed_sch; ///< number of subsequent erroneous SCH bursts + + /** Function whis is used to search a FCCH burst and to compute frequency offset before + * "synchronized" state of the receiver + * + * TODO: Describe the FCCH search algorithm in the documentation + * @param input vector with input signal + * @param nitems number of samples in the input vector + * @return + */ + bool find_fcch_burst(const gr_complex *input, const int nitems); + + /** Computes frequency offset from FCCH burst samples + * + * @param input vector with input samples + * @param first_sample number of the first sample of the FCCH busrt + * @param last_sample number of the last sample of the FCCH busrt + * @return frequency offset + */ + double compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample); + + /** Calls d_tuner's method to set frequency offset from Python level + * + * @param freq_offset absolute frequency offset of the received signal + */ + void set_frequency(double freq_offset); + + /** Computes angle between two complex numbers + * + * @param val1 first complex number + * @param val2 second complex number + * @return + */ + inline float compute_phase_diff(gr_complex val1, gr_complex val2); + + /** Function whis is used to get near to SCH burst + * + * @param nitems number of samples in the gsm_receiver's buffer + * @return true if SCH burst is near, false otherwise + */ + bool reach_sch_burst(const int nitems); + + /** Extracts channel impulse response from a SCH burst and computes first sample number of this burst + * + * @param input vector with input samples + * @param chan_imp_resp complex vector where channel impulse response will be stored + * @return number of first sample of the burst + */ + int get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp); + + /** MLSE detection of a burst bits + * + * Detects bits of burst using viterbi algorithm. + * @param input vector with input samples + * @param chan_imp_resp vector with the channel impulse response + * @param burst_start number of the first sample of the burst + * @param output_binary vector with output bits + */ + void detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary); + + /** Encodes differentially input bits and maps them into MSK states + * + * @param input vector with input bits + * @param nitems number of samples in the "input" vector + * @param gmsk_output bits mapped into MSK states + * @param start_point first state + */ + void gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point); + + /** Correlates MSK mapped sequence with input signal + * + * @param sequence MKS mapped sequence + * @param length length of the sequence + * @param input_signal vector with input samples + * @return correlation value + */ + gr_complex correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input); + + /** Computes autocorrelation of input vector for positive arguments + * + * @param input vector with input samples + * @param out output vector + * @param nitems length of the input vector + */ + inline void autocorrelation(const gr_complex * input, gr_complex * out, int nitems); + + /** Filters input signal through channel impulse response + * + * @param input vector with input samples + * @param nitems number of samples to pass through filter + * @param filter filter taps - channel impulse response + * @param filter_length nember of filter taps + * @param output vector with filtered samples + */ + inline void mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output); + + /** Extracts channel impulse response from a normal burst and computes first sample number of this burst + * + * @param input vector with input samples + * @param chan_imp_resp complex vector where channel impulse response will be stored + * @param search_range possible absolute offset of a channel impulse response start + * @param bcc base station color code - number of a training sequence + * @return first sample number of normal burst + */ + int get_norm_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp, float *corr_max, int bcc); + + /** + * + */ + void send_burst(burst_counter burst_nr, const unsigned char * burst_binary, burst_type b_type); + + /** + * + */ + void configure_receiver(); + + public: + receiver_impl(feval_dd * tuner, int osr, int arfcn); + ~receiver_impl(); + +// void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); + }; + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_RECEIVER_IMPL_H */ + |