2009-04-07 10:47:18 +00:00
/* -*- c++ -*- */
/*
2009-06-12 07:58:14 +00:00
* @ file
* @ author Piotr Krysik < pkrysik @ stud . elka . pw . edu . pl >
* @ section LICENSE
2009-04-10 15:22:31 +00:00
*
2009-06-12 07:58:14 +00:00
* This program is free software ; you can redistribute it and / or modify
2009-04-07 10:47:18 +00:00
* 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 .
2009-04-10 15:22:31 +00:00
*
2009-06-12 07:58:14 +00:00
* This program is distributed in the hope that it will be useful ,
2009-04-07 10:47:18 +00:00
* 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 .
2009-04-10 15:22:31 +00:00
*
2009-04-07 10:47:18 +00:00
* You should have received a copy of the GNU General Public License
2009-06-12 07:58:14 +00:00
* along with this program ; see the file COPYING . If not , write to
2009-04-07 10:47:18 +00:00
* the Free Software Foundation , Inc . , 51 Franklin Street ,
* Boston , MA 02110 - 1301 , USA .
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <gr_io_signature.h>
2009-04-10 15:22:31 +00:00
# include <gr_math.h>
2009-04-20 17:49:01 +00:00
# include <math.h>
2009-04-10 15:22:31 +00:00
# include <Assert.h>
2009-04-30 17:07:08 +00:00
# include <boost/circular_buffer.hpp>
2009-04-20 17:49:01 +00:00
# include <algorithm>
2009-06-18 12:12:25 +00:00
# include <numeric>
2009-05-20 19:14:54 +00:00
# include <gsm_receiver_cf.h>
# include <viterbi_detector.h>
2009-06-27 11:18:13 +00:00
# include <string.h>
2009-05-20 19:14:54 +00:00
# include <sch.h>
2009-04-07 10:47:18 +00:00
2009-06-27 11:18:13 +00:00
# include "RxBurst.h"
# include "GSMCommon.h"
2009-06-01 20:14:14 +00:00
# define SYNC_SEARCH_RANGE 30
2009-06-27 11:18:13 +00:00
// #define TRAIN_SEARCH_RANGE 40
//FIXME: decide to use this define or not
2009-04-30 17:07:08 +00:00
2009-06-13 10:38:16 +00:00
//TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
2010-07-29 19:15:03 +00:00
void decrypt ( const unsigned char * burst_binary , byte * KC , unsigned char * decrypted_data , unsigned FN )
2009-06-27 11:18:13 +00:00
{
byte AtoB [ 2 * DATA_BITS ] ;
2010-07-29 19:15:03 +00:00
/* KC is all zero: no decryption */
if ( KC [ 0 ] = = 0 & & KC [ 1 ] = = 0 & & KC [ 2 ] = = 0 & & KC [ 3 ] = = 0 &
KC [ 4 ] = = 0 & & KC [ 5 ] = = 0 & & KC [ 6 ] = = 0 & & KC [ 7 ] = = 0 ) {
for ( int i = 0 ; i < 148 ; i + + ) {
decrypted_data [ i ] = burst_binary [ i ] ;
}
return ;
}
2009-06-27 11:18:13 +00:00
keysetup ( KC , FN ) ;
runA51 ( AtoB ) ;
2010-07-29 19:15:03 +00:00
/* guard bits */
for ( int i = 0 ; i < 3 ; i + + ) {
2009-06-27 11:18:13 +00:00
decrypted_data [ i ] = burst_binary [ i ] ;
}
for ( int i = 0 ; i < 57 ; i + + ) {
decrypted_data [ i + 3 ] = AtoB [ i ] ^ burst_binary [ i + 3 ] ;
}
2010-07-29 19:15:03 +00:00
/* stealing bits and midamble */
for ( int i = 60 ; i < 88 ; i + + ) {
decrypted_data [ i ] = burst_binary [ i ] ;
}
2009-06-27 11:18:13 +00:00
for ( int i = 0 ; i < 57 ; i + + ) {
decrypted_data [ i + 88 ] = AtoB [ i + 57 ] ^ burst_binary [ i + 88 ] ;
}
2010-07-29 19:15:03 +00:00
/* guard bits */
for ( int i = 145 ; i < 148 ; i + + ) {
decrypted_data [ i ] = burst_binary [ i ] ;
}
}
//TODO: this shouldn't be here */
void dump_bits ( const unsigned char * burst_binary , unsigned char * decrypted_data , burst_counter burst_nr , bool first_burst )
{
int i ;
/* Cipher bits */
printf ( " C%d %d %d: " , first_burst , burst_nr . get_frame_nr ( ) , burst_nr . get_frame_nr_mod ( ) ) ;
for ( int i = 0 ; i < 57 ; i + + )
printf ( " %d " , burst_binary [ i + 3 ] ) ;
for ( int i = 0 ; i < 57 ; i + + )
printf ( " %d " , burst_binary [ i + 88 ] ) ;
printf ( " \n " ) ;
/* Plain bits */
printf ( " P%d %d %d: " , first_burst , burst_nr . get_frame_nr ( ) , burst_nr . get_frame_nr_mod ( ) ) ;
for ( int i = 0 ; i < 57 ; i + + )
printf ( " %d " , decrypted_data [ i + 3 ] ) ;
for ( int i = 0 ; i < 57 ; i + + )
printf ( " %d " , decrypted_data [ i + 88 ] ) ;
printf ( " \n " ) ;
/* Keystream bits */
printf ( " S%d %d %d: " , first_burst , burst_nr . get_frame_nr ( ) , burst_nr . get_frame_nr_mod ( ) ) ;
for ( int i = 0 ; i < 57 ; i + + )
printf ( " %d " , burst_binary [ i + 3 ] ^ decrypted_data [ i + 3 ] ) ;
for ( int i = 0 ; i < 57 ; i + + )
printf ( " %d " , burst_binary [ i + 88 ] ^ decrypted_data [ i + 88 ] ) ;
printf ( " \n " ) ;
2009-06-27 11:18:13 +00:00
}
void gsm_receiver_cf : : read_key ( std : : string key )
{
2010-07-29 19:15:03 +00:00
printf ( " Key: '%s' \n " , key . c_str ( ) ) ;
2009-06-27 11:18:13 +00:00
int i ;
int b ;
for ( i = 0 ; i < 8 ; i + + ) {
b = d_hex_to_int [ ( char ) key [ ( i ) * 2 ] ] * 16 + d_hex_to_int [ ( char ) key [ i * 2 + 1 ] ] ;
d_KC [ i ] = ( byte ) b ;
2009-07-21 18:37:54 +00:00
}
2009-06-27 11:18:13 +00:00
}
2010-07-29 19:15:03 +00:00
void gsm_receiver_cf : : read_configuration ( std : : string configuration )
2009-06-06 21:10:21 +00:00
{
2010-07-29 19:15:03 +00:00
printf ( " Configuration: '%s' \n " , configuration . c_str ( ) ) ;
if ( ( char ) configuration [ 0 ] = = 0 ) {
printf ( " No configuration set. \n " ) ;
return ;
}
/* get timeslot */
int ts = atoi ( configuration . c_str ( ) ) ;
if ( ts < 0 | | ts > 7 ) {
printf ( " Invalid TS: %d \n " , ts ) ;
return ;
}
printf ( " Configuration TS: %d \n " , ts ) ;
if ( ( char ) configuration [ 1 ] = = ' C ' )
d_gs_ctx . ts_ctx [ ts ] . type = TST_FCCH_SCH_BCCH_CCCH_SDCCH4 ;
else if ( ( char ) configuration [ 1 ] = = ' B ' )
d_gs_ctx . ts_ctx [ ts ] . type = TST_FCCH_SCH_BCCH_CCCH ;
else if ( ( char ) configuration [ 1 ] = = ' S ' )
d_gs_ctx . ts_ctx [ ts ] . type = TST_SDCCH8 ;
else if ( ( char ) configuration [ 1 ] = = ' T ' )
d_gs_ctx . ts_ctx [ ts ] . type = TST_TCHF ;
else {
printf ( " Invalid configuration: %c \n " , ( char ) configuration [ 1 ] ) ;
return ;
}
/* any other timeslot than 0: turn TS0 off */
if ( ts ! = 0 ) {
d_gs_ctx . ts_ctx [ 0 ] . type = TST_OFF ;
d_trace_sch = false ;
printf ( " TS0 is turned off \n " ) ;
}
}
void gsm_receiver_cf : : process_normal_burst ( burst_counter burst_nr , const unsigned char * burst_binary , bool first_burst )
{
unsigned char decrypted_data [ 148 ] ;
float decrypted_data_float [ 148 ] ;
2009-06-27 11:18:13 +00:00
unsigned char * voice_frame ;
2010-07-29 19:15:03 +00:00
int ts = burst_nr . get_timeslot_nr ( ) ;
/* no processing if turned off*/
if ( d_gs_ctx . ts_ctx [ ts ] . type = = TST_OFF )
return ;
2009-06-27 11:18:13 +00:00
2010-07-29 19:15:03 +00:00
/* handle traffic timeslots */
#if 0
/* always try to decrypt and decode traffic in TS 1...7 */
/* TODO: this will fail if there is unencrypted traffic in more than one TS */
2009-06-27 11:18:13 +00:00
if ( burst_nr . get_timeslot_nr ( ) > = 1 & & burst_nr . get_timeslot_nr ( ) < = 7 ) {
2010-07-29 19:15:03 +00:00
# else
if ( d_gs_ctx . ts_ctx [ ts ] . type = = TST_TCHF ) {
# endif
2009-06-27 11:18:13 +00:00
decrypt ( burst_binary , d_KC , decrypted_data , burst_nr . get_frame_nr_mod ( ) ) ;
2010-07-29 19:15:03 +00:00
int i ;
for ( i = 0 ; i < 148 ; i + + )
decrypted_data_float [ i ] = decrypted_data [ i ] ;
GSM : : Time time ( burst_nr . get_frame_nr ( ) , ts ) ;
GSM : : RxBurst rxbrst ( decrypted_data_float , time ) ;
if ( ts - TIMESLOT1 > = 0 & & ts - TIMESLOT1 < N_TCH_DECODER ) {
if ( d_tch_decoder [ ts - TIMESLOT1 ] - > processBurst ( rxbrst ) = = true ) {
fwrite ( d_tch_decoder [ ts - TIMESLOT1 ] - > get_voice_frame ( ) , 1 , 33 , d_gsm_file ) ;
2009-06-27 11:18:13 +00:00
}
2010-07-29 19:15:03 +00:00
else if ( rxbrst . Hl ( ) | | rxbrst . Hu ( ) ) {
/* Stolen bits are set, might be FACCH */
GS_process ( & d_gs_ctx , TIMESLOT0 + ts , NORMAL , & decrypted_data [ 3 ] , burst_nr . get_frame_nr ( ) , first_burst ) ;
2009-06-27 11:18:13 +00:00
}
}
}
2010-07-29 19:15:03 +00:00
/* handle SDCCH/8 timeslots */
if ( d_gs_ctx . ts_ctx [ ts ] . type = = TST_SDCCH8 ) {
decrypt ( burst_binary , d_KC , decrypted_data , burst_nr . get_frame_nr_mod ( ) ) ;
# if 1 /* dump cipher, plain and keystream bits */
dump_bits ( burst_binary , decrypted_data , burst_nr , first_burst ) ;
# endif
GS_process ( & d_gs_ctx , TIMESLOT0 + ts , NORMAL , & decrypted_data [ 3 ] , burst_nr . get_frame_nr ( ) , first_burst ) ;
2009-06-06 21:10:21 +00:00
}
2010-07-29 19:15:03 +00:00
/* TS0 is special (TODO) */
if ( ts = = 0 ) {
memcpy ( decrypted_data , burst_binary , sizeof ( decrypted_data ) ) ;
if ( d_gs_ctx . ts_ctx [ ts ] . type = = TST_FCCH_SCH_BCCH_CCCH_SDCCH4 ) {
if ( SDCCH_SACCH_4_MAP [ burst_nr . get_frame_nr ( ) % 51 ] ! = 0 ) { /* SDCCH/4 or SACCH/4 */
decrypt ( burst_binary , d_KC , decrypted_data , burst_nr . get_frame_nr_mod ( ) ) ;
# if 1 /* dump cipher, plain and keystream bits */
dump_bits ( burst_binary , decrypted_data , burst_nr , first_burst ) ;
# endif
}
}
GS_process ( & d_gs_ctx , TIMESLOT0 + ts , NORMAL , & decrypted_data [ 3 ] , burst_nr . get_frame_nr ( ) , first_burst ) ;
}
2009-06-06 21:10:21 +00:00
}
2009-06-13 10:38:16 +00:00
//TODO: this shouldn't be here also - the same reason
2009-06-12 10:01:47 +00:00
void gsm_receiver_cf : : configure_receiver ( )
2009-06-06 21:10:21 +00:00
{
2010-07-29 19:15:03 +00:00
int ts ;
printf ( " configure_receiver \n " ) ;
2009-06-27 11:18:13 +00:00
2010-07-29 19:15:03 +00:00
/* configure TS0, TS0 is special (TODO) */
2009-06-27 11:18:13 +00:00
2010-07-29 19:15:03 +00:00
d_channel_conf . set_multiframe_type ( TIMESLOT0 , multiframe_51 ) ;
d_channel_conf . set_burst_types ( TIMESLOT0 , TEST_CCH_FRAMES , TEST_CCH_FIRST , sizeof ( TEST_CCH_FRAMES ) / sizeof ( unsigned ) , normal_burst ) ;
/* FCCH bursts */
d_channel_conf . set_burst_types ( TIMESLOT0 , FCCH_FRAMES , sizeof ( FCCH_FRAMES ) / sizeof ( unsigned ) , fcch_burst ) ;
/* SCH bursts */
d_channel_conf . set_burst_types ( TIMESLOT0 , SCH_FRAMES , sizeof ( SCH_FRAMES ) / sizeof ( unsigned ) , sch_burst ) ;
2009-06-27 11:18:13 +00:00
2010-07-29 19:15:03 +00:00
/* configure TS1...TS7 */
2009-06-27 11:18:13 +00:00
2010-07-29 19:15:03 +00:00
for ( ts = TIMESLOT1 ; ts < TIMESLOT7 ; ts + + ) {
if ( d_gs_ctx . ts_ctx [ ts ] . type = = TST_TCHF ) {
d_channel_conf . set_multiframe_type ( ts , multiframe_26 ) ;
d_channel_conf . set_burst_types ( ts , TRAFFIC_CHANNEL_F , sizeof ( TRAFFIC_CHANNEL_F ) / sizeof ( unsigned ) , dummy_or_normal ) ;
}
else if ( d_gs_ctx . ts_ctx [ ts ] . type = = TST_SDCCH8 ) {
d_channel_conf . set_multiframe_type ( ts , multiframe_51 ) ;
d_channel_conf . set_burst_types ( ts , SDCCH_SACCH_8_FRAMES , SDCCH_SACCH_8_FIRST , sizeof ( SDCCH_SACCH_8_FRAMES ) / sizeof ( unsigned ) , dummy_or_normal ) ;
}
}
2009-06-06 21:10:21 +00:00
}
2009-05-28 17:39:56 +00:00
2009-04-30 17:07:08 +00:00
typedef std : : list < float > list_float ;
typedef std : : vector < float > vector_float ;
typedef boost : : circular_buffer < float > circular_buffer_float ;
2009-04-10 15:22:31 +00:00
gsm_receiver_cf_sptr
2010-07-29 19:15:03 +00:00
gsm_make_receiver_cf ( gr_feval_dd * tuner , gr_feval_dd * synchronizer , int osr , std : : string key , std : : string configuration )
2009-04-07 10:47:18 +00:00
{
2010-07-29 19:15:03 +00:00
return gsm_receiver_cf_sptr ( new gsm_receiver_cf ( tuner , synchronizer , osr , key , configuration ) ) ;
2009-04-07 10:47:18 +00:00
}
2009-04-10 15:22:31 +00:00
static const int MIN_IN = 1 ; // mininum number of input streams
static const int MAX_IN = 1 ; // maximum number of input streams
static const int MIN_OUT = 0 ; // minimum number of output streams
static const int MAX_OUT = 1 ; // maximum number of output streams
2009-04-07 10:47:18 +00:00
/*
* The private constructor
*/
2010-07-29 19:15:03 +00:00
gsm_receiver_cf : : gsm_receiver_cf ( gr_feval_dd * tuner , gr_feval_dd * synchronizer , int osr , std : : string key , std : : string configuration )
2009-04-10 15:22:31 +00:00
: gr_block ( " gsm_receiver " ,
gr_make_io_signature ( MIN_IN , MAX_IN , sizeof ( gr_complex ) ) ,
gr_make_io_signature ( MIN_OUT , MAX_OUT , 142 * sizeof ( float ) ) ) ,
2009-04-24 17:24:17 +00:00
d_OSR ( osr ) ,
2009-05-28 17:39:56 +00:00
d_chan_imp_length ( CHAN_IMP_RESP_LENGTH ) ,
2009-04-20 17:49:01 +00:00
d_tuner ( tuner ) ,
2009-06-12 07:58:14 +00:00
d_counter ( 0 ) ,
d_fcch_start_pos ( 0 ) ,
2009-04-28 18:08:18 +00:00
d_freq_offset ( 0 ) ,
2009-06-13 10:38:16 +00:00
d_state ( first_fcch_search ) ,
2009-06-13 17:31:43 +00:00
d_burst_nr ( osr ) ,
2009-06-27 11:18:13 +00:00
d_failed_sch ( 0 ) ,
2010-07-29 19:15:03 +00:00
d_trace_sch ( true )
2009-04-07 10:47:18 +00:00
{
2009-06-04 11:12:03 +00:00
int i ;
2009-06-05 19:01:11 +00:00
gmsk_mapper ( SYNC_BITS , N_SYNC_BITS , d_sch_training_seq , gr_complex ( 0.0 , - 1.0 ) ) ;
2009-06-04 11:12:03 +00:00
for ( i = 0 ; i < TRAIN_SEQ_NUM ; i + + ) {
2009-06-18 12:12:25 +00:00
gr_complex startpoint ;
2009-07-21 18:39:17 +00:00
if ( i = = 6 | | i = = 7 ) { //this is nasty hack
2009-06-27 11:18:13 +00:00
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
2009-06-18 12:12:25 +00:00
} else {
startpoint = gr_complex ( 1.0 , 0.0 ) ; //I've checked this hack for bcc==0,1,2,3,4,6
} //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 ) ;
2009-06-04 11:12:03 +00:00
}
2009-07-01 08:06:39 +00:00
2010-07-29 19:15:03 +00:00
for ( i = 0 ; i < N_TCH_DECODER ; i + + )
d_tch_decoder [ i ] = new GSM : : TCHFACCHL1Decoder ( GSM : : gFACCH_TCHFMapping ) ;
d_gsm_file = fopen ( " speech.au.gsm " , " wb " ) ; //!!
2009-07-01 08:06:39 +00:00
d_hex_to_int [ ' 0 ' ] = 0 ; //!!
d_hex_to_int [ ' 4 ' ] = 4 ; //!!
d_hex_to_int [ ' 8 ' ] = 8 ; //!!
d_hex_to_int [ ' c ' ] = 0xc ; //!!
d_hex_to_int [ ' 1 ' ] = 1 ; //!!
d_hex_to_int [ ' 5 ' ] = 5 ; //!!
d_hex_to_int [ ' 9 ' ] = 9 ; //!!
d_hex_to_int [ ' d ' ] = 0xd ; //!!
d_hex_to_int [ ' 2 ' ] = 2 ; //!!
d_hex_to_int [ ' 6 ' ] = 6 ; //!!
d_hex_to_int [ ' a ' ] = 0xa ; //!!
d_hex_to_int [ ' e ' ] = 0xe ; //!!
d_hex_to_int [ ' 3 ' ] = 3 ; //!!
d_hex_to_int [ ' 7 ' ] = 7 ; //!!
d_hex_to_int [ ' b ' ] = 0xb ; //!!
d_hex_to_int [ ' f ' ] = 0xf ; //!!
read_key ( key ) ; //!!
2010-07-29 19:15:03 +00:00
/* Initialize GSM Stack, clear d_gs_ctx */
2009-06-18 12:12:25 +00:00
GS_new ( & d_gs_ctx ) ; //TODO: remove it! it's not a right place for a decoder
2010-07-29 19:15:03 +00:00
/* configuration is stored in d_gs_ctx */
read_configuration ( configuration ) ;
configure_receiver ( ) ;
2009-04-07 10:47:18 +00:00
}
/*
2009-04-07 15:25:12 +00:00
* Virtual destructor .
2009-04-07 10:47:18 +00:00
*/
2009-04-10 15:22:31 +00:00
gsm_receiver_cf : : ~ gsm_receiver_cf ( )
{
}
2009-06-12 07:58:14 +00:00
void gsm_receiver_cf : : forecast ( int noutput_items , gr_vector_int & nitems_items_required )
2009-04-07 10:47:18 +00:00
{
2009-06-12 07:58:14 +00:00
nitems_items_required [ 0 ] = noutput_items * floor ( ( TS_BITS + 2 * GUARD_PERIOD ) * d_OSR ) ;
2009-04-07 10:47:18 +00:00
}
2009-04-10 15:22:31 +00:00
int
gsm_receiver_cf : : general_work ( int noutput_items ,
2009-06-12 07:58:14 +00:00
gr_vector_int & nitems_items ,
2009-04-10 15:22:31 +00:00
gr_vector_const_void_star & input_items ,
gr_vector_void_star & output_items )
2009-04-07 10:47:18 +00:00
{
2009-06-12 07:58:14 +00:00
const gr_complex * input = ( const gr_complex * ) input_items [ 0 ] ;
2009-06-13 10:38:16 +00:00
//float *out = (float *) output_items[0];
2009-06-12 07:58:14 +00:00
int produced_out = 0 ; //how many output elements were produced - this isn't used yet
2009-06-12 09:36:39 +00:00
//probably the gsm receiver will be changed into sink so this variable won't be necessary
2009-04-07 10:47:18 +00:00
2009-04-20 17:49:01 +00:00
switch ( d_state ) {
2009-06-12 09:36:39 +00:00
//bootstrapping
2009-04-28 18:08:18 +00:00
case first_fcch_search :
2009-06-12 07:58:14 +00:00
if ( find_fcch_burst ( input , nitems_items [ 0 ] ) ) { //find frequency correction burst in the input buffer
set_frequency ( d_freq_offset ) ; //if fcch search is successful set frequency offset
//produced_out = 0;
2009-04-28 18:08:18 +00:00
d_state = next_fcch_search ;
2009-04-20 17:49:01 +00:00
} else {
2009-06-12 07:58:14 +00:00
//produced_out = 0;
2009-04-28 18:08:18 +00:00
d_state = first_fcch_search ;
2009-04-20 17:49:01 +00:00
}
break ;
2009-06-18 12:12:25 +00:00
case next_fcch_search : { //this state is used because it takes some time (a bunch of buffered samples)
2009-06-27 11:18:13 +00:00
float prev_freq_offset = d_freq_offset ; //before previous set_frequqency cause change
2009-06-12 09:36:39 +00:00
if ( find_fcch_burst ( input , nitems_items [ 0 ] ) ) {
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
}
//produced_out = 0;
d_state = sch_search ;
} else {
//produced_out = 0;
d_state = next_fcch_search ;
2009-04-30 17:07:08 +00:00
}
2009-06-12 09:36:39 +00:00
break ;
2009-04-28 18:08:18 +00:00
}
2009-06-18 12:12:25 +00:00
2009-06-01 20:14:14 +00:00
case sch_search : {
2009-06-12 09:36:39 +00:00
vector_complex channel_imp_resp ( CHAN_IMP_RESP_LENGTH * d_OSR ) ;
2009-06-06 21:10:21 +00:00
int t1 , t2 , t3 ;
int burst_start = 0 ;
unsigned char output_binary [ BURST_SIZE ] ;
2009-06-12 09:36:39 +00:00
if ( reach_sch_burst ( nitems_items [ 0 ] ) ) { //wait for a SCH burst
2009-06-12 07:58:14 +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
2010-07-29 19:15:03 +00:00
if ( d_trace_sch )
{
DCOUT ( " sch burst_start: " < < burst_start ) ;
DCOUT ( " bcc: " < < d_bcc < < " ncc: " < < d_ncc < < " t1: " < < t1 < < " t2: " < < t2 < < " t3: " < < t3 ) ;
}
2009-06-12 07:58:14 +00:00
d_burst_nr . set ( t1 , t2 , t3 , 0 ) ; //set counter of bursts value
2009-06-12 09:36:39 +00:00
2010-07-29 19:15:03 +00:00
#if 0 /* Dieter: now done in constructor */
2009-06-12 07:58:14 +00:00
//configure the receiver - tell him where to find which burst type
2009-06-12 09:36:39 +00:00
d_channel_conf . set_multiframe_type ( TIMESLOT0 , multiframe_51 ) ; //in the timeslot nr.0 bursts changes according to t3 counter
2009-06-12 10:01:47 +00:00
configure_receiver ( ) ; //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
2010-07-29 19:15:03 +00:00
// Dieter: don't call it, otherwise overwrites configuration of configure_receiver()
2009-06-12 09:36:39 +00:00
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
2010-07-29 19:15:03 +00:00
# endif
2009-06-06 21:10:21 +00:00
d_burst_nr + + ;
2009-06-12 07:58:14 +00:00
consume_each ( burst_start + BURST_SIZE * d_OSR ) ; //consume samples up to next guard period
2009-06-06 21:10:21 +00:00
d_state = synchronized ;
} else {
2009-06-12 07:58:14 +00:00
d_state = next_fcch_search ; //if there is error in the sch burst go back to fcch search phase
2009-06-06 21:10:21 +00:00
}
2009-06-01 20:14:14 +00:00
} else {
2009-06-06 21:10:21 +00:00
d_state = sch_search ;
2009-06-01 20:14:14 +00:00
}
2009-06-06 21:10:21 +00:00
break ;
2009-04-24 17:24:17 +00:00
}
2009-06-06 21:10:21 +00:00
//in this state receiver is synchronized and it processes bursts according to burst type for given burst number
2009-05-28 17:39:56 +00:00
case synchronized : {
2009-06-12 07:58:14 +00:00
vector_complex channel_imp_resp ( CHAN_IMP_RESP_LENGTH * d_OSR ) ;
2009-06-06 21:10:21 +00:00
int burst_start ;
int offset = 0 ;
int to_consume = 0 ;
unsigned char output_binary [ BURST_SIZE ] ;
2009-06-12 07:58:14 +00:00
burst_type b_type = d_channel_conf . get_burst_type ( d_burst_nr ) ; //get burst type for given burst number
2010-07-29 19:15:03 +00:00
bool first_burst = d_channel_conf . get_first_burst ( d_burst_nr ) ; // first burst of four ?
2009-06-12 07:58:14 +00:00
2009-06-06 21:10:21 +00:00
switch ( b_type ) {
2009-06-12 09:36:39 +00:00
case fcch_burst : { //if it's FCCH burst
2009-06-12 07:58:14 +00:00
const unsigned first_sample = ceil ( ( GUARD_PERIOD + 2 * TAIL_BITS ) * d_OSR ) + 1 ;
2009-06-18 12:12:25 +00:00
const unsigned last_sample = first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR ;
2009-06-12 09:36:39 +00:00
double freq_offset = compute_freq_offset ( input , first_sample , last_sample ) ; //extract frequency offset from it
2009-06-18 12:12:25 +00:00
d_freq_offset_vals . push_front ( freq_offset ) ;
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 ( ) ;
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 ( " mean_offset: " < < mean_offset ) ;
DCOUT ( " Adjusting frequency, new frequency offset: " < < d_freq_offset < < " \n " ) ;
}
2009-06-06 21:10:21 +00:00
}
}
break ;
2009-06-12 09:36:39 +00:00
case sch_burst : { //if it's SCH burst
2009-06-06 21:10:21 +00:00
int t1 , t2 , t3 , d_ncc , d_bcc ;
2009-06-12 09:36:39 +00:00
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
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
2009-06-13 17:31:43 +00:00
d_failed_sch = 0 ;
2009-06-12 09:36:39 +00:00
offset = burst_start - floor ( ( GUARD_PERIOD ) * d_OSR ) ; //compute offset from burst_start - burst should start after a guard period
2010-07-29 19:15:03 +00:00
if ( d_trace_sch )
{
DCOUT ( " bcc: " < < d_bcc < < " ncc: " < < d_ncc < < " t1: " < < t1 < < " t2: " < < t2 < < " t3: " < < t3 ) ;
DCOUT ( offset ) ;
}
2009-06-12 09:36:39 +00:00
to_consume + = offset ; //adjust with offset number of samples to be consumed
2009-06-13 17:31:43 +00:00
} else {
d_failed_sch + + ;
2009-06-18 12:12:25 +00:00
if ( d_failed_sch > = MAX_SCH_ERRORS ) {
// d_state = next_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();
DCOUT ( " many sch decoding errors " ) ;
2009-06-13 17:31:43 +00:00
}
2009-06-06 21:10:21 +00:00
}
}
break ;
2009-06-12 09:36:39 +00:00
case normal_burst : //if it's normal burst
2009-06-27 11:18:13 +00:00
burst_start = get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , d_bcc ) ; //get channel impulse response for given training sequence number - d_bcc
2009-06-12 09:36:39 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ; //MLSE detection of bits
2010-07-29 19:15:03 +00:00
process_normal_burst ( d_burst_nr , output_binary , first_burst ) ; //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
2009-06-06 21:10:21 +00:00
break ;
2009-06-18 12:10:17 +00:00
case dummy_or_normal : {
2009-06-27 11:18:13 +00:00
burst_start = get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , TS_DUMMY ) ;
2009-06-18 12:10:17 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ;
std : : vector < unsigned char > v ( 20 ) ;
std : : vector < unsigned char > : : iterator it ;
it = std : : set_difference ( output_binary + TRAIN_POS , output_binary + TRAIN_POS + 16 , & train_seq [ TS_DUMMY ] [ 5 ] , & train_seq [ TS_DUMMY ] [ 21 ] , v . begin ( ) ) ;
int different_bits = ( it - v . begin ( ) ) ;
if ( different_bits > 2 ) {
2009-06-27 11:18:13 +00:00
burst_start = get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , d_bcc ) ;
2009-06-18 12:10:17 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ;
if ( ! output_binary [ 0 ] & & ! output_binary [ 1 ] & & ! output_binary [ 2 ] ) {
2010-07-29 19:15:03 +00:00
process_normal_burst ( d_burst_nr , output_binary , first_burst ) ; //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
2009-06-18 12:10:17 +00:00
}
}
}
2009-06-06 21:10:21 +00:00
case rach_burst :
//implementation of this channel isn't possible in current gsm_receiver
//it would take some realtime processing, counter of samples from USRP to
//stay synchronized with this device and possibility to switch frequency from uplink
//to C0 (where sch is) back and forth
break ;
2009-06-12 09:36:39 +00:00
case dummy : //if it's dummy
2009-06-27 11:18:13 +00:00
burst_start = get_norm_chan_imp_resp ( input , & channel_imp_resp [ 0 ] , TS_DUMMY ) ; //read dummy
2009-06-12 09:36:39 +00:00
detect_burst ( input , & channel_imp_resp [ 0 ] , burst_start , output_binary ) ; // but as far as I know it's pointless
2009-06-06 21:10:21 +00:00
break ;
2009-06-12 09:36:39 +00:00
case empty : //if it's empty burst
break ; //do nothing
2009-06-01 09:09:44 +00:00
}
2009-06-04 11:12:03 +00:00
2009-06-12 09:36:39 +00:00
d_burst_nr + + ; //go to next burst
2009-06-01 20:14:14 +00:00
2009-06-12 09:36:39 +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
//burst_number computes this offset
//but choice of this class to do this was random
2009-06-06 21:10:21 +00:00
consume_each ( to_consume ) ;
}
break ;
2009-04-10 15:22:31 +00:00
}
2009-04-20 17:49:01 +00:00
return produced_out ;
2009-04-07 10:47:18 +00:00
}
2009-04-08 14:35:59 +00:00
2009-06-12 07:58:14 +00:00
bool gsm_receiver_cf : : find_fcch_burst ( const gr_complex * input , const int nitems )
2009-04-08 14:35:59 +00:00
{
2009-06-12 09:36:39 +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
2009-04-20 17:49:01 +00:00
float phase_diff = 0 ;
gr_complex conjprod ;
2009-06-12 07:58:14 +00:00
int start_pos = - 1 ;
2009-04-24 17:24:17 +00:00
int hit_count = 0 ;
int miss_count = 0 ;
2009-04-28 18:08:18 +00:00
float min_phase_diff ;
float max_phase_diff ;
2009-06-06 21:10:21 +00:00
double best_sum = 0 ;
2009-04-28 18:08:18 +00:00
float lowest_max_min_diff = 99999 ;
2009-06-12 09:36:39 +00:00
2009-04-20 17:49:01 +00:00
int to_consume = 0 ;
2009-04-24 17:24:17 +00:00
int sample_number = 0 ;
2009-04-20 17:49:01 +00:00
bool end = false ;
2009-04-24 17:24:17 +00:00
bool result = false ;
2009-04-20 17:49:01 +00:00
circular_buffer_float : : iterator buffer_iter ;
2009-04-08 14:35:59 +00:00
2009-06-12 09:36:39 +00:00
/**@name Possible states of FCCH search algorithm*/
//@{
2009-04-20 17:49:01 +00:00
enum states {
2009-06-12 09:36:39 +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
2009-04-20 17:49:01 +00:00
} fcch_search_state ;
2009-06-12 09:36:39 +00:00
//@}
2009-04-20 17:49:01 +00:00
fcch_search_state = init ;
2009-04-24 17:24:17 +00:00
while ( ! end ) {
2009-04-20 17:49:01 +00:00
switch ( fcch_search_state ) {
2009-06-12 09:36:39 +00:00
case init : //initialize variables
2009-04-20 17:49:01 +00:00
hit_count = 0 ;
miss_count = 0 ;
start_pos = - 1 ;
lowest_max_min_diff = 99999 ;
2009-04-28 18:08:18 +00:00
phase_diff_buffer . clear ( ) ;
2009-04-20 17:49:01 +00:00
fcch_search_state = search ;
break ;
2009-06-12 09:36:39 +00:00
case search : // search for positive samples
2009-04-24 17:24:17 +00:00
sample_number + + ;
2009-04-20 17:49:01 +00:00
2009-06-12 09:36:39 +00:00
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
2009-04-20 17:49:01 +00:00
fcch_search_state = search_fail ;
2009-04-24 17:24:17 +00:00
} else {
2009-06-12 07:58:14 +00:00
phase_diff = compute_phase_diff ( input [ sample_number ] , input [ sample_number - 1 ] ) ;
2009-04-20 17:49:01 +00:00
2009-06-12 09:36:39 +00:00
if ( phase_diff > 0 ) { //if a positive phase difference was found
2009-04-24 17:24:17 +00:00
to_consume = sample_number ;
2009-06-12 09:36:39 +00:00
fcch_search_state = found_something ; //switch to state in which searches for FCCH
2009-04-24 17:24:17 +00:00
} else {
fcch_search_state = search ;
}
2009-04-20 17:49:01 +00:00
}
break ;
2009-06-12 09:36:39 +00:00
case found_something : { // search for FCCH and the best position of it
2009-06-12 07:58:14 +00:00
if ( phase_diff > 0 ) {
2009-06-12 09:36:39 +00:00
hit_count + + ; //positive phase differencies increases hits_count
2009-06-12 07:58:14 +00:00
} else {
2009-06-12 09:36:39 +00:00
miss_count + + ; //negative increases miss_count
2009-04-20 17:49:01 +00:00
}
2009-06-12 07:58:14 +00:00
if ( ( miss_count > = FCCH_MAX_MISSES * d_OSR ) & & ( hit_count < = FCCH_HITS_NEEDED * d_OSR ) ) {
2009-06-12 09:36:39 +00:00
//if miss_count exceeds limit before hit_count
fcch_search_state = init ; //go to init
2009-06-12 07:58:14 +00:00
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 ) ) {
2009-06-12 09:36:39 +00:00
//if hit_count and miss_count exceeds limit then FCCH was found
2009-06-12 07:58:14 +00:00
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 ;
2009-06-12 09:36:39 +00:00
start_pos = sample_number - FCCH_HITS_NEEDED * d_OSR - FCCH_MAX_MISSES * d_OSR ; //store start pos
2009-06-12 07:58:14 +00:00
best_sum = 0 ;
for ( buffer_iter = phase_diff_buffer . begin ( ) ;
buffer_iter ! = ( phase_diff_buffer . end ( ) ) ;
buffer_iter + + ) {
2009-06-12 09:36:39 +00:00
best_sum + = * buffer_iter - ( M_PI / 2 ) / d_OSR ; //store best value of phase offset sum
2009-06-12 07:58:14 +00:00
}
}
}
2009-04-20 17:49:01 +00:00
2009-06-12 07:58:14 +00:00
sample_number + + ;
2009-04-20 17:49:01 +00:00
2009-06-12 09:36:39 +00:00
if ( sample_number > = nitems ) { //if there's no single sample left to check
fcch_search_state = search_fail ; //FCCH search failed
2009-06-12 07:58:14 +00:00
continue ;
}
2009-04-20 17:49:01 +00:00
2009-06-12 07:58:14 +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 ;
}
2009-04-20 17:49:01 +00:00
break ;
2009-06-12 07:58:14 +00:00
case fcch_found : {
DCOUT ( " fcch found on position: " < < d_counter + start_pos ) ;
2009-06-13 10:38:16 +00:00
to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1 ; //consume one FCCH burst
2009-05-28 17:39:56 +00:00
2009-06-12 07:58:14 +00:00
d_fcch_start_pos = d_counter + start_pos ;
2009-05-28 18:27:13 +00:00
2009-06-12 07:58:14 +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 ) ;
end = true ;
result = true ;
break ;
}
2009-04-20 17:49:01 +00:00
case search_fail :
end = true ;
result = false ;
break ;
2009-04-10 15:22:31 +00:00
}
}
2009-06-12 07:58:14 +00:00
d_counter + = to_consume ;
2009-04-20 17:49:01 +00:00
consume_each ( to_consume ) ;
return result ;
}
2009-06-12 07:58:14 +00:00
double gsm_receiver_cf : : compute_freq_offset ( const gr_complex * input , unsigned first_sample , unsigned last_sample )
2009-04-20 17:49:01 +00:00
{
2009-06-12 07:58:14 +00:00
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 ) ;
2009-04-20 17:49:01 +00:00
return freq_offset ;
2009-04-08 14:35:59 +00:00
}
2009-04-24 17:24:17 +00:00
void gsm_receiver_cf : : set_frequency ( double freq_offset )
2009-04-20 17:49:01 +00:00
{
2009-04-24 17:24:17 +00:00
d_tuner - > calleval ( freq_offset ) ;
}
2009-05-14 06:16:51 +00:00
inline float gsm_receiver_cf : : compute_phase_diff ( gr_complex val1 , gr_complex val2 )
{
gr_complex conjprod = val1 * conj ( val2 ) ;
return gr_fast_atan2f ( imag ( conjprod ) , real ( conjprod ) ) ;
}
2009-06-12 09:36:39 +00:00
bool gsm_receiver_cf : : reach_sch_burst ( const int nitems )
2009-04-24 17:24:17 +00:00
{
2009-06-12 09:36:39 +00:00
//it just consumes samples to get near to a SCH burst
2009-04-24 17:24:17 +00:00
int to_consume = 0 ;
bool result = false ;
2009-06-12 07:58:14 +00:00
unsigned sample_nr_near_sch_start = d_fcch_start_pos + ( FRAME_BITS - SAFETY_MARGIN ) * d_OSR ;
2009-06-12 09:36:39 +00:00
//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 ;
2009-04-24 17:24:17 +00:00
}
2009-06-12 09:36:39 +00:00
result = false ;
} else {
to_consume = 0 ;
result = true ;
2009-04-24 17:24:17 +00:00
}
2009-06-12 07:58:14 +00:00
d_counter + = to_consume ;
2009-04-28 18:09:37 +00:00
consume_each ( to_consume ) ;
2009-04-24 17:24:17 +00:00
return result ;
2009-04-20 17:49:01 +00:00
}
2009-06-12 07:58:14 +00:00
int gsm_receiver_cf : : get_sch_chan_imp_resp ( const gr_complex * input , gr_complex * chan_imp_resp )
2009-05-28 17:39:56 +00:00
{
vector_complex correlation_buffer ;
vector_float power_buffer ;
vector_float window_energy_buffer ;
int strongest_window_nr ;
int burst_start = 0 ;
2009-06-06 21:10:21 +00:00
int chan_imp_resp_center = 0 ;
2009-05-28 17:39:56 +00:00
float max_correlation = 0 ;
float energy = 0 ;
2009-06-01 09:09:44 +00:00
for ( int ii = SYNC_POS * d_OSR ; ii < ( SYNC_POS + SYNC_SEARCH_RANGE ) * d_OSR ; ii + + ) {
2009-06-12 07:58:14 +00:00
gr_complex correlation = correlate_sequence ( & d_sch_training_seq [ 5 ] , N_SYNC_BITS - 10 , & input [ ii ] ) ;
2009-05-28 17:39:56 +00:00
correlation_buffer . push_back ( correlation ) ;
power_buffer . push_back ( 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 ;
2009-06-01 09:09:44 +00:00
for ( int ii = 0 ; ii < ( d_chan_imp_length ) * d_OSR ; ii + + , iter_ii + + ) {
2009-05-28 17:39:56 +00:00
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 ( ) ;
2009-06-12 07:58:14 +00:00
// d_channel_imp_resp.clear();
2009-05-28 17:39:56 +00:00
max_correlation = 0 ;
2009-06-01 09:09:44 +00:00
for ( int ii = 0 ; ii < ( d_chan_imp_length ) * d_OSR ; ii + + ) {
2009-05-28 17:39:56 +00:00
gr_complex correlation = correlation_buffer [ strongest_window_nr + ii ] ;
if ( abs ( correlation ) > max_correlation ) {
chan_imp_resp_center = ii ;
max_correlation = abs ( correlation ) ;
}
2009-06-12 07:58:14 +00:00
// d_channel_imp_resp.push_back(correlation);
2009-05-28 17:39:56 +00:00
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 ;
}
2009-06-12 07:58:14 +00:00
void gsm_receiver_cf : : detect_burst ( const gr_complex * input , gr_complex * chan_imp_resp , int burst_start , unsigned char * output_binary )
2009-05-28 17:39:56 +00:00
{
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 ] ) ;
}
2009-06-12 07:58:14 +00:00
mafi ( & input [ burst_start ] , BURST_SIZE , chan_imp_resp , d_chan_imp_length * d_OSR , filtered_burst ) ;
2009-05-28 17:39:56 +00:00
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
2009-06-12 07:58:14 +00:00
void gsm_receiver_cf : : gmsk_mapper ( const unsigned char * input , int nitems , gr_complex * gmsk_output , gr_complex start_point )
2009-04-20 17:49:01 +00:00
{
2009-04-24 17:24:17 +00:00
gr_complex j = gr_complex ( 0.0 , 1.0 ) ;
int current_symbol ;
int encoded_symbol ;
int previous_symbol = 2 * input [ 0 ] - 1 ;
2009-06-04 11:12:03 +00:00
gmsk_output [ 0 ] = start_point ;
2009-04-24 17:24:17 +00:00
2009-06-12 07:58:14 +00:00
for ( int i = 1 ; i < nitems ; i + + ) {
2009-04-24 17:24:17 +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
2009-06-04 11:12:03 +00:00
gmsk_output [ i ] = j * gr_complex ( encoded_symbol , 0.0 ) * gmsk_output [ i - 1 ] ;
2009-04-24 17:24:17 +00:00
previous_symbol = current_symbol ;
}
}
2009-05-28 17:39:56 +00:00
//TODO consider use of some generalized function for correlation and placing it in a separate class for signal processing
2009-06-12 07:58:14 +00:00
gr_complex gsm_receiver_cf : : correlate_sequence ( const gr_complex * sequence , int length , const gr_complex * input )
2009-04-24 17:24:17 +00:00
{
gr_complex result ( 0.0 , 0.0 ) ;
2009-04-28 18:09:37 +00:00
int sample_number = 0 ;
2009-04-24 17:24:17 +00:00
2009-04-30 17:07:08 +00:00
for ( int ii = 0 ; ii < length ; ii + + ) {
2009-04-28 18:09:37 +00:00
sample_number = ( ii * d_OSR ) ;
2009-06-12 07:58:14 +00:00
result + = sequence [ ii ] * conj ( input [ sample_number ] ) ;
2009-04-24 17:24:17 +00:00
}
2009-05-28 17:39:56 +00:00
result = result / gr_complex ( length , 0 ) ;
2009-04-24 17:24:17 +00:00
return result ;
2009-04-20 17:49:01 +00:00
}
2009-04-28 18:09:37 +00:00
2009-06-12 07:58:14 +00:00
//computes autocorrelation for positive arguments
2009-05-28 17:39:56 +00:00
//TODO consider placing this funtion in a separate class for signal processing
2009-06-12 07:58:14 +00:00
inline void gsm_receiver_cf : : autocorrelation ( const gr_complex * input , gr_complex * out , int nitems )
2009-04-28 18:09:37 +00:00
{
2009-05-14 06:16:51 +00:00
int i , k ;
2009-06-12 07:58:14 +00:00
for ( k = nitems - 1 ; k > = 0 ; k - - ) {
2009-05-14 06:16:51 +00:00
out [ k ] = gr_complex ( 0 , 0 ) ;
2009-06-12 07:58:14 +00:00
for ( i = k ; i < nitems ; i + + ) {
2009-05-14 06:16:51 +00:00
out [ k ] + = input [ i ] * conj ( input [ i - k ] ) ;
}
}
}
2009-05-28 17:39:56 +00:00
//TODO consider use of some generalized function for filtering and placing it in a separate class for signal processing
2009-06-12 07:58:14 +00:00
inline void gsm_receiver_cf : : mafi ( const gr_complex * input , int nitems , gr_complex * filter , int filter_length , gr_complex * output )
2009-05-14 06:16:51 +00:00
{
int ii = 0 , n , a ;
2009-05-28 17:39:56 +00:00
2009-06-12 07:58:14 +00:00
for ( n = 0 ; n < nitems ; n + + ) {
2009-06-04 11:12:03 +00:00
a = n * d_OSR ;
output [ n ] = 0 ;
ii = 0 ;
while ( ii < filter_length ) {
2009-06-12 07:58:14 +00:00
if ( ( a + ii ) > = nitems * d_OSR )
2009-06-04 11:12:03 +00:00
break ;
output [ n ] + = input [ a + ii ] * filter [ ii ] ;
ii + + ;
}
}
}
2009-06-14 17:16:20 +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
2009-06-27 11:18:13 +00:00
int gsm_receiver_cf : : get_norm_chan_imp_resp ( const gr_complex * input , gr_complex * chan_imp_resp , int bcc )
2009-06-04 11:12:03 +00:00
{
vector_complex correlation_buffer ;
vector_float power_buffer ;
vector_float window_energy_buffer ;
int strongest_window_nr ;
int burst_start = 0 ;
2009-06-06 21:10:21 +00:00
int chan_imp_resp_center = 0 ;
2009-06-04 11:12:03 +00:00
float max_correlation = 0 ;
float energy = 0 ;
2009-06-05 19:01:11 +00:00
int search_center = ( int ) ( ( TRAIN_POS + GUARD_PERIOD ) * d_OSR ) ;
2009-06-18 12:12:25 +00:00
int search_start_pos = search_center + 1 ;
// int search_start_pos = search_center - d_chan_imp_length * d_OSR;
2009-06-06 21:10:21 +00:00
int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 2 * d_OSR ;
2009-06-04 11:12:03 +00:00
for ( int ii = search_start_pos ; ii < search_stop_pos ; ii + + ) {
2009-06-12 07:58:14 +00:00
gr_complex correlation = correlate_sequence ( & d_norm_training_seq [ bcc ] [ TRAIN_BEGINNING ] , N_TRAIN_BITS - 10 , & input [ ii ] ) ;
2009-06-04 11:12:03 +00:00
correlation_buffer . push_back ( correlation ) ;
power_buffer . push_back ( 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 ;
2009-06-18 12:12:25 +00:00
for ( int ii = 0 ; ii < ( d_chan_imp_length - 2 ) * d_OSR ; ii + + , iter_ii + + ) {
2009-06-14 17:16:20 +00:00
// for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++, iter_ii++) {
2009-06-04 11:12:03 +00:00
if ( iter_ii = = power_buffer . end ( ) ) {
loop_end = true ;
break ;
}
energy + = ( * iter_ii ) ;
}
if ( loop_end ) {
break ;
}
iter + + ;
2009-06-05 19:01:11 +00:00
2009-06-04 11:12:03 +00:00
window_energy_buffer . push_back ( energy ) ;
}
2009-06-14 17:16:20 +00:00
//!why doesn't this work
2009-06-18 12:12:25 +00:00
strongest_window_nr = max_element ( window_energy_buffer . begin ( ) , window_energy_buffer . end ( ) ) - window_energy_buffer . begin ( ) ;
strongest_window_nr = 3 ; //! so I have to override it here
2009-06-04 11:12:03 +00:00
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 ) ;
}
2009-06-12 07:58:14 +00:00
// d_channel_imp_resp.push_back(correlation);
2009-06-04 11:12:03 +00:00
chan_imp_resp [ ii ] = correlation ;
}
2009-06-06 21:10:21 +00:00
// We want to use the first sample of the impulseresponse, and the
// 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.
2009-06-05 19:01:11 +00:00
2009-06-06 21:10:21 +00:00
burst_start = search_start_pos + chan_imp_resp_center + strongest_window_nr - TRAIN_POS * d_OSR ;
2009-06-05 19:01:11 +00:00
2009-06-06 21:10:21 +00:00
// 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 ;
//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";
2009-06-04 11:12:03 +00:00
return burst_start ;
}
2009-06-06 21:10:21 +00:00