2014-02-04 16:57:25 +00:00
/* -*- c++ -*- */
2014-02-05 16:27:32 +00:00
/*
2014-04-17 07:45:50 +00:00
* Copyright 2014 Piotr Krysik < pkrysik @ elka . pw . edu . pl > .
2014-02-05 16:27:32 +00:00
*
2014-02-04 16:57:25 +00:00
* 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 .
2014-02-05 16:27:32 +00:00
*
2014-02-04 16:57:25 +00:00
* 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 .
2014-02-05 16:27:32 +00:00
*
2014-02-04 16:57:25 +00:00
* 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>
2014-02-20 23:02:44 +00:00
# include <boost/scoped_ptr.hpp>
2014-07-08 14:47:53 +00:00
//#include "plotting/plotting.hpp"
2014-02-04 16:57:25 +00:00
# define SYNC_SEARCH_RANGE 30
2014-02-05 16:27:32 +00:00
namespace gr
{
namespace gsm
{
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
typedef std : : list < float > list_float ;
typedef std : : vector < float > vector_float ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
typedef boost : : circular_buffer < float > circular_buffer_float ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
receiver : : sptr
2014-02-20 23:02:44 +00:00
receiver : : make ( feval_dd * tuner , int osr , int arfcn )
2014-02-05 16:27:32 +00:00
{
return gnuradio : : get_initial_sptr
2014-02-20 23:02:44 +00:00
( new receiver_impl ( tuner , osr , arfcn ) ) ;
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
/*
* The private constructor
*/
2014-02-20 23:02:44 +00:00
receiver_impl : : receiver_impl ( feval_dd * tuner , int osr , int arfcn )
2014-05-02 15:24:08 +00:00
: gr : : sync_block ( " receiver " ,
2014-02-05 16:27:32 +00:00
gr : : io_signature : : make ( 1 , 1 , sizeof ( gr_complex ) ) ,
2014-02-08 13:15:27 +00:00
gr : : io_signature : : make ( 0 , 0 , 0 ) ) ,
2014-02-05 16:27:32 +00:00
d_OSR ( osr ) ,
d_chan_imp_length ( CHAN_IMP_RESP_LENGTH ) ,
d_tuner ( tuner ) ,
d_counter ( 0 ) ,
d_fcch_start_pos ( 0 ) ,
2014-08-06 12:10:56 +00:00
d_freq_offset_setting ( 0 ) ,
2014-08-06 13:20:33 +00:00
d_state ( fcch_search ) ,
2014-02-05 16:27:32 +00:00
d_burst_nr ( osr ) ,
2014-02-20 23:02:44 +00:00
d_failed_sch ( 0 ) ,
d_arfcn ( ( int ) ( arfcn ) ) ,
d_signal_dbm ( - 120 )
2014-02-05 16:27:32 +00:00
{
int i ;
2014-08-06 12:10:56 +00:00
//don't send samples to the receiver until there are at least samples for one
2014-07-08 14:47:53 +00:00
set_output_multiple ( floor ( ( TS_BITS + 2 * GUARD_PERIOD ) * d_OSR ) ) ; // burst and two gurad periods (one gurard period is an arbitrary overlap)
2014-02-05 16:27:32 +00:00
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 + + )
2014-02-04 16:57:25 +00:00
{
2014-04-24 08:28:29 +00:00
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
2014-07-08 14:47:53 +00:00
//if first bit of the seqeunce ==1 first symbol ==-1
2014-02-04 16:57:25 +00:00
gmsk_mapper ( train_seq [ i ] , N_TRAIN_BITS , d_norm_training_seq [ i ] , startpoint ) ;
}
2014-02-08 13:15:27 +00:00
message_port_register_out ( pmt : : mp ( " bursts " ) ) ;
2014-08-06 12:10:56 +00:00
message_port_register_out ( pmt : : mp ( " measurements " ) ) ;
2014-04-17 09:33:27 +00:00
configure_receiver ( ) ; //configure the receiver - tell it where to find which burst type
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
/*
* Our virtual destructor .
*/
receiver_impl : : ~ receiver_impl ( )
{
}
int
2014-05-02 15:24:08 +00:00
receiver_impl : : work ( int noutput_items ,
gr_vector_const_void_star & input_items ,
gr_vector_void_star & output_items )
2014-02-05 16:27:32 +00:00
{
const gr_complex * input = ( const gr_complex * ) input_items [ 0 ] ;
2014-08-06 12:10:56 +00:00
std : : vector < tag_t > freq_offset_tags ;
uint64_t start = nitems_read ( 0 ) ;
uint64_t stop = start + noutput_items ;
pmt : : pmt_t key = pmt : : string_to_symbol ( " setting_freq_offset " ) ;
get_tags_in_range ( freq_offset_tags , 0 , start , stop , key ) ;
bool freq_offset_tag_in_fcch = false ;
uint64_t tag_offset = - 1 ; //-1 - just some clearly invalid value
if ( ! freq_offset_tags . empty ( ) ) {
tag_t freq_offset_tag = freq_offset_tags [ 0 ] ;
tag_offset = freq_offset_tag . offset - start ;
burst_type b_type = d_channel_conf . get_burst_type ( d_burst_nr ) ;
if ( d_state = = synchronized & & b_type = = fcch_burst ) {
uint64_t last_sample_nr = ceil ( ( GUARD_PERIOD + 2.0 * TAIL_BITS + 156.25 ) * d_OSR ) + 1 ;
if ( tag_offset < last_sample_nr ) {
DCOUT ( " Freq change inside FCCH burst!!!!!!!!!!!!!! " ) ;
freq_offset_tag_in_fcch = true ;
}
d_freq_offset_setting = pmt : : to_double ( freq_offset_tag . value ) ;
} else {
d_freq_offset_setting = pmt : : to_double ( freq_offset_tag . value ) ;
}
}
2014-02-05 16:27:32 +00:00
switch ( d_state )
2014-02-04 16:57:25 +00:00
{
2014-02-05 16:27:32 +00:00
//bootstrapping
2014-08-06 13:20:33 +00:00
case fcch_search : //this state is used because it takes some time (a bunch of buffered samples)
2014-02-05 16:27:32 +00:00
{
2014-08-06 13:20:33 +00:00
DCOUT ( " FCCH search " ) ;
2014-08-06 12:10:56 +00:00
double freq_offset_tmp ;
if ( find_fcch_burst ( input , noutput_items , freq_offset_tmp ) )
2014-02-05 16:27:32 +00:00
{
2014-08-06 13:20:33 +00:00
pmt : : pmt_t msg = pmt : : make_tuple ( pmt : : mp ( " freq_offset " ) , pmt : : from_double ( freq_offset_tmp - d_freq_offset_setting ) , pmt : : mp ( " fcch_search " ) ) ;
2014-08-06 12:10:56 +00:00
message_port_pub ( pmt : : mp ( " measurements " ) , msg ) ;
2014-02-05 16:27:32 +00:00
d_state = sch_search ;
}
else
{
2014-08-06 13:20:33 +00:00
d_state = fcch_search ;
2014-02-05 16:27:32 +00:00
}
break ;
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
case sch_search :
{
2014-02-08 13:15:27 +00:00
DCOUT ( " SCH search " ) ;
2014-02-05 16:27:32 +00:00
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 ] ;
2014-05-02 15:24:08 +00:00
if ( reach_sch_burst ( noutput_items ) ) //wait for a SCH burst
2014-02-05 16:27:32 +00:00
{
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
{
2014-02-20 23:02:44 +00:00
DCOUT ( " sch burst_start: " < < burst_start ) ;
DCOUT ( " bcc: " < < d_bcc < < " ncc: " < < d_ncc < < " t1: " < < t1 < < " t2: " < < t2 < < " t3: " < < t3 ) ;
2014-02-04 16:57:25 +00:00
d_burst_nr . set ( t1 , t2 , t3 , 0 ) ; //set counter of bursts value
d_burst_nr + + ;
2014-07-08 14:47:53 +00:00
consume_each ( burst_start + BURST_SIZE * d_OSR + 4 * d_OSR ) ; //consume samples up to next guard period
2014-02-04 16:57:25 +00:00
d_state = synchronized ;
2014-02-05 16:27:32 +00:00
}
else
{
2014-08-06 13:20:33 +00:00
d_state = fcch_search ; //if there is error in the sch burst go back to fcch search phase
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
}
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 :
{
2014-02-20 23:02:44 +00:00
DCOUT ( " Synchronized " ) ;
2014-02-05 16:27:32 +00:00
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
2014-08-04 09:28:59 +00:00
double signal_pwr = 0 ;
2014-05-02 15:24:08 +00:00
for ( int ii = 0 ; ii < noutput_items ; ii + + )
2014-02-20 23:02:44 +00:00
{
signal_pwr + = abs ( input [ ii ] ) * abs ( input [ ii ] ) ;
}
2014-08-04 09:28:59 +00:00
d_signal_dbm = static_cast < int8_t > ( round ( 10 * log10 ( signal_pwr / 50 / noutput_items ) ) ) ;
2014-02-20 23:02:44 +00:00
2014-02-05 16:27:32 +00:00
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 ;
2014-08-06 12:10:56 +00:00
double freq_offset_tmp = compute_freq_offset ( input , first_sample , last_sample ) ; //extract frequency offset from it
2014-02-05 16:27:32 +00:00
2014-02-20 23:02:44 +00:00
send_burst ( d_burst_nr , fc_fb , b_type ) ;
2014-08-06 12:10:56 +00:00
pmt : : pmt_t msg = pmt : : make_tuple ( pmt : : mp ( " freq_offset " ) , pmt : : from_double ( freq_offset_tmp - d_freq_offset_setting ) , pmt : : mp ( " synchronized " ) ) ;
message_port_pub ( pmt : : mp ( " measurements " ) , msg ) ;
2014-02-05 16:27:32 +00:00
}
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
2014-08-06 12:10:56 +00:00
2014-02-05 16:27:32 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ; //MLSE detection of bits
2014-02-20 23:02:44 +00:00
send_burst ( d_burst_nr , output_binary , b_type ) ;
2014-02-05 16:27:32 +00:00
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
2014-02-08 13:15:27 +00:00
DCOUT ( " offset: " < < offset ) ;
2014-02-05 16:27:32 +00:00
to_consume + = offset ; //adjust with offset number of samples to be consumed
}
else
{
d_failed_sch + + ;
if ( d_failed_sch > = MAX_SCH_ERRORS )
{
2014-08-06 13:20:33 +00:00
d_state = fcch_search ;
2014-08-06 12:10:56 +00:00
pmt : : pmt_t msg = pmt : : make_tuple ( pmt : : mp ( " freq_offset " ) , pmt : : from_double ( 0.0 ) , pmt : : mp ( " sync_loss " ) ) ;
message_port_pub ( pmt : : mp ( " measurements " ) , msg ) ;
DCOUT ( " Re-Synchronization!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
}
}
break ;
2014-02-04 16:57:25 +00:00
2014-02-05 21:44:30 +00:00
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
2014-02-05 16:27:32 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ; //MLSE detection of bits
2014-04-17 07:48:46 +00:00
send_burst ( d_burst_nr , output_binary , b_type ) ;
2014-02-05 16:27:32 +00:00
break ;
2014-02-05 21:44:30 +00:00
}
2014-02-05 16:27:32 +00:00
case dummy_or_normal :
{
2014-02-05 21:44:30 +00:00
unsigned int normal_burst_start ;
float dummy_corr_max , normal_corr_max ;
get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , & dummy_corr_max , TS_DUMMY ) ;
normal_burst_start = get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , & normal_corr_max , d_bcc ) ;
2014-02-08 13:15:27 +00:00
DCOUT ( " normal_corr_max: " < < normal_corr_max < < " dummy_corr_max: " < < dummy_corr_max ) ;
2014-02-05 21:44:30 +00:00
if ( normal_corr_max > dummy_corr_max )
2014-02-05 16:27:32 +00:00
{
2014-02-05 21:44:30 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , normal_burst_start , output_binary ) ;
2014-04-17 07:48:46 +00:00
send_burst ( d_burst_nr , output_binary , b_type ) ;
2014-02-05 16:27:32 +00:00
}
else
{
2014-02-20 23:02:44 +00:00
send_burst ( d_burst_nr , dummy_burst , b_type ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
}
case rach_burst :
break ;
2014-02-05 21:44:30 +00:00
case dummy :
2014-02-20 23:02:44 +00:00
send_burst ( d_burst_nr , dummy_burst , b_type ) ;
2014-02-05 16:27:32 +00:00
break ;
case empty : //if it's empty burst
break ; //do nothing
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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 ;
2014-02-04 16:57:25 +00:00
}
2014-02-20 23:02:44 +00:00
return 0 ;
2014-02-05 16:27:32 +00:00
}
2014-08-06 12:10:56 +00:00
bool receiver_impl : : find_fcch_burst ( const gr_complex * input , const int nitems , double & computed_freq_offset )
2014-02-05 16:27:32 +00:00
{
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 ;
2014-02-20 23:02:44 +00:00
2014-02-05 16:27:32 +00:00
/**@name Possible states of FCCH search algorithm*/
//@{
enum states
2014-02-04 16:57:25 +00:00
{
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
2014-02-05 16:27:32 +00:00
} fcch_search_state ;
//@}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
fcch_search_state = init ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
while ( ! end )
{
switch ( fcch_search_state )
{
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
case init : //initialize variables
2014-02-04 16:57:25 +00:00
hit_count = 0 ;
miss_count = 0 ;
start_pos = - 1 ;
lowest_max_min_diff = 99999 ;
phase_diff_buffer . clear ( ) ;
fcch_search_state = search ;
break ;
2014-02-08 13:15:27 +00:00
case search : // search for positive samples
2014-02-04 16:57:25 +00:00
sample_number + + ;
2014-02-05 16:27:32 +00:00
if ( sample_number > nitems - FCCH_HITS_NEEDED * d_OSR ) //if it isn't possible to find FCCH because
{
2014-02-08 13:15:27 +00:00
//there's too few samples left to look into,
2014-02-05 16:27:32 +00:00
to_consume = sample_number ; //don't do anything with those samples which are left
2014-02-08 13:15:27 +00:00
//and consume only those which were checked
2014-02-05 16:27:32 +00:00
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 ;
}
2014-02-04 16:57:25 +00:00
}
break ;
2014-02-05 16:27:32 +00:00
case found_something : // search for FCCH and the best position of it
{
if ( phase_diff > 0 )
{
2014-02-04 16:57:25 +00:00
hit_count + + ; //positive phase differencies increases hits_count
2014-02-05 16:27:32 +00:00
}
else
{
2014-02-04 16:57:25 +00:00
miss_count + + ; //negative increases miss_count
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
if ( ( miss_count > = FCCH_MAX_MISSES * d_OSR ) & & ( hit_count < = FCCH_HITS_NEEDED * d_OSR ) )
{
2014-02-04 16:57:25 +00:00
//if miss_count exceeds limit before hit_count
fcch_search_state = init ; //go to init
continue ;
2014-02-05 16:27:32 +00:00
}
else if ( ( ( miss_count > = FCCH_MAX_MISSES * d_OSR ) & & ( hit_count > FCCH_HITS_NEEDED * d_OSR ) ) | | ( hit_count > 2 * FCCH_HITS_NEEDED * d_OSR ) )
{
2014-02-04 16:57:25 +00:00
//if hit_count and miss_count exceeds limit then FCCH was found
fcch_search_state = fcch_found ;
continue ;
2014-02-05 16:27:32 +00:00
}
else if ( ( miss_count < FCCH_MAX_MISSES * d_OSR ) & & ( hit_count > FCCH_HITS_NEEDED * d_OSR ) )
{
2014-02-04 16:57:25 +00:00
//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 ( ) ) ) ;
2014-02-05 16:27:32 +00:00
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
}
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
sample_number + + ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
if ( sample_number > = nitems ) //if there's no single sample left to check
{
2014-02-04 16:57:25 +00:00
fcch_search_state = search_fail ; //FCCH search failed
continue ;
}
2014-02-05 16:27:32 +00:00
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 ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
d_fcch_start_pos = d_counter + start_pos ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
//compute frequency offset
double phase_offset = best_sum / FCCH_HITS_NEEDED ;
2014-08-06 12:10:56 +00:00
double freq_offset = phase_offset * 1625000.0 / 6 / ( 2 * M_PI ) ; //1625000.0/6 - GMSK symbol rate in GSM
computed_freq_offset = freq_offset ;
2014-02-04 16:57:25 +00:00
end = true ;
2014-02-05 16:27:32 +00:00
result = true ;
2014-02-04 16:57:25 +00:00
break ;
}
2014-02-05 16:27:32 +00:00
case search_fail :
end = true ;
result = false ;
break ;
}
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
d_counter + = to_consume ;
consume_each ( to_consume ) ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
return result ;
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
double receiver_impl : : compute_freq_offset ( const gr_complex * input , unsigned first_sample , unsigned last_sample )
{
double phase_sum = 0 ;
unsigned ii ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( ii = first_sample ; ii < last_sample ; ii + + )
2014-02-04 16:57:25 +00:00
{
2014-02-05 16:27:32 +00:00
double phase_diff = compute_phase_diff ( input [ ii ] , input [ ii - 1 ] ) - ( M_PI / 2 ) / d_OSR ;
phase_sum + = phase_diff ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
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 )
2014-02-04 16:57:25 +00:00
{
2014-02-05 16:27:32 +00:00
if ( d_counter + nitems > = sample_nr_near_sch_start )
{
to_consume = sample_nr_near_sch_start - d_counter ;
}
else
{
to_consume = nitems ;
2014-02-04 16:57:25 +00:00
}
result = false ;
2014-02-05 16:27:32 +00:00
}
else
{
2014-02-04 16:57:25 +00:00
to_consume = 0 ;
result = true ;
}
2014-02-05 16:27:32 +00:00
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 ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
int strongest_window_nr ;
int burst_start = 0 ;
int chan_imp_resp_center = 0 ;
float max_correlation = 0 ;
float energy = 0 ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( int ii = SYNC_POS * d_OSR ; ii < ( SYNC_POS + SYNC_SEARCH_RANGE ) * d_OSR ; ii + + )
{
2014-02-04 16:57:25 +00:00
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 ) ) ;
2014-02-05 16:27:32 +00:00
}
2014-07-08 14:47:53 +00:00
//plot(power_buffer);
2014-02-05 16:27:32 +00:00
//compute window energies
vector_float : : iterator iter = power_buffer . begin ( ) ;
bool loop_end = false ;
while ( iter ! = power_buffer . end ( ) )
{
2014-02-04 16:57:25 +00:00
vector_float : : iterator iter_ii = iter ;
energy = 0 ;
2014-02-05 16:27:32 +00:00
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 ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
if ( loop_end )
{
break ;
2014-02-04 16:57:25 +00:00
}
iter + + ;
window_energy_buffer . push_back ( energy ) ;
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
strongest_window_nr = max_element ( window_energy_buffer . begin ( ) , window_energy_buffer . end ( ) ) - window_energy_buffer . begin ( ) ;
2014-02-04 16:57:25 +00:00
// d_channel_imp_resp.clear();
2014-02-05 16:27:32 +00:00
max_correlation = 0 ;
for ( int ii = 0 ; ii < ( d_chan_imp_length ) * d_OSR ; ii + + )
{
2014-02-04 16:57:25 +00:00
gr_complex correlation = correlation_buffer [ strongest_window_nr + ii ] ;
2014-02-05 16:27:32 +00:00
if ( abs ( correlation ) > max_correlation )
{
chan_imp_resp_center = ii ;
max_correlation = abs ( correlation ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
// d_channel_imp_resp.push_back(correlation);
2014-02-04 16:57:25 +00:00
chan_imp_resp [ ii ] = correlation ;
}
2014-02-05 16:27:32 +00:00
burst_start = strongest_window_nr + chan_imp_resp_center - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR ;
return burst_start ;
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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 + + )
2014-02-04 16:57:25 +00:00
{
rhh [ ii ] = conj ( rhh_temp [ ii * d_OSR ] ) ;
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
mafi ( & input [ burst_start ] , BURST_SIZE , chan_imp_resp , d_chan_imp_length * d_OSR , filtered_burst ) ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
viterbi_detector ( filtered_burst , BURST_SIZE , rhh , start_state , stop_states , 2 , output ) ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( int i = 0 ; i < BURST_SIZE ; i + + )
{
2014-02-04 16:57:25 +00:00
output_binary [ i ] = ( output [ i ] > 0 ) ;
}
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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 ) ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
int current_symbol ;
int encoded_symbol ;
int previous_symbol = 2 * input [ 0 ] - 1 ;
gmsk_output [ 0 ] = start_point ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( int i = 1 ; i < nitems ; i + + )
{
2014-02-04 16:57:25 +00:00
//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 ;
}
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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 ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( int ii = 0 ; ii < length ; ii + + )
{
2014-02-04 16:57:25 +00:00
sample_number = ( ii * d_OSR ) ;
result + = sequence [ ii ] * conj ( input [ sample_number ] ) ;
}
2014-02-05 16:27:32 +00:00
result = result / gr_complex ( length , 0 ) ;
return result ;
}
//computes autocorrelation for positive arguments
inline void receiver_impl : : autocorrelation ( const gr_complex * input , gr_complex * out , int nitems )
{
int i , k ;
for ( k = nitems - 1 ; k > = 0 ; k - - )
2014-02-04 16:57:25 +00:00
{
out [ k ] = gr_complex ( 0 , 0 ) ;
2014-02-05 16:27:32 +00:00
for ( i = k ; i < nitems ; i + + )
{
out [ k ] + = input [ i ] * conj ( input [ i - k ] ) ;
2014-02-04 16:57:25 +00:00
}
}
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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 ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( n = 0 ; n < nitems ; n + + )
{
2014-02-04 16:57:25 +00:00
a = n * d_OSR ;
output [ n ] = 0 ;
ii = 0 ;
2014-02-05 16:27:32 +00:00
while ( ii < filter_length )
{
2014-04-24 08:29:38 +00:00
if ( ( a + ii ) > = nitems * d_OSR ) {
2014-02-05 16:27:32 +00:00
break ;
2014-04-24 08:29:38 +00:00
}
2014-02-05 16:27:32 +00:00
output [ n ] + = input [ a + ii ] * filter [ ii ] ;
ii + + ;
2014-02-04 16:57:25 +00:00
}
}
2014-02-05 16:27:32 +00:00
}
//especially computations of strongest_window_nr
2014-02-05 21:44:30 +00:00
int receiver_impl : : get_norm_chan_imp_resp ( const gr_complex * input , gr_complex * chan_imp_resp , float * corr_max , int bcc )
2014-02-05 16:27:32 +00:00
{
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 ;
2014-04-17 07:43:02 +00:00
2014-02-05 16:27:32 +00:00
int search_center = ( int ) ( ( TRAIN_POS + GUARD_PERIOD ) * d_OSR ) ;
2014-02-08 13:15:27 +00:00
int search_start_pos = search_center + 1 - 5 * d_OSR ;
2014-02-04 16:57:25 +00:00
// int search_start_pos = search_center - d_chan_imp_length * d_OSR;
2014-04-17 07:43:02 +00:00
int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 5 * d_OSR ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
for ( int ii = search_start_pos ; ii < search_stop_pos ; ii + + )
{
2014-02-04 16:57:25 +00:00
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 ) ) ;
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
//compute window energies
vector_float : : iterator iter = power_buffer . begin ( ) ;
bool loop_end = false ;
while ( iter ! = power_buffer . end ( ) )
{
2014-02-04 16:57:25 +00:00
vector_float : : iterator iter_ii = iter ;
energy = 0 ;
2014-02-05 16:27:32 +00:00
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 ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
if ( loop_end )
{
break ;
2014-02-04 16:57:25 +00:00
}
iter + + ;
window_energy_buffer . push_back ( energy ) ;
2014-02-05 16:27:32 +00:00
}
2014-02-04 16:57:25 +00:00
2014-04-17 07:43:02 +00:00
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 ;
}
2014-02-20 23:02:44 +00:00
2014-02-05 16:27:32 +00:00
max_correlation = 0 ;
for ( int ii = 0 ; ii < ( d_chan_imp_length ) * d_OSR ; ii + + )
{
2014-02-04 16:57:25 +00:00
gr_complex correlation = correlation_buffer [ strongest_window_nr + ii ] ;
2014-02-05 16:27:32 +00:00
if ( abs ( correlation ) > max_correlation )
{
chan_imp_resp_center = ii ;
max_correlation = abs ( correlation ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
// d_channel_imp_resp.push_back(correlation);
2014-02-04 16:57:25 +00:00
chan_imp_resp [ ii ] = correlation ;
}
2014-02-08 13:15:27 +00:00
2014-02-05 21:44:30 +00:00
* corr_max = max_correlation ;
2014-02-05 16:27:32 +00:00
2014-02-08 13:15:27 +00:00
DCOUT ( " strongest_window_nr_new: " < < strongest_window_nr ) ;
2014-05-02 15:24:08 +00:00
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
2014-02-08 13:15:27 +00:00
DCOUT ( " burst_start: " < < burst_start ) ;
2014-02-05 16:27:32 +00:00
return burst_start ;
}
2014-02-20 23:02:44 +00:00
void receiver_impl : : send_burst ( burst_counter burst_nr , const unsigned char * burst_binary , burst_type b_type )
2014-02-05 16:27:32 +00:00
{
2014-02-20 23:02:44 +00:00
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 ) ;
2014-08-04 09:28:59 +00:00
2014-02-20 23:02:44 +00:00
message_port_pub ( pmt : : mp ( " bursts " ) , msg ) ;
2014-02-05 16:27:32 +00:00
}
2014-02-20 23:02:44 +00:00
2014-02-05 16:27:32 +00:00
void receiver_impl : : configure_receiver ( )
{
2014-04-17 21:37:18 +00:00
d_channel_conf . set_multiframe_type ( TIMESLOT0 , multiframe_51 ) ;
2014-02-05 16:27:32 +00:00
d_channel_conf . set_burst_types ( TIMESLOT0 , TEST51 , sizeof ( TEST51 ) / sizeof ( unsigned ) , dummy_or_normal ) ;
2014-02-04 16:57:25 +00:00
2014-04-17 21:37:18 +00:00
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 ) ;
2014-02-04 16:57:25 +00:00
// 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);
2014-02-05 21:44:30 +00:00
2014-02-05 16:27:32 +00:00
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 ) ;
}
2014-08-04 09:28:59 +00:00
void receiver_impl : : set_arfcn ( int arfcn ) //!!
{
d_arfcn = arfcn ;
}
void receiver_impl : : reset ( )
{
2014-08-06 13:20:33 +00:00
d_state = fcch_search ;
2014-08-04 09:28:59 +00:00
}
2014-02-05 16:27:32 +00:00
} /* namespace gsm */
2014-02-04 16:57:25 +00:00
} /* namespace gr */