aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2012-06-12 18:12:46 +0200
committerHolger Hans Peter Freyther <zecke@selfish.org>2012-06-12 18:17:11 +0200
commita540332df3de735f75a2959814190b6cbf3c6e4c (patch)
tree46451e05a7d9680a611c9f49a458a971944484c3 /contrib
parentad3e31dc4b9ee1063d9e633ca884315b5a5c9710 (diff)
sysmobts-calib: Add a utility to calibrate the sysmobts v2 hardware
It has been tested with the OCXO and the network listen mode of the firmware. For other sources we are not required to synchronize to the network and the tool needs to be adjusted.
Diffstat (limited to 'contrib')
-rw-r--r--contrib/sysmobts-calib/Makefile9
-rw-r--r--contrib/sysmobts-calib/sysmobts-calib.c401
-rw-r--r--contrib/sysmobts-calib/sysmobts-layer1.c686
3 files changed, 1096 insertions, 0 deletions
diff --git a/contrib/sysmobts-calib/Makefile b/contrib/sysmobts-calib/Makefile
new file mode 100644
index 00000000..e6e9b6f6
--- /dev/null
+++ b/contrib/sysmobts-calib/Makefile
@@ -0,0 +1,9 @@
+CFLAGS=`pkg-config --cflags libosmocore` -Wall -Werror
+
+all: sysmobts-calib
+
+sysmobts-calib: sysmobts-calib.o sysmobts-layer1.o
+ $(CC) $(CPPFLAGS) -o $@ $^ -lrt
+
+clean:
+ @rm -f sysmobts-calib *.o
diff --git a/contrib/sysmobts-calib/sysmobts-calib.c b/contrib/sysmobts-calib/sysmobts-calib.c
new file mode 100644
index 00000000..e881b08c
--- /dev/null
+++ b/contrib/sysmobts-calib/sysmobts-calib.c
@@ -0,0 +1,401 @@
+/* OCXO/TCXO based calibration utility */
+
+/*
+ * (C) 2012 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>
+
+#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof((ar)[0]))
+
+enum actions {
+ ACTION_SCAN,
+ ACTION_CALIB,
+};
+
+static const char *modes[] = {
+ [ACTION_SCAN] = "scan",
+ [ACTION_CALIB] = "calibrate",
+};
+
+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 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\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 = atoi(optarg);
+ 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) {
+ 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);
+ }
+ }
+}
+
+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 set_clock_cor(int clock_corr, int calib, int source);
+extern int rf_clock_info(HANDLE *layer1, int *clkErr, int *clkErrRes);
+extern int mph_close(HANDLE layer1);
+extern int wait_for_sync(HANDLE layer1, int cor, int calib, int source);
+
+#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; \
+ }
+
+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_clock_after_sync(HANDLE *layer1)
+{
+ 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(layer1, &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(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(&layer1);
+
+ 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
+ return calib_clock();
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/sysmobts-calib/sysmobts-layer1.c b/contrib/sysmobts-calib/sysmobts-layer1.c
new file mode 100644
index 00000000..d886997f
--- /dev/null
+++ b/contrib/sysmobts-calib/sysmobts-layer1.c
@@ -0,0 +1,686 @@
+/* Layer1 handling for the DSP/FPGA */
+/*
+ * (C) 2012 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>
+
+#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
+ printf("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,
+ prim.u.systemInfoCnf.boardVersion.rev,
+ prim.u.systemInfoCnf.boardVersion.option);
+#undef INFO_DSP
+#undef INFO_FPGA
+ 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;
+
+ prim.u.activateRfReq.rfRx.iClkCor = initial_cor;
+ prim.u.activateRfReq.rfRx.clkSrc = clock_source;
+
+ 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_V;
+ 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;
+}
+
+int follow_bcch(HANDLE layer1)
+{
+ int rc;
+ GsmL1_Prim_t prim;
+
+ /* 1.) Activate BCCH... */
+ memset(&prim, 0, sizeof(prim));
+ prim.u.mphActivateReq.hLayer1 = layer1;
+ prim.u.mphActivateReq.u8Tn = 0;
+ prim.u.mphActivateReq.sapi = GsmL1_Sapi_Bcch;
+ 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;
+ }
+
+ /* 3.) Wait for PhDataInd... */
+ printf("Waiting for BCCH data.\n");
+ rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
+ if (rc != 0) {
+ printf("Didn't get BCCH data.\n");
+ return rc;
+ }
+
+ 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;
+ prim.u.rfClockSetupReq.rfRx.iClkCor = clock_cor;
+ prim.u.rfClockSetupReq.rfRx.clkSrc = calib;
+ 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(HANDLE *layer1, 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;
+}
+
+/**
+ * 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;
+}