diff options
author | Harald Welte <laforge@gnumonks.org> | 2010-02-18 16:46:36 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2010-02-18 16:46:36 +0100 |
commit | fbe7b94c9c65f2df74acd5dff7503c9833ec2579 (patch) | |
tree | 5f47a597f2f396662719c5a76ac6bf26eda69f6c /src/target/firmware/layer1/afc.c |
Initial import of OsmocomBB into git repository
Diffstat (limited to 'src/target/firmware/layer1/afc.c')
-rw-r--r-- | src/target/firmware/layer1/afc.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c new file mode 100644 index 00000000..846d208f --- /dev/null +++ b/src/target/firmware/layer1/afc.c @@ -0,0 +1,122 @@ +/* AFC (Automatic Frequency Correction) Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <gsm.h> + +#include <layer1/afc.h> +#include <layer1/avg.h> +#include <calypso/dsp.h> + +/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */ +#define AFC_PERIOD 40 +/* How many of our measurements have to be valid? */ +#define AFC_MIN_MUN_VALID 8 + +/* The actual AFC code */ + +struct afc_state { + struct running_avg ravg; /* running average */ + int16_t dac_value; /* current DAC output value */ + uint16_t arfcn; +}; + +static void afc_ravg_output(struct running_avg *ravg, int32_t avg); + +static struct afc_state afc_state = { + .ravg = { + .outfn = &afc_ravg_output, + .period = AFC_PERIOD, + .min_valid = AFC_MIN_MUN_VALID, + }, +}; + +/* The AFC DAC in the ABB has to be configured as follows: + * DAC = 1MHz / 947MHz * FreqErr(Hz) / AFCslop(ppm/LSB) + * where: + * 947 MHz is the center of EGSM + * AFCslope is coded F1.15, thus a normalization factor of 2^15 aplpies + */ + +#define AFC_NORM_FACTOR_GSM ((1<<15) / 947) +#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894) + +/* we assume 4.9ppb per LSB, equals 0.0049 * 32768 == 160 */ +#define AFC_SLOPE 160 +//#define AFC_SLOPE 141 + +/* The DSP can measure the frequency error in the following ranges: + * FB_MODE0: +/- 20 kHz + * FB_MODE1: +/- 4 kHz + * Sync Burst: +/- 1 kHz + * Normal Burst: +/- 400 Hz + */ + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn) +{ + int32_t afc_norm_factor; + int16_t delta; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_900: + case GSM_850: + afc_norm_factor = AFC_NORM_FACTOR_GSM; + break; + default: + afc_norm_factor = AFC_NORM_FACTOR_DCS; + } + + delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE); + printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); + afc_state.dac_value += delta; + + /* The AFC DAC has only 13 bits */ + if (afc_state.dac_value > 4095) + afc_state.dac_value = 4095; + else if (afc_state.dac_value < -4096) + afc_state.dac_value = -4096; +} + +void afc_input(int32_t freq_error, uint16_t arfcn, int valid) +{ + afc_state.arfcn = arfcn; + runavg_input(&afc_state.ravg, freq_error, valid); + runavg_check_output(&afc_state.ravg); +} + +/* callback function for runavg */ +static void afc_ravg_output(struct running_avg *ravg, int32_t avg) +{ + afc_correct(avg, afc_state.arfcn); +} + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void) +{ + dsp_api.db_w->d_afc = afc_state.dac_value; + dsp_api.db_w->d_ctrl_abb |= (1 << B_AFC); +} |