blob: 6005c584c2f2f3e1a34ac83a627f8bc2f4947b38 [file] [log] [blame]
Tom Tsoub79895c2017-06-03 18:31:48 -07001/*
2 * GSM Signal Generator
3 *
4 * Copyright (C) 2017 Ettus Research LLC
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * See the COPYING file in the main directory for details.
19 *
20 * Author: Tom Tsou <tom.tsou@ettus.com>
21 */
22
23#include <limits.h>
24#include <unistd.h>
25#include <getopt.h>
26#include <algorithm>
27#include <functional>
28#include <memory>
29#include <map>
30#include <GSMCommon.h>
31#include <Logger.h>
32#include <Configuration.h>
33#include <GSMCommon.h>
34#include "sigProcLib.h"
35#include "radioDevice.h"
36
37extern "C" {
38#include "convolve.h"
39#include "convert.h"
40}
41
42ConfigurationTable gConfig;
43
44#define DEFAULT_TX_SPS 4
45#define DEFAULT_TX_AMPL 0.5
46#define DEFAULT_TX_GAIN 50
47#define DEFAULT_TX_FREQ 1e9
48#define DEFAULT_OFFSET 0.0
49
50using namespace std;
51
52enum GsmModType {
53 MOD_LAURENT4,
54 MOD_LAURENT2,
55 MOD_LAURENT1,
56 MOD_NCO,
57 NUM_MODS,
58};
59
60enum BurstType {
61 BURST_NORMAL,
62 BURST_ACCESS,
63 BURST_FREQ,
64 BURST_SYNC,
65 BURST_EDGE,
66 NUM_BURSTS,
67};
68
69enum BurstTSC {
70 TSC0, TSC1, TSC2, TSC3, TSC4, TSC5, TSC6, TSC7,
71};
72
73struct Config {
74 string args = "";
75 string logl = "NOTICE";
76 unsigned sps = DEFAULT_TX_SPS;
77 double offset = DEFAULT_OFFSET;
78 bool swap = false;
79 float ampl = DEFAULT_TX_AMPL;
80 double freq = DEFAULT_TX_FREQ;
81 double gain = DEFAULT_TX_GAIN;
82 BurstTSC tsc = TSC0;
83 GsmModType mod = MOD_LAURENT2;
84 BurstType burst = BURST_NORMAL;
85 RadioDevice::ReferenceType ref = RadioDevice::REF_INTERNAL;
86};
87
88static shared_ptr<signalVector> modulateGMSK(BitVector &bits, GsmModType modType)
89{
90 switch (modType) {
91 case MOD_LAURENT4: return shared_ptr<signalVector>(modulateBurstLaurent4(bits));
92 case MOD_LAURENT2: return shared_ptr<signalVector>(modulateBurstLaurent2(bits));
93 case MOD_LAURENT1: return shared_ptr<signalVector>(modulateBurstLaurent1(bits));
94 case MOD_NCO: return shared_ptr<signalVector>(modulateBurstNCO(bits));
95 default: return shared_ptr<signalVector>(modulateBurstLaurent2(bits));
96 };
97}
98
99static shared_ptr<signalVector> generateNormalBurst(BurstTSC tsc, GsmModType modType)
100{
101 auto tail = vector<char>(3, 0);
102 auto data0 = vector<char>(57);
103 auto data1 = vector<char>(57);
104 auto steal = vector<char>(1, 0);
105 auto train = vector<char>(26);
106
107 auto ti = begin(GSM::gTrainingSequence[tsc]);
108 for (auto &t : train) t = *ti++;
109 for (auto &d : data0) d = rand() % 2;
110 for (auto &d : data1) d = rand() % 2;
111
112 auto bits = BitVector(NORMAL_BURST_NBITS);
113 auto bi = bits.begin();
114
115 for (auto t : tail) *bi++ = t;
116 for (auto d : data0) *bi++ = d;
117 for (auto s : steal) *bi++ = s;
118 for (auto t : train) *bi++ = t;
119 for (auto s : steal) *bi++ = s;
120 for (auto d : data1) *bi++ = d;
121 for (auto t : tail) *bi++ = t;
122
123 return modulateGMSK(bits, modType);
124}
125
126static shared_ptr<signalVector> generateRABurst(GsmModType modType)
127{
128 auto tail0 = vector<char>(8, 0);
129 auto train = vector<char>(41);
130 auto data = vector<char>(36);
131 auto tail1 = vector<char>(3, 0);
132
133 auto ti = begin(GSM::gRACHBurst);
134 for (auto &t : train) t = *ti++;
135 for (auto &d : data) d = rand() % 2;
136
137 auto bits = BitVector(88);
138 auto bi = bits.begin();
139
140 for (auto t : tail0) *bi++ = t;
141 for (auto t : train) *bi++ = t;
142 for (auto d : data) *bi++ = d;
143 for (auto t : tail1) *bi++ = t;
144
145 return modulateGMSK(bits, modType);
146}
147
148static shared_ptr<signalVector> generateFreqBurst(GsmModType modType)
149{
150 auto tail = vector<char>(3, 0);
151 auto fixed = vector<char>(142);
152
153 auto bits = BitVector(148);
154 auto bi = bits.begin();
155
156 for (auto t : tail) *bi++ = t;
157 for (auto f : fixed) *bi++ = f;
158 for (auto t : tail) *bi++ = t;
159
160 return modulateGMSK(bits, modType);
161}
162
163static shared_ptr<signalVector> generateSyncBurst(GsmModType modType)
164{
165 auto tail = vector<char>(3, 0);
166 auto data0 = vector<char>(39);
167 auto data1 = vector<char>(39);
168
169 /* 64 length synchronization sequence */
170 vector<char> train {
171 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
172 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
173 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
174 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1,
175 };
176 for (auto &d : data0) d = rand() % 2;
177 for (auto &d : data1) d = rand() % 2;
178
179 auto bits = BitVector(148);
180 auto bi = bits.begin();
181
182 for (auto t : tail) *bi++ = t;
183 for (auto d : data0) *bi++ = d;
184 for (auto t : train) *bi++ = t;
185 for (auto d : data1) *bi++ = d;
186 for (auto t : tail) *bi++ = t;
187
188 return modulateGMSK(bits, modType);
189}
190
191static shared_ptr<signalVector> generateEDGEBurst(BurstTSC tsc)
192{
193 auto tail = vector<Complex<float>>(3);
194 auto data0 = vector<Complex<float>>(58);
195 auto train = vector<Complex<float>>(26);
196 auto data1 = vector<Complex<float>>(58);
197
198 extern const Complex<float> psk8_table[8];
199 for (auto &t : tail) t = psk8_table[0b111];
200 for (auto &d : data0) d = psk8_table[rand() % 8];
201 for (auto &d : data1) d = psk8_table[rand() % 8];
202
203 auto ti = begin(GSM::gEdgeTrainingSequence[tsc]);
204 for (auto &t : train) {
205 unsigned i = (*(ti + 0) & 0b001) << 0 |
206 (*(ti + 1) & 0b001) << 1 |
207 (*(ti + 2) & 0b001) << 2;
208 t = psk8_table[i];
209 ti += 3;
210 }
211
212 /* NBITS refers to 148 symbols in this case */
213 auto burst = signalVector(NORMAL_BURST_NBITS);
214 auto bi = burst.begin();
215
216 for (auto t : tail) *bi++ = t;
217 for (auto d : data0) *bi++ = d;
218 for (auto t : train) *bi++ = t;
219 for (auto d : data1) *bi++ = d;
220 for (auto t : tail) *bi++ = t;
221
222 return shared_ptr<signalVector>(shapeEdgeBurst(burst));
223}
224
225/* Perform float-integer conversion and write to the device */
226static void sendBurst(shared_ptr<RadioDevice> usrp, TIMESTAMP &ts,
227 shared_ptr<signalVector> sv, float ampl)
228{
229 auto buffer = vector<Complex<short>>(sv->size());
230
231 transform(sv->begin(), sv->end(), buffer.begin(), [ampl](Complex<float> x) {
232 const float scale = SHRT_MAX * ampl;
233 return Complex<short>(x.real()*scale, x.imag()*scale);
234 });
235
236 auto buffers = vector<short *>(1, reinterpret_cast<short *>(&buffer.front()));
237 ts += usrp->writeSamples(buffers, buffer.size(), nullptr, ts, true);
238}
239
240static void print_help()
241{
242 fprintf(stdout, "Options:\n"
243 " -h, --help This text\n"
244 " -a, --args UHD device args\n"
245 " -l --log Logging level (%s)\n"
246 " -b, --burst Burst type (%s)\n"
247 " -r, --ref Frequency reference (%s)\n"
248 " -f, --freq Tx RF frequency\n"
249 " -g, --gain Tx RF gain\n"
250 " -s, --sps Tx samples-per-symbol (only 4 supported)\n"
251 " -m, --mod GSMK modulator type (%s)\n"
252 " -p, --ampl Tx amplitude (0.0 - 1.0)\n"
253 " -o, --offset Baseband frequency offset\n"
254 " -t, --tsc Normal and EDGE burst training sequence (0-7)\n"
255 " -S, --swap Swap channels\n\n",
256 "'err', 'warn', 'notice', 'info', 'debug'",
257 "'normal', 'access', 'freq', 'sync', 'edge'",
258 "'internal', 'external', 'gps'",
259 "'laurent4', 'laurent2', 'laurent1', 'nco'"
260 );
261}
262
263static void print_config(Config &config)
264{
265 const map<GsmModType, string> modMap = {
266 { MOD_LAURENT4, "Laurent-4" },
267 { MOD_LAURENT2, "Laurent-2" },
268 { MOD_LAURENT1, "Laurent-1" },
269 { MOD_NCO, "NCO" },
270 };
271
272 const map<BurstType, string> burstMap = {
273 { BURST_NORMAL, "Normal" },
274 { BURST_ACCESS, "Access" },
275 { BURST_FREQ, "Frequency" },
276 { BURST_SYNC, "Synchronization" },
277 { BURST_EDGE, "EDGE" },
278 };
279
280 const map<RadioDevice::ReferenceType, string> refMap = {
281 { RadioDevice::REF_INTERNAL, "Internal" },
282 { RadioDevice::REF_EXTERNAL, "External" },
283 { RadioDevice::REF_GPS, "GPS" },
284 };
285
286 auto yesno = [](bool x) { return x ? "yes" : "no"; };
287
288 ostringstream ost("");
289 ost << "Config Settings" << endl;
290 ost << " Log level............... " << config.logl << std::endl;
291 ost << " Device args............. " << "\"" << config.args << "\"" << endl;
292 ost << " Samples-per-Symbol...... " << config.sps << endl;
293 ost << " RF frequency............ " << config.freq/1e9 << " GHz" << endl;
294 ost << " RF gain................. " << config.gain << " dB" << endl;
295 ost << " Reference............... " << refMap.at(config.ref) << endl;
296 ost << " Burst type.............. " << burstMap.at(config.burst) << endl;
297 ost << " Modulator type.......... " << modMap.at(config.mod) << endl;
298 ost << " Baseband offset......... " << config.offset/1e6 << " MHz" << endl;
299 ost << " Swap channels........... " << yesno(config.swap) << endl;
300 cout << ost << endl;
301}
302
303static bool handle_options(int argc, char **argv, Config &config)
304{
305 int option;
306
307 const struct option longopts[] = {
308 { "help", 0, nullptr, 'h' },
309 { "log", 1, nullptr, 'l' },
310 { "args", 1, nullptr, 'a' },
311 { "ref" , 1, nullptr, 'r' },
312 { "freq", 1, nullptr, 'f' },
313 { "gain", 1, nullptr, 'g' },
314 { "mod", 1, nullptr, 'm' },
315 { "offset", 1, nullptr, 'o' },
316 { "sps", 1, nullptr, 's' },
317 { "ampl", 1, nullptr, 'p' },
318 { "tsc", 1, nullptr, 'r' },
319 { "burst", 1, nullptr, 'b' },
320 { "swap", 1, nullptr, 'w' },
321 };
322
323 const map<string, string> logMap = {
324 { "emerg", "EMERG" },
325 { "EMERG", "EMERG" },
326 { "alert", "ALERT" },
327 { "ALERT", "ALERT" },
328 { "err", "ERR" },
329 { "ERR", "ERR" },
330 { "warn", "WARNING" },
331 { "WARN", "WARNING" },
332 { "notice", "NOTICE" },
333 { "NOTICE", "NOTICE" },
334 { "info", "INFO" },
335 { "INFO", "INFO" },
336 { "debug", "DEBUG" },
337 { "DEBUG", "DEBUG" },
338 };
339
340 const map<string, GsmModType> modMap = {
341 { "laurent4", MOD_LAURENT4 },
342 { "laurent2", MOD_LAURENT2 },
343 { "laurent1", MOD_LAURENT1 },
344 { "nco", MOD_NCO },
345 };
346
347 const map<string, BurstType> burstMap = {
348 { "normal", BURST_NORMAL },
349 { "access", BURST_ACCESS },
350 { "freq", BURST_FREQ },
351 { "sync", BURST_SYNC },
352 { "edge", BURST_EDGE },
353 };
354
355 const map<string, RadioDevice::ReferenceType> refMap = {
356 { "internal", RadioDevice::REF_INTERNAL },
357 { "external", RadioDevice::REF_EXTERNAL },
358 { "gpsdo", RadioDevice::REF_GPS },
359 { "gps", RadioDevice::REF_GPS },
360 };
361
362 while ((option = getopt_long(argc, argv, "ha:l:r:f:g:m:o:s:p:t:b:w", longopts, nullptr)) != -1) {
363 switch (option) {
364 case 'a':
365 config.args = optarg;
366 break;
367 case 'f':
368 config.freq = atof(optarg);
369 break;
370 case 'g':
371 config.gain = atof(optarg);
372 break;
373 case 'o':
374 config.offset = atof(optarg);
375 break;
376 case 's':
377 if (atoi(optarg) != 4) {
378 printf("Unsupported SPS = %i\n", atoi(optarg));
379 return false;
380 }
381 break;
382 case 'p':
383 config.ampl = atof(optarg);
384 break;
385 case 't':
386 if (atoi(optarg) < TSC0 || atoi(optarg) > TSC7) {
387 printf("Invalid training sequence %i", atoi(optarg));
388 return false;
389 }
390 config.tsc = static_cast<BurstTSC>(atoi(optarg));
391 break;
392 case 'w':
393 config.swap = true;
394 break;
395 case 'l':
396 if (logMap.count(optarg) > 0) {
397 config.logl = logMap.at(optarg);
398 } else {
399 printf("Invalid log parameter '%s'\n\n", optarg);
400 return false;
401 }
402 break;
403 case 'r':
404 if (refMap.count(optarg) > 0) {
405 config.ref = refMap.at(optarg);
406 } else {
407 printf("Invalid reference parameter '%s'\n\n", optarg);
408 return false;
409 }
410 break;
411 case 'm':
412 if (modMap.count(optarg) > 0) {
413 config.mod = modMap.at(optarg);
414 } else {
415 printf("Invalid modulation parameter '%s'\n\n", optarg);
416 return false;
417 }
418 break;
419 case 'b':
420 if (burstMap.count(optarg) > 0) {
421 config.burst = burstMap.at(optarg);
422 } else {
423 printf("Invalid burst type parameter '%s'\n\n", optarg);
424 return false;
425 }
426 break;
427 case 'h':
428 default:
429 return false;
430 }
431 }
432
433 return true;
434}
435
436int main(int argc, char **argv)
437{
438 Config config;
439 if (!handle_options(argc, argv, config)) {
440 print_help();
441 return -EINVAL;
442 }
443
444 print_config(config);
445
446 gLogInit("osmo-siggen", config.logl.c_str(), LOG_LOCAL7);
447
448 convolve_init();
449 convert_init();
450 sigProcLibSetup();
451
452 /* Device setup */
453 shared_ptr<RadioDevice> usrp(RadioDevice::make(config.sps, config.sps, RadioDevice::NORMAL, 1, config.offset));
454 usrp->open(config.args, config.ref, config.swap);
455 usrp->setTxFreq(config.freq);
456 usrp->setTxGain(config.gain);
457 usrp->start(true);
458 usrp->setPriority(0.5);
459
460 /* Bind all burst-modulator configurations */
461 auto makeBurstGenerator = [&config]()->function<shared_ptr<signalVector>()> {
462 switch (config.burst) {
463 case BURST_EDGE: return bind(generateEDGEBurst, config.tsc);
464 case BURST_ACCESS: return bind(generateRABurst, config.mod);
465 case BURST_FREQ: return bind(generateFreqBurst, config.mod);
466 case BURST_SYNC: return bind(generateSyncBurst, config.mod);
467 case BURST_NORMAL:
468 default: return bind(generateNormalBurst, config.tsc, config.mod);
469 }
470 };
471
472 auto burstGenerator = makeBurstGenerator();
473 auto ts = usrp->initialWriteTimestamp();
474 auto frameTrigger = []() {
475 static int tn = 0;
476 return ++tn % 8 == 0;
477 };
478
479 while (1) {
480 try {
481 if (frameTrigger()) usrp->triggerGPIO(ts);
482 sendBurst(usrp, ts, burstGenerator(), config.ampl);
483 } catch (const exception &e) {
484 cout << e.what() << endl;
485 break;
486 }
487 }
488
489 sigProcLibDestroy();
490}