Merge branch 'dfsg-orig'
[debian/gnuradio] / gr-pager / lib / pager_flex_sync.cc
diff --git a/gr-pager/lib/pager_flex_sync.cc b/gr-pager/lib/pager_flex_sync.cc
new file mode 100644 (file)
index 0000000..6bcee8e
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * 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 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 pager_flex_sync_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;
+}