blob: 5866f0996954528435f65fc2f9fa6b9e864ec696 [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 Chemeris5efe0502016-03-23 17:06:32 +0300190 case Transceiver::FILLER_ACCESS_RAND:
191 fillstr = "Access busrts with random payload";
192 break;
Alexander Chemerisf5fd5782015-05-24 18:56:51 -0400193 }
Thomas Tsou85b179d2013-11-15 21:14:33 -0500194
195 std::ostringstream ost("");
196 ost << "Config Settings" << std::endl;
197 ost << " Log Level............... " << config->log_level << std::endl;
198 ost << " Device args............. " << config->dev_args << std::endl;
199 ost << " TRX Base Port........... " << config->port << std::endl;
200 ost << " TRX Address............. " << config->addr << std::endl;
201 ost << " Channels................ " << config->chans << std::endl;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800202 ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
Alexander Chemeris1ab5e7f2016-04-20 08:44:55 +0300203 ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800204 ost << " EDGE support............ " << edgestr << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500205 ost << " External Reference...... " << refstr << std::endl;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500206 ost << " C0 Filler Table......... " << fillstr << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500207 ost << " Diversity............... " << divstr << std::endl;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500208 ost << " Tuning offset........... " << config->offset << std::endl;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400209 ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400210 ost << " Swap channels........... " << config->swap_channels << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500211 std::cout << ost << std::endl;
212
213 return true;
214}
215
216/* Create radio interface
217 * The interface consists of sample rate changes, frequency shifts,
218 * channel multiplexing, and other conversions. The transceiver core
219 * accepts input vectors sampled at multiples of the GSM symbol rate.
220 * The radio interface connects the main transceiver with the device
221 * object, which may be operating some other rate.
222 */
223RadioInterface *makeRadioInterface(struct trx_config *config,
224 RadioDevice *usrp, int type)
225{
226 RadioInterface *radio = NULL;
227
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800228 if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) {
229 LOG(ALERT) << "Unsupported radio interface configuration";
230 }
231
Thomas Tsou85b179d2013-11-15 21:14:33 -0500232 switch (type) {
233 case RadioDevice::NORMAL:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800234 radio = new RadioInterface(usrp, config->tx_sps,
235 config->rx_sps, config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500236 break;
237 case RadioDevice::RESAMP_64M:
238 case RadioDevice::RESAMP_100M:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800239 radio = new RadioInterfaceResamp(usrp, config->tx_sps,
240 config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500241 break;
242 case RadioDevice::DIVERSITY:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800243 radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
244 config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500245 break;
246 default:
247 LOG(ALERT) << "Unsupported radio interface configuration";
248 return NULL;
249 }
250
251 if (!radio->init(type)) {
252 LOG(ALERT) << "Failed to initialize radio interface";
253 return NULL;
254 }
255
256 return radio;
257}
258
259/* Create transceiver core
260 * The multi-threaded modem core operates at multiples of the GSM rate of
261 * 270.8333 ksps and consists of GSM specific modulation, demodulation,
262 * and decoding schemes. Also included are the socket interfaces for
263 * connecting to the upper layer stack.
264 */
265Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
266{
267 Transceiver *trx;
268 VectorFIFO *fifo;
269
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800270 trx = new Transceiver(config->port, config->addr.c_str(),
271 config->tx_sps, config->rx_sps, config->chans,
272 GSM::Time(3,0), radio, config->rssi_offset);
Tom Tsou64ad7122015-05-19 18:26:31 -0700273 if (!trx->init(config->filler, config->rtsc)) {
Thomas Tsou85b179d2013-11-15 21:14:33 -0500274 LOG(ALERT) << "Failed to initialize transceiver";
275 delete trx;
276 return NULL;
277 }
278
279 for (size_t i = 0; i < config->chans; i++) {
280 fifo = radio->receiveFIFO(i);
281 if (fifo && trx->receiveFIFO(fifo, i))
282 continue;
283
284 LOG(ALERT) << "Could not attach FIFO to channel " << i;
285 delete trx;
286 return NULL;
287 }
288
289 return trx;
290}
291
292static void sig_handler(int signo)
293{
294 fprintf(stdout, "Received shutdown signal");
295 gshutdown = true;
296}
297
298static void setup_signal_handlers()
299{
300 if (signal(SIGINT, sig_handler) == SIG_ERR) {
301 fprintf(stderr, "Failed to install SIGINT signal handler\n");
302 exit(EXIT_FAILURE);
303 }
304 if (signal(SIGTERM, sig_handler) == SIG_ERR) {
305 fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
306 exit( EXIT_FAILURE);
307 }
308}
309
310static void print_help()
311{
312 fprintf(stdout, "Options:\n"
313 " -h This text\n"
314 " -a UHD device args\n"
315 " -l Logging level (%s)\n"
316 " -i IP address of GSM core\n"
317 " -p Base port number\n"
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800318 " -e Enable EDGE receiver\n"
Thomas Tsou85b179d2013-11-15 21:14:33 -0500319 " -d Enable dual channel diversity receiver\n"
320 " -x Enable external 10 MHz reference\n"
321 " -s Samples-per-symbol (1 or 4)\n"
Thomas Tsou15d743e2014-01-25 02:34:03 -0500322 " -c Number of ARFCN channels (default=1)\n"
Thomas Tsou8e17df72014-03-06 14:16:11 -0500323 " -f Enable C0 filler table\n"
Tom Tsou64ad7122015-05-19 18:26:31 -0700324 " -o Set baseband frequency offset (default=auto)\n"
Alexander Chemerise8905a02015-06-03 23:47:56 -0400325 " -r Random burst test mode with TSC\n"
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300326 " -A Random burst test mode with Access Bursts\n"
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400327 " -R RSSI to dBm offset in dB (default=0)\n"
328 " -S Swap channels (UmTRX only)\n",
Thomas Tsou85b179d2013-11-15 21:14:33 -0500329 "EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
330}
331
332static void handle_options(int argc, char **argv, struct trx_config *config)
333{
334 int option;
335
336 config->port = 0;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800337 config->tx_sps = DEFAULT_TX_SPS;
338 config->rx_sps = DEFAULT_RX_SPS;
Tom Tsou64ad7122015-05-19 18:26:31 -0700339 config->chans = DEFAULT_CHANS;
340 config->rtsc = 0;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500341 config->extref = false;
Tom Tsou64ad7122015-05-19 18:26:31 -0700342 config->filler = Transceiver::FILLER_ZERO;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500343 config->diversity = false;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500344 config->offset = 0.0;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400345 config->rssi_offset = 0.0;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400346 config->swap_channels = false;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800347 config->edge = false;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500348
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300349 while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:AR:Se")) != -1) {
Thomas Tsou85b179d2013-11-15 21:14:33 -0500350 switch (option) {
351 case 'h':
352 print_help();
353 exit(0);
354 break;
355 case 'a':
356 config->dev_args = optarg;
357 break;
358 case 'l':
359 config->log_level = optarg;
360 break;
361 case 'i':
362 config->addr = optarg;
363 break;
364 case 'p':
365 config->port = atoi(optarg);
366 break;
367 case 'c':
368 config->chans = atoi(optarg);
369 break;
370 case 'd':
371 config->diversity = true;
372 break;
373 case 'x':
374 config->extref = true;
375 break;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500376 case 'f':
Tom Tsou64ad7122015-05-19 18:26:31 -0700377 config->filler = Transceiver::FILLER_DUMMY;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500378 break;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500379 case 'o':
380 config->offset = atof(optarg);
381 break;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500382 case 's':
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800383 config->tx_sps = atoi(optarg);
Tom Tsou64ad7122015-05-19 18:26:31 -0700384 break;
385 case 'r':
386 config->rtsc = atoi(optarg);
Tom Tsouaf717b22016-03-06 22:19:15 -0800387 config->filler = Transceiver::FILLER_NORM_RAND;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500388 break;
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300389 case 'A':
390 config->filler = Transceiver::FILLER_ACCESS_RAND;
391 break;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400392 case 'R':
393 config->rssi_offset = atof(optarg);
394 break;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400395 case 'S':
396 config->swap_channels = true;
397 break;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800398 case 'e':
399 config->edge = true;
400 config->rx_sps = 4;
401 break;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500402 default:
403 print_help();
404 exit(0);
405 }
406 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700407
Tom Tsouaf717b22016-03-06 22:19:15 -0800408 if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
409 config->filler = Transceiver::FILLER_EDGE_RAND;
410
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800411 if ((config->tx_sps != 1) && (config->tx_sps != 4)) {
412 printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
Tom Tsou64ad7122015-05-19 18:26:31 -0700413 print_help();
414 exit(0);
415 }
416
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800417 if (config->edge && (config->tx_sps != 4)) {
418 printf("EDGE only supported at 4 samples per symbol\n\n");
419 print_help();
420 exit(0);
421 }
422
Tom Tsou64ad7122015-05-19 18:26:31 -0700423 if (config->rtsc > 7) {
424 printf("Invalid training sequence %i\n\n", config->rtsc);
425 print_help();
426 exit(0);
427 }
Thomas Tsou85b179d2013-11-15 21:14:33 -0500428}
429
430int main(int argc, char *argv[])
431{
432 int type, chans;
433 RadioDevice *usrp;
434 RadioInterface *radio = NULL;
435 Transceiver *trx = NULL;
436 struct trx_config config;
437
438 handle_options(argc, argv, &config);
439
440 setup_signal_handlers();
441
442 /* Check database sanity */
443 if (!trx_setup_config(&config)) {
444 std::cerr << "Config: Database failure - exiting" << std::endl;
445 return EXIT_FAILURE;
446 }
447
448 gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
449
450 srandom(time(NULL));
451
452 /* Create the low level device object */
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800453 usrp = RadioDevice::make(config.tx_sps, config.rx_sps, config.chans,
Thomas Tsou8e17df72014-03-06 14:16:11 -0500454 config.diversity, config.offset);
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400455 type = usrp->open(config.dev_args, config.extref, config.swap_channels);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500456 if (type < 0) {
457 LOG(ALERT) << "Failed to create radio device" << std::endl;
458 goto shutdown;
459 }
460
461 /* Setup the appropriate device interface */
462 radio = makeRadioInterface(&config, usrp, type);
463 if (!radio)
464 goto shutdown;
465
466 /* Create the transceiver core */
467 trx = makeTransceiver(&config, radio);
468 if (!trx)
469 goto shutdown;
470
Thomas Tsou85b179d2013-11-15 21:14:33 -0500471 chans = trx->numChans();
472 std::cout << "-- Transceiver active with "
473 << chans << " channel(s)" << std::endl;
474
475 while (!gshutdown)
476 sleep(1);
477
478shutdown:
479 std::cout << "Shutting down transceiver..." << std::endl;
480
481 delete trx;
482 delete radio;
483 delete usrp;
484
485 return 0;
486}