/* -*- c++ -*- */
/*
 * Copyright 2015-2017,2019 Free Software Foundation, Inc.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */

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

#include "dvbt2_framemapper_cc_impl.h"
#include <gnuradio/io_signature.h>
#include <algorithm>

namespace gr {
namespace dtv {

dvbt2_framemapper_cc::sptr
dvbt2_framemapper_cc::make(dvb_framesize_t framesize,
                           dvb_code_rate_t rate,
                           dvb_constellation_t constellation,
                           dvbt2_rotation_t rotation,
                           int fecblocks,
                           int tiblocks,
                           dvbt2_extended_carrier_t carriermode,
                           dvbt2_fftsize_t fftsize,
                           dvb_guardinterval_t guardinterval,
                           dvbt2_l1constellation_t l1constellation,
                           dvbt2_pilotpattern_t pilotpattern,
                           int t2frames,
                           int numdatasyms,
                           dvbt2_papr_t paprmode,
                           dvbt2_version_t version,
                           dvbt2_preamble_t preamble,
                           dvbt2_inputmode_t inputmode,
                           dvbt2_reservedbiasbits_t reservedbiasbits,
                           dvbt2_l1scrambled_t l1scrambled,
                           dvbt2_inband_t inband)
{
    return gnuradio::make_block_sptr<dvbt2_framemapper_cc_impl>(framesize,
                                                                rate,
                                                                constellation,
                                                                rotation,
                                                                fecblocks,
                                                                tiblocks,
                                                                carriermode,
                                                                fftsize,
                                                                guardinterval,
                                                                l1constellation,
                                                                pilotpattern,
                                                                t2frames,
                                                                numdatasyms,
                                                                paprmode,
                                                                version,
                                                                preamble,
                                                                inputmode,
                                                                reservedbiasbits,
                                                                l1scrambled,
                                                                inband);
}

/*
 * The private constructor
 */
dvbt2_framemapper_cc_impl::dvbt2_framemapper_cc_impl(
    dvb_framesize_t framesize,
    dvb_code_rate_t rate,
    dvb_constellation_t constellation,
    dvbt2_rotation_t rotation,
    int fecblocks,
    int tiblocks,
    dvbt2_extended_carrier_t carriermode,
    dvbt2_fftsize_t fftsize,
    dvb_guardinterval_t guardinterval,
    dvbt2_l1constellation_t l1constellation,
    dvbt2_pilotpattern_t pilotpattern,
    int t2frames,
    int numdatasyms,
    dvbt2_papr_t paprmode,
    dvbt2_version_t version,
    dvbt2_preamble_t preamble,
    dvbt2_inputmode_t inputmode,
    dvbt2_reservedbiasbits_t reservedbiasbits,
    dvbt2_l1scrambled_t l1scrambled,
    dvbt2_inband_t inband)
    : gr::block("dvbt2_framemapper_cc",
                gr::io_signature::make(1, 1, sizeof(gr_complex)),
                gr::io_signature::make(1, 1, sizeof(gr_complex)))
{
    L1Pre* l1preinit = &L1_Signalling[0].l1pre_data;
    L1Post* l1postinit = &L1_Signalling[0].l1post_data;
    double normalization;
    double m_16qam_lookup[4] = { 3.0, 1.0, -3.0, -1.0 };
    double m_64qam_lookup[8] = { 7.0, 5.0, 1.0, 3.0, -7.0, -5.0, -1.0, -3.0 };
    int real_index, imag_index;
    int N_punc_temp, N_post_temp;
    if (framesize == FECFRAME_NORMAL) {
        switch (constellation) {
        case MOD_QPSK:
            cell_size = 32400;
            break;
        case MOD_16QAM:
            cell_size = 16200;
            break;
        case MOD_64QAM:
            cell_size = 10800;
            break;
        case MOD_256QAM:
            cell_size = 8100;
            break;
        default:
            cell_size = 0;
            break;
        }
    } else {
        switch (constellation) {
        case MOD_QPSK:
            cell_size = 8100;
            break;
        case MOD_16QAM:
            cell_size = 4050;
            break;
        case MOD_64QAM:
            cell_size = 2700;
            break;
        case MOD_256QAM:
            cell_size = 2025;
            break;
        default:
            cell_size = 0;
            break;
        }
    }
    l1preinit->type = STREAMTYPE_TS;
    l1preinit->bwt_ext = carriermode;
    fft_size = fftsize;
    l1preinit->s1 = preamble;
    l1preinit->s2 = fft_size & 0x7;
    l1preinit->l1_repetition_flag = FALSE;
    l1preinit->guard_interval = guardinterval;
    l1preinit->papr = paprmode;
    l1preinit->l1_mod = l1constellation;
    l1preinit->l1_cod = 0;
    l1preinit->l1_fec_type = 0;
    l1preinit->l1_post_info_size = KSIG_POST - 32;
    l1preinit->pilot_pattern = pilotpattern;
    l1preinit->tx_id_availability = 0;
    l1preinit->cell_id = 0;
    l1preinit->network_id = 0x3085;
    l1preinit->t2_system_id = 0x8001;
    l1preinit->num_t2_frames = t2frames;
    l1preinit->num_data_symbols = numdatasyms;
    l1preinit->regen_flag = FALSE;
    l1preinit->l1_post_extension = FALSE;
    l1preinit->num_rf = 1;
    l1preinit->current_rf_index = 0;
    l1preinit->t2_version = version;
    if (version == VERSION_131) {
        l1preinit->l1_post_scrambled = l1scrambled;
    } else {
        l1preinit->l1_post_scrambled = FALSE;
    }
    l1preinit->t2_base_lite = FALSE;
    if (reservedbiasbits == RESERVED_ON && version == VERSION_131) {
        l1preinit->reserved = 0xf;
    } else {
        l1preinit->reserved = 0x0;
    }

    l1postinit->sub_slices_per_frame = 1;
    l1postinit->num_plp = 1;
    l1postinit->num_aux = 0;
    l1postinit->aux_config_rfu = 0;
    l1postinit->rf_idx = 0;
    l1postinit->frequency = 729833333;
    l1postinit->plp_id = 0;
    l1postinit->plp_type = 1;
    l1postinit->plp_payload_type = 3;
    l1postinit->ff_flag = 0;
    l1postinit->first_rf_idx = 0;
    l1postinit->first_frame_idx = 0;
    l1postinit->plp_group_id = 1;
    switch (rate) {
    case C1_3:
        l1postinit->plp_cod = 6;
        break;
    case C2_5:
        l1postinit->plp_cod = 7;
        break;
    case C1_2:
        l1postinit->plp_cod = 0;
        break;
    case C3_5:
        l1postinit->plp_cod = 1;
        break;
    case C2_3:
        l1postinit->plp_cod = 2;
        break;
    case C3_4:
        l1postinit->plp_cod = 3;
        break;
    case C4_5:
        l1postinit->plp_cod = 4;
        break;
    case C5_6:
        l1postinit->plp_cod = 5;
        break;
    default:
        l1postinit->plp_cod = 0;
        break;
    }
    l1postinit->plp_mod = constellation;
    l1postinit->plp_rotation = rotation;
    l1postinit->plp_fec_type = framesize;
    l1postinit->plp_num_blocks_max = fecblocks;
    l1postinit->frame_interval = 1;
    l1postinit->time_il_length = tiblocks;
    l1postinit->time_il_type = 0;
    l1postinit->in_band_a_flag = 0;
    if (inband == INBAND_ON && version == VERSION_131) {
        l1postinit->in_band_b_flag = 1;
    } else {
        l1postinit->in_band_b_flag = 0;
    }
    if (reservedbiasbits == RESERVED_ON && version == VERSION_131) {
        l1postinit->reserved_1 = 0x7ff;
    } else {
        l1postinit->reserved_1 = 0x0;
    }
    if (version == VERSION_111) {
        l1postinit->plp_mode = 0;
    } else {
        l1postinit->plp_mode = inputmode + 1;
    }
    l1postinit->static_flag = 0;
    l1postinit->static_padding_flag = 0;
    l1postinit->fef_length_msb = 0;
    if (reservedbiasbits == RESERVED_ON && version == VERSION_131) {
        l1postinit->reserved_2 = 0x3fffffff;
    } else {
        l1postinit->reserved_2 = 0;
    }
    l1postinit->frame_idx = 0;
    l1postinit->sub_slice_interval = 0;
    l1postinit->type_2_start = 0;
    l1postinit->l1_change_counter = 0;
    l1postinit->start_rf_idx = 0;
    if (reservedbiasbits == RESERVED_ON && version == VERSION_131) {
        l1postinit->reserved_3 = 0xff;
    } else {
        l1postinit->reserved_3 = 0;
    }
    l1postinit->plp_id_dynamic = 0;
    l1postinit->plp_start = 0;
    l1postinit->plp_num_blocks = fecblocks;
    if (reservedbiasbits == RESERVED_ON && version == VERSION_131) {
        l1postinit->reserved_4 = 0xff;
        l1postinit->reserved_5 = 0xff;
    } else {
        l1postinit->reserved_4 = 0;
        l1postinit->reserved_5 = 0;
    }

    bch_poly_build_tables();
    l1pre_ldpc_lookup_generate();
    m_bpsk[0] = gr_complex(1.0, 0.0);
    m_bpsk[1] = gr_complex(-1.0, 0.0);
    unmodulated = gr_complex(0.0, 0.0);

    l1post_ldpc_lookup_generate();
    switch (l1constellation) {
    case L1_MOD_BPSK:
        eta_mod = 1;
        break;
    case L1_MOD_QPSK:
        normalization = sqrt(2);
        m_qpsk[0] = gr_complex(1.0 / normalization, 1.0 / normalization);
        m_qpsk[1] = gr_complex(1.0 / normalization, -1.0 / normalization);
        m_qpsk[2] = gr_complex(-1.0 / normalization, 1.0 / normalization);
        m_qpsk[3] = gr_complex(-1.0 / normalization, -1.0 / normalization);
        eta_mod = 2;
        break;
    case L1_MOD_16QAM:
        normalization = sqrt(10);
        for (int i = 0; i < 16; i++) {
            real_index = ((i & 0x8) >> 2) | ((i & 0x2) >> 1);
            imag_index = ((i & 0x4) >> 1) | ((i & 0x1) >> 0);
            m_16qam[i] = gr_complex(m_16qam_lookup[real_index] / normalization,
                                    m_16qam_lookup[imag_index] / normalization);
        }
        eta_mod = 4;
        break;
    case L1_MOD_64QAM:
        normalization = sqrt(42);
        for (int i = 0; i < 64; i++) {
            real_index = ((i & 0x20) >> 3) | ((i & 0x8) >> 2) | ((i & 0x2) >> 1);
            imag_index = ((i & 0x10) >> 2) | ((i & 0x4) >> 1) | ((i & 0x1) >> 0);
            m_64qam[i] = gr_complex(m_64qam_lookup[real_index] / normalization,
                                    m_64qam_lookup[imag_index] / normalization);
        }
        eta_mod = 6;
        break;
    }
    N_P2 = 1;
    C_P2 = 0;
    N_FC = 0;
    C_FC = 0;
    C_DATA = 0;
    if ((preamble == PREAMBLE_T2_SISO) || (preamble == PREAMBLE_T2_LITE_SISO)) {
        switch (fft_size) {
        case FFTSIZE_1K:
            N_P2 = 16;
            C_P2 = 558;
            break;
        case FFTSIZE_2K:
            N_P2 = 8;
            C_P2 = 1118;
            break;
        case FFTSIZE_4K:
            N_P2 = 4;
            C_P2 = 2236;
            break;
        case FFTSIZE_8K:
        case FFTSIZE_8K_T2GI:
            N_P2 = 2;
            C_P2 = 4472;
            break;
        case FFTSIZE_16K:
        case FFTSIZE_16K_T2GI:
            N_P2 = 1;
            C_P2 = 8944;
            break;
        case FFTSIZE_32K:
        case FFTSIZE_32K_T2GI:
            N_P2 = 1;
            C_P2 = 22432;
            break;
        }
    } else {
        switch (fft_size) {
        case FFTSIZE_1K:
            N_P2 = 16;
            C_P2 = 546;
            break;
        case FFTSIZE_2K:
            N_P2 = 8;
            C_P2 = 1098;
            break;
        case FFTSIZE_4K:
            N_P2 = 4;
            C_P2 = 2198;
            break;
        case FFTSIZE_8K:
        case FFTSIZE_8K_T2GI:
            N_P2 = 2;
            C_P2 = 4398;
            break;
        case FFTSIZE_16K:
        case FFTSIZE_16K_T2GI:
            N_P2 = 1;
            C_P2 = 8814;
            break;
        case FFTSIZE_32K:
        case FFTSIZE_32K_T2GI:
            N_P2 = 1;
            C_P2 = 17612;
            break;
        }
    }
    switch (fft_size) {
    case FFTSIZE_1K:
        switch (pilotpattern) {
        case PILOT_PP1:
            C_DATA = 764;
            N_FC = 568;
            C_FC = 402;
            break;
        case PILOT_PP2:
            C_DATA = 768;
            N_FC = 710;
            C_FC = 654;
            break;
        case PILOT_PP3:
            C_DATA = 798;
            N_FC = 710;
            C_FC = 490;
            break;
        case PILOT_PP4:
            C_DATA = 804;
            N_FC = 780;
            C_FC = 707;
            break;
        case PILOT_PP5:
            C_DATA = 818;
            N_FC = 780;
            C_FC = 544;
            break;
        case PILOT_PP6:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        case PILOT_PP7:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        case PILOT_PP8:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        }
        if (paprmode == PAPR_TR || paprmode == PAPR_BOTH) {
            if (C_DATA != 0) {
                C_DATA -= 10;
            }
            if (N_FC != 0) {
                N_FC -= 10;
            }
            if (C_FC != 0) {
                C_FC -= 10;
            }
        }
        break;
    case FFTSIZE_2K:
        switch (pilotpattern) {
        case PILOT_PP1:
            C_DATA = 1522;
            N_FC = 1136;
            C_FC = 804;
            break;
        case PILOT_PP2:
            C_DATA = 1532;
            N_FC = 1420;
            C_FC = 1309;
            break;
        case PILOT_PP3:
            C_DATA = 1596;
            N_FC = 1420;
            C_FC = 980;
            break;
        case PILOT_PP4:
            C_DATA = 1602;
            N_FC = 1562;
            C_FC = 1415;
            break;
        case PILOT_PP5:
            C_DATA = 1632;
            N_FC = 1562;
            C_FC = 1088;
            break;
        case PILOT_PP6:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        case PILOT_PP7:
            C_DATA = 1646;
            N_FC = 1632;
            C_FC = 1396;
            break;
        case PILOT_PP8:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        }
        if (paprmode == PAPR_TR || paprmode == PAPR_BOTH) {
            if (C_DATA != 0) {
                C_DATA -= 18;
            }
            if (N_FC != 0) {
                N_FC -= 18;
            }
            if (C_FC != 0) {
                C_FC -= 18;
            }
        }
        break;
    case FFTSIZE_4K:
        switch (pilotpattern) {
        case PILOT_PP1:
            C_DATA = 3084;
            N_FC = 2272;
            C_FC = 1609;
            break;
        case PILOT_PP2:
            C_DATA = 3092;
            N_FC = 2840;
            C_FC = 2619;
            break;
        case PILOT_PP3:
            C_DATA = 3228;
            N_FC = 2840;
            C_FC = 1961;
            break;
        case PILOT_PP4:
            C_DATA = 3234;
            N_FC = 3124;
            C_FC = 2831;
            break;
        case PILOT_PP5:
            C_DATA = 3298;
            N_FC = 3124;
            C_FC = 2177;
            break;
        case PILOT_PP6:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        case PILOT_PP7:
            C_DATA = 3328;
            N_FC = 3266;
            C_FC = 2792;
            break;
        case PILOT_PP8:
            C_DATA = 0;
            N_FC = 0;
            C_FC = 0;
            break;
        }
        if (paprmode == PAPR_TR || paprmode == PAPR_BOTH) {
            if (C_DATA != 0) {
                C_DATA -= 36;
            }
            if (N_FC != 0) {
                N_FC -= 36;
            }
            if (C_FC != 0) {
                C_FC -= 36;
            }
        }
        break;
    case FFTSIZE_8K:
    case FFTSIZE_8K_T2GI:
        if (carriermode == CARRIERS_NORMAL) {
            switch (pilotpattern) {
            case PILOT_PP1:
                C_DATA = 6208;
                N_FC = 4544;
                C_FC = 3218;
                break;
            case PILOT_PP2:
                C_DATA = 6214;
                N_FC = 5680;
                C_FC = 5238;
                break;
            case PILOT_PP3:
                C_DATA = 6494;
                N_FC = 5680;
                C_FC = 3922;
                break;
            case PILOT_PP4:
                C_DATA = 6498;
                N_FC = 6248;
                C_FC = 5662;
                break;
            case PILOT_PP5:
                C_DATA = 6634;
                N_FC = 6248;
                C_FC = 4354;
                break;
            case PILOT_PP6:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP7:
                C_DATA = 6698;
                N_FC = 6532;
                C_FC = 5585;
                break;
            case PILOT_PP8:
                C_DATA = 6698;
                N_FC = 0;
                C_FC = 0;
                break;
            }
        } else {
            switch (pilotpattern) {
            case PILOT_PP1:
                C_DATA = 6296;
                N_FC = 4608;
                C_FC = 3264;
                break;
            case PILOT_PP2:
                C_DATA = 6298;
                N_FC = 5760;
                C_FC = 5312;
                break;
            case PILOT_PP3:
                C_DATA = 6584;
                N_FC = 5760;
                C_FC = 3978;
                break;
            case PILOT_PP4:
                C_DATA = 6588;
                N_FC = 6336;
                C_FC = 5742;
                break;
            case PILOT_PP5:
                C_DATA = 6728;
                N_FC = 6336;
                C_FC = 4416;
                break;
            case PILOT_PP6:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP7:
                C_DATA = 6788;
                N_FC = 6624;
                C_FC = 5664;
                break;
            case PILOT_PP8:
                C_DATA = 6788;
                N_FC = 0;
                C_FC = 0;
                break;
            }
        }
        if (paprmode == PAPR_TR || paprmode == PAPR_BOTH) {
            if (C_DATA != 0) {
                C_DATA -= 72;
            }
            if (N_FC != 0) {
                N_FC -= 72;
            }
            if (C_FC != 0) {
                C_FC -= 72;
            }
        }
        break;
    case FFTSIZE_16K:
    case FFTSIZE_16K_T2GI:
        if (carriermode == CARRIERS_NORMAL) {
            switch (pilotpattern) {
            case PILOT_PP1:
                C_DATA = 12418;
                N_FC = 9088;
                C_FC = 6437;
                break;
            case PILOT_PP2:
                C_DATA = 12436;
                N_FC = 11360;
                C_FC = 10476;
                break;
            case PILOT_PP3:
                C_DATA = 12988;
                N_FC = 11360;
                C_FC = 7845;
                break;
            case PILOT_PP4:
                C_DATA = 13002;
                N_FC = 12496;
                C_FC = 11324;
                break;
            case PILOT_PP5:
                C_DATA = 13272;
                N_FC = 12496;
                C_FC = 8709;
                break;
            case PILOT_PP6:
                C_DATA = 13288;
                N_FC = 13064;
                C_FC = 11801;
                break;
            case PILOT_PP7:
                C_DATA = 13416;
                N_FC = 13064;
                C_FC = 11170;
                break;
            case PILOT_PP8:
                C_DATA = 13406;
                N_FC = 0;
                C_FC = 0;
                break;
            }
        } else {
            switch (pilotpattern) {
            case PILOT_PP1:
                C_DATA = 12678;
                N_FC = 9280;
                C_FC = 6573;
                break;
            case PILOT_PP2:
                C_DATA = 12698;
                N_FC = 11600;
                C_FC = 10697;
                break;
            case PILOT_PP3:
                C_DATA = 13262;
                N_FC = 11600;
                C_FC = 8011;
                break;
            case PILOT_PP4:
                C_DATA = 13276;
                N_FC = 12760;
                C_FC = 11563;
                break;
            case PILOT_PP5:
                C_DATA = 13552;
                N_FC = 12760;
                C_FC = 8893;
                break;
            case PILOT_PP6:
                C_DATA = 13568;
                N_FC = 13340;
                C_FC = 12051;
                break;
            case PILOT_PP7:
                C_DATA = 13698;
                N_FC = 13340;
                C_FC = 11406;
                break;
            case PILOT_PP8:
                C_DATA = 13688;
                N_FC = 0;
                C_FC = 0;
                break;
            }
        }
        if (paprmode == PAPR_TR || paprmode == PAPR_BOTH) {
            if (C_DATA != 0) {
                C_DATA -= 144;
            }
            if (N_FC != 0) {
                N_FC -= 144;
            }
            if (C_FC != 0) {
                C_FC -= 144;
            }
        }
        break;
    case FFTSIZE_32K:
    case FFTSIZE_32K_T2GI:
        if (carriermode == CARRIERS_NORMAL) {
            switch (pilotpattern) {
            case PILOT_PP1:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP2:
                C_DATA = 24886;
                N_FC = 22720;
                C_FC = 20952;
                break;
            case PILOT_PP3:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP4:
                C_DATA = 26022;
                N_FC = 24992;
                C_FC = 22649;
                break;
            case PILOT_PP5:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP6:
                C_DATA = 26592;
                N_FC = 26128;
                C_FC = 23603;
                break;
            case PILOT_PP7:
                C_DATA = 26836;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP8:
                C_DATA = 26812;
                N_FC = 0;
                C_FC = 0;
                break;
            }
        } else {
            switch (pilotpattern) {
            case PILOT_PP1:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP2:
                C_DATA = 25412;
                N_FC = 23200;
                C_FC = 21395;
                break;
            case PILOT_PP3:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP4:
                C_DATA = 26572;
                N_FC = 25520;
                C_FC = 23127;
                break;
            case PILOT_PP5:
                C_DATA = 0;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP6:
                C_DATA = 27152;
                N_FC = 26680;
                C_FC = 24102;
                break;
            case PILOT_PP7:
                C_DATA = 27404;
                N_FC = 0;
                C_FC = 0;
                break;
            case PILOT_PP8:
                C_DATA = 27376;
                N_FC = 0;
                C_FC = 0;
                break;
            }
        }
        if (paprmode == PAPR_TR || paprmode == PAPR_BOTH) {
            if (C_DATA != 0) {
                C_DATA -= 288;
            }
            if (N_FC != 0) {
                N_FC -= 288;
            }
            if (C_FC != 0) {
                C_FC -= 288;
            }
        }
        break;
    }
    if ((preamble == PREAMBLE_T2_SISO) || (preamble == PREAMBLE_T2_LITE_SISO)) {
        if (guardinterval == GI_1_128 && pilotpattern == PILOT_PP7) {
            N_FC = 0;
            C_FC = 0;
        }
        if (guardinterval == GI_1_32 && pilotpattern == PILOT_PP4) {
            N_FC = 0;
            C_FC = 0;
        }
        if (guardinterval == GI_1_16 && pilotpattern == PILOT_PP2) {
            N_FC = 0;
            C_FC = 0;
        }
        if (guardinterval == GI_19_256 && pilotpattern == PILOT_PP2) {
            N_FC = 0;
            C_FC = 0;
        }
    }
    N_punc_temp = (6 * (KBCH_1_2 - KSIG_POST)) / 5;
    N_post_temp = KSIG_POST + NBCH_PARITY + 9000 - N_punc_temp;
    if (N_P2 == 1) {
        N_post = ceil((float)N_post_temp / (2 * (float)eta_mod)) * 2 * eta_mod;
    } else {
        N_post =
            ceil((float)N_post_temp / ((float)eta_mod * (float)N_P2)) * eta_mod * N_P2;
    }
    N_punc = N_punc_temp - (N_post - N_post_temp);
    l1preinit->l1_post_size = N_post / eta_mod;
    add_l1pre(&l1pre_cache[0]);
    l1_constellation = l1constellation;
    t2_frames = t2frames;
    t2_frame_num = 0;
    if (version == VERSION_131) {
        l1_scrambled = l1scrambled;
    } else {
        l1_scrambled = FALSE;
    }
    stream_items = cell_size * fecblocks;
    if (N_FC == 0) {
        set_output_multiple((N_P2 * C_P2) + (numdatasyms * C_DATA));
        mapped_items = (N_P2 * C_P2) + (numdatasyms * C_DATA);
        if (mapped_items < (stream_items + 1840 + (N_post / eta_mod) + (N_FC - C_FC))) {
            this->d_logger->warn("Frame Mapper, too many FEC blocks in T2 frame.");
            mapped_items = stream_items + 1840 + (N_post / eta_mod) +
                           (N_FC - C_FC); /* avoid segfault */
        }
        zigzag_interleave = (gr_complex*)malloc(sizeof(gr_complex) * mapped_items);
        if (zigzag_interleave == NULL) {
            this->d_logger->fatal(
                "Frame Mapper, cannot allocate memory for zigzag_interleave.");
            throw std::bad_alloc();
        }
    } else {
        set_output_multiple((N_P2 * C_P2) + ((numdatasyms - 1) * C_DATA) + N_FC);
        mapped_items = (N_P2 * C_P2) + ((numdatasyms - 1) * C_DATA) + N_FC;
        if (mapped_items < (stream_items + 1840 + (N_post / eta_mod) + (N_FC - C_FC))) {
            this->d_logger->warn("Frame Mapper, too many FEC blocks in T2 frame.");
            mapped_items = stream_items + 1840 + (N_post / eta_mod) +
                           (N_FC - C_FC); /* avoid segfault */
        }
        zigzag_interleave = (gr_complex*)malloc(sizeof(gr_complex) * mapped_items);
        if (zigzag_interleave == NULL) {
            this->d_logger->fatal(
                "Frame Mapper, cannot allocate memory for zigzag_interleave.");
            throw std::bad_alloc();
        }
    }
    dummy_randomize =
        (gr_complex*)malloc(sizeof(gr_complex) * mapped_items - stream_items - 1840 -
                            (N_post / eta_mod) - (N_FC - C_FC));
    if (dummy_randomize == NULL) {
        free(zigzag_interleave);
        this->d_logger->fatal(
            "Frame Mapper, cannot allocate memory for dummy_randomize.");
        throw std::bad_alloc();
    }
    init_dummy_randomizer();
    init_l1_randomizer();
}

/*
 * Our virtual destructor.
 */
dvbt2_framemapper_cc_impl::~dvbt2_framemapper_cc_impl()
{
    free(dummy_randomize);
    free(zigzag_interleave);
}

void dvbt2_framemapper_cc_impl::forecast(int noutput_items,
                                         gr_vector_int& ninput_items_required)
{
    ninput_items_required[0] = stream_items * (noutput_items / mapped_items);
}

#define CRC_POLY 0x04C11DB7

int dvbt2_framemapper_cc_impl::add_crc32_bits(unsigned char* in, int length)
{
    unsigned int crc = 0xffffffff;
    int b;
    int i = 0;

    for (int n = 0; n < length; n++) {
        b = in[i++] ^ ((crc >> 31) & 0x01);
        crc <<= 1;
        if (b) {
            crc ^= CRC_POLY;
        }
    }

    for (int n = 31; n >= 0; n--) {
        in[i++] = (crc & (1 << n)) ? 1 : 0;
    }
    return 32;
}

int dvbt2_framemapper_cc_impl::poly_mult(
    const int* ina, int lena, const int* inb, int lenb, int* out)
{
    memset(out, 0, sizeof(int) * (lena + lenb));

    for (int i = 0; i < lena; i++) {
        for (int j = 0; j < lenb; j++) {
            if (ina[i] * inb[j] > 0) {
                out[i + j]++; // count number of terms for this pwr of x
            }
        }
    }
    int max = 0;
    for (int i = 0; i < lena + lenb; i++) {
        out[i] = out[i] & 1; // If even ignore the term
        if (out[i])
            max = i;
    }
    // return the size of array to house the result.
    return max + 1;
}

void dvbt2_framemapper_cc_impl::poly_pack(const int* pin, unsigned int* pout, int len)
{
    int lw = len / 32;
    int ptr = 0;
    unsigned int temp;
    if (len % 32) {
        lw++;
    }

    for (int i = 0; i < lw; i++) {
        temp = 0x80000000;
        pout[i] = 0;
        for (int j = 0; j < 32; j++) {
            if (pin[ptr++]) {
                pout[i] |= temp;
            }
            temp >>= 1;
        }
    }
}

void dvbt2_framemapper_cc_impl::bch_poly_build_tables(void)
{
    // Short polynomials
    const int polys01[] = { 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
    const int polys02[] = { 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1 };
    const int polys03[] = { 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1 };
    const int polys04[] = { 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1 };
    const int polys05[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1 };
    const int polys06[] = { 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1 };
    const int polys07[] = { 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1 };
    const int polys08[] = { 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1 };
    const int polys09[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1 };
    const int polys10[] = { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1 };
    const int polys11[] = { 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1 };
    const int polys12[] = { 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1 };

    int len;
    int polyout[2][200] = {};

    len = poly_mult(polys01, 15, polys02, 15, polyout[0]);
    len = poly_mult(polys03, 15, polyout[0], len, polyout[1]);
    len = poly_mult(polys04, 15, polyout[1], len, polyout[0]);
    len = poly_mult(polys05, 15, polyout[0], len, polyout[1]);
    len = poly_mult(polys06, 15, polyout[1], len, polyout[0]);
    len = poly_mult(polys07, 15, polyout[0], len, polyout[1]);
    len = poly_mult(polys08, 15, polyout[1], len, polyout[0]);
    len = poly_mult(polys09, 15, polyout[0], len, polyout[1]);
    len = poly_mult(polys10, 15, polyout[1], len, polyout[0]);
    len = poly_mult(polys11, 15, polyout[0], len, polyout[1]);
    len = poly_mult(polys12, 15, polyout[1], len, polyout[0]);
    poly_pack(polyout[0], m_poly_s_12, 168);
}

inline void dvbt2_framemapper_cc_impl::reg_6_shift(unsigned int* sr)
{
    sr[5] = (sr[5] >> 1) | (sr[4] << 31);
    sr[4] = (sr[4] >> 1) | (sr[3] << 31);
    sr[3] = (sr[3] >> 1) | (sr[2] << 31);
    sr[2] = (sr[2] >> 1) | (sr[1] << 31);
    sr[1] = (sr[1] >> 1) | (sr[0] << 31);
    sr[0] = (sr[0] >> 1);
}

void dvbt2_framemapper_cc_impl::l1pre_ldpc_lookup_generate(void)
{
    int im;
    int index;
    int pbits;
    int q;
    index = 0;
    im = 0;

    pbits = FRAME_SIZE_SHORT - NBCH_1_4; // number of parity bits
    q = 36;

    for (int row = 0; row < 9; row++) {
        for (int n = 0; n < 360; n++) {
            for (int col = 1; col <= ldpc_tab_1_4S[row][0]; col++) {
                l1pre_ldpc_encode.p[index] = (ldpc_tab_1_4S[row][col] + (n * q)) % pbits;
                l1pre_ldpc_encode.d[index] = im;
                index++;
            }
            im++;
        }
    }
    l1pre_ldpc_encode.table_length = index;
}

void dvbt2_framemapper_cc_impl::l1post_ldpc_lookup_generate(void)
{
    int im;
    int index;
    int pbits;
    int q;
    index = 0;
    im = 0;

    pbits = FRAME_SIZE_SHORT - NBCH_1_2; // number of parity bits
    q = 25;

    for (int row = 0; row < 20; row++) {
        for (int n = 0; n < 360; n++) {
            for (int col = 1; col <= ldpc_tab_1_2S[row][0]; col++) {
                l1post_ldpc_encode.p[index] = (ldpc_tab_1_2S[row][col] + (n * q)) % pbits;
                l1post_ldpc_encode.d[index] = im;
                index++;
            }
            im++;
        }
    }
    l1post_ldpc_encode.table_length = index;
}

void dvbt2_framemapper_cc_impl::add_l1pre(gr_complex* out)
{
    int temp, offset_bits = 0;
    unsigned char b, value;
    unsigned int shift[6];
    int plen = FRAME_SIZE_SHORT - NBCH_1_4;
    const unsigned char* d;
    unsigned char* p;
    unsigned char* l1pre = l1_temp;
    L1Pre* l1preinit = &L1_Signalling[0].l1pre_data;
    int g, o, index;

    temp = l1preinit->type;
    for (int n = 7; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1pre[offset_bits++] = l1preinit->bwt_ext;
    temp = l1preinit->s1;
    for (int n = 2; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->s2;
    for (int n = 2; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1pre[offset_bits++] = 0;
    l1pre[offset_bits++] = l1preinit->l1_repetition_flag;
    temp = l1preinit->guard_interval;
    for (int n = 2; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->papr;
    for (int n = 3; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->l1_mod;
    for (int n = 3; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->l1_cod;
    for (int n = 1; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->l1_fec_type;
    for (int n = 1; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->l1_post_size;
    for (int n = 17; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->l1_post_info_size;
    for (int n = 17; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->pilot_pattern;
    for (int n = 3; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->tx_id_availability;
    for (int n = 7; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->cell_id;
    for (int n = 15; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->network_id;
    for (int n = 15; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->t2_system_id;
    for (int n = 15; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->num_t2_frames;
    for (int n = 7; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->num_data_symbols;
    for (int n = 11; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->regen_flag;
    for (int n = 2; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1pre[offset_bits++] = l1preinit->l1_post_extension;
    temp = l1preinit->num_rf;
    for (int n = 2; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->current_rf_index;
    for (int n = 2; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1preinit->t2_version;
    for (int n = 3; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1pre[offset_bits++] = l1preinit->l1_post_scrambled;
    l1pre[offset_bits++] = l1preinit->t2_base_lite;
    temp = l1preinit->reserved;
    for (int n = 3; n >= 0; n--) {
        l1pre[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    offset_bits += add_crc32_bits(l1pre, offset_bits);
    /* Padding */
    for (int n = KBCH_1_4 - offset_bits - 1; n >= 0; n--) {
        l1pre[offset_bits++] = 0;
    }
    /* BCH */
    offset_bits = 0;
    memset(shift, 0, sizeof(unsigned int) * 6);
    for (int j = 0; j < KBCH_1_4; j++) {
        value = l1pre[offset_bits++];
        b = (value ^ ((shift[5] & 0x01000000) ? 1 : 0));
        reg_6_shift(shift);
        if (b) {
            shift[0] ^= m_poly_s_12[0];
            shift[1] ^= m_poly_s_12[1];
            shift[2] ^= m_poly_s_12[2];
            shift[3] ^= m_poly_s_12[3];
            shift[4] ^= m_poly_s_12[4];
            shift[5] ^= m_poly_s_12[5];
        }
    }
    for (int n = 0; n < NBCH_PARITY; n++) {
        l1pre[offset_bits++] = (shift[5] & 0x01000000) ? 1 : 0;
        reg_6_shift(shift);
    }
    /* LDPC */
    d = l1_temp;
    p = &l1_temp[NBCH_1_4];
    memset(p, 0, sizeof(unsigned char) * plen);
    for (int j = 0; j < l1pre_ldpc_encode.table_length; j++) {
        p[l1pre_ldpc_encode.p[j]] ^= d[l1pre_ldpc_encode.d[j]];
    }
    for (int j = 1; j < plen; j++) {
        p[j] ^= p[j - 1];
    }
    /* Puncturing */
    for (int c = 0; c < 31; c++) {
        g = pre_puncture[c];
        for (int c2 = 0; c2 < 360; c2++) {
            o = (c2 * 36) + g + NBCH_1_4;
            l1_temp[o] = 0x55;
        }
    }
    g = pre_puncture[31];
    for (int c2 = 0; c2 < 328; c2++) {
        o = (c2 * 36) + g + NBCH_1_4;
        l1_temp[o] = 0x55;
    }
    /* remove padding and punctured bits, BPSK modulate */
    index = 0;
    for (int w = 0; w < KSIG_PRE; w++) {
        out[index++] = m_bpsk[l1_temp[w]];
    }
    for (int w = 0; w < NBCH_PARITY; w++) {
        out[index++] = m_bpsk[l1_temp[w + KBCH_1_4]];
    }
    for (int w = 0; w < FRAME_SIZE_SHORT - NBCH_1_4; w++) {
        if (l1_temp[w + NBCH_1_4] != 0x55) {
            out[index++] = m_bpsk[l1_temp[w + NBCH_1_4]];
        }
    }
}

void dvbt2_framemapper_cc_impl::add_l1post(gr_complex* out, int t2_frame_num)
{
    int temp, offset_bits = 0;
    unsigned char b, value;
    unsigned int shift[6];
    int plen = FRAME_SIZE_SHORT - NBCH_1_2;
    const unsigned char* d;
    unsigned char* p;
    unsigned char* l1post = l1_interleave;
    L1Post* l1postinit = &L1_Signalling[0].l1post_data;
    int m, g, o, last, index;
    const int* post_padding;
    const int* post_puncture;
    int rows, numCols, mod, offset, pack, produced;
    unsigned char* cols[12];

    temp = l1postinit->sub_slices_per_frame;
    for (int n = 14; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->num_plp;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->num_aux;
    for (int n = 3; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->aux_config_rfu;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->rf_idx;
    for (int n = 2; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->frequency;
    for (int n = 31; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_id;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_type;
    for (int n = 2; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_payload_type;
    for (int n = 4; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1post[offset_bits++] = l1postinit->ff_flag;
    temp = l1postinit->first_rf_idx;
    for (int n = 2; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->first_frame_idx;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_group_id;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_cod;
    for (int n = 2; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_mod;
    for (int n = 2; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1post[offset_bits++] = l1postinit->plp_rotation;
    temp = l1postinit->plp_fec_type;
    for (int n = 1; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_num_blocks_max;
    for (int n = 9; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->frame_interval;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->time_il_length;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1post[offset_bits++] = l1postinit->time_il_type;
    l1post[offset_bits++] = l1postinit->in_band_a_flag;
    l1post[offset_bits++] = l1postinit->in_band_b_flag;
    temp = l1postinit->reserved_1;
    for (int n = 10; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_mode;
    for (int n = 1; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    l1post[offset_bits++] = l1postinit->static_flag;
    l1post[offset_bits++] = l1postinit->static_padding_flag;
    temp = l1postinit->fef_length_msb;
    for (int n = 1; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->reserved_2;
    for (int n = 29; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = t2_frame_num;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->sub_slice_interval;
    for (int n = 21; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->type_2_start;
    for (int n = 21; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->l1_change_counter;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->start_rf_idx;
    for (int n = 2; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->reserved_3;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_id_dynamic;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_start;
    for (int n = 21; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->plp_num_blocks;
    for (int n = 9; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->reserved_4;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    temp = l1postinit->reserved_5;
    for (int n = 7; n >= 0; n--) {
        l1post[offset_bits++] = temp & (1 << n) ? 1 : 0;
    }
    offset_bits += add_crc32_bits(l1post, offset_bits);
    if (l1_scrambled == TRUE) {
        for (int n = 0; n < offset_bits; n++) {
            l1post[n] = l1post[n] ^ l1_randomize[n];
        }
    }
    /* Padding */
    switch (l1_constellation) {
    case L1_MOD_BPSK:
        post_padding = post_padding_bqpsk;
        break;
    case L1_MOD_QPSK:
        post_padding = post_padding_bqpsk;
        break;
    case L1_MOD_16QAM:
        post_padding = post_padding_16qam;
        break;
    case L1_MOD_64QAM:
        post_padding = post_padding_64qam;
        break;
    default:
        post_padding = post_padding_bqpsk;
        break;
    }
    memset(l1_map, 0, KBCH_1_2);
    if (offset_bits <= 360) {
        m = 20 - 1;
        last = 360 - offset_bits;
    } else {
        m = (KBCH_1_2 - offset_bits) / 360;
        last = KBCH_1_2 - offset_bits - (360 * m);
    }
    for (int n = 0; n < m; n++) {
        index = post_padding[n] * 360;
        if (post_padding[n] == 19) {
            for (int w = 0; w < 192; w++) {
                l1_map[index++] = 0x7;
            }
        } else {
            for (int w = 0; w < 360; w++) {
                l1_map[index++] = 0x7;
            }
        }
    }
    if (post_padding[m] == 19) {
        index = (post_padding[m] * 360) + 192 - last;
    } else {
        index = (post_padding[m] * 360) + 360 - last;
    }
    for (int w = 0; w < last; w++) {
        l1_map[index++] = 0x7;
    }
    index = 0;
    l1post = l1_temp;
    for (int n = 0; n < KBCH_1_2; n++) {
        if (l1_map[n] != 0x7) {
            l1post[n] = l1_interleave[index++];
        } else {
            l1post[n] = 0;
        }
    }
    /* BCH */
    offset_bits = 0;
    memset(shift, 0, sizeof(unsigned int) * 6);
    for (int j = 0; j < KBCH_1_2; j++) {
        value = l1post[offset_bits++];
        b = (value ^ ((shift[5] & 0x01000000) ? 1 : 0));
        reg_6_shift(shift);
        if (b) {
            shift[0] ^= m_poly_s_12[0];
            shift[1] ^= m_poly_s_12[1];
            shift[2] ^= m_poly_s_12[2];
            shift[3] ^= m_poly_s_12[3];
            shift[4] ^= m_poly_s_12[4];
            shift[5] ^= m_poly_s_12[5];
        }
    }
    for (int n = 0; n < NBCH_PARITY; n++) {
        l1post[offset_bits++] = (shift[5] & 0x01000000) ? 1 : 0;
        reg_6_shift(shift);
    }
    /* LDPC */
    d = l1_temp;
    p = &l1_temp[NBCH_1_2];
    memset(p, 0, sizeof(unsigned char) * plen);
    for (int j = 0; j < l1post_ldpc_encode.table_length; j++) {
        p[l1post_ldpc_encode.p[j]] ^= d[l1post_ldpc_encode.d[j]];
    }
    for (int j = 1; j < plen; j++) {
        p[j] ^= p[j - 1];
    }
    /* Puncturing */
    switch (l1_constellation) {
    case L1_MOD_BPSK:
        post_puncture = post_puncture_bqpsk;
        break;
    case L1_MOD_QPSK:
        post_puncture = post_puncture_bqpsk;
        break;
    case L1_MOD_16QAM:
        post_puncture = post_puncture_16qam;
        break;
    case L1_MOD_64QAM:
        post_puncture = post_puncture_64qam;
        break;
    default:
        post_puncture = post_puncture_bqpsk;
        break;
    }
    for (int c = 0; c < (N_punc / 360); c++) {
        g = post_puncture[c];
        for (int c2 = 0; c2 < 360; c2++) {
            o = (c2 * 25) + g + NBCH_1_2;
            l1_temp[o] = 0x55;
        }
    }
    g = post_puncture[(N_punc / 360)];
    for (int c2 = 0; c2 < (N_punc - ((N_punc / 360) * 360)); c2++) {
        o = (c2 * 25) + g + NBCH_1_2;
        l1_temp[o] = 0x55;
    }
    /* remove padding and punctured bits */
    index = 0;
    for (int w = 0; w < KBCH_1_2; w++) {
        if (l1_map[w] != 0x7) {
            l1_interleave[index++] = l1_temp[w];
        }
    }
    for (int w = 0; w < NBCH_PARITY; w++) {
        l1_interleave[index++] = l1_temp[w + KBCH_1_2];
    }
    for (int w = 0; w < FRAME_SIZE_SHORT - NBCH_1_2; w++) {
        if (l1_temp[w + NBCH_1_2] != 0x55) {
            l1_interleave[index++] = l1_temp[w + NBCH_1_2];
        }
    }
    /* Bit interleave for 16QAM and 64QAM */
    if (l1_constellation == L1_MOD_16QAM || l1_constellation == L1_MOD_64QAM) {
        if (l1_constellation == L1_MOD_16QAM) {
            numCols = 8;
            rows = N_post / 8;
        } else {
            numCols = 12;
            rows = N_post / 12;
        }
        for (int j = 0; j < numCols; j++) {
            cols[j] = &l1_interleave[rows * j];
        }
        index = 0;
        for (int k = 0; k < rows; k++) {
            for (int w = 0; w < numCols; w++) {
                *l1post++ = *(cols[w] + index);
            }
            index++;
        }
    }
    switch (l1_constellation) {
    case L1_MOD_BPSK:
        index = 0;
        produced = 0;
        for (int d = 0; d < N_post; d++) {
            out[produced++] = m_bpsk[l1_interleave[index++]];
        }
        break;
    case L1_MOD_QPSK:
        mod = 2;
        index = 0;
        produced = 0;
        for (int d = 0; d < N_post / mod; d++) {
            pack = 0;
            for (int e = 0; e < mod; e++) {
                pack |= l1_interleave[index++];
                pack <<= 1;
            }
            pack >>= 1;
            out[produced++] = m_qpsk[pack];
        }
        break;
    case L1_MOD_16QAM:
        mod = 4;
        index = 0;
        produced = 0;
        for (int d = 0; d < N_post / (mod * 2); d++) {
            pack = 0;
            for (int e = 0; e < (mod * 2); e++) {
                offset = mux16[e];
                pack |= l1_temp[index + offset];
                pack <<= 1;
            }
            pack >>= 1;
            out[produced++] = m_16qam[pack >> 4];
            out[produced++] = m_16qam[pack & 0xf];
            index += (mod * 2);
        }
        break;
    case L1_MOD_64QAM:
        mod = 6;
        index = 0;
        produced = 0;
        for (int d = 0; d < N_post / (mod * 2); d++) {
            pack = 0;
            for (int e = 0; e < (mod * 2); e++) {
                offset = mux64[e];
                pack |= l1_temp[index + offset];
                pack <<= 1;
            }
            pack >>= 1;
            out[produced++] = m_64qam[pack >> 6];
            out[produced++] = m_64qam[pack & 0x3f];
            index += (mod * 2);
        }
        break;
    }
}

void dvbt2_framemapper_cc_impl::init_dummy_randomizer(void)
{
    int sr = 0x4A80;
    int num = mapped_items - stream_items - 1840 - (N_post / eta_mod) - (N_FC - C_FC);
    std::fill_n(&dummy_randomize[0], num, 0);
    for (int i = 0; i < num; i++) {
        int b = ((sr) ^ (sr >> 1)) & 1;
        dummy_randomize[i] = (b ? -1.0 : 1.0);
        sr >>= 1;
        if (b) {
            sr |= 0x4000;
        }
    }
}

void dvbt2_framemapper_cc_impl::init_l1_randomizer(void)
{
    int sr = 0x4A80;
    for (int i = 0; i < KBCH_1_2; i++) {
        int b = ((sr) ^ (sr >> 1)) & 1;
        l1_randomize[i] = b;
        sr >>= 1;
        if (b) {
            sr |= 0x4000;
        }
    }
}

int dvbt2_framemapper_cc_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 gr_complex* in = (const gr_complex*)input_items[0];
    gr_complex* out = (gr_complex*)output_items[0];
    int index = 0;
    int read, save, count = 0;
    gr_complex* interleave = zigzag_interleave;

    for (int i = 0; i < noutput_items; i += mapped_items) {
        if (N_P2 == 1) {
            for (int j = 0; j < 1840; j++) {
                *out++ = l1pre_cache[index++];
            }
            add_l1post(out, t2_frame_num);
            t2_frame_num = (t2_frame_num + 1) % t2_frames;
            out += N_post / eta_mod;
            for (int j = 0; j < stream_items; j++) {
                *out++ = *in++;
            }
            index = 0;
            for (int j = 0; j < mapped_items - stream_items - 1840 - (N_post / eta_mod) -
                                    (N_FC - C_FC);
                 j++) {
                *out++ = dummy_randomize[index++];
            }
            for (int j = 0; j < N_FC - C_FC; j++) {
                *out++ = unmodulated;
            }
        } else {
            for (int j = 0; j < 1840; j++) {
                *interleave++ = l1pre_cache[index++];
            }
            add_l1post(interleave, t2_frame_num);
            t2_frame_num = (t2_frame_num + 1) % t2_frames;
            interleave += N_post / eta_mod;
            for (int j = 0; j < stream_items; j++) {
                *interleave++ = *in++;
            }
            index = 0;
            for (int j = 0; j < mapped_items - stream_items - 1840 - (N_post / eta_mod) -
                                    (N_FC - C_FC);
                 j++) {
                *interleave++ = dummy_randomize[index++];
            }
            for (int j = 0; j < N_FC - C_FC; j++) {
                *interleave++ = unmodulated;
            }
            interleave = zigzag_interleave;
            read = 0;
            index = 0;
            for (int n = 0; n < N_P2; n++) {
                save = read;
                for (int j = 0; j < 1840 / N_P2; j++) {
                    out[index++] = interleave[read];
                    count++;
                    read += N_P2;
                }
                read = save + 1;
                index += C_P2 - (1840 / N_P2);
            }
            read = 1840;
            index = 1840 / N_P2;
            for (int n = 0; n < N_P2; n++) {
                save = read;
                for (int j = 0; j < (N_post / eta_mod) / N_P2; j++) {
                    out[index++] = interleave[read];
                    count++;
                    read += N_P2;
                }
                read = save + 1;
                index += C_P2 - ((N_post / eta_mod) / N_P2);
            }
            read = 1840 + (N_post / eta_mod);
            index = (1840 / N_P2) + ((N_post / eta_mod) / N_P2);
            for (int n = 0; n < N_P2; n++) {
                for (int j = 0; j < C_P2 - (1840 / N_P2) - ((N_post / eta_mod) / N_P2);
                     j++) {
                    out[index++] = interleave[read++];
                    count++;
                }
                index += C_P2 - (C_P2 - (1840 / N_P2) - ((N_post / eta_mod) / N_P2));
            }
            index -= C_P2 - (C_P2 - (1840 / N_P2) - ((N_post / eta_mod) / N_P2));
            for (int j = 0; j < mapped_items - count; j++) {
                out[index++] = interleave[read++];
            }
            out += mapped_items;
        }
    }

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

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

const int dvbt2_framemapper_cc_impl::ldpc_tab_1_4S[9][13] = {
    { 12, 6295, 9626, 304, 7695, 4839, 4936, 1660, 144, 11203, 5567, 6347, 12557 },
    { 12, 10691, 4988, 3859, 3734, 3071, 3494, 7687, 10313, 5964, 8069, 8296, 11090 },
    { 12, 10774, 3613, 5208, 11177, 7676, 3549, 8746, 6583, 7239, 12265, 2674, 4292 },
    { 12, 11869, 3708, 5981, 8718, 4908, 10650, 6805, 3334, 2627, 10461, 9285, 11120 },
    { 3, 7844, 3079, 10773, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 3, 3385, 10854, 5747, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 3, 1360, 12010, 12202, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 3, 6189, 4241, 2343, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 3, 9840, 12726, 4977, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};

const int dvbt2_framemapper_cc_impl::ldpc_tab_1_2S[20][9] = {
    { 8, 20, 712, 2386, 6354, 4061, 1062, 5045, 5158 },
    { 8, 21, 2543, 5748, 4822, 2348, 3089, 6328, 5876 },
    { 8, 22, 926, 5701, 269, 3693, 2438, 3190, 3507 },
    { 8, 23, 2802, 4520, 3577, 5324, 1091, 4667, 4449 },
    { 8, 24, 5140, 2003, 1263, 4742, 6497, 1185, 6202 },
    { 3, 0, 4046, 6934, 0, 0, 0, 0, 0 },
    { 3, 1, 2855, 66, 0, 0, 0, 0, 0 },
    { 3, 2, 6694, 212, 0, 0, 0, 0, 0 },
    { 3, 3, 3439, 1158, 0, 0, 0, 0, 0 },
    { 3, 4, 3850, 4422, 0, 0, 0, 0, 0 },
    { 3, 5, 5924, 290, 0, 0, 0, 0, 0 },
    { 3, 6, 1467, 4049, 0, 0, 0, 0, 0 },
    { 3, 7, 7820, 2242, 0, 0, 0, 0, 0 },
    { 3, 8, 4606, 3080, 0, 0, 0, 0, 0 },
    { 3, 9, 4633, 7877, 0, 0, 0, 0, 0 },
    { 3, 10, 3884, 6868, 0, 0, 0, 0, 0 },
    { 3, 11, 8935, 4996, 0, 0, 0, 0, 0 },
    { 3, 12, 3028, 764, 0, 0, 0, 0, 0 },
    { 3, 13, 5988, 1057, 0, 0, 0, 0, 0 },
    { 3, 14, 7411, 3450, 0, 0, 0, 0, 0 }
};

const int dvbt2_framemapper_cc_impl::pre_puncture[36] = {
    27, 13, 29, 32, 5,  0, 11, 21, 33, 20, 25, 28, 18, 35, 8, 3, 9,  31,
    22, 24, 7,  14, 17, 4, 2,  26, 16, 34, 19, 10, 12, 23, 1, 6, 30, 15
};

const int dvbt2_framemapper_cc_impl::post_padding_bqpsk[20] = {
    18, 17, 16, 15, 14, 13, 12, 11, 4, 10, 9, 8, 3, 2, 7, 6, 5, 1, 19, 0
};

const int dvbt2_framemapper_cc_impl::post_padding_16qam[20] = {
    18, 17, 16, 15, 14, 13, 12, 11, 4, 10, 9, 8, 7, 3, 2, 1, 6, 5, 19, 0
};

const int dvbt2_framemapper_cc_impl::post_padding_64qam[20] = {
    18, 17, 16, 4, 15, 14, 13, 12, 3, 11, 10, 9, 2, 8, 7, 1, 6, 5, 19, 0
};

const int dvbt2_framemapper_cc_impl::post_puncture_bqpsk[25] = {
    6,  4,  18, 9,  13, 8, 15, 20, 5,  17, 2, 24, 10,
    22, 12, 3,  16, 23, 1, 14, 0,  21, 19, 7, 11
};

const int dvbt2_framemapper_cc_impl::post_puncture_16qam[25] = {
    6, 4,  13, 9,  18, 8,  15, 20, 5,  17, 2,  22, 24,
    7, 12, 1,  16, 23, 14, 0,  21, 10, 19, 11, 3
};

const int dvbt2_framemapper_cc_impl::post_puncture_64qam[25] = {
    6,  15, 13, 10, 3,  17, 21, 8, 5,  19, 2,  23, 16,
    24, 7,  18, 1,  12, 20, 0,  4, 14, 9,  11, 22
};

const int dvbt2_framemapper_cc_impl::mux16[8] = { 7, 1, 3, 5, 2, 4, 6, 0 };

const int dvbt2_framemapper_cc_impl::mux64[12] = { 11, 8, 5, 2, 10, 7, 4, 1, 9, 6, 3, 0 };

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