/*
 * rectwaveguide.cpp - rectangular waveguide class implementation
 *
 * Copyright (C) 2001 Gopal Narayanan <gopal@astro.umass.edu>
 * Copyright (C) 2005, 2006 Stefan Jahn <stefan@lkcc.org>
 *
 * 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; either version 2
 * 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this package; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include <stdio.h>
#include <string.h>
#include <math.h>

#include <units.h>
#include <transline.h>
#include <rectwaveguide.h>

RECTWAVEGUIDE::RECTWAVEGUIDE() : TRANSLINE()
{
    m_name = "RectWaveGuide";
}


/*
 * returns square of k
 */
double RECTWAVEGUIDE::kval_square()
{
    double kval;

    kval = 2.0* M_PI* f* sqrt( mur* er ) / C0;

    return kval * kval;
}


/*
 * given mode numbers m and n
 * returns square of cutoff kc value
 */
double RECTWAVEGUIDE::kc_square( int m, int n )
{
    return pow( (m * M_PI / a), 2.0 ) + pow( (n * M_PI / b), 2.0 );
}


/*
 * given mode numbers m and n
 * returns cutoff fc value
 */
double RECTWAVEGUIDE::fc( int m, int n )
{
    return sqrt( kc_square( m, n ) / mur / er ) * C0 / 2.0 / M_PI;
}


/*
 * alphac - returns attenuation due to conductor losses for all propagating
 * modes in the waveguide
 */
double RECTWAVEGUIDE::alphac()
{
    double Rs, f_c;
    double ac;
    short  m, n, mmax, nmax;

    Rs   = sqrt( M_PI * f * murC * MU0 / sigma );
    ac   = 0.0;
    mmax = (int) floor( f / fc( 1, 0 ) );
    nmax = mmax;

    /* below from Ramo, Whinnery & Van Duzer */

    /* TE(m,n) modes */
    for( n = 0; n<= nmax; n++ )
    {
        for( m = 1; m <= mmax; m++ )
        {
            f_c = fc( m, n );
            if( f > f_c )
            {
                switch( n )
                {
                case 0:
                    ac += ( Rs / ( b * ZF0 * sqrt( 1.0 - pow( (f_c / f), 2.0 ) ) ) ) *
                          ( 1.0 + ( (2 * b / a) * pow( (f_c / f), 2.0 ) ) );
                    break;

                default:
                    ac += ( (2. * Rs) / ( b * ZF0 * sqrt( 1.0 - pow( (f_c / f), 2.0 ) ) ) ) *
                          ( ( ( 1. + (b / a) ) * pow( (f_c / f), 2.0 ) ) +
                           ( ( 1. -
                              pow( (f_c / f),
                                  2.0 ) ) *
                            ( ( (b / a) * ( ( (b / a) * pow( m, 2. ) ) + pow( n, 2. ) ) ) /
                    ( pow( (b * m / a),
                          2.0 ) + pow( n, 2.0 ) ) ) ) );
                    break;
                }
            }
        }
    }

    /* TM(m,n) modes */
    for( n = 1; n<= nmax; n++ )
    {
        for( m = 1; m<= mmax; m++ )
        {
            f_c = fc( m, n );
            if( f > f_c )
            {
                ac += ( (2. * Rs) / ( b * ZF0 * sqrt( 1.0 - pow( (f_c / f), 2.0 ) ) ) ) *
                      ( ( ( pow( m, 2.0 ) * pow( (b / a), 3.0 ) ) + pow( n, 2. ) ) /
                       ( ( pow( (m * b / a), 2. ) ) + pow( n, 2.0 ) ) );
            }
        }
    }

    ac = ac * 20.0 * log10( exp( 1. ) ); /* convert from Np/m to db/m */
    return ac;
}


/*
 * alphac_cutoff - returns attenuation for a cutoff wg
 */
double RECTWAVEGUIDE::alphac_cutoff()
{
    double acc;

    acc = sqrt( kc_square( 1, 0 ) - kval_square() );
    acc = 20 * log10( exp( 1.0 ) ) * acc;
    return acc;
}


/*
 * returns attenuation due to dielectric losses
 */
double RECTWAVEGUIDE::alphad()
{
    double k_square, beta;
    double ad;

    k_square = kval_square();
    beta     = sqrt( k_square - kc_square( 1, 0 ) );

    ad = (k_square * tand) / (2.0 * beta);
    ad = ad * 20.0 * log10( exp( 1. ) ); /* convert from Np/m to db/m */
    return ad;
}


/*
 * get_rectwaveguide_sub
 * get and assign rectwaveguide substrate parameters
 * into rectwaveguide structure
 */
void RECTWAVEGUIDE::get_rectwaveguide_sub()
{
    er    = getProperty( EPSILONR_PRM );
    mur   = getProperty( MUR_PRM );
    murC  = getProperty( MURC_PRM );
    sigma = 1.0 / getProperty( RHO_PRM );
    tand  = getProperty( TAND_PRM );
    tanm  = getProperty( TANM_PRM );
}


/*
 * get_rectwaveguide_comp
 * get and assign rectwaveguide component parameters
 * into rectwaveguide structure
 */
void RECTWAVEGUIDE::get_rectwaveguide_comp()
{
    f = getProperty( FREQUENCY_PRM );
}


/*
 * get_rectwaveguide_elec
 * get and assign rectwaveguide electrical parameters
 * into rectwaveguide structure
 */
void RECTWAVEGUIDE::get_rectwaveguide_elec()
{
    Z0    = getProperty( Z0_PRM );
    ang_l = getProperty( ANG_L_PRM );
}


/*
 * get_rectwaveguide_phys
 * get and assign rectwaveguide physical parameters
 * into rectwaveguide structure
 */
