summaryrefslogtreecommitdiff
path: root/gr-atsc/src/lib/atsci_viterbi_gen.cc
blob: bff3c47736a1352e36958a1c90a00e5df541a404 (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
/* -*- c++ -*- */
/*
 * Copyright 2002,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.
 */

#include <iostream>
#include <stdio.h>

using std::cerr;

/*
 * Trellis-encode a whole pile of 12 data segments for ATSC.
 * This also includes scrambling the data among twelve Trellis encoders.
 *
 * Input is twelve 207-byte blocks of raw data (Reed-Solomon output that's
 * been scrambled up by interleaving with other blocks).
 *
 * Output is 12 x 208 x 4 bytes, each byte containing a 3-bit symbol.
 * The first 4 bytes are the segment sync symbol.
 *
    Got the first version of Trellis encoder coded.  Compiles, but I
    didn't realize that each data segment contains an odd number of BITS!
    The second data segment in a field starts by pulling bits out of the
    middles of the bytes it's encoding.  You actually have to read all the
    entries in that table on page 59 AND 60 to get it.

    There's a 4-segment asymmetric pattern of bit accesses.
    There's a 3-segment asymmetric pattern of muxing encoders.

    The result is there's a 12-segment pattern that repeats throughout
    the encoding of a field.  So this routine now encodes 12 segments at once.

    This encoding system was either designed by a complete idiot or by
    a complete genius.  It's highly complex when it could have been very
    simple.  Now the question is whether
    this incredible complexity buys us anything subtle and important.
 */

#define SEGMENT_SIZE	207
#define	INPUT_SIZE	(SEGMENT_SIZE * 12)
#define	DIBITS_PER_BYTE	4
#define	EXTRAS		(4 * 12)	/* FIXME, sync symbols and such */
#define	SYMBOLS_OUT	((INPUT_SIZE * DIBITS_PER_BYTE) + EXTRAS)
#define SEGOF(x)	( (x) / ((SEGMENT_SIZE+1) * DIBITS_PER_BYTE))
#define SYMOF(x)	(((x) % ((SEGMENT_SIZE+1) * DIBITS_PER_BYTE))-4)
#define	ENCODERS	12
#define	ENCODER_SEG_BUMP	4


/* Shift counts to bit numbers (high order, low order); 9x entries unused */
static const int bit1[8] = {1, 99, 3, 98, 5, 97, 7, 96};
static const int bit2[8] = {0, 99, 2, 98, 4, 97, 6, 96};

/* Detailed Debugging */
int debug_dec = 0;

/*
 * Build indirect data structures to say which symbols go into which
 * encoder, and then where the resulting dibits from the encoders go.
 */
int
build_decode_structures (char *fileout)
{
  int retval = 0;
  int i;
  int encoder;
  int trellis_wheredata[ENCODERS];
  unsigned char *symp, *next_sym_seg;
  unsigned char symbols[SYMBOLS_OUT];
  int chunk;
  int shift;
  int skip_encoder_bump;
  int *enco_syms[ENCODERS];
  int *enco_dibits[ENCODERS];
  int j;
  /* The data structures we'll build and then spit out... */
  int sync_symbol_indices[1000];
  int sync_symbol_indices_max;
  int enco_which_syms[ENCODERS][INPUT_SIZE];
  int enco_which_dibits[ENCODERS][INPUT_SIZE];
  int enco_which_max;
  #define	BIT_PTR(int,shif) (((int) << 3) | ((shif) & 0x7))
  /* Running indices into them as we build 'em... */
  int *syncsyms = sync_symbol_indices;

  /* Start our running pointers at the start of our per-encoder subarrays */
  for (i = 0; i < ENCODERS; i++) {
    enco_dibits[i] = enco_which_dibits[i];
    enco_syms[i] = enco_which_syms[i];
  }

  encoder = ENCODERS - ENCODER_SEG_BUMP;
  skip_encoder_bump = 0;
  symp = symbols;
  next_sym_seg = symp;

  for (chunk = 0;
       chunk < INPUT_SIZE;
       chunk += ENCODERS) {
    /* Associate data bytes with the Trellis encoders.
       They get loaded or stored in an order that depends on where we are in the
       segment sync progress (sigh).
       GRR!  When the chunk reload happens at the same time as the
       segment boundary, we should bump the encoder NOW for the reload,
       rather than LATER during the bitshift transition!!! */
    if (symp >= next_sym_seg) {
      encoder = (encoder + ENCODER_SEG_BUMP) % ENCODERS;
      skip_encoder_bump = 1;
    }

    /* Remember where the data bytes are going to go, once we've
       accumulated them from the 12 interleaved decoders */
    for (i = 0; i < ENCODERS; i++) {
      trellis_wheredata[encoder] = chunk+i;
      encoder++;
      if (encoder >= ENCODERS) encoder = 0;
    }

    for (shift = 6; shift >= 0; shift -= 2) {

      /* Segment boundaries happen to occur on some bitshift transitions. */
      if (symp >= next_sym_seg) {
	/* Segment transition.  Output a data segment sync symbol, and
	   mess with the trellis encoder mux.  */
	*syncsyms++ = symp - symbols;
	symp += 4;
        next_sym_seg = symp + (SEGMENT_SIZE * DIBITS_PER_BYTE);

	if (!skip_encoder_bump)
	  encoder = (encoder + ENCODER_SEG_BUMP) % ENCODERS;
	skip_encoder_bump = 0;
      }

      /* Now run each of the 12 Trellis encoders to spit out 12 symbols.
         Each encoder takes input from the same byte of the chunk, but the
         outputs of the encoders come out in various orders.
         NOPE -- this is false.  The encoders take input from various
         bytes of the chunk (which changes at segment sync time), AND
         they also come out in various orders.  You really do have to
         keep separate track of:  the datasegs bytes, the encoders, and
         the symbol bytes -- because they're all moving with respect to
         each other!!!  */
      for (i = 0; i < ENCODERS; i++) {
        if (debug_dec)
	  printf ("Seg %ld Symb %3ld Trell %2d Byte %6d Bits %d-%d = ",
		  (long) SEGOF(symp-symbols), (long) SYMOF(symp-symbols),
		  encoder, trellis_wheredata[encoder],
		  bit1[shift], bit2[shift]);

	/* Decoding:  Grab symbol, run through decoder, slice dibit into
	   buffer.  */
	/* This symbol goes into this encoder next */
	*(enco_syms[encoder]++) = symp - symbols;
        symp++;
	/* The next output from this encoder goes into these dibits */
	*(enco_dibits[encoder]++) = BIT_PTR(trellis_wheredata[encoder], shift);

        encoder++; if (encoder >= ENCODERS) encoder = 0;
      } /* Encoders */
    } /* Bit shifts */

#if 0
    /* Now dump out the chunk of 12 data bytes that the twelve decoders have
       accumulated. */
  unsigned char trellis_buffer[ENCODERS];
  unsigned char dibit;
  unsigned char symbol;
  int save_state;
    for (i = 0; i < ENCODERS; i++) {
      datasegs [trellis_wheredata[encoder]] = trellis_buffer[encoder];
      encoder++;
      if (encoder >= ENCODERS) encoder = 0;
    } /* Dumping encoder bytes */
#endif
  } /* Chunks */

  /* Now print the resulting data structures in C++ */

  if (!freopen(fileout, "w", stdout))
    return 2;

  printf ("/*\n\
 * atsc_viterbi_mux.cc\n\
 *\n\
 * Data structures for knowing which symbols are fed to which\n\
 * Viterbi decoders, and then where to put the resulting output dibits.\n\
 *\n\
 * Generated by 'atsc_viterbi_gen.cc'.\n\
 */\n\n");
  sync_symbol_indices_max = syncsyms - sync_symbol_indices;
  printf ("const unsigned int sync_symbol_indices_max = %d;\n",
	  sync_symbol_indices_max);
  printf ("const unsigned int sync_symbol_indices[%d] = {\n  ",
	  sync_symbol_indices_max);
  for (i = 0; i < sync_symbol_indices_max; i++) {
    printf ("%d,%s", sync_symbol_indices[i], (7 == i%8)? "\n  ": " ");
  }
  printf ("};\n\n");

  enco_which_max = enco_dibits[0] - enco_which_dibits[0];
  for (i = 0; i < ENCODERS; i++)
    if (enco_which_max != enco_dibits[i] - enco_which_dibits[i]) {
      cerr << "Encoder " << i << " has different max_dibits " <<
	enco_dibits[i] - enco_which_dibits[i] << " than " << enco_which_max;
      retval = 3;
    }

  printf ("const unsigned int enco_which_max = %d;\n" , enco_which_max);

  printf ("const unsigned int enco_which_syms[%d][%d] = {\n",
	  ENCODERS, enco_which_max);
  for (i = 0; i < ENCODERS; i++) {
    printf ("  /* %d */\n  {", i);
    for (j = 0; j < enco_which_max; j++)
      printf ("%d,%s", enco_which_syms[i][j], (7 == j%8)? "\n   ": " ");
    printf ("},\n");
  }
  printf ("};\n\n");

  printf ("const unsigned int enco_which_dibits[%d][%d] = {\n",
	  ENCODERS, enco_which_max);
  for (i = 0; i < ENCODERS; i++) {
    printf ("  /* %d */\n  {", i);
    for (j = 0; j < enco_which_max; j++)
      printf ("%d,%s", enco_which_dibits[i][j], (7 == j%8)? "\n   ": " ");
    printf ("},\n");
  }
  printf ("};\n\n");
  return retval;
}

int
usage()
{
  cerr << "atsc_viterbi_gen: Usage:\n";
  cerr << "  ./atsc_viterbi_gen -o atsc_viterbi_mux.cc\n";
  cerr << "That's all, folks!\n";
  return 1;
}


int
main(int argc, char **argv)
{
  if (argc != 3) return usage();
  if (argv[1][0] != '-'
   || argv[1][1] != 'o'
   || argv[1][2] != 0   ) return usage();
  return build_decode_structures(argv[2]);
}