aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Tsou <tom@tsou.cc>2013-11-15 21:14:33 -0500
committerThomas Tsou <tom@tsou.cc>2013-11-16 01:44:07 -0500
commit85b179d125bb0b969188599f57a2f5dbb9b0151d (patch)
treea489a19f64f0e28e97008ffc814b5391f7874498
parent2e622ff131e06907f26f1eb1edd603e2f8ada118 (diff)
Transceiver52M: Create new osmo-trx executable
Create new main executable with full command line option parsing of relevant parameters. Database configuration table still exists (and must exist because of the global gConfig object), but can be bypassed with command line options. Signed-off-by: Thomas Tsou <tom@tsou.cc>
-rw-r--r--Transceiver52M/Makefile.am11
-rw-r--r--Transceiver52M/Transceiver.h3
-rw-r--r--Transceiver52M/osmo-trx.cpp412
-rw-r--r--Transceiver52M/runTransceiver.cpp231
4 files changed, 420 insertions, 237 deletions
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index 21b6e2e..d0a20f4 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -64,8 +64,7 @@ libtransceiver_la_SOURCES = \
radioInterfaceResamp.cpp \
radioInterfaceDiversity.cpp
-noinst_PROGRAMS = \
- transceiver
+bin_PROGRAMS = osmo-trx
noinst_HEADERS = \
Complex.h \
@@ -83,8 +82,8 @@ noinst_HEADERS = \
common/scale.h \
common/mult.h
-transceiver_SOURCES = runTransceiver.cpp
-transceiver_LDADD = \
+osmo_trx_SOURCES = osmo-trx.cpp
+osmo_trx_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
@@ -92,8 +91,8 @@ transceiver_LDADD = \
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
-transceiver_LDADD += $(USRP_LIBS)
+osmo_trx_LDADD += $(USRP_LIBS)
else
libtransceiver_la_SOURCES += UHDDevice.cpp
-transceiver_LDADD += $(UHD_LIBS)
+osmo_trx_LDADD += $(UHD_LIBS)
endif
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index 51b6e24..e6b9e12 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -199,6 +199,9 @@ public:
return true;
}
+ /** accessor for number of channels */
+ size_t numChans() const { return mChans; };
+
/** Codes for channel combinations */
typedef enum {
FILL, ///< Channel is transmitted, but unused
diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp
new file mode 100644
index 0000000..3135dc6
--- /dev/null
+++ b/Transceiver52M/osmo-trx.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Transceiver.h"
+#include "radioDevice.h"
+
+#include <time.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <GSMCommon.h>
+#include <Logger.h>
+#include <Configuration.h>
+
+#define CONFIGDB "/etc/OpenBTS/OpenBTS.db"
+
+/* Samples-per-symbol for downlink path
+ * 4 - Uses precision modulator (more computation, less distortion)
+ * 1 - Uses minimized modulator (less computation, more distortion)
+ *
+ * Other values are invalid. Receive path (uplink) is always
+ * downsampled to 1 sps. Default to 4 sps for all cases except for
+ * ARM and non-SIMD enabled architectures.
+ */
+#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
+#define DEFAULT_SPS 1
+#else
+#define DEFAULT_SPS 4
+#endif
+
+/* Default configuration parameters
+ * Note that these values are only used if the particular key does not
+ * exist in the configuration database. IP port and address values will
+ * typically be overwritten by the OpenBTS.db values. Other values will
+ * not be in the database by default.
+ */
+#define DEFAULT_TRX_PORT 5700
+#define DEFAULT_TRX_IP "127.0.0.1"
+#define DEFAULT_EXTREF false
+#define DEFAULT_DIVERSITY false
+#define DEFAULT_CHANS 1
+
+struct trx_config {
+ std::string log_level;
+ std::string addr;
+ std::string dev_args;
+ unsigned port;
+ unsigned sps;
+ unsigned chans;
+ bool extref;
+ bool diversity;
+};
+
+ConfigurationTable gConfig(CONFIGDB);
+
+volatile bool gshutdown = false;
+
+/* Run sanity check on configuration table
+ * The global table constructor cannot provide notification in the
+ * event of failure. Make sure that we can open the database file,
+ * write to it, and that it contains the bare minimum required keys.
+ */
+bool testConfig(const char *filename)
+{
+ int rc, val = 9999;
+ sqlite3 *db;
+ std::string test = "asldfkjsaldkf";
+ const char *key = "Log.Level";
+
+ /* Try to open the database */
+ rc = sqlite3_open(filename, &db);
+ if (rc || !db) {
+ std::cerr << std::endl;
+ std::cerr << "Config: Database could not be opened - "
+ << "check that database exists with read access"
+ << std::endl;
+ return false;
+ } else {
+ sqlite3_close(db);
+ }
+
+ /* Attempt to set a test value in the global config */
+ if (!gConfig.set(test, val)) {
+ std::cerr << std::endl;
+ std::cerr << "Config: Failed to set test key - "
+ << "permission to write to database directory?"
+ << std::endl;
+ return false;
+ } else {
+ gConfig.remove(test);
+ }
+
+ /* Attempt to query */
+ try {
+ gConfig.getStr(key);
+ } catch (...) {
+ std::cerr << std::endl;
+ std::cerr << "Config: Failed query required key " << key
+ << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+/* Setup configuration values
+ * Don't query the existence of the Log.Level because it's a
+ * mandatory value. That is, if it doesn't exist, the configuration
+ * table will crash or will have already crashed. So we check for
+ * it in the initial database sanity check. Everything else we can
+ * survive without and use default values if the database entries
+ * are empty.
+ */
+bool trx_setup_config(struct trx_config *config)
+{
+ std::string refstr, divstr;
+
+ if (!testConfig(CONFIGDB))
+ return false;
+
+ if (config->log_level == "")
+ config->log_level = gConfig.getStr("Log.Level");
+
+ if (!config->port) {
+ if (gConfig.defines("TRX.Port"))
+ config->port = gConfig.getNum("TRX.Port");
+ else
+ config->port = DEFAULT_TRX_PORT;
+ }
+
+ if (config->addr == "") {
+ if (gConfig.defines("TRX.IP"))
+ config->addr = gConfig.getStr("TRX.IP");
+ else
+ config->addr = DEFAULT_TRX_IP;
+ }
+
+ if (!config->extref) {
+ if (gConfig.defines("TRX.Reference"))
+ config->extref = gConfig.getNum("TRX.Reference");
+ else
+ config->extref = DEFAULT_EXTREF;
+ }
+
+ if (!config->diversity) {
+ if (gConfig.defines("TRX.Diversity"))
+ config->diversity = gConfig.getNum("TRX.Diversity");
+ else
+ config->diversity = DEFAULT_DIVERSITY;
+ }
+
+ if (!config->sps)
+ config->sps = DEFAULT_SPS;
+
+ if (!config->chans)
+ config->chans = DEFAULT_CHANS;
+
+ /* Diversity only supported on 2 channels */
+ if (config->diversity)
+ config->chans = 2;
+
+ refstr = config->extref ? "Enabled" : "Disabled";
+ divstr = config->diversity ? "Enabled" : "Disabled";
+
+ std::ostringstream ost("");
+ ost << "Config Settings" << std::endl;
+ ost << " Log Level............... " << config->log_level << std::endl;
+ ost << " Device args............. " << config->dev_args << std::endl;
+ ost << " TRX Base Port........... " << config->port << std::endl;
+ ost << " TRX Address............. " << config->addr << std::endl;
+ ost << " Channels................ " << config->chans << std::endl;
+ ost << " Samples-per-Symbol...... " << config->sps << std::endl;
+ ost << " External Reference...... " << refstr << std::endl;
+ ost << " Diversity............... " << divstr << std::endl;
+ std::cout << ost << std::endl;
+
+ return true;
+}
+
+/* Create radio interface
+ * The interface consists of sample rate changes, frequency shifts,
+ * channel multiplexing, and other conversions. The transceiver core
+ * accepts input vectors sampled at multiples of the GSM symbol rate.
+ * The radio interface connects the main transceiver with the device
+ * object, which may be operating some other rate.
+ */
+RadioInterface *makeRadioInterface(struct trx_config *config,
+ RadioDevice *usrp, int type)
+{
+ RadioInterface *radio = NULL;
+
+ switch (type) {
+ case RadioDevice::NORMAL:
+ radio = new RadioInterface(usrp, config->sps, config->chans);
+ break;
+ case RadioDevice::RESAMP_64M:
+ case RadioDevice::RESAMP_100M:
+ radio = new RadioInterfaceResamp(usrp,
+ config->sps, config->chans);
+ break;
+ case RadioDevice::DIVERSITY:
+ radio = new RadioInterfaceDiversity(usrp,
+ config->sps, config->chans);
+ break;
+ default:
+ LOG(ALERT) << "Unsupported radio interface configuration";
+ return NULL;
+ }
+
+ if (!radio->init(type)) {
+ LOG(ALERT) << "Failed to initialize radio interface";
+ return NULL;
+ }
+
+ return radio;
+}
+
+/* Create transceiver core
+ * The multi-threaded modem core operates at multiples of the GSM rate of
+ * 270.8333 ksps and consists of GSM specific modulation, demodulation,
+ * and decoding schemes. Also included are the socket interfaces for
+ * connecting to the upper layer stack.
+ */
+Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
+{
+ Transceiver *trx;
+ VectorFIFO *fifo;
+
+ trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
+ config->chans, GSM::Time(3,0), radio);
+ if (!trx->init()) {
+ LOG(ALERT) << "Failed to initialize transceiver";
+ delete trx;
+ return NULL;
+ }
+
+ for (size_t i = 0; i < config->chans; i++) {
+ fifo = radio->receiveFIFO(i);
+ if (fifo && trx->receiveFIFO(fifo, i))
+ continue;
+
+ LOG(ALERT) << "Could not attach FIFO to channel " << i;
+ delete trx;
+ return NULL;
+ }
+
+ return trx;
+}
+
+static void sig_handler(int signo)
+{
+ fprintf(stdout, "Received shutdown signal");
+ gshutdown = true;
+}
+
+static void setup_signal_handlers()
+{
+ if (signal(SIGINT, sig_handler) == SIG_ERR) {
+ fprintf(stderr, "Failed to install SIGINT signal handler\n");
+ exit(EXIT_FAILURE);
+ }
+ if (signal(SIGTERM, sig_handler) == SIG_ERR) {
+ fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
+ exit( EXIT_FAILURE);
+ }
+}
+
+static void print_help()
+{
+ fprintf(stdout, "Options:\n"
+ " -h This text\n"
+ " -a UHD device args\n"
+ " -l Logging level (%s)\n"
+ " -i IP address of GSM core\n"
+ " -p Base port number\n"
+ " -d Enable dual channel diversity receiver\n"
+ " -x Enable external 10 MHz reference\n"
+ " -s Samples-per-symbol (1 or 4)\n"
+ " -c Number of ARFCN channels (default=1)\n",
+ "EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
+}
+
+static void handle_options(int argc, char **argv, struct trx_config *config)
+{
+ int option;
+
+ config->port = 0;
+ config->sps = 0;
+ config->chans = 0;
+ config->extref = false;
+ config->diversity = false;
+
+ while ((option = getopt(argc, argv, "ha:l:i:p:c:dxs:")) != -1) {
+ switch (option) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 'a':
+ config->dev_args = optarg;
+ break;
+ case 'l':
+ config->log_level = optarg;
+ break;
+ case 'i':
+ config->addr = optarg;
+ break;
+ case 'p':
+ config->port = atoi(optarg);
+ break;
+ case 'c':
+ config->chans = atoi(optarg);
+ break;
+ case 'd':
+ config->diversity = true;
+ break;
+ case 'x':
+ config->extref = true;
+ break;
+ case 's':
+ config->sps = atoi(optarg);
+ if ((config->sps != 1) && (config->sps != 4)) {
+ printf("Unsupported samples-per-symbol\n\n");
+ print_help();
+ exit(0);
+ }
+ break;
+ default:
+ print_help();
+ exit(0);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int type, chans;
+ RadioDevice *usrp;
+ RadioInterface *radio = NULL;
+ Transceiver *trx = NULL;
+ struct trx_config config;
+
+ handle_options(argc, argv, &config);
+
+ setup_signal_handlers();
+
+ /* Check database sanity */
+ if (!trx_setup_config(&config)) {
+ std::cerr << "Config: Database failure - exiting" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
+
+ srandom(time(NULL));
+
+ /* Create the low level device object */
+ usrp = RadioDevice::make(config.sps, config.chans, config.diversity);
+ type = usrp->open(config.dev_args, config.extref);
+ if (type < 0) {
+ LOG(ALERT) << "Failed to create radio device" << std::endl;
+ goto shutdown;
+ }
+
+ /* Setup the appropriate device interface */
+ radio = makeRadioInterface(&config, usrp, type);
+ if (!radio)
+ goto shutdown;
+
+ /* Create the transceiver core */
+ trx = makeTransceiver(&config, radio);
+ if (!trx)
+ goto shutdown;
+
+ trx->start();
+
+ chans = trx->numChans();
+ std::cout << "-- Transceiver active with "
+ << chans << " channel(s)" << std::endl;
+
+ while (!gshutdown)
+ sleep(1);
+
+shutdown:
+ std::cout << "Shutting down transceiver..." << std::endl;
+
+ delete trx;
+ delete radio;
+ delete usrp;
+
+ return 0;
+}
diff --git a/Transceiver52M/runTransceiver.cpp b/Transceiver52M/runTransceiver.cpp
deleted file mode 100644
index a2267e2..0000000
--- a/Transceiver52M/runTransceiver.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
-* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
-* Copyright 2010 Kestrel Signal Processing, Inc.
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- 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/>.
-
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "Transceiver.h"
-#include "radioDevice.h"
-
-#include <time.h>
-#include <signal.h>
-
-#include <GSMCommon.h>
-#include <Logger.h>
-#include <Configuration.h>
-
-#define CONFIGDB "/etc/OpenBTS/OpenBTS.db"
-
-/* Samples-per-symbol for downlink path
- * 4 - Uses precision modulator (more computation, less distortion)
- * 1 - Uses minimized modulator (less computation, more distortion)
- *
- * Other values are invalid. Receive path (uplink) is always
- * downsampled to 1 sps. Default to 4 sps for all cases except for
- * ARM and non-SIMD enabled architectures.
- */
-#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
-#define SPS 1
-#else
-#define SPS 4
-#endif
-
-ConfigurationTable gConfig(CONFIGDB);
-
-volatile bool gbShutdown = false;
-
-static void ctrlCHandler(int signo)
-{
- std::cout << "Received shutdown signal" << std::endl;
- gbShutdown = true;
-}
-
-/*
- * Attempt to open and test the database file before
- * accessing the configuration table. We do this because
- * the global table constructor cannot provide notification
- * in the event of failure.
- */
-int testConfig(const char *filename)
-{
- int rc, val = 9999;
- sqlite3 *db;
- std::string test = "sadf732zdvj2";
-
- const char *keys[3] = {
- "Log.Level",
- "TRX.Port",
- "TRX.IP",
- };
-
- /* Try to open the database */
- rc = sqlite3_open(filename, &db);
- if (rc || !db) {
- std::cerr << "Config: Database could not be opened" << std::endl;
- return -1;
- } else {
- sqlite3_close(db);
- }
-
- /* Attempt to set a value in the global config */
- if (!gConfig.set(test, val)) {
- std::cerr << "Config: Failed to set test key - "
- << "permission to access the database?" << std::endl;
- return -1;
- } else {
- gConfig.remove(test);
- }
-
- /* Attempt to query */
- for (int i = 0; i < 3; i++) {
- try {
- gConfig.getStr(keys[i]);
- } catch (...) {
- std::cerr << "Config: Failed query on " << keys[i] << std::endl;
- return -1;
- }
- }
-
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- int trxPort, radioType, chans = 1, extref = 0, fail = 0, diversity = 0;
- std::string logLevel, trxAddr, deviceArgs = "";
- RadioDevice *usrp = NULL;
- RadioInterface *radio = NULL;
- Transceiver *trx = NULL;
- VectorFIFO *fifo = NULL;
-
- if (argc == 3) {
- deviceArgs = std::string(argv[2]);
- chans = atoi(argv[1]);
- } else if (argc == 2) {
- chans = atoi(argv[1]);
- } else if (argc != 1) {
- std::cout << argv[0] << " <number of channels> <device args>" << std::endl;
- }
-
- if (signal(SIGINT, ctrlCHandler) == SIG_ERR) {
- std::cerr << "Couldn't install signal handler for SIGINT" << std::endl;
- return EXIT_FAILURE;
- }
-
- if (signal(SIGTERM, ctrlCHandler) == SIG_ERR) {
- std::cerr << "Couldn't install signal handler for SIGTERM" << std::endl;
- return EXIT_FAILURE;
- }
-
- // Configure logger.
- if (testConfig(CONFIGDB) < 0) {
- std::cerr << "Config: Database failure" << std::endl;
- return EXIT_FAILURE;
- }
-
- logLevel = gConfig.getStr("Log.Level");
- trxPort = gConfig.getNum("TRX.Port");
- trxAddr = gConfig.getStr("TRX.IP");
-
- if (gConfig.defines("TRX.Reference"))
- extref = gConfig.getNum("TRX.Reference");
-
- if (extref)
- std::cout << "Using external clock reference" << std::endl;
- else
- std::cout << "Using internal clock reference" << std::endl;
-
- gLogInit("transceiver", logLevel.c_str(), LOG_LOCAL7);
-
- srandom(time(NULL));
-
- if (diversity)
- chans = 2;
-
- usrp = RadioDevice::make(SPS, chans, diversity);
- radioType = usrp->open(deviceArgs, extref);
- if (radioType < 0) {
- LOG(ALERT) << "Transceiver exiting..." << std::endl;
- return EXIT_FAILURE;
- }
-
- switch (radioType) {
- case RadioDevice::NORMAL:
- radio = new RadioInterface(usrp, SPS, chans);
- break;
- case RadioDevice::RESAMP_64M:
- case RadioDevice::RESAMP_100M:
- radio = new RadioInterfaceResamp(usrp, SPS, chans);
- break;
- case RadioDevice::DIVERSITY:
- radio = new RadioInterfaceDiversity(usrp, SPS, chans);
- break;
- default:
- LOG(ALERT) << "Unsupported configuration";
- fail = 1;
- goto shutdown;
- }
- if (!radio->init(radioType)) {
- LOG(ALERT) << "Failed to initialize radio interface";
- fail = 1;
- goto shutdown;
- }
-
- trx = new Transceiver(trxPort, trxAddr.c_str(),
- SPS, chans, GSM::Time(3,0), radio);
- if (!trx->init()) {
- LOG(ALERT) << "Failed to initialize transceiver";
- fail = 1;
- goto shutdown;
- }
-
- for (int i = 0; i < chans; i++) {
- fifo = radio->receiveFIFO(i);
- if (fifo && trx->receiveFIFO(fifo, i))
- continue;
-
- LOG(ALERT) << "Could not attach FIFO to channel " << i;
- fail = 1;
- goto shutdown;
- }
-
- trx->start();
-
- while (!gbShutdown)
- sleep(1);
-
-shutdown:
- std::cout << "Shutting down transceiver..." << std::endl;
-
- delete trx;
- delete radio;
- delete usrp;
-
- if (fail)
- return EXIT_FAILURE;
-
- return 0;
-}