summaryrefslogtreecommitdiff
path: root/gr-digital/lib/ofdm_cyclic_prefixer_impl.cc
blob: cb05338b1f18f57d826f835f3cd3a2be0a1ddbad (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
/* -*- c++ -*- */
/*
 * Copyright 2013, 2018 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */

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

#include "ofdm_cyclic_prefixer_impl.h"
#include <gnuradio/io_signature.h>

namespace gr {
namespace digital {

// Do not break backwards compatibility and overload the make function.
ofdm_cyclic_prefixer::sptr ofdm_cyclic_prefixer::make(size_t input_size,
                                                      size_t output_size,
                                                      int rolloff_len,
                                                      const std::string& len_tag_key)
{
    int fft_len = input_size;
    std::vector<int> cp_lengths(
        1, static_cast<int>(output_size - input_size)); // Cast to silence compiler :(
    return gnuradio::make_block_sptr<ofdm_cyclic_prefixer_impl>(
        fft_len, cp_lengths, rolloff_len, len_tag_key);
}

ofdm_cyclic_prefixer::sptr ofdm_cyclic_prefixer::make(int fft_len,
                                                      const std::vector<int>& cp_lengths,
                                                      int rolloff_len,
                                                      const std::string& len_tag_key)
{
    return gnuradio::make_block_sptr<ofdm_cyclic_prefixer_impl>(
        fft_len, cp_lengths, rolloff_len, len_tag_key);
}

ofdm_cyclic_prefixer_impl::ofdm_cyclic_prefixer_impl(int fft_len,
                                                     const std::vector<int>& cp_lengths,
                                                     int rolloff_len,
                                                     const std::string& len_tag_key)
    : gr::tagged_stream_block("ofdm_cyclic_prefixer",
                              gr::io_signature::make(1, 1, fft_len * sizeof(gr_complex)),
                              gr::io_signature::make(1, 1, sizeof(gr_complex)),
                              len_tag_key),
      d_fft_len(fft_len),
      d_state(0),
      d_cp_max(0),
      d_cp_min(std::numeric_limits<int>::max()),
      d_rolloff_len(rolloff_len),
      d_cp_lengths(cp_lengths),
      d_up_flank((rolloff_len ? rolloff_len - 1 : 0), 0),
      d_down_flank((rolloff_len ? rolloff_len - 1 : 0), 0),
      d_delay_line(0, 0),
      d_len_tag_key(len_tag_key)
{
    // Sanity
    if (d_cp_lengths.empty()) {
        throw std::invalid_argument(this->alias() +
                                    std::string(": CP lengths vector can not be empty."));
    }
    for (size_t i = 0; i < d_cp_lengths.size(); i++) {
        if (d_cp_lengths[i] != 0) {
            break;
        }
        if (i == d_cp_lengths.size() - 1) {
            throw std::invalid_argument(
                this->alias() +
                std::string(": Please provide at least one CP which is != 0."));
        }
    }
    for (const int cp_length : d_cp_lengths) {
        d_cp_max = std::max(d_cp_max, cp_length);
        d_cp_min = std::min(d_cp_min, cp_length);
    }
    if (d_cp_min < 0) {
        throw std::invalid_argument(this->alias() +
                                    std::string(": The minimum CP allowed is 0."));
    }
    // Give the buffer allocator and scheduler a hint about the ratio between input and
    // output.
    set_relative_rate(d_cp_max + d_fft_len);
    // Flank of length 1 would just be rectangular.
    if (d_rolloff_len == 1) {
        d_rolloff_len = 0;
        GR_LOG_WARN(d_logger,
                    "Set rolloff to 0, because 1 would result in a boxcar function.");
    }
    if (d_rolloff_len) {
        d_delay_line.resize(d_rolloff_len - 1, 0);
        // More sanity
        if (d_rolloff_len > d_cp_min) {
            throw std::invalid_argument(
                this->alias() + std::string(": Rolloff length must be smaller than any "
                                            "of the cyclic prefix lengths."));
        }
        /* The actual flanks are one sample shorter than d_rolloff_len, because the
           first sample of the up- and down flank is always zero and one, respectively.*/
        for (int i = 1; i < d_rolloff_len; i++) {
            d_up_flank[i - 1] = 0.5 * (1 + cos(M_PI * i / rolloff_len - M_PI));
            d_down_flank[i - 1] =
                0.5 * (1 + cos(M_PI * (rolloff_len - i) / rolloff_len - M_PI));
        }
    }
    if (d_len_tag_key.empty()) {
        // noutput_items is set to be a multiple of the largest possible output size.
        // It is always OK to return less (in case of the shorter CP).
        set_output_multiple(d_fft_len + d_cp_max);
    } else {
        // Avoid automatic tag propagation and propagate them manually.
        set_tag_propagation_policy(TPP_DONT);
    }
}

ofdm_cyclic_prefixer_impl::~ofdm_cyclic_prefixer_impl() {}

int ofdm_cyclic_prefixer_impl::calculate_output_stream_length(
    const gr_vector_int& ninput_items)
{
    int noutput_items = ninput_items[0] * (d_cp_max + d_fft_len) +
                        (d_len_tag_key.empty() ? 0 : d_delay_line.size());
    return noutput_items;
}


// Operates in two ways:
// - When there's a length tag name specified, operates in packet mode.
//   Here, an entire OFDM frame is processed at once. The final OFDM symbol
//   is postfixed with the delay line of the pulse shape.
//   We manually propagate tags.
// - Otherwise, we're in freewheeling mode. Process as many OFDM symbols as
//   are space for in the output buffer. The delay line is never flushed.
//   Tags are propagated by the scheduler.
int ofdm_cyclic_prefixer_impl::work(int noutput_items,
                                    gr_vector_int& ninput_items,
                                    gr_vector_const_void_star& input_items,
                                    gr_vector_void_star& output_items)
{
    gr_complex* in = (gr_complex*)input_items[0];
    gr_complex* out = (gr_complex*)output_items[0];
    int symbols_to_read = 0;
    // 1) Figure out if we're in freewheeling or packet mode.
    if (!d_len_tag_key.empty()) {
        symbols_to_read = ninput_items[0];
    } else {
        symbols_to_read =
            std::min(noutput_items / (int)(d_fft_len + d_cp_max), ninput_items[0]);
    }
    noutput_items = 0;
    // 2) Do the cyclic prefixing and, optionally, the pulse shaping.
    for (int sym_idx = 0; sym_idx < symbols_to_read; sym_idx++) {
        memcpy(static_cast<void*>(out + d_cp_lengths[d_state]),
               static_cast<void*>(in),
               d_fft_len * sizeof(gr_complex));
        memcpy(static_cast<void*>(out),
               static_cast<void*>(in + d_fft_len - d_cp_lengths[d_state]),
               d_cp_lengths[d_state] * sizeof(gr_complex));
        if (d_rolloff_len) {
            for (int i = 0; i < d_rolloff_len - 1; i++) {
                out[i] = out[i] * d_up_flank[i] + d_delay_line[i];
                /* This is basically a cyclic suffix, but completely shifted into the next
                   symbol. The data rate does not change. */
                d_delay_line[i] = in[i] * d_down_flank[i];
            }
        }
        in += d_fft_len;
        out += d_fft_len + d_cp_lengths[d_state];
        // Raise the number of noutput_items depending on how long the current output was.
        noutput_items += d_fft_len + d_cp_lengths[d_state];
        // Propagate tags.
        unsigned last_state = d_state > 0 ? d_state - 1 : d_cp_lengths.size() - 1;
        std::vector<tag_t> tags;
        get_tags_in_range(
            tags, 0, nitems_read(0) + sym_idx, nitems_read(0) + sym_idx + 1);
        for (unsigned i = 0; i < tags.size(); i++) {
            tags[i].offset = ((tags[i].offset - nitems_read(0)) *
                              (d_fft_len + d_cp_lengths[last_state])) +
                             nitems_written(0);
            add_item_tag(0, tags[i].offset, tags[i].key, tags[i].value);
        }
        // Finally switch to next state.
        ++d_state;
        d_state %= d_cp_lengths.size();
    }
    /* 3) If we're in packet mode:
          - flush the delay line, if applicable */
    if (!d_len_tag_key.empty()) {
        if (d_rolloff_len) {
            std::memcpy(static_cast<void*>(out),
                        static_cast<void*>(d_delay_line.data()),
                        sizeof(gr_complex) * d_delay_line.size());
            d_delay_line.assign(d_delay_line.size(), 0);
            // Make last symbol a bit longer.
            noutput_items += d_delay_line.size();
        }
    } else {
        consume_each(symbols_to_read);
    }

    return noutput_items;
}

} /* namespace digital */
} /* namespace gr */