📄 synch_complex_rcv.c
字号:
/*************************************************************************** * synch_complex_rcv.c - Software Radio to detect primary synch * ------------------- * begin : 02/10/22 * authors : Linus Gasser * emails : Linus.Gasser@epfl.ch ***************************************************************************//*************************************************************************** * Changes * ------- * date - name - description * 02/10/22 - ineiti - init * 03/11/24 - ineiti - added complex function * 04/03/08 - ineiti - adjuste description * 04/03/08 - ineiti - deleted synch_move * 04/03/11 - ineiti - added skip-configuration, like complex_ics_rcv * 04/03/27 - ineiti - fixed skip-configuration to be less paranoid, like * ics_rcv ;) * **************************************************************************//*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************//** * The desynchronisation uses a convolution to find the most probable * synchronistaion-signal. Due to the special format of the synch-signal, * it is possible to implement this as two convolutions, which reduces a * lot the computational complexity of the task. * This is different in that it takes a complex input. */#include "spc.h"#include "mmx.h"#include "antenna.h"#include "synch.h"#include <math.h>#include <complex.h>#define DBG_LVL 0#define PRE_POST_LENGTH 32typedef struct { // If this is set to a valid id, it will set the offset for the stfa int stfa_id; // -1 // If this is 1, the sycnh_rcv also sets the offset_samples_rcv of the stfa int fine_adj; // 0 // Skip synchronisation if more than this number of other high peaks. // Useful numbers start at 5, 10 is a good bet. int skip; // 10}config_t;typedef struct { // The position of the strongest synchronisation signal int synch_pos; // The amplitude of the strongest signal int synch_amp; // The fine position, 0-3 int fine_pos; // The fine amplitude int fine_amp; // The internal buffer - not really interesting block_t buffer; // The synchronisation signal - OK to look at it block_t synch;}stats_t;typedef struct { complex double *buffer; int *synch; int slot_length_chips; int stfa_id; int offset; int fine_pos; int fine_adj; SYMBOL_COMPLEX *output; int skip;}private_t;/* * The initialisation function, or constructor, * is called the first time this module is instantiated. */int rcv_complex_init( swr_sdb_t *context ) { // Definition of variables - don't touch if you're not an expert config_t *config; stats_t *stats; MOD_INC_USE_COUNT; if ( sizeof( private_t ) > 0 ) { context->private_data = swr_malloc( sizeof( private_t ) ); } swr_sdb_get_config_struct( context->id, (void**)&config ); swr_sdb_get_stats_struct( context->id, (void**)&stats ); private->slot_length_chips = 0; private->buffer = NULL; private->synch = NULL; private->output = NULL; private->fine_pos = 0; private->stfa_id = config->stfa_id = -1; stats->buffer.data = 0; stats->buffer.size = 0; stats->buffer.type = SIG_DOUBLE_COMPLEX; stats->synch.data = 0; stats->synch.size = 0; stats->synch.type = SIG_S32; stats->synch_pos = 0; stats->synch_amp = 0; stats->fine_pos = 0; stats->fine_amp = 0; config->fine_adj = 0; config->skip = 10; port_out(0).flags |= SWR_PORT_OWN_MALLOC; // Definition - don't touch swr_sdb_free_config_struct( context->id, (void**)&config ); swr_sdb_free_stats_struct( context->id, (void**)&stats ); return 0;}/* * Every time modules from the outside change the value of a configuration parameter, * this function is called. */int rcv_complex_reconfig( swr_sdb_t *context ) { // Definition of variables - don't touch config_t *config; swr_sdb_get_config_struct( context->id, (void**)&config ); if ( config->stfa_id != private->stfa_id ) { private->stfa_id = config->stfa_id; if ( private->stfa_id >= 0 ) { private->offset = swr_sdb_get_config_int( private->stfa_id, "offset_chips_rcv" ); } } private->fine_adj = config->fine_adj; private->skip = config->skip; PR_DBG( 4, "(%i) reconfig: %p\n", context->id, port_in(0).data ); // Definition - don't touch swr_sdb_free_config_struct( context->id, (void**)&config ); return(0);}/* * Reconfigure outputs, abused to set up internal structures */int rcv_complex_configure_outputs( swr_sdb_t *context ) { stats_t *stats; config_t *config; int len; swr_sdb_get_stats_struct( context->id, (void**)&stats ); swr_sdb_get_config_struct( context->id, (void**)&config ); if ( private->slot_length_chips != size_in(0) ) { if ( private->slot_length_chips ) { swr_free( private->buffer ); swr_free( private->synch ); swr_free( private->output ); } private->slot_length_chips = port_in(0).size; len = ( private->slot_length_chips + 240 ) * sizeof( complex double ); private->buffer = swr_malloc( len ); memset( private->buffer, 0, len ); stats->buffer.data = private->buffer; stats->buffer.size = len / sizeof( complex double ); len = private->slot_length_chips * sizeof( S32 ); private->synch = swr_malloc( len ); memset( private->synch, 0, len ); stats->synch.data = private->synch; stats->synch.size = len / sizeof( S32 ); // This is in order to offer some 'buffer-room' to the matched filter // before and after the actual slot, so as to simplify the filtering. len = private->slot_length_chips - SYNCH_PRIM_LENGTH_SYMBOLS + 2 * PRE_POST_LENGTH; // PR_DBG( 0, "Allocating %i bytes for synch_complex_rcv_output\n", len ); private->output = swr_malloc( len * sizeof( SYMBOL_COMPLEX ) ); port_out(0).data = private->output + PRE_POST_LENGTH; } // Output 0 size_out(0) = size_in(0) - SYNCH_PRIM_LENGTH_SYMBOLS; PR_DBG( 4, "(%i) config-out: %p\n", context->id, port_in(0).data ); swr_sdb_free_config_struct( context->id, (void**)&config ); swr_sdb_free_stats_struct( context->id, (void**)&stats ); return 0;}/* * This is function that impements the `main method' of the class * Every calss has got just ONE method/working-mode. */int rcv_complex_pdata( swr_sdb_t *context ) { // Definition of variables - don't touch stats_t *stats; config_t *config; int i, slot_length_symbols, synch_pos, fine_pos = -1, fine_amp = 0, j, synch_amp, total_energy, other_peaks; complex double *buffer; S32 *synch; SYMBOL_COMPLEX *in, *out; // SAMPLE_S12 *out, *in_s12; short int gold_inner[] = { +1, -1, -1, +1, -1, +1, -1, +1, -1, -1, +1, +1, +1, +1, +1, +1 }; short int gold_outer[] = { +1, +1, -1, +1, -1, +1, +1, +1, -1, -1, +1, -1, -1, +1, +1, +1 }; swr_sdb_get_config_struct( context->id, (void**)&config ); swr_sdb_free_config_struct( context->id, (void**)&config ); slot_length_symbols = private->slot_length_chips; buffer = private->buffer + 240; synch = private->synch; in = buffer_in(0); // As total_energy is divided by SLOT_LENGTH_CHIPS and then used // as a divisor itself, it has to be at least 1!!! total_energy = slot_length_symbols; synch_amp = 0; synch_pos = -1; for ( i=16; i<slot_length_symbols; i++ ) { complex double res = 0; for ( j=0; j<16; j++ ){ res += ( in[ i - j ].real + in[ i - j ].imag * I ) * gold_inner[ j ]; } buffer[ i ] = res; memcpy( private->buffer, buffer + slot_length_symbols - 240, 240 * sizeof( complex double ) ); res = buffer[ i ] * gold_outer[ 0 ]; for ( j=1; j<16; j++ ){ res += buffer[ i - j * 16 ] * gold_outer[ j ]; if( (i-j*16) < 0 ) { PR_DBG( 4, "buffer[%i]=%g+%gi\n", i-j*16, creal( buffer[i-j*16] ), cimag( buffer[i-j*16] ) ); } } synch[i] = cabs( res ); total_energy += cabs( res ); if (synch[i] > synch_amp) { synch_pos = i % slot_length_symbols; synch_amp = synch[i]; PR_DBG(4,"amp[%i]=%i\n",i,synch_amp); } } // Search for other, erroneous peaks for ( other_peaks = 0, i=0; i<slot_length_symbols; i++ ) { if ( synch[i] > synch_amp / 2 && synch_pos != i ){ // There is another peak with at least half the amplitude, // let's search further other_peaks++; } } total_energy /= slot_length_symbols; // One of these magic numbers... synch_pos -= SYNCH_PRIM_LENGTH_SYMBOLS - 1 + private->fine_adj / 2; // If there are too many other peaks, consider another place if ( other_peaks > private->skip && private->skip ){ PR_DBG( 2, "Other_peaks: %i\n", other_peaks ); synch_pos = -slot_length_symbols / 2; synch_amp /= other_peaks * other_peaks; } // Perhaps we have to synchronise? if ( private->stfa_id >= 0 ) { private->offset += synch_pos; PR_DBG( 4, "Setting new synch: %i(%i)\n", private->offset, private->fine_adj ); swr_sdb_set_config_int( private->stfa_id, "offset_chips_rcv", private->offset ); swr_sdb_set_config_int( private->stfa_id, "offset_samples_rcv", ( private->fine_adj % 2 ) ); } // Copy the input-buffer to the output-buffer with omitting the middle out = buffer_out(0); if ( out ) { memcpy( out - PRE_POST_LENGTH, in - PRE_POST_LENGTH + SYNCH_PRIM_LENGTH_SYMBOLS, ( size_out(0) + 2 * PRE_POST_LENGTH ) * sizeof( SYMBOL_COMPLEX ) ); } swr_sdb_get_stats_struct( context->id, (void**)&stats ); if ( synch_amp > ( 1 << 24 ) ) { PR_DBG( 3, "Synch amp is REALLY big: %i\n", synch_amp ); } stats->synch_amp = synch_amp; // To have synch_pos in chips stats->synch_pos = synch_pos; stats->fine_pos = fine_pos; stats->fine_amp = fine_amp; swr_sdb_free_stats_struct( context->id, (void**)&stats ); PR_DBG( 4, "Tot_energy: %i, synch_amp: %i, pos: %i, offset: %i\n", total_energy, synch_amp, synch_pos, private->offset ); return(0);}/* * This is the `destructor'. */int rcv_complex_finalize( swr_sdb_t *context ) { swr_free( private->buffer ); swr_free( private->synch ); swr_free( private ); MOD_DEC_USE_COUNT; return(0);}/* * This function is called upon "insmod" and is used to register the * different parts of the module to the SPM. */swr_spc_id_t rcv_complex_id;int rcv_complex_module_init(void) { swr_spc_desc_t *desc; // Get a description-part from SPM // Give the following parameters: // In-ports, output-ports, config-params, stat-params desc = swr_spc_get_new_desc( 1, 1, 3, 6 ); if ( !desc ) { PR_DBG( 0, "Can't initialise the module. This is BAD!\n" ); return -1; } // Define the different parts of config and stats UM_CONFIG_INT( "stfa_id" ); UM_CONFIG_INT( "fine_adj" ); UM_CONFIG_INT( "skip" ); UM_STATS_INT( "synch_pos" ); UM_STATS_INT( "synch_amp" ); UM_STATS_INT( "fine_pos" ); UM_STATS_INT( "fine_amp" ); UM_STATS_BLOCK( "buffer" ); UM_STATS_BLOCK( "synch" ); UM_INPUT( SIG_SYMBOL_COMPLEX, 0 ); UM_OUTPUT( SIG_SYMBOL_COMPLEX, 0 ); // Initialise the callback-functions. NULL for not-used functions desc->fn_init = rcv_complex_init; desc->fn_reconfigure = rcv_complex_reconfig; desc->fn_process_data = rcv_complex_pdata; desc->fn_finalize = rcv_complex_finalize; desc->fn_configure_outputs = rcv_complex_configure_outputs; // And register the module in the SPM rcv_complex_id = swr_cdb_register_spc( &desc, "synch_complex_rcv" ); if ( rcv_complex_id == SWR_SPM_INVALID_ID ) { swr_spc_free_desc( desc ); PR_DBG( 0, "Couldn't register the module!\n" ); return 1; } PR_DBG( 4, "Ready to synch\n" ); return 0;}/* * This is called upon rmmod */void rcv_complex_module_exit( void ) { PR_DBG( 4, "Freeing id: %i\n", rcv_complex_id ); if ( swr_cdb_unregister_spc( rcv_complex_id ) < 0 ) { PR_DBG( 0, "Still in use somewhere\n" ); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -