-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy path00eaa51d1dba34e043a3c074bf7dd9dbaf2ab8df.diff
473 lines (456 loc) · 20.6 KB
/
00eaa51d1dba34e043a3c074bf7dd9dbaf2ab8df.diff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
diff --git a/gsm-receiver/src/lib/gsm_receiver_cf.cc b/gsm-receiver/src/lib/gsm_receiver_cf.cc
index c461a80..c5a45f3 100644
--- a/gsm-receiver/src/lib/gsm_receiver_cf.cc
+++ b/gsm-receiver/src/lib/gsm_receiver_cf.cc
@@ -93,10 +93,11 @@ void decrypt(const unsigned char * burst_binary, byte * KC, unsigned char * decr
-void dump_bits_gprsdecode(GS_CTX *ctx, char * decrypted_data, burst_counter burst_nr)
+void dump_bits_gprsdecode(GS_CTX *ctx, char * decrypted_data, burst_counter burst_nr,
+ bool is_uplink)
{
ctx->gprsdecode_burst->frame_nr = htonl(burst_nr.get_frame_nr());
- ctx->gprsdecode_burst->band_arfcn = 0;//htons(617 | ARFCN_UPLINK);
+ ctx->gprsdecode_burst->band_arfcn = htons(is_uplink?ARFCN_UPLINK:0);
ctx->gprsdecode_burst->chan_nr = RSL_CHAN_Bm_ACCHs | burst_nr.get_timeslot_nr();
ctx->gprsdecode_burst->flags = 0;
ctx->gprsdecode_burst->rx_level = 0;
@@ -113,11 +114,14 @@ void dump_bits_gprsdecode(GS_CTX *ctx, char * decrypted_data, burst_counter burs
fwrite(ctx->gprsdecode_burst, sizeof(*ctx->gprsdecode_burst), 1, ctx->gprsdecode_file);
/* Plain bits */
- printf("P0 %d %d: ", burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
- for (int i = 0; i < 57; i++)
+ printf("%s P0 %d %d: ", is_uplink?"UL":"DL", burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
+ for (int i = 0; i < 144; i++)
printf("%d", decrypted_data[i+3]);
- for (int i = 0; i < 57; i++)
- printf("%d", decrypted_data[i+88]);
+// for (int i = 0; i < 57; i++)
+// printf("%d", decrypted_data[i+3]);
+// for (int i = 0; i < 57; i++)
+// printf("%d", decrypted_data[i+88]);
+ printf(" %d%d", decrypted_data[60], decrypted_data[87]);
printf("\n");
}
@@ -127,7 +131,7 @@ void dump_bits(const unsigned char * burst_binary, unsigned char * decrypted_dat
int i;
/* Cipher bits */
- printf("C%d %d %d: ", first_burst, burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
+ printf("DL C%d %d %d: ", first_burst, burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
for (int i = 0; i < 57; i++)
printf("%d", burst_binary[i+3]);
for (int i = 0; i < 57; i++)
@@ -135,7 +139,7 @@ void dump_bits(const unsigned char * burst_binary, unsigned char * decrypted_dat
printf("\n");
/* Plain bits */
- printf("P%d %d %d: ", first_burst, burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
+ printf("DL P%d %d %d: ", first_burst, burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
for (int i = 0; i < 57; i++)
printf("%d", decrypted_data[i+3]);
for (int i = 0; i < 57; i++)
@@ -143,7 +147,7 @@ void dump_bits(const unsigned char * burst_binary, unsigned char * decrypted_dat
printf("\n");
/* Keystream bits */
- printf("S%d %d %d: ", first_burst, burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
+ printf("DL S%d %d %d: ", first_burst, burst_nr.get_frame_nr(), burst_nr.get_frame_nr_mod());
for (int i = 0; i < 57; i++)
printf("%d", burst_binary[i+3] ^ decrypted_data[i+3]);
for (int i = 0; i < 57; i++)
@@ -204,12 +208,12 @@ void gsm_receiver_cf::read_configuration(std::string configuration)
/* any other timeslot than 0: turn TS0 off */
if(ts != 0) {
d_gs_ctx.ts_ctx[0].type = TST_OFF;
- d_trace_sch = false;
+ d_trace_sch = true;
printf(" TS0 is turned off\n");
}
}
-void gsm_receiver_cf::process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary, bool first_burst)
+void gsm_receiver_cf::process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary, bool first_burst, bool is_uplink)
{
unsigned char decrypted_data[148];
float decrypted_data_float[148];
@@ -270,7 +274,7 @@ void gsm_receiver_cf::process_normal_burst(burst_counter burst_nr, const unsigne
if (d_gs_ctx.ts_ctx[ts].type == TST_PDCH) {
// No encryption in GPRS on Um level
#if 1 /* dump cipher, plain and keystream bits */
- dump_bits_gprsdecode(&d_gs_ctx, (char*)burst_binary, burst_nr);
+ dump_bits_gprsdecode(&d_gs_ctx, (char*)burst_binary, burst_nr, is_uplink);
#endif
// TODO: Implement further processing instead of relying on 'gprsdecode'
#if 0
@@ -329,7 +333,7 @@ void gsm_receiver_cf::configure_receiver()
}
else if (d_gs_ctx.ts_ctx[ts].type == TST_PDCH) {
d_channel_conf.set_multiframe_type(ts, multiframe_52);
- d_channel_conf.set_burst_types(ts, PDTCH_FRAMES, PDTCH_FIRST, sizeof(PDTCH_FRAMES) / sizeof(unsigned), dummy_or_normal);
+ d_channel_conf.set_burst_types(ts, PDTCH_FRAMES, PDTCH_FIRST, sizeof(PDTCH_FRAMES) / sizeof(unsigned), normal_burst);
}
}
}
@@ -347,7 +351,7 @@ void gsm_receiver_cf::configure_receiver()
}
static const int MIN_IN = 1; // mininum number of input streams
-static const int MAX_IN = 1; // maximum number of input streams
+static const int MAX_IN = 2; // maximum number of input streams
static const int MIN_OUT = 0; // minimum number of output streams
static const int MAX_OUT = 1; // maximum number of output streams
@@ -367,8 +371,13 @@ void gsm_receiver_cf::configure_receiver()
d_state(first_fcch_search),
d_burst_nr(osr),
d_failed_sch(0),
- d_trace_sch(true)
+ d_trace_sch(true),
+ d_is_uplink(true),
+ d_load_sync(false)
{
+ if (d_load_sync) d_state = load_sync_state;
+ if (d_is_uplink) d_state = sync_uplink;
+
const unsigned char amr_nb_magic[6] = { 0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a };
int i;
gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
@@ -454,12 +463,42 @@ void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_re
gr_vector_void_star &output_items)
{
const gr_complex *input = (const gr_complex *) input_items[0];
+ const gr_complex *input_ul = (const gr_complex *) input_items[1];
//float *out = (float *) output_items[0];
int produced_out = 0; //how many output elements were produced - this isn't used yet
//probably the gsm receiver will be changed into sink so this variable won't be necessary
switch (d_state) {
//bootstrapping
+ case sync_uplink: {
+ // adjust to uplink
+ int target = 3*BURST_SIZE * d_OSR;
+
+ //consume samples until d_counter will be equal to sample_nr_near_sch_start
+ if (d_counter < target) {
+ int to_consume = 0;
+ if (d_counter + nitems_items[0] >= target) {
+ to_consume = target - d_counter;
+ DCOUT("sync_uplink finished, to_consume " << to_consume
+ << " total " << d_counter+to_consume);
+ d_counter = 0;
+ d_state = first_fcch_search;
+ } else {
+ to_consume = nitems_items[0];
+ d_counter += to_consume;
+ DCOUT("sync_uplink working, to_consume " << to_consume
+ << " total " << d_counter);
+ }
+ consume(1, to_consume); //consume samples
+ } else {
+ DCOUT("sync_uplink finished, nothing to consume "
+ << " total " << d_counter);
+ d_counter = 0;
+ d_state = first_fcch_search;
+ }
+
+ break;
+ }
case first_fcch_search:
if (find_fcch_burst(input, nitems_items[0])) { //find frequency correction burst in the input buffer
set_frequency(d_freq_offset); //if fcch search is successful set frequency offset
@@ -496,9 +535,11 @@ void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_re
burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response from it
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //detect bits using MLSE detection
if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) { //decode SCH burst
+ d_counter += burst_start;
if(d_trace_sch)
{
DCOUT("sch burst_start: " << burst_start);
+ DCOUT("sch absolute position: " << d_counter);
DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3);
}
d_burst_nr.set(t1, t2, t3, 0); //set counter of bursts value
@@ -525,6 +566,54 @@ void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_re
}
break;
}
+ case load_sync_state: {
+ int t1, t2, t3;
+
+ int target = 83654 + BURST_SIZE * d_OSR;
+ // adjust to uplink
+// target += 3*BURST_SIZE * d_OSR;
+
+ //consume samples until d_counter will be equal to sample_nr_near_sch_start
+ int to_consume = 0;
+ bool result = false;
+ if (d_counter < target) {
+ if (d_counter + nitems_items[0] >= target) {
+ to_consume = target - d_counter;
+ } else {
+ to_consume = nitems_items[0];
+ }
+ result = false;
+ consume_each(to_consume); //consume samples
+ } else {
+ to_consume = 0;
+ result = true;
+ }
+ d_counter += to_consume;
+
+ if (result) {
+ d_freq_offset = -400.727966;
+ set_frequency(d_freq_offset); //call set_frequncy only frequency offset change is greater than some value
+
+ d_bcc = 7;
+ d_ncc = 7;
+ t1 = 597;
+ t2 = 15;
+ t3 = 21;
+ d_burst_nr.set(t1, t2, t3, 0); //set counter of bursts value
+ d_burst_nr++;
+
+ if(d_trace_sch)
+ {
+ DCOUT("sch absolute position: " << d_counter);
+ DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3);
+ }
+
+
+ d_state = synchronized;
+ }
+
+ break;
+ }
//in this state receiver is synchronized and it processes bursts according to burst type for given burst number
case synchronized: {
vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
@@ -543,15 +632,17 @@ void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_re
double freq_offset = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it
d_freq_offset_vals.push_front(freq_offset);
+ DCOUT("FCCH found, frequency offset: " << d_freq_offset+freq_offset);
if (d_freq_offset_vals.size() >= 10) {
double sum = std::accumulate(d_freq_offset_vals.begin(), d_freq_offset_vals.end(), 0);
double mean_offset = sum / d_freq_offset_vals.size(); //compute mean
+ DCOUT("mean frequency offset: " << d_freq_offset+mean_offset);
d_freq_offset_vals.clear();
if (abs(mean_offset) > FCCH_MAX_FREQ_OFFSET) {
d_freq_offset -= mean_offset; //and adjust frequency if it have changed beyond
set_frequency(d_freq_offset); //some limit
- DCOUT("mean_offset: " << mean_offset);
+// DCOUT("mean frequency offset: " << mean_offset);
DCOUT("Adjusting frequency, new frequency offset: " << d_freq_offset << "\n");
}
}
@@ -585,7 +676,11 @@ void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_re
case normal_burst: //if it's normal burst
burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], d_bcc); //get channel impulse response for given training sequence number - d_bcc
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits
- process_normal_burst(d_burst_nr, output_binary, first_burst); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+ process_normal_burst(d_burst_nr, output_binary, first_burst, false); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+
+ burst_start = get_norm_chan_imp_resp(input_ul, &channel_imp_resp[0], d_bcc); //get channel impulse response for given training sequence number - d_bcc
+ detect_burst(input_ul, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits
+ process_normal_burst(d_burst_nr, output_binary, first_burst, true); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
break;
case dummy_or_normal: {
@@ -601,10 +696,11 @@ void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_re
burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], d_bcc);
detect_burst(input, &channel_imp_resp[0], burst_start, output_binary);
if (!output_binary[0] && !output_binary[1] && !output_binary[2]) {
- process_normal_burst(d_burst_nr, output_binary, first_burst); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+ process_normal_burst(d_burst_nr, output_binary, first_burst, false); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
}
}
}
+ break;
case rach_burst:
//implementation of this channel isn't possible in current gsm_receiver
//it would take some realtime processing, counter of samples from USRP to
diff --git a/gsm-receiver/src/lib/gsm_receiver_cf.h b/gsm-receiver/src/lib/gsm_receiver_cf.h
index 039a774..2d3a45f 100644
--- a/gsm-receiver/src/lib/gsm_receiver_cf.h
+++ b/gsm-receiver/src/lib/gsm_receiver_cf.h
@@ -60,6 +60,8 @@ class gsm_receiver_cf : public gr_block
uint8_t d_KC[8]; //!!
GSM::TCHFACCHL1Decoder *d_tch_decoder[N_TCH_DECODER]; //!!
bool d_trace_sch;
+ bool d_is_uplink; // Are we loading uplink?
+ bool d_load_sync;
enum {
TM_NONE,
@@ -105,6 +107,7 @@ class gsm_receiver_cf : public gr_block
//@{
enum states {
first_fcch_search, next_fcch_search, sch_search, // synchronization search part
+ sync_uplink, load_sync_state, // load existing synchronization
synchronized // receiver is synchronized in this state
} d_state;
//@}
@@ -241,7 +244,7 @@ class gsm_receiver_cf : public gr_block
/**
*
*/
- void process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary, bool first_burst);
+ void process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary, bool first_burst, bool is_uplink);
/**
*
diff --git a/gsm-receiver/src/python/go2.sh b/gsm-receiver/src/python/go2.sh
new file mode 100755
index 0000000..55ac2a9
--- /dev/null
+++ b/gsm-receiver/src/python/go2.sh
@@ -0,0 +1,28 @@
+#! /bin/sh
+
+#echo "go.sh <file_dl.cfile> <file_ul.cfile> [decim==112]"
+
+KEY=$5
+CONFIGURATION=$4
+DECIM=$3
+FILE_UL=$2
+FILE=$1
+
+if [ $DECIM"x" = x ]; then
+ DECIM=48
+fi
+
+if [ $CONFIGURATION"x" = x ]; then
+# CONFIGURATION="0C"
+ CONFIGURATION="7P"
+fi
+
+if [ "$KEY""x" = x ]; then
+ KEY="00 00 00 00 00 00 00 00"
+fi
+
+# Use GSMTAP with WireShark instead of gmsdecode !
+
+#./gsm_receive.py -d "$DECIM" -I "$FILE" -c "$CONFIGURATION" -k "$KEY" | ../../../gsmdecode/src/gsmdecode -i
+
+./gsm_receive_multi.py -d "$DECIM" -I "$FILE" -U "$FILE_UL" -c "$CONFIGURATION" -k "$KEY"
diff --git a/gsm-receiver/src/python/gsm_receive_multi.py b/gsm-receiver/src/python/gsm_receive_multi.py
new file mode 100755
index 0000000..1dd3f11
--- /dev/null
+++ b/gsm-receiver/src/python/gsm_receive_multi.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from gnuradio import gr, gru, blks2
+#, gsm
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+from os import sys
+
+for extdir in ['../../debug/src/lib','../../debug/src/lib/.libs','../lib','../lib/.libs']:
+ if extdir not in sys.path:
+ sys.path.append(extdir)
+import gsm
+
+class tuner(gr.feval_dd):
+ def __init__(self, top_block):
+ gr.feval_dd.__init__(self)
+ self.top_block = top_block
+ def eval(self, freq_offet):
+ self.top_block.set_center_frequency(freq_offet)
+ return freq_offet
+
+class synchronizer(gr.feval_dd):
+ def __init__(self, top_block):
+ gr.feval_dd.__init__(self)
+ self.top_block = top_block
+
+ def eval(self, timing_offset):
+ self.top_block.set_timing(timing_offset)
+ return freq_offet
+
+class gsm_receiver_first_blood(gr.top_block):
+ def __init__(self):
+ gr.top_block.__init__(self)
+ (options, args) = self._process_options()
+ self.tuner_callback = tuner(self)
+ self.synchronizer_callback = synchronizer(self)
+ self.options = options
+ self.args = args
+ self._set_rates()
+ self.source1 = self._set_source(self.options.inputfile_dl)
+ self.filtr1 = self._set_filter()
+ self.source2 = self._set_source(self.options.inputfile_ul)
+ self.filtr2 = self._set_filter()
+ self.interpolator = self._set_interpolator()
+ self.receiver = self._set_receiver()
+ self.converter = self._set_converter()
+ self.sink = self._set_sink()
+
+# self.connect(self.source, self.filtr, self.interpolator, self.receiver, self.converter, self.sink)
+ self.connect(self.source1, self.filtr1, self.receiver, self.converter, self.sink)
+ self.connect(self.source2, self.filtr2, (self.receiver,1))
+
+ def _set_sink(self):
+ nazwa_pliku_wy = self.options.outputfile
+ ujscie = gr.file_sink(gr.sizeof_float, nazwa_pliku_wy)
+ return ujscie
+
+ def _set_source(self, filename):
+ zrodlo = gr.file_source(gr.sizeof_gr_complex, filename, False)
+ return zrodlo
+
+ def _set_rates(self):
+ options = self.options
+ clock_rate = 52e6
+ self.clock_rate = clock_rate
+ self.input_rate = clock_rate / options.decim
+ self.gsm_symb_rate = 1625000.0 / 6.0
+ self.sps = self.input_rate / self.gsm_symb_rate / self.options.osr
+ print self.sps
+
+ def _set_filter(self):
+ filter_cutoff = 145e3
+ filter_t_width = 10e3
+ offset = 0
+ print "input_rate:", self.input_rate, "sample rate:", self.sps, " filter_cutoff:", filter_cutoff, " filter_t_width:", filter_t_width
+ filter_taps = gr.firdes.low_pass(1.0, self.input_rate, filter_cutoff, filter_t_width, gr.firdes.WIN_HAMMING)
+ filtr = gr.freq_xlating_fir_filter_ccf(1, filter_taps, offset, self.input_rate)
+ return filtr
+
+ def _set_converter(self):
+ v2s = gr.vector_to_stream(gr.sizeof_float, 142)
+ return v2s
+
+ def _set_interpolator(self):
+ interpolator = gr.fractional_interpolator_cc(0, self.sps)
+ return interpolator
+
+ def _set_receiver(self):
+ receiver = gsm.receiver_cf(self.tuner_callback, self.synchronizer_callback, self.options.osr, self.options.key.replace(' ', '').lower(), self.options.configuration.upper())
+ return receiver
+
+ def _process_options(self):
+ parser = OptionParser(option_class=eng_option)
+ parser.add_option("-d", "--decim", type="int", default=112,
+ help="Set USRP decimation rate to DECIM [default=%default]")
+ parser.add_option("-r", "--osr", type="int", default=4,
+ help="Oversampling ratio [default=%default]")
+ parser.add_option("-I", "--inputfile_dl", type="string", default="cfile",
+ help="Input filename for downlink")
+ parser.add_option("-U", "--inputfile_ul", type="string", default="cfile.ul",
+ help="Input filename for uplink")
+ parser.add_option("-O", "--outputfile", type="string", default="cfile2.out",
+ help="Output filename")
+ parser.add_option("-k", "--key", type="string", default="AD 6A 3E C2 B4 42 E4 00",
+ help="KC session key")
+ parser.add_option("-c", "--configuration", type="string", default="",
+ help="Decoder configuration")
+
+ (options, args) = parser.parse_args ()
+ return (options, args)
+
+ def set_center_frequency(self, center_freq):
+ self.filtr1.set_center_freq(center_freq)
+ self.filtr2.set_center_freq(center_freq)
+
+ def set_timing(self, timing_offset):
+ pass
+
+def main():
+ try:
+ gsm_receiver_first_blood().run()
+ except KeyboardInterrupt:
+ pass
+
+if __name__ == '__main__':
+ main()