summaryrefslogtreecommitdiffstats
path: root/src/target/firmware/layer1/prim_bts_sync.c
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2013-02-27 08:31:48 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2013-02-27 08:31:48 +0100
commitf183f43f447f4e92ab6a12d4947e80b666a19274 (patch)
tree143f0e13d2530b73b4f10a99b5bdf72a2d114e97 /src/target/firmware/layer1/prim_bts_sync.c
parent49ef59bc62495eae904a11a017f747e839fdf886 (diff)
New sync task for calypso BTS, using a single frame per multiframe
By using single frame only, loss of packets is reduced. For normal data frames (and for TCH/H speech), one out of four parts of data gets lost. For TCH/F speech, only one out of eight parts of data gets lost. The error correction can compensate the missing parts. The sync code can use the extended DSP task, but it is currently disabled, because the current DSP code does not allow scheduling TSC for each individual task, but we need to use TSC of sync cell here.
Diffstat (limited to 'src/target/firmware/layer1/prim_bts_sync.c')
-rw-r--r--src/target/firmware/layer1/prim_bts_sync.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/target/firmware/layer1/prim_bts_sync.c b/src/target/firmware/layer1/prim_bts_sync.c
new file mode 100644
index 00000000..60cb1576
--- /dev/null
+++ b/src/target/firmware/layer1/prim_bts_sync.c
@@ -0,0 +1,324 @@
+/* Layer 1 - Receiving Normal Bursts */
+
+/* (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 <string.h>
+#include <stdlib.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <rffe.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/afc.h>
+#include <layer1/toa.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+#include <layer1/rfch.h>
+#include <layer1/prim.h>
+#include <layer1/agc.h>
+
+#include <l1ctl_proto.h>
+
+/* ------------------------------------------------------------------------ */
+/* DSP extensions API */
+/* ------------------------------------------------------------------------ */
+
+#define BASE_API 0xFFD00000
+
+#define API_DSP2ARM(x) (BASE_API + ((x) - 0x0800) * sizeof(uint16_t))
+
+#define BASE_API_EXT_DB0 API_DSP2ARM(0x2000)
+#define BASE_API_EXT_DB1 API_DSP2ARM(0x2200)
+#define BASE_API_EXT_NDB API_DSP2ARM(0x2400)
+
+struct dsp_ext_ndb {
+ uint16_t active;
+ uint16_t tsc;
+} __attribute__((packed));
+
+struct dsp_ext_db {
+ /* TX command and data ptr */
+ struct {
+#define DSP_EXT_TX_CMD_NONE 0
+#define DSP_EXT_TX_CMD_FB 1
+#define DSP_EXT_TX_CMD_SB 2
+#define DSP_EXT_TX_CMD_DUMMY 3
+#define DSP_EXT_TX_CMD_AB 4
+#define DSP_EXT_TX_CMD_NB 5
+ uint16_t cmd;
+ uint16_t data;
+ } tx[8];
+
+ /* RX command and data ptr */
+ struct {
+#define DSP_EXT_RX_CMD_NONE 0
+#define DSP_EXT_RX_CMD_AB 1
+#define DSP_EXT_RX_CMD_NB 2
+ uint16_t cmd;
+ uint16_t data;
+ } rx[8];
+
+ /* SCH data */
+ uint16_t sch[2];
+
+ /* Generic data array */
+ uint16_t data[0];
+} __attribute__((packed));
+
+struct dsp_ext_api {
+ struct dsp_ext_ndb *ndb;
+ struct dsp_ext_db *db[2];
+};
+
+static struct dsp_ext_api dsp_ext_api = {
+ .ndb = (struct dsp_ext_ndb *) BASE_API_EXT_NDB,
+ .db = {
+ (struct dsp_ext_db *) BASE_API_EXT_DB0,
+ (struct dsp_ext_db *) BASE_API_EXT_DB1,
+ },
+};
+
+static inline struct dsp_ext_db *
+dsp_ext_get_db(int r_wn /* 0=W, 1=R */)
+{
+ int idx = r_wn ? dsp_api.r_page : dsp_api.w_page;
+ return dsp_ext_api.db[idx];
+}
+
+
+
+struct l1s_rxnb_state {
+ struct l1s_meas_hdr meas[4];
+
+ struct msgb *msg;
+ struct l1ctl_info_dl *dl;
+ struct l1ctl_data_ind *di;
+};
+
+static struct l1s_rxnb_state rxnb;
+
+static int l1s_nb_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
+{
+ struct gsm_time rx_time;
+ uint16_t rf_arfcn;
+ uint8_t tsc, tn;
+ int i;
+
+#if 1
+ putchart('n');
+
+ /* just for debugging, d_task_d should not be 0 */
+ if (dsp_api.db_r->d_task_d == 0) {
+ puts("EMPTY\n");
+ return 0;
+ }
+
+ /* DSP burst ID needs to correspond with what we expect */
+ if (dsp_api.db_r->d_burst_d != 0) {
+ printf("BURST ID %u!=0\n", dsp_api.db_r->d_burst_d);
+ return 0;
+ }
+
+ /* get radio parameters for _this_ burst */
+ gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 1);
+ rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn);
+
+ /* collect measurements */
+ rxnb.meas[0].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA];
+ rxnb.meas[0].pm_dbm8 =
+ agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3);
+ rxnb.meas[0].freq_err =
+ ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]);
+ rxnb.meas[0].snr = dsp_api.db_r->a_serv_demod[D_SNR];
+
+ /* feed computed frequency error into AFC loop */
+ if (rxnb.meas[0].snr > AFC_SNR_THRESHOLD)
+ afc_input(rxnb.meas[0].freq_err, rf_arfcn, 1);
+ else
+ afc_input(rxnb.meas[0].freq_err, rf_arfcn, 0);
+
+ /* feed computed TOA into TA loop */
+ printf("TOA=%d snr=%d (thres %d) freq_err=%d pm=%d\n",
+ rxnb.meas[0].toa_qbit, rxnb.meas[0].snr, 2560,
+ rxnb.meas[0].freq_err, rxnb.meas[0].pm_dbm8 / 8);
+ for (i = 0; i < 10; i++)
+ toa_input(rxnb.meas[0].toa_qbit << 2, rxnb.meas[0].snr);
+
+ /* Tell the RF frontend to set the gain appropriately */
+ rffe_compute_gain(rxnb.meas[0].pm_dbm8/8, CAL_DSP_TGT_BB_LVL);
+
+ {
+ /* Get radio parameters for the first burst */
+ gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 4);
+ rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn);
+
+ rxnb.dl->chan_nr = 0x80;
+ rxnb.dl->link_id = 0x00;
+
+ rxnb.dl->band_arfcn = htons(rf_arfcn);
+
+ rxnb.dl->frame_nr = htonl(rx_time.fn);
+
+ rxnb.dl->snr = rxnb.meas[0].snr;
+ rxnb.dl->rx_level = dbm2rxlev(rxnb.meas[0].pm_dbm8 / 8);
+
+ /* update rx level for pm report */
+ pu_update_rx_level(rxnb.dl->rx_level);
+
+ l1_queue_for_l2(rxnb.msg);
+ rxnb.msg = NULL; rxnb.dl = NULL; rxnb.di = NULL;
+
+ /* clear downlink task */
+ dsp_api.db_w->d_task_d = 0;
+ }
+
+ /* mark READ page as being used */
+ dsp_api.r_page_used = 1;
+#else
+ struct dsp_ext_db *db = dsp_ext_get_db(1);
+ uint16_t *d = &db->data[32];
+
+ /* get radio parameters for _this_ burst */
+ gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 1);
+ rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn);
+
+ /* collect measurements */
+ rxnb.meas[0].toa_qbit = d[D_TOA];
+ rxnb.meas[0].pm_dbm8 =
+ agc_inp_dbm8_by_pm(d[D_PM] >> 3);
+ rxnb.meas[0].freq_err =
+ ANGLE_TO_FREQ(d[D_ANGLE]);
+ rxnb.meas[0].snr = d[D_SNR];
+
+ /* feed computed frequency error into AFC loop */
+ if (rxnb.meas[0].snr > AFC_SNR_THRESHOLD)
+ afc_input(rxnb.meas[0].freq_err, rf_arfcn, 1);
+ else
+ afc_input(rxnb.meas[0].freq_err, rf_arfcn, 0);
+
+ /* feed computed TOA into TA loop */
+ printf("arfcn=%d tn=%d TOA=%d snr=%d (thres %d) freq_err=%d pm=%d\n",
+ rf_arfcn, tn,
+ rxnb.meas[0].toa_qbit, rxnb.meas[0].snr, 2560,
+ rxnb.meas[0].freq_err, rxnb.meas[0].pm_dbm8 / 8);
+ for (i = 0; i < 10; i++)
+ toa_input(rxnb.meas[0].toa_qbit << 2, rxnb.meas[0].snr);
+
+ /* Tell the RF frontend to set the gain appropriately */
+ rffe_compute_gain(rxnb.meas[0].pm_dbm8/8, CAL_DSP_TGT_BB_LVL);
+
+ {
+ rxnb.dl->chan_nr = 0x80;
+ rxnb.dl->link_id = 0x00;
+
+ rxnb.dl->band_arfcn = htons(rf_arfcn);
+
+ rxnb.dl->frame_nr = htonl(rx_time.fn);
+
+ rxnb.dl->snr = rxnb.meas[0].snr;
+ rxnb.dl->rx_level = dbm2rxlev(rxnb.meas[0].pm_dbm8 / 8);
+
+ /* update rx level for pm report */
+ pu_update_rx_level(rxnb.dl->rx_level);
+
+ l1_queue_for_l2(rxnb.msg);
+ rxnb.msg = NULL; rxnb.dl = NULL; rxnb.di = NULL;
+ }
+
+ /* We're done with this */
+ dsp_api.r_page_used = 1;
+#endif
+
+ return 0;
+}
+
+static int l1s_nb_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ uint16_t arfcn;
+ uint8_t tsc, tn;
+
+ putchart('N');
+
+ if (rxnb.msg) {
+ /* Can happen when resetting ... */
+ printf("nb_cmd(0) and rxnb.msg != NULL\n");
+ msgb_free(rxnb.msg);
+ }
+ /* allocate msgb as needed. FIXME: from L1A ?? */
+ rxnb.msg = l1ctl_msgb_alloc(L1CTL_DATA_IND);
+ if (!rxnb.msg)
+ printf("nb_cmd(0): unable to allocate msgb\n");
+ rxnb.dl = (struct l1ctl_info_dl *) msgb_put(rxnb.msg, sizeof(*rxnb.dl));
+ rxnb.di = (struct l1ctl_data_ind *) msgb_put(rxnb.msg, sizeof(*rxnb.di));
+
+ rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn);
+
+ dsp_ext_api.ndb->tsc = tsc;
+#if 1
+ /* DDL_DSP_TASK, four normal bursts */
+ dsp_load_tch_param(&l1s.next_time,
+ SIG_ONLY_MODE, SDCCH_4, 0, 0, 0, tn);
+
+ dsp_load_rx_task( dsp_task_iq_swap(ALLC_DSP_TASK, arfcn, 0), 0, tsc);
+#else
+ struct dsp_ext_db *db = dsp_ext_get_db(0);
+
+ /* Enable extensions */
+ dsp_ext_api.ndb->active = 1;
+
+ db->rx[0].data = 0x000;
+ db->rx[0].cmd = DSP_EXT_RX_CMD_NB;
+
+ /* Enable dummy bursts detection */
+ dsp_api.db_w->d_ctrl_system |= (1 << B_BCCH_FREQ_IND);
+
+ /* Enable task */
+ dsp_api.db_w->d_task_d = 23;
+#endif
+
+ l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0);
+
+ return 0;
+}
+
+const struct tdma_sched_item bts_sync_sched_set[] = {
+ SCHED_ITEM_DT(l1s_nb_cmd, 2, 0, 0), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_nb_resp, -5, 0, 0), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};