blob: 98112bb0b75b86489a00c64a4e5d2f43a07712f3 [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)
44#define DEFAULT_SPS 1
45#else
46#define DEFAULT_SPS 4
47#endif
48
49/* Default configuration parameters
50 * Note that these values are only used if the particular key does not
51 * exist in the configuration database. IP port and address values will
52 * typically be overwritten by the OpenBTS.db values. Other values will
53 * not be in the database by default.
54 */
55#define DEFAULT_TRX_PORT 5700
56#define DEFAULT_TRX_IP "127.0.0.1"
57#define DEFAULT_EXTREF false
58#define DEFAULT_DIVERSITY false
59#define DEFAULT_CHANS 1
60
61struct trx_config {
62 std::string log_level;
63 std::string addr;
64 std::string dev_args;
65 unsigned port;
66 unsigned sps;
67 unsigned chans;
68 bool extref;
69 bool diversity;
70};
71
Thomas Tsou4de70be2013-11-17 18:54:52 -050072ConfigurationTable gConfig;
Thomas Tsou85b179d2013-11-15 21:14:33 -050073
74volatile bool gshutdown = false;
75
76/* Run sanity check on configuration table
77 * The global table constructor cannot provide notification in the
Thomas Tsou4de70be2013-11-17 18:54:52 -050078 * event of failure. Make sure that we can access the database,
Thomas Tsou85b179d2013-11-15 21:14:33 -050079 * write to it, and that it contains the bare minimum required keys.
80 */
Thomas Tsou4de70be2013-11-17 18:54:52 -050081bool testConfig()
Thomas Tsou85b179d2013-11-15 21:14:33 -050082{
Thomas Tsou4de70be2013-11-17 18:54:52 -050083 int val = 9999;
Thomas Tsou85b179d2013-11-15 21:14:33 -050084 std::string test = "asldfkjsaldkf";
85 const char *key = "Log.Level";
86
Thomas Tsou85b179d2013-11-15 21:14:33 -050087 /* Attempt to query */
88 try {
89 gConfig.getStr(key);
90 } catch (...) {
91 std::cerr << std::endl;
92 std::cerr << "Config: Failed query required key " << key
93 << std::endl;
94 return false;
95 }
96
Thomas Tsou4de70be2013-11-17 18:54:52 -050097 /* Attempt to set a test value in the global config */
98 if (!gConfig.set(test, val)) {
99 std::cerr << std::endl;
100 std::cerr << "Config: Failed to set test key" << std::endl;
101 return false;
102 } else {
103 gConfig.remove(test);
104 }
105
Thomas Tsou85b179d2013-11-15 21:14:33 -0500106 return true;
107}
108
Thomas Tsou4de70be2013-11-17 18:54:52 -0500109
Thomas Tsou85b179d2013-11-15 21:14:33 -0500110/* Setup configuration values
111 * Don't query the existence of the Log.Level because it's a
112 * mandatory value. That is, if it doesn't exist, the configuration
Thomas Tsou4de70be2013-11-17 18:54:52 -0500113 * table will crash or will have already crashed. Everything else we
114 * can survive without and use default values if the database entries
Thomas Tsou85b179d2013-11-15 21:14:33 -0500115 * are empty.
116 */
117bool trx_setup_config(struct trx_config *config)
118{
119 std::string refstr, divstr;
120
Thomas Tsou4de70be2013-11-17 18:54:52 -0500121 if (!testConfig())
Thomas Tsou85b179d2013-11-15 21:14:33 -0500122 return false;
123
124 if (config->log_level == "")
125 config->log_level = gConfig.getStr("Log.Level");
126
127 if (!config->port) {
128 if (gConfig.defines("TRX.Port"))
129 config->port = gConfig.getNum("TRX.Port");
130 else
131 config->port = DEFAULT_TRX_PORT;
132 }
133
134 if (config->addr == "") {
135 if (gConfig.defines("TRX.IP"))
136 config->addr = gConfig.getStr("TRX.IP");
137 else
138 config->addr = DEFAULT_TRX_IP;
139 }
140
141 if (!config->extref) {
142 if (gConfig.defines("TRX.Reference"))
143 config->extref = gConfig.getNum("TRX.Reference");
144 else
145 config->extref = DEFAULT_EXTREF;
146 }
147
148 if (!config->diversity) {
149 if (gConfig.defines("TRX.Diversity"))
150 config->diversity = gConfig.getNum("TRX.Diversity");
151 else
152 config->diversity = DEFAULT_DIVERSITY;
153 }
154
155 if (!config->sps)
156 config->sps = DEFAULT_SPS;
157
158 if (!config->chans)
159 config->chans = DEFAULT_CHANS;
160
161 /* Diversity only supported on 2 channels */
162 if (config->diversity)
163 config->chans = 2;
164
165 refstr = config->extref ? "Enabled" : "Disabled";
166 divstr = config->diversity ? "Enabled" : "Disabled";
167
168 std::ostringstream ost("");
169 ost << "Config Settings" << std::endl;
170 ost << " Log Level............... " << config->log_level << std::endl;
171 ost << " Device args............. " << config->dev_args << std::endl;
172 ost << " TRX Base Port........... " << config->port << std::endl;
173 ost << " TRX Address............. " << config->addr << std::endl;
174 ost << " Channels................ " << config->chans << std::endl;
175 ost << " Samples-per-Symbol...... " << config->sps << std::endl;
176 ost << " External Reference...... " << refstr << std::endl;
177 ost << " Diversity............... " << divstr << std::endl;
178 std::cout << ost << std::endl;
179
180 return true;
181}
182
183/* Create radio interface
184 * The interface consists of sample rate changes, frequency shifts,
185 * channel multiplexing, and other conversions. The transceiver core
186 * accepts input vectors sampled at multiples of the GSM symbol rate.
187 * The radio interface connects the main transceiver with the device
188 * object, which may be operating some other rate.
189 */
190RadioInterface *makeRadioInterface(struct trx_config *config,
191 RadioDevice *usrp, int type)
192{
193 RadioInterface *radio = NULL;
194
195 switch (type) {
196 case RadioDevice::NORMAL:
197 radio = new RadioInterface(usrp, config->sps, config->chans);
198 break;
199 case RadioDevice::RESAMP_64M:
200 case RadioDevice::RESAMP_100M:
201 radio = new RadioInterfaceResamp(usrp,
202 config->sps, config->chans);
203 break;
204 case RadioDevice::DIVERSITY:
205 radio = new RadioInterfaceDiversity(usrp,
206 config->sps, config->chans);
207 break;
208 default:
209 LOG(ALERT) << "Unsupported radio interface configuration";
210 return NULL;
211 }
212
213 if (!radio->init(type)) {
214 LOG(ALERT) << "Failed to initialize radio interface";
215 return NULL;
216 }
217
218 return radio;
219}
220
221/* Create transceiver core
222 * The multi-threaded modem core operates at multiples of the GSM rate of
223 * 270.8333 ksps and consists of GSM specific modulation, demodulation,
224 * and decoding schemes. Also included are the socket interfaces for
225 * connecting to the upper layer stack.
226 */
227Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
228{
229 Transceiver *trx;
230 VectorFIFO *fifo;
231
232 trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
233 config->chans, GSM::Time(3,0), radio);
234 if (!trx->init()) {
235 LOG(ALERT) << "Failed to initialize transceiver";
236 delete trx;
237 return NULL;
238 }
239
240 for (size_t i = 0; i < config->chans; i++) {
241 fifo = radio->receiveFIFO(i);
242 if (fifo && trx->receiveFIFO(fifo, i))
243 continue;
244
245 LOG(ALERT) << "Could not attach FIFO to channel " << i;
246 delete trx;
247 return NULL;
248 }
249
250 return trx;
251}
252
253static void sig_handler(int signo)
254{
255 fprintf(stdout, "Received shutdown signal");
256 gshutdown = true;
257}
258
259static void setup_signal_handlers()
260{
261 if (signal(SIGINT, sig_handler) == SIG_ERR) {
262 fprintf(stderr, "Failed to install SIGINT signal handler\n");
263 exit(EXIT_FAILURE);
264 }
265 if (signal(SIGTERM, sig_handler) == SIG_ERR) {
266 fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
267 exit( EXIT_FAILURE);
268 }
269}
270
271static void print_help()
272{
273 fprintf(stdout, "Options:\n"
274 " -h This text\n"
275 " -a UHD device args\n"
276 " -l Logging level (%s)\n"
277 " -i IP address of GSM core\n"
278 " -p Base port number\n"
279 " -d Enable dual channel diversity receiver\n"
280 " -x Enable external 10 MHz reference\n"
281 " -s Samples-per-symbol (1 or 4)\n"
282 " -c Number of ARFCN channels (default=1)\n",
283 "EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
284}
285
286static void handle_options(int argc, char **argv, struct trx_config *config)
287{
288 int option;
289
290 config->port = 0;
291 config->sps = 0;
292 config->chans = 0;
293 config->extref = false;
294 config->diversity = false;
295
296 while ((option = getopt(argc, argv, "ha:l:i:p:c:dxs:")) != -1) {
297 switch (option) {
298 case 'h':
299 print_help();
300 exit(0);
301 break;
302 case 'a':
303 config->dev_args = optarg;
304 break;
305 case 'l':
306 config->log_level = optarg;
307 break;
308 case 'i':
309 config->addr = optarg;
310 break;
311 case 'p':
312 config->port = atoi(optarg);
313 break;
314 case 'c':
315 config->chans = atoi(optarg);
316 break;
317 case 'd':
318 config->diversity = true;
319 break;
320 case 'x':
321 config->extref = true;
322 break;
323 case 's':
324 config->sps = atoi(optarg);
325 if ((config->sps != 1) && (config->sps != 4)) {
326 printf("Unsupported samples-per-symbol\n\n");
327 print_help();
328 exit(0);
329 }
330 break;
331 default:
332 print_help();
333 exit(0);
334 }
335 }
336}
337
338int main(int argc, char *argv[])
339{
340 int type, chans;
341 RadioDevice *usrp;
342 RadioInterface *radio = NULL;
343 Transceiver *trx = NULL;
344 struct trx_config config;
345
346 handle_options(argc, argv, &config);
347
348 setup_signal_handlers();
349
350 /* Check database sanity */
351 if (!trx_setup_config(&config)) {
352 std::cerr << "Config: Database failure - exiting" << std::endl;
353 return EXIT_FAILURE;
354 }
355
356 gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
357
358 srandom(time(NULL));
359
360 /* Create the low level device object */
361 usrp = RadioDevice::make(config.sps, config.chans, config.diversity);
362 type = usrp->open(config.dev_args, config.extref);
363 if (type < 0) {
364 LOG(ALERT) << "Failed to create radio device" << std::endl;
365 goto shutdown;
366 }
367
368 /* Setup the appropriate device interface */
369 radio = makeRadioInterface(&config, usrp, type);
370 if (!radio)
371 goto shutdown;
372
373 /* Create the transceiver core */
374 trx = makeTransceiver(&config, radio);
375 if (!trx)
376 goto shutdown;
377
378 trx->start();
379
380 chans = trx->numChans();
381 std::cout << "-- Transceiver active with "
382 << chans << " channel(s)" << std::endl;
383
384 while (!gshutdown)
385 sleep(1);
386
387shutdown:
388 std::cout << "Shutting down transceiver..." << std::endl;
389
390 delete trx;
391 delete radio;
392 delete usrp;
393
394 return 0;
395}