summaryrefslogtreecommitdiff
path: root/gr-pager/src/pager_flex_deframer.cc
blob: 1f8c9020992b66d7708b2904818bebca3e427a62 (plain)
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
/*
 * Copyright 2004,2006 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <pager_flex_deframer.h>
#include <pageri_flex_modes.h>
#include <gr_io_signature.h>
#include <gr_count_bits.h>

pager_flex_deframer_sptr pager_make_flex_deframer(int rate)
{
    return pager_flex_deframer_sptr(new pager_flex_deframer(rate));
}

// FLEX deframer block takes input from symbol stream with alphabet [0, 1, 2, 3]
// at specified channel rate and and outputs deinterleaved 32-bit FLEX code words 
// at 50, 100, or 200 code words per second (based on detected mode in sync
// word).

pager_flex_deframer::pager_flex_deframer(int rate) :
    gr_block ("flex_deframer",
              gr_make_io_signature (1, 1, sizeof(unsigned char)),
              gr_make_io_signature (1, 1, sizeof(gr_int32))),
    d_sync(rate/1600) // Maximum samples per baud
{
    d_rate = rate;
    set_output_multiple(1);
    enter_idle();
}

void pager_flex_deframer::forecast(int noutput_items, gr_vector_int &inputs_required)
{
    // samples per bit X 32 bits * number of outputs needed
    int items = noutput_items*sizeof(gr_int32)*8*d_spb;
    for (unsigned int i = 0; i < inputs_required.size(); i++)
        inputs_required[i] = items;
}

int pager_flex_deframer::index_avg(int start, int end)
{
    // modulo average
    if (start < end)
        return (end + start)/2;
    else
        return ((end + start)/2 + d_spb/2) % d_spb;
}

bool pager_flex_deframer::test_sync(unsigned char sym)
{
    // 64-bit FLEX sync code:
    // AAAA:BBBBBBBB:CCCC
    //
    // Where BBBBBBBB is always 0xA6C6AAAA
    // and AAAA^CCCC is 0xFFFF
    // 
    // Specific values of AAAA determine what bps and encoding the
    // packet is beyond the frame information word
    //
    // First we match on the marker field with a hamming distance < 4
    // Then we match on the outer code with a hamming distance < 4

    d_sync[d_index] = (d_sync[d_index] << 1) | (sym < 2);
    gr_int64 val = d_sync[d_index];
    gr_int32 marker = ((val & 0x0000FFFFFFFF0000ULL)) >> 16;

    if (gr_count_bits32(marker^FLEX_SYNC_MARKER) < 4) {
        gr_int32 code = ((val & 0xFFFF000000000000ULL) >> 32) |
                         (val & 0x000000000000FFFFULL);

        for (int i = 0; i < num_flex_modes; i++) {
            if (gr_count_bits32(code^flex_modes[i].sync) < 4) {
                d_mode = i;
                return true;
            }
        }

        // Marker received but doesn't match known codes
        // All codes have high word inverted to low word
        unsigned short high = (code & 0xFFFF0000) >> 16;
        unsigned short low = code & 0x0000FFFF;
        unsigned short syn = high^low;
        if (syn == 0xFFFF)
            fprintf(stderr, "Unknown sync code detected: %08X\n", code);
    }

    return false;
}

void pager_flex_deframer::enter_idle()
{
    d_state = ST_IDLE;
    d_index = 0;
    d_start = 0;
    d_center = 0;
    d_end = 0;
    d_count = 0;
    d_mode = 0;
    d_baudrate = 1600;
    d_levels = 2;
    d_spb = d_rate/d_baudrate;
    d_hibit = false;
    d_cdi = 0;
    d_cdw = 0;
    d_blk = 0;
}

void pager_flex_deframer::enter_syncing()
{
    d_start = d_index;
    d_state = ST_SYNCING;
}

void pager_flex_deframer::enter_sync1()
{
    d_state = ST_SYNC1;
    d_end = d_index;
    d_center = index_avg(d_start, d_end);
    d_count = 0;
    fprintf(stderr, "SYNC1=%08X\n", flex_modes[d_mode].sync);
}

void pager_flex_deframer::enter_sync2()
{
    d_state = ST_SYNC2;
    d_count = 0;
    d_baudrate = flex_modes[d_mode].baud;
    d_levels = flex_modes[d_mode].levels;
    d_spb = d_rate/d_baudrate;

    if (d_baudrate == 3200) {
        // Oversampling buffer just got halved
        d_center = d_center/2;
        d_index = d_index/2-d_spb/2; // We're here at the center of a 1600 baud bit
        d_count = -1;                // So this hack gets us in the right place for 3200
    }
}

void pager_flex_deframer::enter_data()
{
    d_state = ST_DATA;
    d_count = 0;
}

void pager_flex_deframer::enter_output()
{
    d_state = ST_OUTPUT;
    d_count = flex_modes[d_mode].phases*88; // Now d_count will count codewords, not bits
    d_output_phase = 0;    // Always phase A
    d_output_index = 0;
}

void pager_flex_deframer::accumulate_frames(unsigned char sym)
{
    // Bits are encoded with 2-bit gray code 
    // 
    // SYM      Bit1 Bit2
    // ---      ---- ----
    //  0         1    1
    //  1         1    0
    //  2         0    0
    //  3         0    1

    // Codewords are interleaved in blocks of 8
    //
    // ABCDEFGH
    // ABCDEFGH
    // ABCDEFGH
    // ABCDEFGH
    // ...
    // etc., for all 32 bits. So each successive incoming bit is multiplexed
    // into a different d_frames[][], indexed by d_cdw

    d_cdw = d_blk*8+d_cdi;
    assert(d_cdw < 88);

    if (d_baudrate == 1600) {
        d_frames[0][d_cdw] <<= 1;
        d_frames[0][d_cdw]  |= (sym < 2);

        if (d_levels == 4) {
            d_frames[1][d_cdw] <<= 1;
            d_frames[1][d_cdw]  |= (sym == 0 || sym == 3);
        }
    }
    else { 
        if (!d_hibit) {
            d_frames[0][d_cdw] <<= 1;
            d_frames[0][d_cdw]  |= (sym < 2);

            if (d_levels == 4) {
                d_frames[1][d_cdw] <<= 1;
                d_frames[1][d_cdw]  |= (sym == 0 || sym == 3);
            }
            d_hibit = true;
        }
        else {
            d_frames[2][d_cdw] <<= 1;
            d_frames[2][d_cdw]  |= (sym < 2);

            if (d_levels == 4) {
                d_frames[3][d_cdw] <<= 1;
                d_frames[3][d_cdw]  |= (sym == 0 || sym == 3);
            }
            d_hibit = false;
        }
    }

    d_cdi = ++d_cdi % 8;
    if (++d_count % (d_baudrate*4/25) == 0)
        d_blk++;    
}

int pager_flex_deframer::general_work(int noutput_items,
    gr_vector_int &ninput_items,
    gr_vector_const_void_star &input_items,
	gr_vector_void_star &output_items)
{
    const unsigned char *in = (const unsigned char *)input_items[0];
    gr_int32 *out = (gr_int32 *)output_items[0];

    int i = 0, j = 0;
    int ninputs = ninput_items[0];

    while (i < ninputs && j < noutput_items) {
        unsigned char sym = 0;
        if (d_state != ST_OUTPUT) {
            sym = *in++; i++;
            d_index = ++d_index % d_spb;
        }
    
        switch (d_state) {
            case ST_IDLE:
                if (test_sync(sym))
                    enter_syncing();
                break;
    
            case ST_SYNCING:
                if (!test_sync(sym)) {
                    enter_sync1();
                    // Output sync code
                    *out++ = flex_modes[d_mode].sync; j++;
                }
                break;
    
            case ST_SYNC1:
                if (d_index == d_center) {
                    d_sync[d_index] = (d_sync[d_index] << 1) | (sym < 2);
                    if (++d_count == 48) { // Skip 16 bits of dotting
                        // Output frame information word
                        *out++ = (gr_int32)(d_sync[d_index] & 0x00000000FFFFFFFFULL); j++;
                        enter_sync2();
                    }
                }
                break;
    
            case ST_SYNC2:
                if (d_index == d_center) {
                    // Skip 25 ms = 40 bits @ 1600 bps, 80 @ 3200 bps
                    if (++d_count == d_baudrate/40)
                        enter_data();
                }
                break;
    
            case ST_DATA:
                if (d_index == d_center) {
                    accumulate_frames(sym);
                    if (d_count == d_baudrate*1760/1000)
                        enter_output();
                }
                break;

            case ST_OUTPUT:
                output_codeword(out++); j++;
                if (--d_count == 0)
                    enter_idle();
                break;

            default:
                assert(0); // memory corruption of d_state if ever gets here
                break;

        }
    }

    consume_each(i);
    return j;
}

void pager_flex_deframer::output_codeword(gr_int32 *out)
{
    *out = d_frames[d_output_phase][d_output_index++];
   
    if (d_output_index == 88) {
        d_output_index = 0;
        d_output_phase++;
        if (d_output_phase == 1 && !flex_modes[d_mode].phase_b)
            d_output_phase++;
        if (d_output_phase == 2 && !flex_modes[d_mode].phase_c)
            d_output_phase++;
        if (d_output_phase == 3 && !flex_modes[d_mode].phase_d)
            d_output_phase++;
    }
}