summaryrefslogtreecommitdiff
path: root/gr-pager/lib/pager_flex_sync.cc
blob: 61cf4ad3827f7485abba2a8c8879d7dbc2d0c76a (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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
 * Copyright 2004,2006,2010 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 3, 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_sync.h>
#include <pageri_flex_modes.h>
#include <pageri_bch3221.h>
#include <pageri_util.h>
#include <gr_io_signature.h>
#include <gr_count_bits.h>
#include <cstdio>

pager_flex_sync_sptr pager_make_flex_sync()
{
    return gnuradio::get_initial_sptr(new pager_flex_sync());
}

// FLEX sync block takes input from sliced baseband stream [0-3] at specified 
// channel rate.  Symbol timing is established based on receiving one of the
// defined FLEX protocol synchronization words.  The block outputs one FLEX frame
// worth of bits on each output phase for the data portion of the frame. Unused phases
// get all zeros, which are considered idle code words.

pager_flex_sync::pager_flex_sync() :
    gr_block ("flex_sync",
    gr_make_io_signature (1, 1, sizeof(unsigned char)),
    gr_make_io_signature (4, 4, sizeof(unsigned char))),
    d_sync(10) // Fixed at 10 samples per baud (@ 1600 baud)
{
    enter_idle();
}

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

int pager_flex_sync::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_sync::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_sync::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 = 16000/d_baudrate;
    d_bit_a = 0;
    d_bit_b = 0;
    d_bit_c = 0;
    d_bit_d = 0;
    d_hibit = false;
    fflush(stdout);
}

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

void pager_flex_sync::enter_sync1()
{
    d_state = ST_SYNC1;
    d_end = d_index;
    d_center = index_avg(d_start, d_end); // Center of goodness
    d_count = 0;
}

void pager_flex_sync::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 = 16000/d_baudrate;

    if (d_baudrate == 3200) {
        // Oversampling buffer just got halved
        d_center = d_center/2;

	// We're here at the center of a 1600 baud bit
	// So this hack puts the index and bit counter
	// in the right place for 3200 bps.
        d_index = d_index/2-d_spb/2;         
	d_count = -1;                
    }				     
}

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

void pager_flex_sync::parse_fiw()
{
    // Nothing is done with these now, but these will end up getting
    // passed as metadata when mblocks are available

    // Bits 31-28 are frame number related, but unknown function
    // This might be a checksum
    d_unknown2 = pageri_reverse_bits8((d_fiw >> 24) & 0xF0);
	
    // Cycle is bits 27-24, reversed
    d_cycle = pageri_reverse_bits8((d_fiw >> 20) & 0xF0);

    // Frame is bits 23-17, reversed
    d_frame = pageri_reverse_bits8((d_fiw >> 16) & 0xFE);

    // Bits 16-11 are some sort of marker, usually identical across
    // many frames but sometimes changes between frames or modes
    d_unknown1 = (d_fiw >> 11) & 0x3F;

    //printf("CYC:%02i FRM:%03i\n", d_cycle, d_frame);
}

int pager_flex_sync::output_symbol(unsigned char sym)
{
    // Here is where we output a 1 or 0 on each phase according
    // to current FLEX mode and symbol value.  Unassigned phases
    // are zero from the enter_idle() initialization.
    //
    // FLEX can transmit the data portion of the frame at either
    // 1600 bps or 3200 bps, and can use either two- or four-level
    // FSK encoding.
    //
    // At 1600 bps, 2-level, a single "phase" is transmitted with bit
    // value '0' using level '3' and bit value '1' using level '0'.
    //
    // At 1600 bps, 4-level, a second "phase" is transmitted, and the 
    // di-bits are encoded with a gray code:
    //
    // Symbol	Phase 1  Phase 2
    // ------   -------  -------
    //   0         1        1
    //   1         1        0
    //   2         0        0
    //   3         0        1
    //
    // At 1600 bps, 4-level, these are called PHASE A and PHASE B.
    //
    // At 3200 bps, the same 1 or 2 bit encoding occurs, except that
    // additionally two streams are interleaved on alternating symbols.
    // Thus, PHASE A (and PHASE B if 4-level) are decoded on one symbol,
    // then PHASE C (and PHASE D if 4-level) are decoded on the next.
    
    int bits = 0;
    
    if (d_baudrate == 1600) {
	d_bit_a = (sym < 2);
	if (d_levels == 4)
	    d_bit_b = (sym == 0) || (sym == 3);

	*d_phase_a++ = d_bit_a;
	*d_phase_b++ = d_bit_b;
	*d_phase_c++ = d_bit_c;
	*d_phase_d++ = d_bit_d;
	bits++;
    }
    else {
	if (!d_hibit) {
	    d_bit_a = (sym < 2);
	    if (d_levels == 4)
		d_bit_b = (sym == 0) || (sym == 3);
	    d_hibit = true;
	}
	else {
	    d_bit_c = (sym < 2);
	    if (d_levels == 4)
		d_bit_d = (sym == 0) || (sym == 3);
	    d_hibit = false;

	    *d_phase_a++ = d_bit_a;
	    *d_phase_b++ = d_bit_b;
	    *d_phase_c++ = d_bit_c;
	    *d_phase_d++ = d_bit_d;
	    bits++;
	}
    }

    return bits;
}

int pager_flex_sync::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];
    d_phase_a = (unsigned char *)output_items[0];
    d_phase_b = (unsigned char *)output_items[1];
    d_phase_c = (unsigned char *)output_items[2];
    d_phase_d = (unsigned char *)output_items[3];

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

    while (i < ninputs && j < noutput_items) {
        unsigned char sym = *in++; i++;
        d_index = ++d_index % d_spb;
    
        switch (d_state) {
            case ST_IDLE:
		// Continually compare the received symbol stream
		// against the known FLEX sync words.
                if (test_sync(sym))
                    enter_syncing();
                break;
    
            case ST_SYNCING:
		// Wait until we stop seeing sync, then calculate
		// the center of the bit period (d_center)
                if (!test_sync(sym))
                    enter_sync1();
                break;
    
            case ST_SYNC1:
		// Skip 16 bits of dotting, then accumulate 32 bits
		// of Frame Information Word.
                if (d_index == d_center) {
		    d_fiw = (d_fiw << 1) | (sym > 1);
                    if (++d_count == 48) {
			// FIW is accumulated, call BCH to error correct it
			pageri_bch3221(d_fiw);
			parse_fiw();
                        enter_sync2();  
		    }
                }
                break;
    
            case ST_SYNC2:
		// This part and the remainder of the frame are transmitted
		// at either 1600 bps or 3200 bps based on the received
		// FLEX sync word. The second SYNC header is 25ms of idle bits
		// at either speed.
                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:
		// The data portion of the frame is 1760 ms long at either 
		// baudrate.  This is 2816 bits @ 1600 bps and 5632 bits @ 3200 bps.
		// The output_symbol() routine decodes and doles out the bits
		// to each of the four transmitted phases of FLEX interleaved codes.
                if (d_index == d_center) {
		    j += output_symbol(sym);		    
                    if (++d_count == d_baudrate*1760/1000)
                        enter_idle();
		}
                break;

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

    consume_each(i);
    return j;
}