aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bts-sysmo/misc
diff options
context:
space:
mode:
authorMax <msuraev@sysmocom.de>2018-01-16 15:42:48 +0100
committerMax <msuraev@sysmocom.de>2018-01-16 15:53:00 +0100
commitbef6eae05b959b2ce395b3f949675d2a93053f0d (patch)
tree15aba39795584a98bd10b3ff4b0cc1efe649c79c /src/osmo-bts-sysmo/misc
parent5df57cb84c83546a7c23135845deb8bf615819c1 (diff)
Move sysmobts-calib into osmo-bts-sysmo
It's prerequisite for jenkins tests fix after migration to stow. The sysmobts-calib uses hand-coded Makefile instead of automake which makes it hard to properly propagate build flags. Also, make it optional to enable excluding it from certain jenkins tests. Change-Id: I3b54bfa5ef1d89092f6cf13dc27de10874b31b18
Diffstat (limited to 'src/osmo-bts-sysmo/misc')
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts-calib.c537
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts-layer1.c800
-rw-r--r--src/osmo-bts-sysmo/misc/sysmobts-layer1.h45
3 files changed, 1382 insertions, 0 deletions
diff --git a/src/osmo-bts-sysmo/misc/sysmobts-calib.c b/src/osmo-bts-sysmo/misc/sysmobts-calib.c
new file mode 100644
index 00000000..a111d1d5
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts-calib.c
@@ -0,0 +1,537 @@
+/* OCXO/TCXO based calibration utility */
+
+/*
+ * (C) 2012-2013 Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <math.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <sysmocom/femtobts/superfemto.h>
+#include <sysmocom/femtobts/gsml1types.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/core/utils.h>
+
+#include "sysmobts-layer1.h"
+
+enum actions {
+ ACTION_SCAN,
+ ACTION_CALIB,
+ ACTION_BCCH,
+ ACTION_BCCH_CCCH,
+};
+
+static const char *modes[] = {
+ [ACTION_SCAN] = "scan",
+ [ACTION_CALIB] = "calibrate",
+ [ACTION_BCCH] = "bcch",
+ [ACTION_BCCH_CCCH] = "bcch_ccch",
+};
+
+static const char *bands[] = {
+ [GsmL1_FreqBand_850] = "850",
+ [GsmL1_FreqBand_900] = "900",
+ [GsmL1_FreqBand_1800] = "1800",
+ [GsmL1_FreqBand_1900] = "1900",
+};
+
+struct channel_pair {
+ int min;
+ int max;
+};
+
+static const struct channel_pair arfcns[] = {
+ [GsmL1_FreqBand_850] = { .min = 128, .max = 251 },
+ [GsmL1_FreqBand_900] = { .min = 1, .max = 124 },
+ [GsmL1_FreqBand_1800] = { .min = 512, .max = 885 },
+ [GsmL1_FreqBand_1900] = { .min = 512, .max = 810 },
+
+};
+
+static const char *clk_source[] = {
+ [SuperFemto_ClkSrcId_Ocxo] = "ocxo",
+ [SuperFemto_ClkSrcId_Tcxo] = "tcxo",
+ [SuperFemto_ClkSrcId_External] = "external",
+ [SuperFemto_ClkSrcId_GpsPps] = "gps",
+ [SuperFemto_ClkSrcId_Trx] = "trx",
+ [SuperFemto_ClkSrcId_Rx] = "rx",
+ [SuperFemto_ClkSrcId_Edge] = "edge",
+ [SuperFemto_ClkSrcId_NetList] = "netlisten",
+};
+
+static const struct value_string sapi_names[GsmL1_Sapi_NUM+1] = {
+ { GsmL1_Sapi_Fcch, "FCCH" },
+ { GsmL1_Sapi_Sch, "SCH" },
+ { GsmL1_Sapi_Sacch, "SACCH" },
+ { GsmL1_Sapi_Sdcch, "SDCCH" },
+ { GsmL1_Sapi_Bcch, "BCCH" },
+ { GsmL1_Sapi_Pch, "PCH" },
+ { GsmL1_Sapi_Agch, "AGCH" },
+ { GsmL1_Sapi_Cbch, "CBCH" },
+ { GsmL1_Sapi_Rach, "RACH" },
+ { GsmL1_Sapi_TchF, "TCH/F" },
+ { GsmL1_Sapi_FacchF, "FACCH/F" },
+ { GsmL1_Sapi_TchH, "TCH/H" },
+ { GsmL1_Sapi_FacchH, "FACCH/H" },
+ { GsmL1_Sapi_Nch, "NCH" },
+ { GsmL1_Sapi_Pdtch, "PDTCH" },
+ { GsmL1_Sapi_Pacch, "PACCH" },
+ { GsmL1_Sapi_Pbcch, "PBCCH" },
+ { GsmL1_Sapi_Pagch, "PAGCH" },
+ { GsmL1_Sapi_Ppch, "PPCH" },
+ { GsmL1_Sapi_Pnch, "PNCH" },
+ { GsmL1_Sapi_Ptcch, "PTCCH" },
+ { GsmL1_Sapi_Prach, "PRACH" },
+ { 0, NULL }
+};
+
+static int action = ACTION_SCAN;
+static int band = GsmL1_FreqBand_900;
+static int calib = SuperFemto_ClkSrcId_Ocxo;
+static int source = SuperFemto_ClkSrcId_NetList;
+static int dsp_flags = 0x0;
+static int cal_arfcn = 0;
+static int initial_cor = 0;
+static int steps = -1;
+
+static void print_usage(void)
+{
+ printf("Usage: sysmobts-calib ARGS\n");
+}
+
+static void print_help(void)
+{
+ printf(" -h --help this text\n");
+ printf(" -c --clock "
+ "ocxo|tcxo|external|gps|trx|rx|edge\n");
+ printf(" -s --calibration-source "
+ "ocxo|tcxo|external|gps|trx|rx|edge|netlisten\n");
+ printf(" -b --band 850|900|1800|1900\n");
+ printf(" -m --mode scan|calibrate|bcch|bcch_ccch\n");
+ printf(" -a --arfcn NR arfcn for calibration\n");
+ printf(" -d --dsp-flags NR dsp mask for debug log\n");
+ printf(" -t --threshold level\n");
+ printf(" -i --initial-clock-correction COR.\n");
+ printf(" -t --steps STEPS\n");
+}
+
+static int find_value(const char **array, int size, char *value)
+{
+ int i = 0;
+ for (i = 0; i < size; ++i) {
+ if (array[i] == NULL)
+ continue;
+ if (strcmp(value, array[i]) == 0)
+ return i;
+ }
+
+ printf("Failed to find: '%s'\n", value);
+ exit(-2);
+}
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"calibration-source", 1, 0, 's'},
+ {"clock", 1, 0, 'c'},
+ {"mode", 1, 0, 'm'},
+ {"band", 1, 0, 'b'},
+ {"dsp-flags", 1, 0, 'd'},
+ {"arfcn", 1, 0, 'a'},
+ {"initial-clock-correction", 1, 0, 'i'},
+ {"steps", 1, 0, 't'},
+ {0, 0, 0, 0},
+ };
+
+ c = getopt_long(argc, argv, "hs:c:m:b:d:a:i:t:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ print_usage();
+ print_help();
+ exit(0);
+ case 's':
+ source = find_value(clk_source,
+ ARRAY_SIZE(clk_source), optarg);
+ break;
+ case 'c':
+ calib = find_value(clk_source,
+ ARRAY_SIZE(clk_source), optarg);
+ break;
+ case 'm':
+ action = find_value(modes,
+ ARRAY_SIZE(modes), optarg);
+ break;
+ case 'b':
+ band = find_value(bands,
+ ARRAY_SIZE(bands), optarg);
+ break;
+ case 'd':
+ dsp_flags = strtol(optarg, NULL, 16);
+ break;
+ case 'a':
+ cal_arfcn = atoi(optarg);
+ break;
+ case 'i':
+ initial_cor = atoi(optarg);
+ break;
+ case 't':
+ steps = atoi(optarg);
+ break;
+ default:
+ printf("Unhandled option, terminating.\n");
+ exit(-1);
+ }
+ }
+
+ if (source == calib) {
+ printf("Clock source and reference clock may not be the same.\n");
+ exit(-3);
+ }
+
+ if (calib == SuperFemto_ClkSrcId_NetList) {
+ printf("Clock may not be network listen.\n");
+ exit(-4);
+ }
+
+ if (action == ACTION_CALIB && source == SuperFemto_ClkSrcId_NetList) {
+ if (cal_arfcn == 0) {
+ printf("Please specify the reference ARFCN.\n");
+ exit(-5);
+ }
+
+ if (cal_arfcn < arfcns[band].min || cal_arfcn > arfcns[band].max) {
+ printf("ARFCN(%d) is not in the given band.\n", cal_arfcn);
+ exit(-6);
+ }
+ }
+}
+
+#define CHECK_RC(rc) \
+ if (rc != 0) \
+ return EXIT_FAILURE;
+
+#define CHECK_RC_MSG(rc, msg) \
+ if (rc != 0) { \
+ printf("%s: %d\n", msg, rc); \
+ return EXIT_FAILURE; \
+ }
+#define CHECK_COND_MSG(cond, rc, msg) \
+ if (cond) { \
+ printf("%s: %d\n", msg, rc); \
+ return EXIT_FAILURE; \
+ }
+
+struct scan_result
+{
+ uint16_t arfcn;
+ float rssi;
+};
+
+static int scan_cmp(const void *arg1, const void *arg2)
+{
+ struct scan_result *elem1 = (struct scan_result *) arg1;
+ struct scan_result *elem2 = (struct scan_result * )arg2;
+
+ float diff = elem1->rssi - elem2->rssi;
+ if (diff > 0.0)
+ return 1;
+ else if (diff < 0.0)
+ return -1;
+ else
+ return 0;
+}
+
+static int scan_band()
+{
+ int arfcn, rc, i;
+
+ /* Scan results.. at most 400 items */
+ struct scan_result results[400];
+ memset(&results, 0, sizeof(results));
+ int num_scan_results = 0;
+
+ printf("Going to scan bands.\n");
+
+ for (arfcn = arfcns[band].min; arfcn <= arfcns[band].max; ++arfcn) {
+ float mean_rssi;
+
+ printf(".");
+ fflush(stdout);
+ rc = power_scan(band, arfcn, 10, &mean_rssi);
+ CHECK_RC_MSG(rc, "Power Measurement failed");
+
+ results[num_scan_results].arfcn = arfcn;
+ results[num_scan_results].rssi = mean_rssi;
+ num_scan_results++;
+ }
+
+ qsort(results, num_scan_results, sizeof(struct scan_result), scan_cmp);
+ printf("\nSorted scan results (weakest first):\n");
+ for (i = 0; i < num_scan_results; ++i)
+ printf("ARFCN %3d: %.4f\n", results[i].arfcn, results[i].rssi);
+
+ return 0;
+}
+
+static int calib_get_clock_error(void)
+{
+ int rc, clkErr, clkErrRes;
+
+ printf("Going to determine the clock offset.\n");
+
+ rc = rf_clock_info(&clkErr, &clkErrRes);
+ CHECK_RC_MSG(rc, "Clock info failed.\n");
+
+ if (clkErr == 0 && clkErrRes == 0) {
+ printf("Failed to get the clock info. Are both clocks present?\n");
+ return -1;
+ }
+
+ /*
+ * Empiric gps error determination. With revE and firmware v3.3
+ * the clock error for TCXO to GPS appears to have a different
+ * sign. The device in question doesn't have a networklisten mode
+ * so it is impossible to verify that this only applies to GPS.
+ */
+ if (source == SuperFemto_ClkSrcId_GpsPps)
+ clkErr *= -1;
+
+
+ /* this is an absolute clock error */
+ printf("The calibration value is: %d\n", clkErr);
+ return 0;
+}
+
+static int calib_clock_after_sync(void)
+{
+ int rc, clkErr, clkErrRes, iteration, cor;
+
+ iteration = 0;
+ cor = initial_cor;
+
+ printf("Trying to calibrate now and reducing clock error.\n");
+
+ for (iteration = 0; iteration < steps || steps <= 0; ++iteration) {
+ if (steps > 0)
+ printf("Iteration %d/%d with correction: %d\n", iteration, steps, cor);
+ else
+ printf("Iteration %d with correction: %d\n", iteration, cor);
+
+ rc = rf_clock_info(&clkErr, &clkErrRes);
+ CHECK_RC_MSG(rc, "Clock info failed.\n");
+
+ /*
+ * TODO: use the clock error resolution here, implement it as a
+ * a PID controller..
+ */
+
+ /* Picocell class requires 0.1ppm.. but that is 'too easy' */
+ if (fabs(clkErr / 1000.0f) <= 0.05f) {
+ printf("The calibration value is: %d\n", cor);
+ return 1;
+ }
+
+ cor -= clkErr / 2;
+ rc = set_clock_cor(cor, calib, source);
+ CHECK_RC_MSG(rc, "Clock correction failed.\n");
+ }
+
+ return -1;
+}
+
+static int find_initial_clock(HANDLE layer1, int *clock)
+{
+ int i;
+
+ printf("Trying to find an initial clock value.\n");
+
+ for (i = 0; i < 1000; ++i) {
+ int rc;
+ int cor = i * 150;
+ rc = wait_for_sync(layer1, cor, calib, source);
+ if (rc == 1) {
+ printf("Found initial clock offset: %d\n", cor);
+ *clock = cor;
+ break;
+ } else {
+ CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
+ }
+
+ cor = i * -150;
+ rc = wait_for_sync(layer1, cor, calib, source);
+ if (rc == 1) {
+ printf("Found initial clock offset: %d\n", cor);
+ *clock = cor;
+ break;
+ } else {
+ CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
+ }
+ }
+
+ return 0;
+}
+
+static int calib_clock_netlisten(void)
+{
+ int rc, cor = initial_cor;
+ float mean_rssi;
+ HANDLE layer1;
+
+ rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
+ CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
+ if (mean_rssi < -118.0f)
+ printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
+
+ /* initial lock */
+ rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
+ if (rc == -23)
+ rc = find_initial_clock(layer1, &cor);
+ CHECK_RC_MSG(rc, "Following SCH failed");
+
+ /* now try to calibrate it */
+ rc = set_clock_cor(cor, calib, source);
+ CHECK_RC_MSG(rc, "Clock setup failed.");
+
+ calib_clock_after_sync();
+
+ rc = mph_close(layer1);
+ CHECK_RC_MSG(rc, "MPH-Close");
+
+ return EXIT_SUCCESS;
+}
+
+static int calib_clock(void)
+{
+ int rc;
+
+ /* now try to calibrate it */
+ rc = set_clock_cor(initial_cor, calib, source);
+ CHECK_RC_MSG(rc, "Clock setup failed.");
+
+ calib_get_clock_error();
+
+ return EXIT_SUCCESS;
+}
+
+static int bcch_follow(void)
+{
+ int rc, cor = initial_cor;
+ float mean_rssi;
+ HANDLE layer1;
+
+ rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
+ CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
+ if (mean_rssi < -118.0f)
+ printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
+
+ /* initial lock */
+ rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
+ if (rc == -23)
+ rc = find_initial_clock(layer1, &cor);
+ CHECK_RC_MSG(rc, "Following SCH failed");
+
+ /* identify the BSIC and set it as TSC */
+ rc = find_bsic();
+ CHECK_COND_MSG(rc < 0, rc, "Identifying the BSIC failed");
+ rc = set_tsc_from_bsic(layer1, rc);
+ CHECK_RC_MSG(rc, "Setting the TSC failed");
+
+
+ /* follow the bcch */
+ rc = follow_bcch(layer1);
+ CHECK_RC_MSG(rc, "Follow BCCH");
+
+ /* follow the pch */
+ if (action == ACTION_BCCH_CCCH) {
+ rc = follow_pch(layer1);
+ CHECK_RC_MSG(rc, "Follow BCCH/CCCH");
+ }
+
+ /* now wait for the PhDataInd */
+ for (;;) {
+ uint32_t fn;
+ uint8_t block;
+ uint8_t data[23];
+ size_t size;
+ struct gsm_time gsmtime;
+ GsmL1_Sapi_t sapi;
+
+ rc = wait_for_data(data, &size, &fn, &block, &sapi);
+ if (rc == 1)
+ continue;
+ CHECK_RC_MSG(rc, "No Data Indication");
+
+ gsm_fn2gsmtime(&gsmtime, fn);
+ printf("%02u/%02u/%02u %6s %s\n",
+ gsmtime.t1, gsmtime.t2, gsmtime.t3,
+ get_value_string(sapi_names, sapi),
+ osmo_hexdump(data, size));
+ }
+
+ rc = mph_close(layer1);
+ CHECK_RC_MSG(rc, "MPH-Close");
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ handle_options(argc, argv);
+ printf("Initializing the Layer1\n");
+ rc = initialize_layer1(dsp_flags);
+ CHECK_RC(rc);
+
+ printf("Fetching system info.\n");
+ rc = print_system_info();
+ CHECK_RC(rc);
+
+ printf("Opening RF frontend with clock(%d) and correction(%d)\n",
+ calib, initial_cor);
+ rc = activate_rf_frontend(calib, initial_cor);
+ CHECK_RC(rc);
+
+ if (action == ACTION_SCAN)
+ return scan_band();
+ else if (action == ACTION_BCCH || action == ACTION_BCCH_CCCH)
+ return bcch_follow();
+ else {
+ if (source == SuperFemto_ClkSrcId_NetList)
+ return calib_clock_netlisten();
+ return calib_clock();
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/osmo-bts-sysmo/misc/sysmobts-layer1.c b/src/osmo-bts-sysmo/misc/sysmobts-layer1.c
new file mode 100644
index 00000000..4b34f50e
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts-layer1.c
@@ -0,0 +1,800 @@
+/* Layer1 handling for the DSP/FPGA */
+/*
+ * (C) 2012-2013 Holger Hans Peter Freyther
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sysmocom/femtobts/superfemto.h>
+#include <sysmocom/femtobts/gsml1prim.h>
+
+#include "sysmobts-layer1.h"
+
+#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof((ar)[0]))
+
+#define BTS_DSP2ARM "/dev/msgq/superfemto_dsp2arm"
+#define BTS_ARM2DSP "/dev/msgq/superfemto_arm2dsp"
+#define L1_SIG_ARM2DSP "/dev/msgq/gsml1_sig_arm2dsp"
+#define L1_SIG_DSP2ARM "/dev/msgq/gsml1_sig_dsp2arm"
+
+int set_clock_cor(int clock_cor, int calib, int source);
+static int wait_read_ignore(int seconds);
+
+static int sys_dsp2arm = -1,
+ sys_arm2dsp = -1,
+ sig_dsp2arm = -1,
+ sig_arm2dsp = -1;
+
+static int sync_indicated = 0;
+static int time_indicated = 0;
+
+static int open_devices()
+{
+ sys_dsp2arm = open(BTS_DSP2ARM, O_RDONLY);
+ if (sys_dsp2arm == -1) {
+ perror("Failed to open dsp2arm system queue");
+ return -1;
+ }
+
+ sys_arm2dsp = open(BTS_ARM2DSP, O_WRONLY);
+ if (sys_arm2dsp == -1) {
+ perror("Failed to open arm2dsp system queue");
+ return -2;
+ }
+
+ sig_dsp2arm = open(L1_SIG_DSP2ARM, O_RDONLY);
+ if (sig_dsp2arm == -1) {
+ perror("Failed to open dsp2arm sig queue");
+ return -3;
+ }
+
+ sig_arm2dsp = open(L1_SIG_ARM2DSP, O_WRONLY);
+ if (sig_arm2dsp == -1) {
+ perror("Failed to open arm2dsp sig queue");
+ return -4;
+ }
+
+ return 0;
+}
+
+/**
+ * Send a primitive to the system queue
+ */
+static int send_primitive(int primitive, SuperFemto_Prim_t *prim)
+{
+ prim->id = primitive;
+ return write(sys_arm2dsp, prim, sizeof(*prim)) != sizeof(*prim);
+}
+
+/**
+ * Wait for a confirmation
+ */
+static int wait_primitive(int wait_for, SuperFemto_Prim_t *prim)
+{
+ memset(prim, 0, sizeof(*prim));
+ int rc = read(sys_dsp2arm, prim, sizeof(*prim));
+ if (rc != sizeof(*prim)) {
+ printf("Short read in %s: %d\n", __func__, rc);
+ return -1;
+ }
+
+ if (prim->id != wait_for) {
+ printf("Got primitive %d but waited for %d\n",
+ prim->id, wait_for);
+ return -2;
+ }
+
+ return 0;
+}
+
+/* The Cnf for the Req, assume it is a +1 */
+static int answer_for(int primitive)
+{
+ return primitive + 1;
+}
+
+static int send_recv_primitive(int p, SuperFemto_Prim_t *prim)
+{
+ int rc;
+ rc = send_primitive(p, prim);
+ if (rc != 0)
+ return -1;
+
+ rc = wait_primitive(answer_for(p), prim);
+ if (rc != 0)
+ return -2;
+ return 0;
+}
+
+static int answer_for_sig(int prim)
+{
+ static const GsmL1_PrimId_t cnf[] = {
+ [GsmL1_PrimId_MphInitReq] = GsmL1_PrimId_MphInitCnf,
+ [GsmL1_PrimId_MphCloseReq] = GsmL1_PrimId_MphCloseCnf,
+ [GsmL1_PrimId_MphConnectReq] = GsmL1_PrimId_MphConnectCnf,
+ [GsmL1_PrimId_MphActivateReq] = GsmL1_PrimId_MphActivateCnf,
+ [GsmL1_PrimId_MphConfigReq] = GsmL1_PrimId_MphConfigCnf,
+ [GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
+ };
+
+ if (prim < 0 || prim >= ARRAY_SIZE(cnf)) {
+ printf("Unknown primitive: %d\n", prim);
+ exit(-3);
+ }
+
+ return cnf[prim];
+}
+
+static int is_indication(int prim)
+{
+ return
+ prim == GsmL1_PrimId_MphTimeInd ||
+ prim == GsmL1_PrimId_MphSyncInd ||
+ prim == GsmL1_PrimId_PhConnectInd ||
+ prim == GsmL1_PrimId_PhReadyToSendInd ||
+ prim == GsmL1_PrimId_PhDataInd ||
+ prim == GsmL1_PrimId_PhRaInd;
+}
+
+
+static int send_recv_sig_prim(int p, GsmL1_Prim_t *prim)
+{
+ int rc;
+ prim->id = p;
+ rc = write(sig_arm2dsp, prim, sizeof(*prim));
+ if (rc != sizeof(*prim)) {
+ printf("Failed to write: %d\n", rc);
+ return -1;
+ }
+
+ do {
+ rc = read(sig_dsp2arm, prim, sizeof(*prim));
+ if (rc != sizeof(*prim)) {
+ printf("Failed to read: %d\n", rc);
+ return -2;
+ }
+ } while (is_indication(prim->id));
+
+ if (prim->id != answer_for_sig(p)) {
+ printf("Wrong L1 result got %d wanted %d for prim: %d\n",
+ prim->id, answer_for_sig(p), p);
+ return -3;
+ }
+
+ return 0;
+}
+
+static int wait_for_indication(int p, GsmL1_Prim_t *prim)
+{
+ int rc;
+ memset(prim, 0, sizeof(*prim));
+
+ struct timespec start_time, now_time;
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+ /*
+ * TODO: select.... with timeout. The below will work 99% as we will
+ * get time indications very soonish after the connect
+ */
+ for (;;) {
+ clock_gettime(CLOCK_MONOTONIC, &now_time);
+ if (now_time.tv_sec - start_time.tv_sec > 10) {
+ printf("Timeout waiting for indication.\n");
+ return -4;
+ }
+
+ rc = read(sig_dsp2arm, prim, sizeof(*prim));
+ if (rc != sizeof(*prim)) {
+ printf("Failed to read.\n");
+ return -1;
+ }
+
+ if (!is_indication(prim->id)) {
+ printf("No indication: %d\n", prim->id);
+ return -2;
+ }
+
+ if (p != prim->id && prim->id == GsmL1_PrimId_MphSyncInd) {
+ printf("Got sync.\n");
+ sync_indicated = 1;
+ continue;
+ }
+ if (p != prim->id && prim->id == GsmL1_PrimId_MphTimeInd) {
+ time_indicated = 1;
+ continue;
+ }
+
+ if (p != prim->id) {
+ printf("Wrong indication got %d wanted %d\n",
+ prim->id, p);
+ return -3;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+static int set_trace_flags(uint32_t dsp)
+{
+ SuperFemto_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ prim.u.setTraceFlagsReq.u32Tf = dsp;
+ return send_primitive(SuperFemto_PrimId_SetTraceFlagsReq, &prim);
+}
+
+static int reset_and_wait()
+{
+ int rc;
+ SuperFemto_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ rc = send_recv_primitive(SuperFemto_PrimId_Layer1ResetReq, &prim);
+ if (rc != 0)
+ return -1;
+ if (prim.u.layer1ResetCnf.status != GsmL1_Status_Success)
+ return -2;
+ return 0;
+}
+
+/**
+ * Open the message queues and (re-)initialize the DSP and FPGA
+ */
+int initialize_layer1(uint32_t dsp_flags)
+{
+ if (open_devices() != 0) {
+ printf("Failed to open devices.\n");
+ return -1;
+ }
+
+ if (set_trace_flags(dsp_flags) != 0) {
+ printf("Failed to set dsp flags.\n");
+ return -2;
+ }
+ if (reset_and_wait() != 0) {
+ printf("Failed to reset the firmware.\n");
+ return -3;
+ }
+ return 0;
+}
+
+/**
+ * Print systems infos
+ */
+int print_system_info()
+{
+ int rc;
+ SuperFemto_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ rc = send_recv_primitive(SuperFemto_PrimId_SystemInfoReq, &prim);
+ if (rc != 0) {
+ printf("Failed to send SystemInfoRequest.\n");
+ return -1;
+ }
+
+ if (prim.u.systemInfoCnf.status != GsmL1_Status_Success) {
+ printf("Failed to request SystemInfoRequest.\n");
+ return -2;
+ }
+
+#define INFO_DSP(x) x.u.systemInfoCnf.dspVersion
+#define INFO_FPGA(x) x.u.systemInfoCnf.fpgaVersion
+#ifdef FEMTOBTS_NO_BOARD_VERSION
+#define BOARD_REV(x) -1
+#define BOARD_OPT(x) -1
+#define COMPILED_MAJOR (FEMTOBTS_API_VERSION >> 16)
+#define COMPILED_MINOR ((FEMTOBTS_API_VERSION >> 8) & 0xff)
+#define COMPILED_BUILD (FEMTOBTS_API_VERSION & 0xff)
+#else
+#define BOARD_REV(x) x.u.systemInfoCnf.boardVersion.rev
+#define BOARD_OPT(x) x.u.systemInfoCnf.boardVersion.option
+#define COMPILED_MAJOR (SUPERFEMTO_API_VERSION >> 16)
+#define COMPILED_MINOR ((SUPERFEMTO_API_VERSION >> 8) & 0xff)
+#define COMPILED_BUILD (SUPERFEMTO_API_VERSION & 0xff)
+#endif
+
+ printf("Compiled against: v%u.%u.%u\n",
+ COMPILED_MAJOR, COMPILED_MINOR, COMPILED_BUILD);
+ printf("Running DSP v%d.%d.%d FPGA v%d.%d.%d Rev: %d Option: %d\n",
+ INFO_DSP(prim).major, INFO_DSP(prim).minor, INFO_DSP(prim).build,
+ INFO_FPGA(prim).major, INFO_FPGA(prim).minor, INFO_FPGA(prim).build,
+ BOARD_REV(prim), BOARD_OPT(prim));
+
+ if (COMPILED_MAJOR != INFO_DSP(prim).major || COMPILED_MINOR != INFO_DSP(prim).minor) {
+ printf("WARNING! WARNING! WARNING! WARNING! WARNING\n");
+ printf("You might run this against an incompatible firmware.\n");
+ printf("Continuing anyway but the result might be broken\n");
+ }
+#undef INFO_DSP
+#undef INFO_FPGA
+#undef BOARD_REV
+#undef BOARD_OPT
+#undef COMPILED_MAJOR
+#undef COMPILED_MINOR
+#undef COMPILED_BUILD
+ return 0;
+}
+
+int activate_rf_frontend(int clock_source, int initial_cor)
+{
+ int rc;
+ SuperFemto_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ prim.u.activateRfReq.timing.u8TimSrc = 1;
+ prim.u.activateRfReq.msgq.u8UseTchMsgq = 0;
+ prim.u.activateRfReq.msgq.u8UsePdtchMsgq = 0;
+
+ prim.u.activateRfReq.rfTrx.iClkCor = initial_cor;
+ prim.u.activateRfReq.rfTrx.clkSrc = clock_source;
+#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
+ prim.u.activateRfReq.rfRx.iClkCor = initial_cor;
+ prim.u.activateRfReq.rfRx.clkSrc = clock_source;
+#endif
+
+ rc = send_recv_primitive(SuperFemto_PrimId_ActivateRfReq, &prim);
+ return rc;
+}
+
+static int mph_init(int band, int arfcn, HANDLE *layer1)
+{
+ int rc;
+ GsmL1_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ prim.u.mphInitReq.deviceParam.devType = GsmL1_DevType_Rxd;
+ prim.u.mphInitReq.deviceParam.freqBand = band;
+ prim.u.mphInitReq.deviceParam.u16Arfcn = arfcn;
+ prim.u.mphInitReq.deviceParam.u16BcchArfcn = arfcn;
+ prim.u.mphInitReq.deviceParam.fRxPowerLevel = -75.f;
+ prim.u.mphInitReq.deviceParam.u8AutoTA = 1;
+
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphInitReq, &prim);
+ if (rc != 0) {
+ printf("Failed to initialize the physical channel.\n");
+ return -1;
+ }
+
+ if (prim.u.mphInitCnf.status != GsmL1_Status_Success) {
+ printf("MPH Init failed.\n");
+ return -2;
+ }
+
+#if 0
+ if (prim.u.mphInitCnf.freqBand != band) {
+ printf("Layer1 ignored the band: %d\n",
+ prim.u.mphInitCnf.freqBand);
+ return -3;
+ }
+#endif
+
+ *layer1 = prim.u.mphInitCnf.hLayer1;
+ return 0;
+}
+
+int mph_close(HANDLE layer1)
+{
+ int rc;
+ GsmL1_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ prim.u.mphCloseReq.hLayer1 = layer1;
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphCloseReq, &prim);
+ if (rc != 0) {
+ printf("Failed to close the MPH\n");
+ return -6;
+ }
+ if (prim.u.mphCloseCnf.status != GsmL1_Status_Success) {
+ printf("MPH Close failed.\n");
+ return -7;
+ }
+
+ return 0;
+}
+
+int follow_sch(int band, int arfcn, int clock, int ref, HANDLE *layer1)
+{
+ int rc;
+ GsmL1_Prim_t prim;
+
+ time_indicated = 0;
+ sync_indicated = 0;
+
+ rc = mph_init(band, arfcn, layer1);
+ if (rc != 0)
+ return rc;
+
+ /* 1.) Connect */
+ memset(&prim, 0, sizeof(prim));
+ prim.u.mphConnectReq.hLayer1 = *layer1;
+ prim.u.mphConnectReq.u8Tn = 0;
+ prim.u.mphConnectReq.logChComb = GsmL1_LogChComb_IV;
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphConnectReq, &prim);
+ if (rc != 0) {
+ printf("Failed to connect.\n");
+ return -1;
+ }
+ if (prim.u.mphConnectCnf.status != GsmL1_Status_Success) {
+ printf("Connect failed.\n");
+ return -2;
+ }
+ if (prim.u.mphConnectCnf.u8Tn != 0) {
+ printf("Wrong timeslot.\n");
+ return -3;
+ }
+
+ /* 2.) Activate */
+ memset(&prim, 0, sizeof(prim));
+ prim.u.mphActivateReq.hLayer1 = *layer1;
+ prim.u.mphActivateReq.u8Tn = 0;
+ prim.u.mphActivateReq.sapi = GsmL1_Sapi_Sch;
+ prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
+ if (rc != 0) {
+ printf("Activation failed.\n");
+ return -4;
+ }
+ if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
+ printf("Activation not successful.\n");
+ return -5;
+ }
+
+ /* 3.) Wait for indication... TODO: check... */
+ printf("Waiting for connect indication.\n");
+ rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
+ if (rc != 0) {
+ printf("Didn't get a connect indication.\n");
+ return rc;
+ }
+
+ /* 4.) Indication Syndication TODO: check... */
+ if (!sync_indicated) {
+ printf("Waiting for sync indication.\n");
+ rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
+ if (rc < 0) {
+ printf("Didn't get a sync indication.\n");
+ return -23;
+ } else if (rc == 0) {
+ if (!prim.u.mphSyncInd.u8Synced) {
+ printf("Failed to get sync.\n");
+ return -23;
+ } else {
+ printf("Synced.\n");
+ }
+ }
+ } else {
+ printf("Already synced.\n");
+ }
+
+ return 0;
+}
+
+static int follow_sapi(HANDLE layer1, const GsmL1_Sapi_t sapi)
+{
+ int rc;
+ GsmL1_Prim_t prim;
+
+ /* 1.) Activate BCCH or such... */
+ memset(&prim, 0, sizeof(prim));
+ prim.u.mphActivateReq.hLayer1 = layer1;
+ prim.u.mphActivateReq.u8Tn = 0;
+ prim.u.mphActivateReq.sapi = sapi;
+ prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
+
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
+ if (rc != 0) {
+ printf("Activation failed.\n");
+ return -4;
+ }
+ if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
+ printf("Activation not successful.\n");
+ return -5;
+ }
+
+ /* 2.) Wait for indication... */
+ printf("Waiting for connect indication.\n");
+ rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
+ if (rc != 0) {
+ printf("Didn't get a connect indication.\n");
+ return rc;
+ }
+
+ if (prim.u.phConnectInd.sapi != sapi) {
+ printf("Got a connect indication for the wrong type: %d\n",
+ prim.u.phConnectInd.sapi);
+ return -6;
+ }
+
+ /* 3.) Wait for PhDataInd... */
+ printf("Waiting for data.\n");
+ rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
+ if (rc != 0) {
+ printf("Didn't get data.\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+int follow_bcch(HANDLE layer1)
+{
+ return follow_sapi(layer1, GsmL1_Sapi_Bcch);
+}
+
+int follow_pch(HANDLE layer1)
+{
+ return follow_sapi(layer1, GsmL1_Sapi_Pch);
+}
+
+int find_bsic(void)
+{
+ int rc, i;
+ GsmL1_Prim_t prim;
+
+ printf("Waiting for SCH data.\n");
+ for (i = 0; i < 10; ++i) {
+ uint8_t bsic;
+ rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
+ if (rc < 0) {
+ printf("Didn't get SCH data.\n");
+ return rc;
+ }
+ if (prim.u.phDataInd.sapi != GsmL1_Sapi_Sch)
+ continue;
+
+ bsic = (prim.u.phDataInd.msgUnitParam.u8Buffer[0] >> 2) & 0xFF;
+ return bsic;
+ }
+
+ printf("Giving up finding the SCH\n");
+ return -1;
+}
+
+int set_tsc_from_bsic(HANDLE layer1, int bsic)
+{
+ int rc;
+ int tsc = bsic & 0x7;
+ GsmL1_Prim_t prim;
+
+ memset(&prim, 0, sizeof(prim));
+ prim.u.mphConfigReq.hLayer3 = 0x23;
+ prim.u.mphConfigReq.hLayer1 = layer1;
+ prim.u.mphConfigReq.cfgParamId = GsmL1_ConfigParamId_SetNbTsc;
+ prim.u.mphConfigReq.cfgParams.setNbTsc.u8NbTsc = tsc;
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphConfigReq, &prim);
+ if (rc != 0) {
+ printf("Failed to send configure.\n");
+ }
+
+ if (prim.u.mphConfigCnf.status != GsmL1_Status_Success) {
+ printf("Failed to set the config cnf.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int set_clock_cor(int clock_cor, int calib, int source)
+{
+ int rc;
+ SuperFemto_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ prim.u.rfClockSetupReq.rfTrx.iClkCor = clock_cor;
+ prim.u.rfClockSetupReq.rfTrx.clkSrc = calib;
+#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
+ prim.u.rfClockSetupReq.rfRx.iClkCor = clock_cor;
+ prim.u.rfClockSetupReq.rfRx.clkSrc = calib;
+#endif
+ prim.u.rfClockSetupReq.rfTrxClkCal.clkSrc = source;
+
+ rc = send_recv_primitive(SuperFemto_PrimId_RfClockSetupReq, &prim);
+ if (rc != 0) {
+ printf("Failed to set the clock setup.\n");
+ return -1;
+ }
+ if (prim.u.rfClockSetupCnf.status != GsmL1_Status_Success) {
+ printf("Clock setup was not successfull.\n");
+ return -2;
+ }
+
+ return 0;
+}
+
+int rf_clock_info(int *clkErr, int *clkErrRes)
+{
+ SuperFemto_Prim_t prim;
+ memset(&prim, 0, sizeof(prim));
+
+ int rc;
+
+ /* reset the counter */
+ prim.u.rfClockInfoReq.u8RstClkCal = 1;
+ rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
+ if (rc != 0) {
+ printf("Failed to reset the clock info.\n");
+ return -1;
+ }
+
+ /* wait for a value */
+ wait_read_ignore(15);
+
+ /* ask for the current counter/error */
+ memset(&prim, 0, sizeof(prim));
+ prim.u.rfClockInfoReq.u8RstClkCal = 0;
+ rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
+ if (rc != 0) {
+ printf("Failed to get the clock info.\n");
+ return -2;
+ }
+
+ printf("Error: %d Res: %d\n",
+ prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr,
+ prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes);
+ *clkErr = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr;
+ *clkErrRes = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes;
+ return 0;
+}
+
+int power_scan(int band, int arfcn, int duration, float *mean_rssi)
+{
+ int rc;
+ HANDLE layer1;
+ GsmL1_Prim_t prim;
+
+ /* init */
+ rc = mph_init(band, arfcn, &layer1);
+ if (rc != 0)
+ return rc;
+
+ /* mph measure request */
+ memset(&prim, 0, sizeof(prim));
+ prim.u.mphMeasureReq.hLayer1 = layer1;
+ prim.u.mphMeasureReq.u32Duration = duration;
+ rc = send_recv_sig_prim(GsmL1_PrimId_MphMeasureReq, &prim);
+ if (rc != 0) {
+ printf("Failed to send measurement request.\n");
+ return -4;
+ }
+
+ if (prim.u.mphMeasureCnf.status != GsmL1_Status_Success) {
+ printf("MphMeasureReq was not confirmed.\n");
+ return -5;
+ }
+
+ *mean_rssi = prim.u.mphMeasureCnf.fMeanRssi;
+
+ /* close */
+ rc = mph_close(layer1);
+ return rc;
+}
+
+/**
+ * Wait for indication...
+ */
+int wait_for_sync(HANDLE layer1, int cor, int calib, int source)
+{
+ GsmL1_Prim_t prim;
+ int rc;
+
+ rc = set_clock_cor(cor, calib, source);
+ if (rc != 0) {
+ printf("Failed to set the clock correction.\n");
+ return -1;
+ }
+
+ sync_indicated = 0;
+ rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
+ if (rc < 0 && rc != -4) {
+ return rc;
+ } else if (rc == 0) {
+ if (!prim.u.mphSyncInd.u8Synced) {
+ printf("Failed to get sync.\n");
+ return 0;
+ }
+ printf("Synced.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sap)
+{
+ GsmL1_Prim_t prim;
+ int rc;
+
+ rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
+ if (rc < 0)
+ return rc;
+ if (prim.u.phDataInd.sapi == GsmL1_Sapi_Sch)
+ return 1;
+
+ *size = prim.u.phDataInd.msgUnitParam.u8Size;
+ *fn = prim.u.phDataInd.u32Fn;
+ *block = prim.u.phDataInd.u8BlockNbr;
+ *sap = prim.u.phDataInd.sapi;
+ memcpy(data, prim.u.phDataInd.msgUnitParam.u8Buffer, *size);
+ return 0;
+}
+
+/**
+ * Make sure the pipe is not running full.
+ *
+ */
+static int wait_read_ignore(int seconds)
+{
+ int max, rc;
+ fd_set fds;
+ struct timeval timeout;
+
+ max = sys_dsp2arm > sig_dsp2arm ? sys_dsp2arm : sig_dsp2arm;
+
+ timeout.tv_sec = seconds;
+ timeout.tv_usec = 0;
+
+ while (1) {
+ FD_ZERO(&fds);
+ FD_SET(sys_dsp2arm, &fds);
+ FD_SET(sig_dsp2arm, &fds);
+
+
+ rc = select(max + 1, &fds, NULL, NULL, &timeout);
+ if (rc == -1) {
+ printf("Failed to select.\n");
+ return -1;
+ } else if (rc) {
+ if (FD_ISSET(sys_dsp2arm, &fds)) {
+ SuperFemto_Prim_t prim;
+ rc = read(sys_dsp2arm, &prim, sizeof(prim));
+ if (rc != sizeof(prim)) {
+ perror("Failed to read system primitive");
+ return -2;
+ }
+ }
+ if (FD_ISSET(sig_dsp2arm, &fds)) {
+ GsmL1_Prim_t prim;
+ rc = read(sig_dsp2arm, &prim, sizeof(prim));
+ if (rc != sizeof(prim)) {
+ perror("Failed to read signal primitiven");
+ return -3;
+ }
+ }
+ } else if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0) {
+ break;
+ }
+
+#ifndef __linux__
+#error "Non portable code"
+#endif
+ }
+ return 0;
+}
diff --git a/src/osmo-bts-sysmo/misc/sysmobts-layer1.h b/src/osmo-bts-sysmo/misc/sysmobts-layer1.h
new file mode 100644
index 00000000..e7d59c94
--- /dev/null
+++ b/src/osmo-bts-sysmo/misc/sysmobts-layer1.h
@@ -0,0 +1,45 @@
+#ifndef SYSMOBTS_LAYER_H
+#define SYSMOBTS_LAYER_H
+
+#include <sysmocom/femtobts/superfemto.h>
+
+#ifdef FEMTOBTS_API_VERSION
+#define SuperFemto_PrimId_t FemtoBts_PrimId_t
+#define SuperFemto_Prim_t FemtoBts_Prim_t
+#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
+#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
+#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
+#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
+#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
+#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
+#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
+#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
+#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
+#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
+#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
+#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
+#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
+#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
+#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
+#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
+#define HW_SYSMOBTS_V1 1
+#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
+#endif
+
+extern int initialize_layer1(uint32_t dsp_flags);
+extern int print_system_info();
+extern int activate_rf_frontend(int clock_source, int clock_cor);
+extern int power_scan(int band, int arfcn, int duration, float *mean_rssi);
+extern int follow_sch(int band, int arfcn, int calib, int reference, HANDLE *layer1);
+extern int follow_bch(HANDLE layer1);
+extern int find_bsic(void);
+extern int set_tsc_from_bsic(HANDLE layer1, int bsic);
+extern int set_clock_cor(int clock_corr, int calib, int source);
+extern int rf_clock_info(int *clkErr, int *clkErrRes);
+extern int mph_close(HANDLE layer1);
+extern int wait_for_sync(HANDLE layer1, int cor, int calib, int source);
+extern int follow_bcch(HANDLE layer1);
+extern int follow_pch(HANDLE layer1);
+extern int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sapi);
+
+#endif