void RECTWAVEGUIDE::get_rectwaveguide_phys()
{
    a = getProperty( PHYS_WIDTH_PRM );
    b = getProperty( PHYS_S_PRM );
    l = getProperty( PHYS_LEN_PRM );
}


/*
 * analyze - analysis function
 */
void RECTWAVEGUIDE::analyze()
{
    double lambda_g;
    double k_square;

    /* Get and assign substrate parameters */
    get_rectwaveguide_sub();

    /* Get and assign component parameters */
    get_rectwaveguide_comp();

    /* Get and assign physical parameters */
    get_rectwaveguide_phys();

    k_square = kval_square();

    if( kc_square( 1, 0 ) <= k_square )
    {
        /* propagating modes */

        // Z0 definition using fictive voltages and currents
        Z0 = 2.0* ZF0* sqrt( mur / er ) * (b / a) / sqrt( 1.0 - pow( (fc( 1, 0 ) / f), 2.0 ) );

        /* calculate electrical angle */
        lambda_g   = 2.0 * M_PI / sqrt( k_square - kc_square( 1, 0 ) );
        ang_l      = 2.0 * M_PI * l / lambda_g; /* in radians */
        atten_cond = alphac() * l;
        atten_dielectric = alphad() * l;
        er_eff = ( 1.0 - pow( fc( 1, 0 ) / f, 2.0 ) );
    }
    else
    {
        /* evanascent modes */
        Z0     = 0;
        ang_l  = 0;
        er_eff = 0;
        atten_dielectric = 0.0;
        atten_cond = alphac_cutoff() * l;
    }

    setProperty( Z0_PRM, Z0 );
    setProperty( ANG_L_PRM, ang_l );

    show_results();
}


/*
 * synthesize - synthesis function
 */
void RECTWAVEGUIDE::synthesize()
{
    double lambda_g, k_square, beta;

    /* Get and assign substrate parameters */
    get_rectwaveguide_sub();

    /* Get and assign component parameters */
    get_rectwaveguide_comp();

    /* Get and assign electrical parameters */
    get_rectwaveguide_elec();

    /* Get and assign physical parameters */
    get_rectwaveguide_phys();


    if( isSelected( PHYS_S_PRM ) )
    {
        /* solve for b */
        b = Z0 * a * sqrt( 1.0 - pow( fc( 1, 0 ) / f, 2.0 ) ) / ( 2.0 * ZF0 * sqrt( mur / er ) );
        setProperty( PHYS_S_PRM, b );
    }
    else if( isSelected( PHYS_WIDTH_PRM ) )
    {
        /* solve for a */
        a = sqrt( pow( 2.0 * ZF0 * b / Z0, 2.0 ) + pow( C0 / (2.0 * f), 2.0 ) );
        setProperty( PHYS_WIDTH_PRM, a );
    }

    k_square = kval_square();
    beta     = sqrt( k_square - kc_square( 1, 0 ) );
    lambda_g = 2.0 * M_PI / beta;
    l = (ang_l * lambda_g) / (2.0 * M_PI); /* in m */

    setProperty( PHYS_LEN_PRM, l );

    if( kc_square( 1, 0 ) <= k_square )
    {
        /*propagating modes */
        beta = sqrt( k_square - kc_square( 1, 0 ) );
        lambda_g   = 2.0 * M_PI / beta;
        atten_cond = alphac() * l;
        atten_dielectric = alphad() * l;
        er_eff = ( 1.0 - pow( (fc( 1, 0 ) / f), 2.0 ) );
    }
    else
    {
        /*evanascent modes */
        Z0     = 0;
        ang_l  = 0;
        er_eff = 0;
        atten_dielectric = 0.0;
        atten_cond = alphac_cutoff() * l;
    }

    show_results();
}


#define MAXSTRLEN 128
void RECTWAVEGUIDE::show_results()
{
    int  m, n, max = 6;
    char text[MAXSTRLEN], txt[32];

    // Z0EH = Ey / Hx (definition with field quantities)
    Z0EH = ZF0 * sqrt( kval_square() / ( kval_square() - kc_square( 1, 0 ) ) );
    setResult( 0, Z0EH, "Ohm" );

    setResult( 1, er_eff, "" );
    setResult( 2, atten_cond, "dB" );
    setResult( 3, atten_dielectric, "dB" );

    // show possible TE modes (H modes)
    if( f < fc( 1, 0 ) )
        strcpy( text, "none" );
    else
    {
        strcpy( text, "" );
        for( m = 0; m<= max; m++ )
        {
            for( n = 0; n<= max; n++ )
            {
                if( (m == 0) && (n == 0) )
                    continue;
                if( f >= ( fc( m, n ) ) )
                {
                    sprintf( txt, "H(%u,%u) ", m, n );
                    if( (strlen( text ) + strlen( txt ) + 5) < MAXSTRLEN )
                        strcat( text, txt );
                    else
                    {
                        strcat( text, "..." );
                        m = n = max + 1; // print no more modes
                    }
                }
            }
        }
    }
    setResult( 4, text );

    // show possible TM modes (E modes)
    if( f < fc( 1, 1 ) )
        strcpy( text, "none" );
    else
    {
        strcpy( text, "" );
        for( m = 1; m<= max; m++ )
        {
            for( n = 1; n<= max; n++ )
            {
                if( f >= fc( m, n ) )
                {
                    sprintf( txt, "E(%u,%u) ", m, n );
                    if( (strlen( text ) + strlen( txt ) + 5) < MAXSTRLEN )
                        strcat( text, txt );
                    else
                    {
                        strcat( text, "..." );
                        m = n = max + 1; // print no more modes
                    }
                }
            }
        }
    }
    setResult( 5, text );
}
