///////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE <jon@beniston.com>                         //
//                                                                                   //
// This program 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 as version 3 of the License, or                      //
// (at your option) any later version.                                               //
//                                                                                   //
// This program 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 V3 for more details.                                   //
//                                                                                   //
// You should have received a copy of the GNU General Public License                 //
// along with this program. If not, see <http://www.gnu.org/licenses/>.              //
///////////////////////////////////////////////////////////////////////////////////////
#include "DVBS2.h"

void DVBS2::b_64_7_code( unsigned char in, int *out )
{
    unsigned long temp,bit;

    temp = 0;

    if(in&0x40) temp ^= g[0];
    if(in&0x20) temp ^= g[1];
    if(in&0x10) temp ^= g[2];
    if(in&0x08) temp ^= g[3];
    if(in&0x04) temp ^= g[4];
    if(in&0x02) temp ^= g[5];

    bit = 0x80000000;
    for( int m = 0; m < 32; m++ )
    {
        out[(m*2)]   = (temp&bit)?1:0;
        out[(m*2)+1] = out[m*2]^(in&0x01);
        bit >>= 1;
    }
    // Randomise it
    for( int m = 0; m < 64; m++ )
    {
        out[m] = out[m] ^ ph_scram_tab[m];
    }
}
//[MODCOD 6:2 ][TYPE 1:0 ]
void DVBS2::s2_pl_header_encode( u8 modcod, u8 type, int *out)
{
    unsigned char code;

    code = (modcod<<2) | type;
    //printf("MODCOD %d TYPE %d %d\n",modcod,type,code);
    // Add the modcod and type information and scramble it
    b_64_7_code( code, out );
}
void DVBS2::s2_pl_header_create(void)
{
    int type, modcod;

    modcod = 0;

    if( m_format[0].frame_type == FRAME_NORMAL )
        type = 0;
    else
        type = 2;

    if( m_format[0].pilots ) type |= 1;

    // Mode and code rate
    if( m_format[0].constellation == M_QPSK )
    {
        switch( m_format[0].code_rate )
        {
        case CR_1_4:
            modcod = 1;
            break;
        case CR_1_3:
            modcod = 2;
            break;
        case CR_2_5:
            modcod = 3;
            break;
        case CR_1_2:
            modcod = 4;
            break;
        case CR_3_5:
            modcod = 5;
            break;
        case CR_2_3:
            modcod = 6;
            break;
        case CR_3_4:
            modcod = 7;
            break;
        case CR_4_5:
            modcod = 8;
            break;
        case CR_5_6:
            modcod = 9;
            break;
        case CR_8_9:
            modcod = 10;
            break;
        case CR_9_10:
            modcod = 11;
            break;
        default:
            modcod = 0;
            break;
        }
    }

    if( m_format[0].constellation == M_8PSK )
    {
        switch( m_format[0].code_rate )
        {
            case CR_3_5:
                modcod = 12;
                break;
            case CR_2_3:
                modcod = 13;
                break;
            case CR_3_4:
                modcod = 14;
                break;
            case CR_5_6:
                modcod = 15;
                break;
            case CR_8_9:
                modcod = 16;
                break;
            case CR_9_10:
                modcod = 17;
                break;
            default:
                modcod = 0;
                break;
            }
    }

    if( m_format[0].constellation == M_16APSK )
    {
        switch( m_format[0].code_rate )
        {
        case CR_2_3:
                modcod = 18;
                break;
        case CR_3_4:
                modcod = 19;
                break;
        case CR_4_5:
                modcod = 20;
                break;
        case CR_5_6:
                modcod = 21;
                break;
        case CR_8_9:
                modcod = 22;
                break;
        case CR_9_10:
                modcod = 23;
                break;
        default:
                modcod = 0;
                break;
        }
    }

    if( m_format[0].constellation == M_32APSK )
    {
        switch( m_format[0].code_rate )
        {
        case CR_3_4:
                modcod = 24;
                break;
        case CR_4_5:
                modcod = 25;
                break;
        case CR_5_6:
                modcod = 26;
                break;
        case CR_8_9:
                modcod = 27;
                break;
        case CR_9_10:
                modcod = 28;
                break;
        default:
                modcod = 0;
                break;
        }
    }
    // Now create the PL header.
    int b[90];
    // Add the sync sequence SOF
    for( int i = 0; i < 26; i++ ) b[i] = ph_sync_seq[i];
    // Add the mode and code
    s2_pl_header_encode( modcod, type, &b[26] );

    // BPSK modulate and add the header
    for( int i = 0; i < 90; i++ )
    {
       m_pl[i] =  m_bpsk[i&1][b[i]];
    }
}
//
// m_symbols is the total number of complex symbols in the frame
// Modulate the data starting at symbol 90
//
int  DVBS2::s2_pl_data_pack( void )
{
    int m = 0;
    int n = 90;// Jump over header
    int blocks = m_payload_symbols/90;
    int block_count = 0;

    // See if PSK
    if( m_format[0].constellation == M_QPSK )
    {
        for( int i = 0; i < blocks; i++ )
        {
            for( int j = 0; j < 90; j++ )
            {
                m_pl[n++] = m_qpsk[m_iframe[m++]&0x3];
            }
            block_count = (block_count+1)%16;
            if((block_count == 0)&&(i<blocks-1))
            {
                if( m_format[0].pilots )
                {
                    // Add pilots if needed
                    for( int k = 0; k < 36; k++ )
                    {
                        m_pl[n++] = m_bpsk[0][0];
                    }
                }
            }
        }
    }
    // See if 8 PSK
    if( m_format[0].constellation == M_8PSK )
    {
        for( int i = 0; i < blocks; i++ )
        {
            for( int j = 0; j < 90; j++ )
            {
                m_pl[n++] = m_8psk[m_iframe[m++]&0x7];
            }
            block_count = (block_count+1)%16;
            if((block_count == 0)&&(i<blocks-1))
            {
                if( m_format[0].pilots )
                {
                    // Add pilots if needed
                    for( int k = 0; k < 36; k++ )
                    {
                        m_pl[n++] = m_bpsk[0][0];
                    }
                }
            }
        }
    }
    // See if 16 PSK
    if( m_format[0].constellation == M_16APSK )
    {
        for( int i = 0; i < blocks; i++ )
        {
            for( int j = 0; j < 90; j++ )
            {
                   m_pl[n++] = m_16apsk[m_iframe[m++]&0xF];
            }
            block_count = (block_count+1)%16;
            if((block_count == 0)&&(i<blocks-1))
            {
                if( m_format[0].pilots )
                {
                    // Add pilots if needed
                    for( int k = 0; k < 36; k++ )
                    {
                        m_pl[n++] = m_bpsk[0][0];
                    }
                }
            }
        }
    }
    // See if 32 APSK
    if( m_format[0].constellation == M_32APSK )
    {
        for( int i = 0; i < blocks; i++ )
        {
            for( int j = 0; j < 90; j++ )
            {
                m_pl[n++] = m_32apsk[m_iframe[m++]&0x1F];
            }
            block_count = (block_count+1)%16;
            if((block_count == 0)&&(i<blocks-1))
            {
                if( m_format[0].pilots )
                {
                    // Add pilots if needed
                    for( int k = 0; k < 36; k++ )
                    {
                        m_pl[n++] = m_bpsk[0][0];
                    }
                }
            }
        }
    }
    // Now apply the scrambler to the data part not the header
    pl_scramble_symbols( &m_pl[90], n - 90 );
    // Return the length
    return n;
}
//
// This is not used for Broadcast mode
//
void DVBS2::pl_build_dummy( void )
{
    int n = 0;
    int b[90];

    // Add the sync sequence SOF
    for( int i = 0; i < 26; i++ ) b[i] = ph_sync_seq[i];
    // Add the mode and code and sync sequence
    s2_pl_header_encode( 0, 0, &b[26] );
    // BPSK Modulate
    for( int i = 0; i < 90; i++ )
    {
       m_pl[i].re = m_bpsk[i&1][b[i]].re;
       m_pl[i].im = m_bpsk[i&1][b[i]].im;
    }
    n += (90*36);
    pl_scramble_dummy_symbols( n );
    m_dummy_frame_length = n;
}

scmplx * DVBS2::pl_get_frame( void )
{
    return m_pl;
}

scmplx * DVBS2::pl_get_dummy( int &len )
{
    scmplx * frame;

    len   = m_dummy_frame_length;
    frame = m_pl_dummy;
    return frame;
}
