blob: e7b2a16aeab5b7c691a019f53ff14fc3c70cf57b [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;
184 case Transceiver::FILLER_RAND:
185 fillstr = "Normal busrts with random payload";
186 break;
187 }
Thomas Tsou85b179d2013-11-15 21:14:33 -0500188
189 std::ostringstream ost("");
190 ost << "Config Settings" << std::endl;
191 ost << " Log Level............... " << config->log_level << std::endl;
192 ost << " Device args............. " << config->dev_args << std::endl;
193 ost << " TRX Base Port........... " << config->port << std::endl;
194 ost << " TRX Address............. " << config->addr << std::endl;
195 ost << " Channels................ " << config->chans << std::endl;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800196 ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800197 ost << " EDGE support............ " << edgestr << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500198 ost << " External Reference...... " << refstr << std::endl;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500199 ost << " C0 Filler Table......... " << fillstr << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500200 ost << " Diversity............... " << divstr << std::endl;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500201 ost << " Tuning offset........... " << config->offset << std::endl;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400202 ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400203 ost << " Swap channels........... " << config->swap_channels << std::endl;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500204 std::cout << ost << std::endl;
205
206 return true;
207}
208
209/* Create radio interface
210 * The interface consists of sample rate changes, frequency shifts,
211 * channel multiplexing, and other conversions. The transceiver core
212 * accepts input vectors sampled at multiples of the GSM symbol rate.
213 * The radio interface connects the main transceiver with the device
214 * object, which may be operating some other rate.
215 */
216RadioInterface *makeRadioInterface(struct trx_config *config,
217 RadioDevice *usrp, int type)
218{
219 RadioInterface *radio = NULL;
220
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800221 if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) {
222 LOG(ALERT) << "Unsupported radio interface configuration";
223 }
224
Thomas Tsou85b179d2013-11-15 21:14:33 -0500225 switch (type) {
226 case RadioDevice::NORMAL:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800227 radio = new RadioInterface(usrp, config->tx_sps,
228 config->rx_sps, config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500229 break;
230 case RadioDevice::RESAMP_64M:
231 case RadioDevice::RESAMP_100M:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800232 radio = new RadioInterfaceResamp(usrp, config->tx_sps,
233 config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500234 break;
235 case RadioDevice::DIVERSITY:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800236 radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
237 config->chans);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500238 break;
239 default:
240 LOG(ALERT) << "Unsupported radio interface configuration";
241 return NULL;
242 }
243
244 if (!radio->init(type)) {
245 LOG(ALERT) << "Failed to initialize radio interface";
246 return NULL;
247 }
248
249 return radio;
250}
251
252/* Create transceiver core
253 * The multi-threaded modem core operates at multiples of the GSM rate of
254 * 270.8333 ksps and consists of GSM specific modulation, demodulation,
255 * and decoding schemes. Also included are the socket interfaces for
256 * connecting to the upper layer stack.
257 */
258Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
259{
260 Transceiver *trx;
261 VectorFIFO *fifo;
262
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800263 trx = new Transceiver(config->port, config->addr.c_str(),
264 config->tx_sps, config->rx_sps, config->chans,
265 GSM::Time(3,0), radio, config->rssi_offset);
Tom Tsou64ad7122015-05-19 18:26:31 -0700266 if (!trx->init(config->filler, config->rtsc)) {
Thomas Tsou85b179d2013-11-15 21:14:33 -0500267 LOG(ALERT) << "Failed to initialize transceiver";
268 delete trx;
269 return NULL;
270 }
271
272 for (size_t i = 0; i < config->chans; i++) {
273 fifo = radio->receiveFIFO(i);
274 if (fifo && trx->receiveFIFO(fifo, i))
275 continue;
276
277 LOG(ALERT) << "Could not attach FIFO to channel " << i;
278 delete trx;
279 return NULL;
280 }
281
282 return trx;
283}
284
285static void sig_handler(int signo)
286{
287 fprintf(stdout, "Received shutdown signal");
288 gshutdown = true;
289}
290
291static void setup_signal_handlers()
292{
293 if (signal(SIGINT, sig_handler) == SIG_ERR) {
294 fprintf(stderr, "Failed to install SIGINT signal handler\n");
295 exit(EXIT_FAILURE);
296 }
297 if (signal(SIGTERM, sig_handler) == SIG_ERR) {
298 fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
299 exit( EXIT_FAILURE);
300 }
301}
302
303static void print_help()
304{
305 fprintf(stdout, "Options:\n"
306 " -h This text\n"
307 " -a UHD device args\n"
308 " -l Logging level (%s)\n"
309 " -i IP address of GSM core\n"
310 " -p Base port number\n"
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800311 " -e Enable EDGE receiver\n"
Thomas Tsou85b179d2013-11-15 21:14:33 -0500312 " -d Enable dual channel diversity receiver\n"
313 " -x Enable external 10 MHz reference\n"
314 " -s Samples-per-symbol (1 or 4)\n"
Thomas Tsou15d743e2014-01-25 02:34:03 -0500315 " -c Number of ARFCN channels (default=1)\n"
Thomas Tsou8e17df72014-03-06 14:16:11 -0500316 " -f Enable C0 filler table\n"
Tom Tsou64ad7122015-05-19 18:26:31 -0700317 " -o Set baseband frequency offset (default=auto)\n"
Alexander Chemerise8905a02015-06-03 23:47:56 -0400318 " -r Random burst test mode with TSC\n"
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400319 " -R RSSI to dBm offset in dB (default=0)\n"
320 " -S Swap channels (UmTRX only)\n",
Thomas Tsou85b179d2013-11-15 21:14:33 -0500321 "EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
322}
323
324static void handle_options(int argc, char **argv, struct trx_config *config)
325{
326 int option;
327
328 config->port = 0;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800329 config->tx_sps = DEFAULT_TX_SPS;
330 config->rx_sps = DEFAULT_RX_SPS;
Tom Tsou64ad7122015-05-19 18:26:31 -0700331 config->chans = DEFAULT_CHANS;
332 config->rtsc = 0;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500333 config->extref = false;
Tom Tsou64ad7122015-05-19 18:26:31 -0700334 config->filler = Transceiver::FILLER_ZERO;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500335 config->diversity = false;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500336 config->offset = 0.0;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400337 config->rssi_offset = 0.0;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400338 config->swap_channels = false;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800339 config->edge = false;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500340
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800341 while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:Se")) != -1) {
Thomas Tsou85b179d2013-11-15 21:14:33 -0500342 switch (option) {
343 case 'h':
344 print_help();
345 exit(0);
346 break;
347 case 'a':
348 config->dev_args = optarg;
349 break;
350 case 'l':
351 config->log_level = optarg;
352 break;
353 case 'i':
354 config->addr = optarg;
355 break;
356 case 'p':
357 config->port = atoi(optarg);
358 break;
359 case 'c':
360 config->chans = atoi(optarg);
361 break;
362 case 'd':
363 config->diversity = true;
364 break;
365 case 'x':
366 config->extref = true;
367 break;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500368 case 'f':
Tom Tsou64ad7122015-05-19 18:26:31 -0700369 config->filler = Transceiver::FILLER_DUMMY;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500370 break;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500371 case 'o':
372 config->offset = atof(optarg);
373 break;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500374 case 's':
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800375 config->tx_sps = atoi(optarg);
Tom Tsou64ad7122015-05-19 18:26:31 -0700376 break;
377 case 'r':
378 config->rtsc = atoi(optarg);
379 config->filler = Transceiver::FILLER_RAND;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500380 break;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400381 case 'R':
382 config->rssi_offset = atof(optarg);
383 break;
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400384 case 'S':
385 config->swap_channels = true;
386 break;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800387 case 'e':
388 config->edge = true;
389 config->rx_sps = 4;
390 break;
Thomas Tsou85b179d2013-11-15 21:14:33 -0500391 default:
392 print_help();
393 exit(0);
394 }
395 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700396
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800397 if ((config->tx_sps != 1) && (config->tx_sps != 4)) {
398 printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
Tom Tsou64ad7122015-05-19 18:26:31 -0700399 print_help();
400 exit(0);
401 }
402
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800403 if (config->edge && (config->tx_sps != 4)) {
404 printf("EDGE only supported at 4 samples per symbol\n\n");
405 print_help();
406 exit(0);
407 }
408
Tom Tsou64ad7122015-05-19 18:26:31 -0700409 if (config->rtsc > 7) {
410 printf("Invalid training sequence %i\n\n", config->rtsc);
411 print_help();
412 exit(0);
413 }
Thomas Tsou85b179d2013-11-15 21:14:33 -0500414}
415
416int main(int argc, char *argv[])
417{
418 int type, chans;
419 RadioDevice *usrp;
420 RadioInterface *radio = NULL;
421 Transceiver *trx = NULL;
422 struct trx_config config;
423
424 handle_options(argc, argv, &config);
425
426 setup_signal_handlers();
427
428 /* Check database sanity */
429 if (!trx_setup_config(&config)) {
430 std::cerr << "Config: Database failure - exiting" << std::endl;
431 return EXIT_FAILURE;
432 }
433
434 gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
435
436 srandom(time(NULL));
437
438 /* Create the low level device object */
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800439 usrp = RadioDevice::make(config.tx_sps, config.rx_sps, config.chans,
Thomas Tsou8e17df72014-03-06 14:16:11 -0500440 config.diversity, config.offset);
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400441 type = usrp->open(config.dev_args, config.extref, config.swap_channels);
Thomas Tsou85b179d2013-11-15 21:14:33 -0500442 if (type < 0) {
443 LOG(ALERT) << "Failed to create radio device" << std::endl;
444 goto shutdown;
445 }
446
447 /* Setup the appropriate device interface */
448 radio = makeRadioInterface(&config, usrp, type);
449 if (!radio)
450 goto shutdown;
451
452 /* Create the transceiver core */
453 trx = makeTransceiver(&config, radio);
454 if (!trx)
455 goto shutdown;
456
Thomas Tsou85b179d2013-11-15 21:14:33 -0500457 chans = trx->numChans();
458 std::cout << "-- Transceiver active with "
459 << chans << " channel(s)" << std::endl;
460
461 while (!gshutdown)
462 sleep(1);
463
464shutdown:
465 std::cout << "Shutting down transceiver..." << std::endl;
466
467 delete trx;
468 delete radio;
469 delete usrp;
470
471 return 0;
472}