2014-02-04 16:57:25 +00:00
/* -*- c++ -*- */
2014-02-05 16:27:32 +00:00
/*
2014-02-04 16:57:25 +00:00
* Copyright 2014 < + YOU OR YOUR COMPANY + > .
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>
# 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
receiver : : make ( feval_dd * tuner , int osr )
{
return gnuradio : : get_initial_sptr
( new receiver_impl ( tuner , osr ) ) ;
}
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
/*
* The private constructor
*/
receiver_impl : : receiver_impl ( feval_dd * tuner , int osr )
: gr : : block ( " receiver " ,
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 ) ,
d_freq_offset ( 0 ) ,
d_state ( first_fcch_search ) ,
d_burst_nr ( osr ) ,
d_failed_sch ( 0 )
{
2014-02-08 13:15:27 +00:00
2014-02-05 16:27:32 +00:00
int i ;
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
{
gr_complex startpoint ;
2014-02-05 16:27:32 +00:00
if ( i = = 6 | | i = = 7 ) //this is nasty hack
{
startpoint = gr_complex ( - 1.0 , 0.0 ) ; //if I don't change it here all bits of normal bursts for BTSes with bcc=6 will have reversed values
}
else
{
startpoint = gr_complex ( 1.0 , 0.0 ) ; //I've checked this hack for bcc==0,1,2,3,4,6
2014-02-04 16:57:25 +00:00
} //I don't know what about bcc==5 and 7 yet
//TODO:find source of this situation - this is purely mathematical problem I guess
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-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 ( )
{
}
void receiver_impl : : forecast ( int noutput_items , gr_vector_int & ninput_items_required )
{
ninput_items_required [ 0 ] = noutput_items * floor ( ( TS_BITS + 2 * GUARD_PERIOD ) * d_OSR ) ;
}
int
receiver_impl : : general_work ( int noutput_items ,
gr_vector_int & ninput_items ,
gr_vector_const_void_star & input_items ,
gr_vector_void_star & output_items )
{
const gr_complex * input = ( const gr_complex * ) input_items [ 0 ] ;
//float *out = (float *) output_items[0];
int produced_out = 0 ; //how many output elements were produced - this isn't used yet
//probably the gsm receiver will be changed into sink so this variable won't be necessary
2014-02-08 13:15:27 +00:00
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
case first_fcch_search :
2014-02-05 21:44:30 +00:00
DCOUT ( " FCCH search " ) ;
2014-02-05 16:27:32 +00:00
if ( find_fcch_burst ( input , ninput_items [ 0 ] ) ) //find frequency correction burst in the input buffer
{
2014-02-05 17:10:05 +00:00
//set_frequency(d_freq_offset); //if fcch search is successful set frequency offset
2014-02-05 21:44:30 +00:00
DCOUT ( " Freq offset " < < d_freq_offset ) ;
2014-02-04 16:57:25 +00:00
//produced_out = 0;
d_state = next_fcch_search ;
2014-02-05 16:27:32 +00:00
}
else
{
2014-02-04 16:57:25 +00:00
//produced_out = 0;
d_state = first_fcch_search ;
2014-02-05 16:27:32 +00:00
}
break ;
case next_fcch_search : //this state is used because it takes some time (a bunch of buffered samples)
{
2014-02-05 21:44:30 +00:00
DCOUT ( " NEXT FCCH search " ) ;
2014-02-05 16:27:32 +00:00
float prev_freq_offset = d_freq_offset ; //before previous set_frequqency cause change
if ( find_fcch_burst ( input , ninput_items [ 0 ] ) )
{
if ( abs ( prev_freq_offset - d_freq_offset ) > FCCH_MAX_FREQ_OFFSET )
{
2014-02-05 17:10:05 +00:00
//set_frequency(d_freq_offset); //call set_frequncy only frequency offset change is greater than some value
2014-02-08 13:15:27 +00:00
COUT ( " Freq offset " < < d_freq_offset ) ;
2014-02-04 16:57:25 +00:00
}
2014-02-05 16:27:32 +00:00
//produced_out = 0;
d_state = sch_search ;
}
else
{
//produced_out = 0;
d_state = next_fcch_search ;
}
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 ] ;
if ( reach_sch_burst ( ninput_items [ 0 ] ) ) //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
{
2014-02-05 21:44:30 +00:00
//COUT("sch burst_start: " << burst_start);
//COUT("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
//configure the receiver - tell him where to find which burst type
d_channel_conf . set_multiframe_type ( TIMESLOT0 , multiframe_51 ) ; //in the timeslot nr.0 bursts changes according to t3 counter
configure_receiver ( ) ; //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
d_channel_conf . set_burst_types ( TIMESLOT0 , FCCH_FRAMES , sizeof ( FCCH_FRAMES ) / sizeof ( unsigned ) , fcch_burst ) ; //tell where to find fcch bursts
d_channel_conf . set_burst_types ( TIMESLOT0 , SCH_FRAMES , sizeof ( SCH_FRAMES ) / sizeof ( unsigned ) , sch_burst ) ; //sch bursts
d_channel_conf . set_burst_types ( TIMESLOT0 , BCCH_FRAMES , sizeof ( BCCH_FRAMES ) / sizeof ( unsigned ) , normal_burst ) ; //!and maybe normal bursts of the BCCH logical channel
d_burst_nr + + ;
consume_each ( burst_start + BURST_SIZE * d_OSR ) ; //consume samples up to next guard period
d_state = synchronized ;
2014-02-05 16:27:32 +00:00
}
else
{
2014-02-04 16:57:25 +00:00
d_state = next_fcch_search ; //if there is error in the sch burst go back to fcch search phase
}
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 :
{
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
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 ) ;
2014-02-08 13:15:27 +00:00
send_burst ( d_burst_nr , fc_fb ) ;
2014-02-05 16:27:32 +00:00
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 ( ) ;
2014-02-08 13:15:27 +00:00
DCOUT ( " mean offset " < < mean_offset ) ;
2014-02-05 16:27:32 +00:00
if ( abs ( mean_offset ) > FCCH_MAX_FREQ_OFFSET )
{
2014-02-08 13:15:27 +00:00
//d_freq_offset -= mean_offset; //and adjust frequency if it have changed beyond
2014-02-05 17:10:05 +00:00
//set_frequency(d_freq_offset); //some limit
2014-02-05 21:44:30 +00:00
DCOUT ( " Adjusting frequency, new frequency offset: " < < d_freq_offset < < " \n " ) ;
2014-02-04 16:57:25 +00:00
}
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
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ; //MLSE detection of bits
2014-02-08 13:15:27 +00:00
send_burst ( d_burst_nr , output_binary ) ;
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 )
{
d_state = first_fcch_search ; //TODO: this isn't good, the receiver is going wild when it goes back to next_fcch_search from here
d_freq_offset_vals . clear ( ) ;
d_freq_offset = 0 ;
2014-02-08 13:15:27 +00:00
//set_frequency(0);
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-02-08 13:15:27 +00:00
send_burst ( d_burst_nr , output_binary ) ; //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
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 ;
2014-02-08 13:15:27 +00:00
DCOUT ( " Dummy " ) ;
2014-02-05 21:44:30 +00:00
get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , & dummy_corr_max , TS_DUMMY ) ;
2014-02-08 13:15:27 +00:00
DCOUT ( " Normal " ) ;
2014-02-05 21:44:30 +00:00
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-02-08 13:15:27 +00:00
send_burst ( d_burst_nr , output_binary ) ; //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
2014-02-05 16:27:32 +00:00
}
else
{
2014-02-08 13:15:27 +00:00
send_burst ( d_burst_nr , dummy_burst ) ;
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-08 13:15:27 +00:00
send_burst ( d_burst_nr , dummy_burst ) ;
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
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
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-05 16:27:32 +00:00
return produced_out ;
}
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
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 ;
double freq_offset = phase_offset * 1625000.0 / ( 12.0 * M_PI ) ;
d_freq_offset - = freq_offset ;
DCOUT ( " freq_offset: " < < d_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-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 ) * 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
//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 ) ;
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
//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 ;
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
//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 - - )
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
//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 ;
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 )
{
if ( ( a + ii ) > = nitems * d_OSR )
break ;
output [ n ] + = input [ a + ii ] * filter [ ii ] ;
ii + + ;
2014-02-04 16:57:25 +00:00
}
}
2014-02-05 16:27:32 +00:00
}
//TODO: get_norm_chan_imp_resp is similar to get_sch_chan_imp_resp - consider joining this two functions
//TODO: this is place where most errors are introduced and can be corrected by improvements to this fuction
//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 ;
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-02-05 16:27:32 +00:00
int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 2 * 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
}
//!why doesn't this work
int strongest_window_nr_new = max_element ( window_energy_buffer . begin ( ) , window_energy_buffer . end ( ) ) - window_energy_buffer . begin ( ) ;
2014-02-08 13:15:27 +00:00
strongest_window_nr = strongest_window_nr_new - d_OSR ; //! so I have to override it here
2014-02-04 16:57:25 +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 ;
// We want to use the first sample of the impulse response, and the
2014-02-05 16:27:32 +00:00
// corresponding samples of the received signal.
// the variable sync_w should contain the beginning of the used part of
// training sequence, which is 3+57+1+6=67 bits into the burst. That is
// we have that sync_t16 equals first sample in bit number 67.
burst_start = search_start_pos + chan_imp_resp_center + strongest_window_nr - TRAIN_POS * d_OSR ;
// GMSK modulator introduces ISI - each bit is expanded for 3*Tb
// and it's maximum value is in the last bit period, so burst starts
// 2*Tb earlier
burst_start - = 2 * d_OSR ;
burst_start + = 2 ;
//COUT("Poczatek ###############################");
//std::cout << " burst_start: " << burst_start << " center: " << ((float)(search_start_pos + strongest_window_nr + chan_imp_resp_center)) / d_OSR << " stronegest window nr: " << strongest_window_nr << "\n";
//COUT("burst_start_new: " << (search_start_pos + strongest_window_nr_new - TRAIN_POS * d_OSR));
2014-02-08 13:15:27 +00:00
DCOUT ( " strongest_window_nr_new: " < < strongest_window_nr ) ;
burst_start = ( search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR ) ;
DCOUT ( " burst_start: " < < burst_start ) ;
2014-02-05 16:27:32 +00:00
return burst_start ;
}
2014-02-08 13:15:27 +00:00
void receiver_impl : : send_burst ( burst_counter burst_nr , const unsigned char * burst_binary )
2014-02-05 16:27:32 +00:00
{
int ii ;
2014-02-08 13:15:27 +00:00
static const int nelements = 148 ;
pmt : : pmt_t burst = pmt : : make_s8vector ( nelements , 0 ) ; // Initializes all 64 elements to 0
size_t vec_size ;
int8_t * burst_elements = pmt : : s8vector_writable_elements ( burst , vec_size ) ; // Returns pointer, vec_size is set to 64
memcpy ( burst_elements , burst_binary , nelements ) ;
message_port_pub ( pmt : : mp ( " bursts " ) , burst ) ;
2014-02-05 16:27:32 +00:00
}
//TODO: this shouldn't be here also - the same reason
void receiver_impl : : configure_receiver ( )
{
d_channel_conf . set_multiframe_type ( TSC0 , multiframe_51 ) ;
d_channel_conf . set_burst_types ( TIMESLOT0 , TEST51 , sizeof ( TEST51 ) / sizeof ( unsigned ) , dummy_or_normal ) ;
2014-02-04 16:57:25 +00:00
2014-02-05 16:27:32 +00:00
d_channel_conf . set_burst_types ( TSC0 , TEST_CCH_FRAMES , sizeof ( TEST_CCH_FRAMES ) / sizeof ( unsigned ) , dummy_or_normal ) ;
d_channel_conf . set_burst_types ( TSC0 , FCCH_FRAMES , sizeof ( FCCH_FRAMES ) / sizeof ( unsigned ) , fcch_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 ) ;
}
} /* namespace gsm */
2014-02-04 16:57:25 +00:00
} /* namespace gr */