summaryrefslogtreecommitdiff
path: root/gr-dtv/lib/dvbt/dvbt_map_impl.cc
blob: 48afea560ee3bbec3dc1d8286b9ce923472993cb (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
/* -*- c++ -*- */
/*
 * Copyright 2015,2016 Free Software Foundation, Inc.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */

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

#include "dvbt_map_impl.h"
#include <gnuradio/io_signature.h>
#include <math.h>
#include <complex>

namespace gr {
namespace dtv {

dvbt_map::sptr dvbt_map::make(int nsize,
                              dvb_constellation_t constellation,
                              dvbt_hierarchy_t hierarchy,
                              dvbt_transmission_mode_t transmission,
                              float gain)
{
    return gnuradio::make_block_sptr<dvbt_map_impl>(
        nsize, constellation, hierarchy, transmission, gain);
}

/*
 * The private constructor
 */
dvbt_map_impl::dvbt_map_impl(int nsize,
                             dvb_constellation_t constellation,
                             dvbt_hierarchy_t hierarchy,
                             dvbt_transmission_mode_t transmission,
                             float gain)
    : block("dvbt_map",
            io_signature::make(1, 1, sizeof(unsigned char) * nsize),
            io_signature::make(1, 1, sizeof(gr_complex) * nsize)),
      config(constellation,
             hierarchy,
             gr::dtv::C1_2,
             gr::dtv::C1_2,
             gr::dtv::GI_1_32,
             transmission),
      d_nsize(nsize),
      d_constellation_size(0),
      d_step(0),
      d_alpha(0),
      d_gain(0.0)
{
    // Get parameters from config object
    d_constellation_size = config.d_constellation_size;
    d_transmission_mode = config.d_transmission_mode;
    d_step = config.d_step;
    d_alpha = config.d_alpha;
    d_gain = gain * config.d_norm;

    d_constellation_points = new (std::nothrow) gr_complex[d_constellation_size];
    if (d_constellation_points == NULL) {
        GR_LOG_FATAL(d_logger,
                     "DVB-T Map, cannot allocate memory for d_constellation_points.");
        throw std::bad_alloc();
    }

    make_constellation_points(d_constellation_size, d_step, d_alpha);
}

/*
 * Our virtual destructor.
 */
dvbt_map_impl::~dvbt_map_impl() { delete[] d_constellation_points; }

unsigned int dvbt_map_impl::bin_to_gray(unsigned int val) { return (val >> 1) ^ val; }

void dvbt_map_impl::make_constellation_points(int size, int step, int alpha)
{
    // The symmetry of the constellation is used to calculate
    // 16-QAM from QPSK and 64-QAM form 16-QAM

    int bits_per_axis = log2(size) / 2;
    int steps_per_axis = sqrt(size) / 2 - 1;

    for (int i = 0; i < size; i++) {
        // This is the quadrant made of the first two bits starting from MSB
        int q = i >> (2 * (bits_per_axis - 1)) & 3;
        // Sign for correctly calculate I and Q in each quadrant
        int sign0 = (q >> 1) ? -1 : 1;
        int sign1 = (q & 1) ? -1 : 1;

        int x = (i >> (bits_per_axis - 1)) & ((1 << (bits_per_axis - 1)) - 1);
        int y = i & ((1 << (bits_per_axis - 1)) - 1);

        int xval = alpha + (steps_per_axis - x) * step;
        int yval = alpha + (steps_per_axis - y) * step;

        int val = (bin_to_gray(x) << (bits_per_axis - 1)) + bin_to_gray(y);

        // ETSI EN 300 744 Clause 4.3.5
        // Actually the constellation is gray coded
        // but the bits on each axis are not taken in consecutive order
        // So we need to convert from b0b2b4b1b3b5->b0b1b2b3b4b5(QAM64)

        x = 0;
        y = 0;

        for (int j = 0; j < (bits_per_axis - 1); j++) {
            x += ((val >> (1 + 2 * j)) & 1) << j;
            y += ((val >> (2 * j)) & 1) << j;
        }

        val = (q << 2 * (bits_per_axis - 1)) + (x << (bits_per_axis - 1)) + y;

        // Keep corresponding symbol bits->complex symbol in one vector
        // Normalize the signal using gain
        d_constellation_points[val] = d_gain * gr_complex(sign0 * xval, sign1 * yval);
    }
}

gr_complex dvbt_map_impl::find_constellation_point(int val)
{
    return d_constellation_points[val];
}

void dvbt_map_impl::forecast(int noutput_items, gr_vector_int& ninput_items_required)
{
    ninput_items_required[0] = noutput_items;
}

int dvbt_map_impl::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_complex* out = (gr_complex*)output_items[0];

    for (int i = 0; i < (noutput_items * d_nsize); i++) {
        out[i] = find_constellation_point(in[i]);
    }

    // Tell runtime system how many input items we consumed on
    // each input stream.
    consume_each(noutput_items);

    // Tell runtime system how many output items we produced.
    return noutput_items;
}

} /* namespace dtv */
} /* namespace gr */