blob: 3026f554c20aafc9c2b91538feae21f238464b70 [file] [log] [blame]
Thomas Tsou85b179d2013-11-15 21:14:33 -05001/*
2 * Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include "Transceiver.h"
24#include "radioDevice.h"
25
26#include <time.h>
27#include <signal.h>
28#include <stdlib.h>
29#include <unistd.h>
30
31#include <GSMCommon.h>
32#include <Logger.h>
33#include <Configuration.h>
34
Thomas Tsou85b179d2013-11-15 21:14:33 -050035/* Samples-per-symbol for downlink path
36 * 4 - Uses precision modulator (more computation, less distortion)
37 * 1 - Uses minimized modulator (less computation, more distortion)
38 *
39 * Other values are invalid. Receive path (uplink) is always
40 * downsampled to 1 sps. Default to 4 sps for all cases except for
41 * ARM and non-SIMD enabled architectures.
42 */
43#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
Tom Tsou5cd70dc2016-03-06 01:28:40 -080044#define DEFAULT_TX_SPS 1
Thomas Tsou85b179d2013-11-15 21:14:33 -050045#else
Tom Tsou5cd70dc2016-03-06 01:28:40 -080046#define DEFAULT_TX_SPS 4
Thomas Tsou85b179d2013-11-15 21:14:33 -050047#endif
48
Tom Tsou5cd70dc2016-03-06 01:28:40 -080049/*
50 * Samples-per-symbol for uplink (receiver) path
51 * Do not modify this value. EDGE configures 4 sps automatically on
52 * B200/B210 devices only. Use of 4 sps on the receive path for other
53 * configurations is not supported.
54 */
55#define DEFAULT_RX_SPS 1
56
Thomas Tsou85b179d2013-11-15 21:14:33 -050057/* Default configuration parameters
58 * Note that these values are only used if the particular key does not
59 * exist in the configuration database. IP port and address values will
60 * typically be overwritten by the OpenBTS.db values. Other values will
61 * not be in the database by default.
62 */
63#define DEFAULT_TRX_PORT 5700
64#define DEFAULT_TRX_IP "127.0.0.1"
65#define DEFAULT_EXTREF false
66#define DEFAULT_DIVERSITY false
67#define DEFAULT_CHANS 1
68
69struct trx_config {
70 std::string log_level;
71 std::string addr;
72 std::string dev_args;
73 unsigned port;
Tom Tsou5cd70dc2016-03-06 01:28:40 -080074 unsigned tx_sps;
75 unsigned rx_sps;
Thomas Tsou85b179d2013-11-15 21:14:33 -050076 unsigned chans;
Tom Tsou64ad7122015-05-19 18:26:31 -070077 unsigned rtsc;
Thomas Tsou85b179d2013-11-15 21:14:33 -050078 bool extref;
Alexander Chemerisf5fd5782015-05-24 18:56:51 -040079 Transceiver::FillerType filler;
Thomas Tsou85b179d2013-11-15 21:14:33 -050080 bool diversity;
Thomas Tsou8e17df72014-03-06 14:16:11 -050081 double offset;
Alexander Chemerise8905a02015-06-03 23:47:56 -040082 double rssi_offset;
Alexander Chemeris50747dc2015-06-07 01:07:45 -040083 bool swap_channels;
Tom Tsoub0aefcb2016-03-06 03:44:34 -080084 bool edge;
Thomas Tsou85b179d2013-11-15 21:14:33 -050085};
86
Thomas Tsou4de70be2013-11-17 18:54:52 -050087ConfigurationTable gConfig;
Thomas Tsou85b179d2013-11-15 21:14:33 -050088
89volatile bool gshutdown = false;
90
91/* Run sanity check on configuration table
92 * The global table constructor cannot provide notification in the
Thomas Tsou4de70be2013-11-17 18:54:52 -050093 * event of failure. Make sure that we can access the database,
Thomas Tsou85b179d2013-11-15 21:14:33 -050094 * write to it, and that it contains the bare minimum required keys.
95 */
Thomas Tsou4de70be2013-11-17 18:54:52 -050096bool testConfig()
Thomas Tsou85b179d2013-11-15 21:14:33 -050097{
Thomas Tsou4de70be2013-11-17 18:54:52 -050098 int val = 9999;
Thomas Tsou85b179d2013-11-15 21:14:33 -050099 std::string test = "asldfkjsaldkf";
100 const char *key = "Log.Level";
101
Thomas Tsou85b179d2013-11-15 21:14:33 -0500102 /* Attempt to query */
103 try {
104 gConfig.getStr(key);
105 } catch (...) {
106 std::cerr << std::endl;
107 std::cerr << "Config: Failed query required key " << key
108 << std::endl;
109 return false;
110 }
111
Thomas Tsou4de70be2013-11-17 18:54:52 -0500112 /* Attempt to set a test value in the global config */
113 if (!gConfig.set(test, val)) {
114 std::cerr << std::endl;
115 std::cerr << "Config: Failed to set test key" << std::endl;
116 return false;
117 } else {
118 gConfig.remove(test);
119 }
120
Thomas Tsou85b179d2013-11-15 21:14:33 -0500121 return true;
122}
123
Thomas Tsou4de70be2013-11-17 18:54:52 -0500124
Thomas Tsou85b179d2013-11-15 21:14:33 -0500125/* Setup configuration values
126 * Don't query the existence of the Log.Level because it's a
127 * mandatory value. That is, if it doesn't exist, the configuration
Thomas Tsou4de70be2013-11-17 18:54:52 -0500128 * table will crash or will have already crashed. Everything else we
129 * can survive without and use default values if the database entries
Thomas Tsou85b179d2013-11-15 21:14:33 -0500130 * are empty.
131 */
132bool trx_setup_config(struct trx_config *config)
133{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800134 std::string refstr, fillstr, divstr, edgestr;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500135
Thomas Tsou4de70be2013-11-17 18:54:52 -0500136 if (!testConfig())
Thomas Tsou85b179d2013-11-15 21:14:33 -0500137 return false;
138
139 if (config->log_level == "")
140 config->log_level = gConfig.getStr("Log.Level");
141
142 if (!config->port) {
143 if (gConfig.defines("TRX.Port"))
144 config->port = gConfig.getNum("TRX.Port");
145 else
146 config->port = DEFAULT_TRX_PORT;
147 }
148
149 if (config->addr == "") {
150 if (gConfig.defines("TRX.IP"))
151 config->addr = gConfig.getStr("TRX.IP");
152 else
153 config->addr = DEFAULT_TRX_IP;
154 }
155
156 if (!config->extref) {
157 if (gConfig.defines("TRX.Reference"))
158 config->extref = gConfig.getNum("TRX.Reference");
159 else
160 config->extref = DEFAULT_EXTREF;
161 }
162
163 if (!config->diversity) {
164 if (gConfig.defines("TRX.Diversity"))
165 config->diversity = gConfig.getNum("TRX.Diversity");
166 else
167 config->diversity = DEFAULT_DIVERSITY;
168 }
169
Thomas Tsou85b179d2013-11-15 21:14:33 -0500170 /* Diversity only supported on 2 channels */
171 if (config->diversity)
172 config->chans = 2;
173
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800174 edgestr = config->edge ? "Enabled" : "Disabled";
Thomas Tsou85b179d2013-11-15 21:14:33 -0500175 refstr = config->extref ? "Enabled" : "Disabled";
176 divstr = config->diversity ? "Enabled" : "Disabled";
Alexander Chemerisf5fd5782015-05-24 18:56:51 -0400177 switch (config->filler) {
178 case Transceiver::FILLER_DUMMY:
179 fillstr = "Dummy bursts";
180 break;
181 case Transceiver::FILLER_ZERO:
182 fillstr = "Disabled";
183 break;
Tom Tsouaf717b22016-03-06 22:19:15 -0800184 case Transceiver::FILLER_NORM_RAND:
Alexander Chemerisf5fd5782015-05-24 18:56:51 -0400185 fillstr = "Normal busrts with random payload";
186 break;
Tom Tsouaf717b22016-03-06 22:19:15 -0800187 case Transceiver::FILLER_EDGE_RAND:
188 fillstr = "EDGE busrts with random payload";
189 break;
Alexander Chemerisf5fd5782015-05-24 18:56:51 -0400190 }
Thomas Tsou85b179d2013-11-15 21:14:33 -0500191
192 std::ostringstream ost("");
193 ost << "Config Settings" << std::endl;
194 ost << " Log Level............... " << config->log_level << std::endl;
195 ost << " Device args............. " << config->dev_args << std::endl;
196 ost << " TRX Base Port........... " << config->port << std::endl;
197 ost << " TRX Address............. " << config->addr << std::endl;
198 ost << " Channels................ " << config->chans << std::endl;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800199 ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800200 ost << " EDGE support............ " << edgestr << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500201 ost << " External Reference...... " << refstr << std::endl;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500202 ost << " C0 Filler Table......... " << fillstr << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500203 ost << " Diversity............... " << divstr << std::endl;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500204 ost << " Tuning offset........... " << config->offset << std::endl;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400205 ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400206 ost << " Swap channels........... " << config->swap_channels << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500207 std::cout << ost << std::endl;
208
209 return true;
210}
211
212/* Create radio interface
213 * The interface consists of sample rate changes, frequency shifts,
214 * channel multiplexing, and other conversions. The transceiver core
215 * accepts input vectors sampled at multiples of the GSM symbol rate.
216 * The radio interface connects the main transceiver with the device
217 * object, which may be operating some other rate.
218 */
219RadioInterface *makeRadioInterface(struct trx_config *config,
220 RadioDevice *usrp, int type)
221{
222 RadioInterface *radio = NULL;
223
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800224 if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) {
225 LOG(ALERT) << "Unsupported radio interface configuration";
226 }
227
Thomas Tsou85b179d2013-11-15 21:14:33 -0500228 switch (type) {
229 case RadioDevice::NORMAL:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800230 radio = new RadioInterface(usrp, config->tx_sps,
231 config->rx_sps, config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500232 break;
233 case RadioDevice::RESAMP_64M:
234 case RadioDevice::RESAMP_100M:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800235 radio = new RadioInterfaceResamp(usrp, config->tx_sps,
236 config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500237 break;
238 case RadioDevice::DIVERSITY:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800239 radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
240 config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500241 break;
242 default:
243 LOG(ALERT) << "Unsupported radio interface configuration";
244 return NULL;
245 }
246
247 if (!radio->init(type)) {
248 LOG(ALERT) << "Failed to initialize radio interface";
249 return NULL;
250 }
251
252 return radio;
253}
254
255/* Create transceiver core
256 * The multi-threaded modem core operates at multiples of the GSM rate of
257 * 270.8333 ksps and consists of GSM specific modulation, demodulation,
258 * and decoding schemes. Also included are the socket interfaces for
259 * connecting to the upper layer stack.
260 */
261Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
262{
263 Transceiver *trx;
264 VectorFIFO *fifo;
265
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800266 trx = new Transceiver(config->port, config->addr.c_str(),
267 config->tx_sps, config->rx_sps, config->chans,
268 GSM::Time(3,0), radio, config->rssi_offset);
Tom Tsou64ad7122015-05-19 18:26:31 -0700269 if (!trx->init(config->filler, config->rtsc)) {
Thomas Tsou85b179d2013-11-15 21:14:33 -0500270 LOG(ALERT) << "Failed to initialize transceiver";
271 delete trx;
272 return NULL;
273 }
274
275 for (size_t i = 0; i < config->chans; i++) {
276 fifo = radio->receiveFIFO(i);
277 if (fifo && trx->receiveFIFO(fifo, i))
278 continue;
279
280 LOG(ALERT) << "Could not attach FIFO to channel " << i;
281 delete trx;
282 return NULL;
283 }
284
285 return trx;
286}
287
288static void sig_handler(int signo)
289{
290 fprintf(stdout, "Received shutdown signal");
291 gshutdown = true;
292}
293
294static void setup_signal_handlers()
295{
296 if (signal(SIGINT, sig_handler) == SIG_ERR) {
297 fprintf(stderr, "Failed to install SIGINT signal handler\n");
298 exit(EXIT_FAILURE);
299 }
300 if (signal(SIGTERM, sig_handler) == SIG_ERR) {
301 fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
302 exit( EXIT_FAILURE);
303 }
304}
305
306static void print_help()
307{
308 fprintf(stdout, "Options:\n"
309 " -h This text\n"
310 " -a UHD device args\n"
311 " -l Logging level (%s)\n"
312 " -i IP address of GSM core\n"
313 " -p Base port number\n"
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800314 " -e Enable EDGE receiver\n"
Thomas Tsou85b179d2013-11-15 21:14:33 -0500315 " -d Enable dual channel diversity receiver\n"
316 " -x Enable external 10 MHz reference\n"
317 " -s Samples-per-symbol (1 or 4)\n"
Thomas Tsou15d743e2014-01-25 02:34:03 -0500318 " -c Number of ARFCN channels (default=1)\n"
Thomas Tsou8e17df72014-03-06 14:16:11 -0500319 " -f Enable C0 filler table\n"
Tom Tsou64ad7122015-05-19 18:26:31 -0700320 " -o Set baseband frequency offset (default=auto)\n"
Alexander Chemerise8905a02015-06-03 23:47:56 -0400321 " -r Random burst test mode with TSC\n"
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400322 " -R RSSI to dBm offset in dB (default=0)\n"
323 " -S Swap channels (UmTRX only)\n",
Thomas Tsou85b179d2013-11-15 21:14:33 -0500324 "EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
325}
326
327static void handle_options(int argc, char **argv, struct trx_config *config)
328{
329 int option;
330
331 config->port = 0;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800332 config->tx_sps = DEFAULT_TX_SPS;
333 config->rx_sps = DEFAULT_RX_SPS;
Tom Tsou64ad7122015-05-19 18:26:31 -0700334 config->chans = DEFAULT_CHANS;
335 config->rtsc = 0;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500336 config->extref = false;
Tom Tsou64ad7122015-05-19 18:26:31 -0700337 config->filler = Transceiver::FILLER_ZERO;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500338 config->diversity = false;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500339 config->offset = 0.0;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400340 config->rssi_offset = 0.0;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400341 config->swap_channels = false;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800342 config->edge = false;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500343
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800344 while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:Se")) != -1) {
Thomas Tsou85b179d2013-11-15 21:14:33 -0500345 switch (option) {
346 case 'h':
347 print_help();
348 exit(0);
349 break;
350 case 'a':
351 config->dev_args = optarg;
352 break;
353 case 'l':
354 config->log_level = optarg;
355 break;
356 case 'i':
357 config->addr = optarg;
358 break;
359 case 'p':
360 config->port = atoi(optarg);
361 break;
362 case 'c':
363 config->chans = atoi(optarg);
364 break;
365 case 'd':
366 config->diversity = true;
367 break;
368 case 'x':
369 config->extref = true;
370 break;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500371 case 'f':
Tom Tsou64ad7122015-05-19 18:26:31 -0700372 config->filler = Transceiver::FILLER_DUMMY;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500373 break;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500374 case 'o':
375 config->offset = atof(optarg);
376 break;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500377 case 's':
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800378 config->tx_sps = atoi(optarg);
Tom Tsou64ad7122015-05-19 18:26:31 -0700379 break;
380 case 'r':
381 config->rtsc = atoi(optarg);
Tom Tsouaf717b22016-03-06 22:19:15 -0800382 config->filler = Transceiver::FILLER_NORM_RAND;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500383 break;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400384 case 'R':
385 config->rssi_offset = atof(optarg);
386 break;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400387 case 'S':
388 config->swap_channels = true;
389 break;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800390 case 'e':
391 config->edge = true;
392 config->rx_sps = 4;
393 break;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500394 default:
395 print_help();
396 exit(0);
397 }
398 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700399
Tom Tsouaf717b22016-03-06 22:19:15 -0800400 if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
401 config->filler = Transceiver::FILLER_EDGE_RAND;
402
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800403 if ((config->tx_sps != 1) && (config->tx_sps != 4)) {
404 printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
Tom Tsou64ad7122015-05-19 18:26:31 -0700405 print_help();
406 exit(0);
407 }
408
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800409 if (config->edge && (config->tx_sps != 4)) {
410 printf("EDGE only supported at 4 samples per symbol\n\n");
411 print_help();
412 exit(0);
413 }
414
Tom Tsou64ad7122015-05-19 18:26:31 -0700415 if (config->rtsc > 7) {
416 printf("Invalid training sequence %i\n\n", config->rtsc);
417 print_help();
418 exit(0);
419 }
Thomas Tsou85b179d2013-11-15 21:14:33 -0500420}
421
422int main(int argc, char *argv[])
423{
424 int type, chans;
425 RadioDevice *usrp;
426 RadioInterface *radio = NULL;
427 Transceiver *trx = NULL;
428 struct trx_config config;
429
430 handle_options(argc, argv, &config);
431
432 setup_signal_handlers();
433
434 /* Check database sanity */
435 if (!trx_setup_config(&config)) {
436 std::cerr << "Config: Database failure - exiting" << std::endl;
437 return EXIT_FAILURE;
438 }
439
440 gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
441
442 srandom(time(NULL));
443
444 /* Create the low level device object */
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800445 usrp = RadioDevice::make(config.tx_sps, config.rx_sps, config.chans,
Thomas Tsou8e17df72014-03-06 14:16:11 -0500446 config.diversity, config.offset);
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400447 type = usrp->open(config.dev_args, config.extref, config.swap_channels);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500448 if (type < 0) {
449 LOG(ALERT) << "Failed to create radio device" << std::endl;
450 goto shutdown;
451 }
452
453 /* Setup the appropriate device interface */
454 radio = makeRadioInterface(&config, usrp, type);
455 if (!radio)
456 goto shutdown;
457
458 /* Create the transceiver core */
459 trx = makeTransceiver(&config, radio);
460 if (!trx)
461 goto shutdown;
462
Thomas Tsou85b179d2013-11-15 21:14:33 -0500463 chans = trx->numChans();
464 std::cout << "-- Transceiver active with "
465 << chans << " channel(s)" << std::endl;
466
467 while (!gshutdown)
468 sleep(1);
469
470shutdown:
471 std::cout << "Shutting down transceiver..." << std::endl;
472
473 delete trx;
474 delete radio;
475 delete usrp;
476
477 return 0;
478}