/*

Copyright (C) 2001  Gopal Narayanan <gopal@astro.umass.edu>

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 program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

*/

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

#include "rectwaveguide.h"

/*char *drawing_filename;*/
/*char *image_dir;*/

gboolean widthfixed, heightfixed;

/*
 * get_rectwaveguide_sub
 * get and assign rectwaveguide substrate parameters
 * into rectwaveguide structure
 */


/*
 * returns k
 */
gfloat
kval (rectwaveguide *rw)
{
  gfloat mur, er, f;
  gfloat kval;

  mur = rw->mur;
  er = rw->er;
  f = rw->f;

  kval = 2. * M_PI * f * sqrt(mur * er)/C0;
  return kval;
}
 
/*
 * given mode numbers m and n 
 * returns cutoff kc value
 */
   
gfloat 
kc (rectwaveguide *rw, int m, int n)
{
  gfloat a, b;
  gfloat kcval;

  a = rw->a;
  b = rw->b;
  
  kcval = sqrt(pow((m*M_PI/a),2.0) + pow((n*M_PI/b),2.0));
  return kcval;
}

/*
 * given mode numbers m and n 
 * returns cutoff fc value
 */
   
gfloat 
fc (rectwaveguide *rw, int m, int n)
{
  gfloat mur, er;
  gfloat fcval;

  mur = rw->mur;
  er = rw->er;
  
  fcval =  (kc(rw, m, n) * C0)/ (2 * M_PI * sqrt(mur * er));
  return fcval;
}

/*
 * alphac - returns attenuation due to conductor losses for all propagating
 * modes in the waveguide
 */

gfloat 
alphac (rectwaveguide *rw)
{

  gfloat a, b, sigma, mur, f;
  gfloat Rs, k, f_c;
  gfloat ac;
  short m, n, mmax, nmax;

  a = rw->a;
  b = rw->b;
  mur = rw->mur;
  sigma = rw->sigma;
  f = rw->f;

  Rs = sqrt((M_PI * f * mur* MU0)/sigma);
  k = kval(rw);
  ac = 0.0;
  mmax = (int) floor (f/fc(rw,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(rw, m, n);
      if (f > f_c) {
	switch (n) {
	case 0:
	  ac += (Rs/(b * 120.0 * M_PI * 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 * 120.0 * M_PI * 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(rw, m, n);
      if (f > f_c) {
	ac += ((2. * Rs)/(b * 120.0 * M_PI * 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
 */
gfloat 
alphac_cutoff (rectwaveguide *rw)
{
  gfloat acc;

  acc = sqrt(pow(kc(rw,1,0),2.0) - pow(kval(rw),2.0));
  acc = 20 * log10(exp(1.0)) * acc;
  return acc;
}

/*
 * returns attenuation due to dielectric losses
 */

gfloat
alphad(rectwaveguide *rw)
{
  gfloat tand;
  gfloat k, beta;
  gfloat ad;

  tand = rw->tand;
  k = kval(rw);
  beta = sqrt(pow(k,2.) - pow(kc(rw,1,0),2.0));  
  
  ad = (pow(k,2.0) * tand)/(2.0 * beta);
  ad = ad * 20.0 * log10(exp(1.)); /* convert from Np/m to db/m */
  return ad;
}
  
  

void 
get_rectwaveguide_sub (trans_win *rwin,
			 rectwaveguide *rw)
{


  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->subparam_text[0])), 
	      "%g", &rw->er) != 1) error_mes("Error: rw->er");

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->subparam_text[1])), 
	      "%g", &rw->mur) != 1) error_mes("Error: rw->mur");

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->subparam_text[2])), 
	      "%g", &rw->sigma) != 1) error_mes("Error: rw->sigma");
  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->subparam_text[3])), 
	      "%g", &rw->tand) != 1) error_mes("Error: rw->tand");
  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->subparam_text[4])), 
	      "%g", &rw->tanm) != 1) error_mes("Error: rw->tanm");
}

/*
 * get_rectwaveguide_comp
 * get and assign rectwaveguide component parameters
 * into rectwaveguide structure
 */

void 
get_rectwaveguide_comp (trans_win *rwin,
			  rectwaveguide *rw)
{
  short curr_unit;

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->component_param_text[0])), 
	      "%g", &rw->f) != 1) error_mes("Error: rw->f");
  curr_unit = getunit (gtk_entry_get_text (GTK_ENTRY 
					   (GTK_COMBO(rwin->component_param_combo[0])->entry)));
  rw->f = rw->f * conv_freq[curr_unit][FREQ_HZ];
}

/*
 * get_rectwaveguide_elec
 * get and assign rectwaveguide electrical parameters
 * into rectwaveguide structure
 */

void 
get_rectwaveguide_elec (trans_win *rwin,
			  rectwaveguide *rw)
{
  short curr_unit;

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->electrical_param_text[0])), 
	      "%g", &rw->Z0) != 1) error_mes("Error: rw->Z0");
  curr_unit = getunit (gtk_entry_get_text (GTK_ENTRY 
					   (GTK_COMBO(rwin->electrical_param_combo[0])->entry)));
  rw->Z0 = rw->Z0 * conv_res[curr_unit][RES_OHM];

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->electrical_param_text[1])), 
	      "%g", &rw->ang_l) != 1) error_mes("Error: rw->ang_l");
  curr_unit = getunit (gtk_entry_get_text (GTK_ENTRY 
					   (GTK_COMBO(rwin->electrical_param_combo[1])->entry)));
  rw->ang_l = rw->ang_l * conv_ang[curr_unit][ANG_RAD];
}


/*
 * get_rectwaveguide_phys
 * get and assign rectwaveguide physical parameters
 * into rectwaveguide structure
 */

void 
get_rectwaveguide_phys (trans_win *rwin,
			  rectwaveguide *rw)
{
  short curr_unit;

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->physical_param_text[0])), 
	      "%g", &rw->a) != 1) error_mes("Error: rw->a");
  curr_unit = getunit (gtk_entry_get_text (GTK_ENTRY 
					   (GTK_COMBO(rwin->physical_param_combo[0])->entry)));
  rw->a = rw->a * conv_length[curr_unit][LENGTH_M];

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->physical_param_text[1])), 
	      "%g", &rw->b) != 1) error_mes("Error: rw->b");
  curr_unit = getunit (gtk_entry_get_text (GTK_ENTRY 
					   (GTK_COMBO(rwin->physical_param_combo[1])->entry)));
  rw->b = rw->b * conv_length[curr_unit][LENGTH_M];

  if (sscanf (gtk_entry_get_text(GTK_ENTRY (rwin->physical_param_text[2])), 
	      "%g", &rw->l) != 1) error_mes("Error: rw->l");
  curr_unit = getunit (gtk_entry_get_text (GTK_ENTRY 
					   (GTK_COMBO(rwin->physical_param_combo[2])->entry)));
  rw->l = rw->l * conv_length[curr_unit][LENGTH_M];

}



/*
 * analyze_rectwaveguide - analysis function
 */
void
analyze_rectwaveguide (GtkWidget *parent,
		       trans_win *rwin)
{
  gchar *text, *results, *temp;
  rectwaveguide *rw;
  short required_unit;
  gfloat lambda_g;
  gfloat k;
  gfloat beta;
  short m, n, mmax, nmax;
  gfloat f;
  gfloat a,b;

  /*allocate memory for text */
  if ((text = (char *) malloc(10*sizeof(char)))  == NULL){
    perror("text error: malloc");
    exit(-1);
  }


  /*allocate memory for pointer rw */
  if ((rw = g_malloc(sizeof *rw)) != NULL)
    {

      /* Get and assign substrate parameters */
      get_rectwaveguide_sub(rwin, rw);

      /* Get and assign component parameters */
      get_rectwaveguide_comp(rwin, rw);
      
      /* Get and assign physical parameters */
      get_rectwaveguide_phys(rwin, rw);

      f = rw->f;
      a = rw->a;
      b = rw->b;

      k = kval(rw);
      
      if (kc(rw,1,0) <= k) {
	/*propagating modes */
	beta = sqrt(pow(k,2.) - pow(kc(rw,1,0),2.0));
	lambda_g = (2. * M_PI)/beta;
	/*	rw->Z0 = (k * 120. * M_PI)/beta; */
	rw->Z0 = 2.0 * 120.0 * M_PI * (b/a) * 1/
	  sqrt(1.0 - pow((fc(rw,1,0)/f),2.0));

	/* calculate electrical angle */
	lambda_g = (2. * M_PI)/beta;
	rw->ang_l = (2.0 * M_PI * rw->l)/lambda_g;    /* in radians */
	rw->atten_cond = alphac (rw) * rw->l;
	rw->atten_dielectric = alphad (rw) * rw->l;
	rw->er_eff = (1.0 - pow((fc(rw,1,0)/rw->f),2.0));
      } else { 
	/*evanascent modes */	
	rw->Z0 = 0;
	rw->ang_l = 0;
	rw->er_eff = 0;
	rw->atten_dielectric = 0.0;
	rw->atten_cond = alphac_cutoff (rw) * rw->l;
      }
     
      required_unit = getunit (gtk_entry_get_text 
			       (GTK_ENTRY 
				(GTK_COMBO(rwin->electrical_param_combo[0])->entry)));
      sprintf(text,"%g", (float) (rw->Z0 * conv_res[RES_OHM][required_unit]));
      gtk_entry_set_text (GTK_ENTRY (rwin->electrical_param_text[0]), text);
      
      required_unit = getunit (gtk_entry_get_text 
			       (GTK_ENTRY 
				(GTK_COMBO(rwin->electrical_param_combo[1])->entry)));
      sprintf(text,"%g", (float) (rw->ang_l * conv_ang[ANG_RAD][required_unit]));
      gtk_entry_set_text (GTK_ENTRY (rwin->electrical_param_text[1]), text);  

      
      free(text);
      
      if ((text = (char *) malloc(1000*sizeof(char)))  == NULL){
	perror("results text error: malloc");
	exit(-1);
      }
      if ((results = (char *) malloc(1000*sizeof(char)))  == NULL){
	perror("results text error: malloc");
	exit(-1);
      }
      if ((temp = (char *) malloc(1000*sizeof(char)))  == NULL){
	perror("results text error: malloc");
	exit(-1);
      }
      
      sprintf(results, "\nEffective dielectic constant, er_eff = %.4g\n", 
	      rw->er_eff);
      required_unit = getunit(gtk_entry_get_text (GTK_ENTRY 
						  (GTK_COMBO 
						   (rwin->physical_param_combo[2])->entry)));
      sprintf(text,"Conductor Losses = %.4g dB\n", 
              rw->atten_cond);
      strcat(results,text);
      sprintf(text, "Dielectric Losses = %.4g dB\n", 
              rw->atten_dielectric);
      strcat(results, text);

      if (f >= (2.*fc(rw,1,0))) {
	  /* multiple modes possible in waveguide */
	  strcpy(text, "\nWarning: The following additional \nTE modes can propagate:\n");
	  strcat(results, text);
	  /*	  mmax = floor(f/fc(rw,1,0));*/
	  mmax = 5;
	  nmax = mmax;
	  strcpy(text, " ");
	  for (m = 2; m<= mmax; m++) {
	    for (n=0; n<= nmax; n++) {
	      if (f >= (fc(rw,m,n))){
		sprintf(temp,"TE(%u,%u) ",m, n);
		strcat(text,temp);
	      }
	    }
	  }
	  strcat(results,text);
	  strcat(results,"\n");
	}
	if (f >= fc(rw,1,1)){ /*TM(1,1) mode possible*/
	  strcpy(text, "\nWarning: The following additional \nTM modes can propagate:\n");
	  strcat(results, text);
	  /*	  mmax = floor(f/fc(rw,1,1));*/
	  mmax = 5;
	  nmax = mmax;
	  strcpy(text, " ");
	  for (m = 1; m<= mmax; m++) {
	    for (n=1; n<= nmax; n++) {
	      if (f >= (fc(rw,m,n))){
		sprintf(temp,"TM(%u,%u) ",m, n);
		strcat(text,temp);
	      }
	    }
	  }
	  strcat(results,text);
	  strcat(results,"\n");
	}	  

	gtk_label_set (GTK_LABEL (rwin->results_text), results);
	free(results);
	free(text);
	free(temp);

        if (statusexists){
          if (statusint != CONSISTENT) {
            gtk_label_set_text (GTK_LABEL (rwin->status), "Values are consistent");
          } 
        }
        statusint = CONSISTENT;
	
    } else {
      perror("malloc rw");
      exit(-1);
    }

}

/*
 * synthesize_rectwaveguide - synthesis function
 */
void
synthesize_rectwaveguide (GtkWidget *parent,
		       trans_win *rwin)
{
  gchar *text, *results, *temp;
  rectwaveguide *rw;
  short required_unit;
  gfloat lambda_g;
  gfloat a, b, f;
  gfloat Z0;
  gfloat beta, k;
  short m, n, mmax, nmax;
  
  /*allocate memory for text */
  if ((text = (char *) malloc(10*sizeof(char)))  == NULL){
    perror("text error: malloc");
    exit(-1);
  }

  /*allocate memory for pointer rw */
  if ((rw = g_malloc(sizeof *rw)) != NULL)
    {

      /* Get and assign substrate parameters */
      get_rectwaveguide_sub(rwin, rw);

      /* Get and assign component parameters */
      get_rectwaveguide_comp(rwin, rw);
      
      /* Get and assign electrical parameters */
      get_rectwaveguide_elec(rwin, rw);

      /* Get and assign physical parameters */
      get_rectwaveguide_phys(rwin, rw);

      f = rw->f;
      a = rw->a;
      b = rw->b;
      Z0 = rw->Z0;

      if (widthfixed) {
	/* solve for b */
        rw->b = Z0 * a * sqrt(1.0 - pow((fc(rw,1,0)/f),2.0))/
	  (2. * 120. * M_PI);
	required_unit = getunit (gtk_entry_get_text 
			       (GTK_ENTRY 
				(GTK_COMBO(rwin->physical_param_combo[1])->entry)));
	sprintf(text,"%g", (float) (rw->b * conv_length[LENGTH_M][required_unit]));
	gtk_entry_set_text (GTK_ENTRY (rwin->physical_param_text[1]), text);
      } else {
	/* solve for a */
	rw->a = sqrt(pow((2.0 * 120. * M_PI * b/Z0), 2.0) + 
		     pow((C0/(2.0 * f)),2.0));
	required_unit = getunit (gtk_entry_get_text 
				 (GTK_ENTRY 
				  (GTK_COMBO(rwin->physical_param_combo[0])->entry)));
	sprintf(text,"%g", (float) (rw->a * conv_length[LENGTH_M][required_unit]));
	gtk_entry_set_text (GTK_ENTRY (rwin->physical_param_text[0]), text);
      }

      k = kval (rw);
      beta = sqrt(pow(k,2.) - pow(kc(rw,1,0),2.0));
      lambda_g = (2. * M_PI)/beta;
      rw->l = (rw->ang_l * lambda_g)/(2.0 * M_PI);    /* in m */
      
      required_unit = getunit (gtk_entry_get_text 
                               (GTK_ENTRY 
				(GTK_COMBO(rwin->physical_param_combo[2])->entry)));
      sprintf(text,"%g", (float) (rw->l * conv_length[LENGTH_M][required_unit]));
      gtk_entry_set_text (GTK_ENTRY (rwin->physical_param_text[2]), text);

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


      free(text);
      if ((text = (char *) malloc(500*sizeof(char)))  == NULL){
        perror("results text error: malloc");
        exit(-1);
      }
      
      if ((results = (char *) malloc(500*sizeof(char)))  == NULL){
        perror("results text error: malloc");
        exit(-1);
      }
      
      if ((temp = (char *) malloc(1000*sizeof(char)))  == NULL){
	perror("results text error: malloc");
	exit(-1);
      }

      
      sprintf(results, "\nEffective dielectic constant, er_eff = %.4g\n", 
	      rw->er_eff);
      required_unit = getunit(gtk_entry_get_text (GTK_ENTRY 
						  (GTK_COMBO 
						   (rwin->physical_param_combo[2])->entry)));
      sprintf(text,"Conductor Losses = %.4g dB\n", 
              rw->atten_cond);
      strcat(results,text);
      sprintf(text, "Dielectric Losses = %.4g dB\n", 
              rw->atten_dielectric);
      strcat(results, text);
      
      if (f >= (2.*fc(rw,1,0))) {
	/* multiple modes possible in waveguide */
	strcpy(text, "\nWarning: The following additional \nTE modes can propagate:\n");
	strcat(results, text);
	/*	  mmax = floor(f/fc(rw,1,0));*/
	mmax = 5;
	nmax = mmax;
	strcpy(text, " ");
	for (m = 2; m<= mmax; m++) {
	  for (n=0; n<= nmax; n++) {
	    if (f >= (fc(rw,m,n))){
	      sprintf(temp,"TE(%u,%u) ",m, n);
	      strcat(text,temp);
	    }
	  }
	}
	strcat(results,text);
	strcat(results,"\n");
      }
      if (f >= fc(rw,1,1)){ /*TM(1,1) mode possible*/
	strcpy(text, "\nWarning: The following additional \nTM modes can propagate:\n");
	strcat(results, text);
	/*	  mmax = floor(f/fc(rw,1,1));*/
	mmax = 5;
	nmax = mmax;
	strcpy(text, " ");
	for (m = 1; m<= mmax; m++) {
	  for (n=1; n<= nmax; n++) {
	    if (f >= (fc(rw,m,n))){
	      sprintf(temp,"TM(%u,%u) ",m, n);
	      strcat(text,temp);
	    }
	  }
	}
	strcat(results,text);
	strcat(results,"\n");
      }	  
      
      gtk_label_set (GTK_LABEL (rwin->results_text), results);
      free(results);
      free(text);
      free(temp);
      

      if (statusexists){
        if (statusint != CONSISTENT) {
          gtk_label_set_text (GTK_LABEL (rwin->status), 
			      "Values are consistent");
        } 
      }
      statusint = CONSISTENT;
    } else {
      perror("malloc rw");
      exit(-1);
    }
}


void
fixwidth (GtkWidget *parent, trans_win *rwin)
{
  
  gtk_label_set_text (GTK_LABEL (GTK_BIN (parent)->child), "Fixed");
  gtk_widget_set_sensitive (GTK_WIDGET (parent), FALSE);  
  gtk_label_set_text (GTK_LABEL (GTK_BIN (rwin->physical_param_fix[1])->child), "Fix");
  gtk_widget_set_sensitive (GTK_WIDGET (rwin->physical_param_fix[1]), TRUE);  
  widthfixed = TRUE;
  heightfixed = FALSE;
}

void
fixheight (GtkWidget *parent, trans_win *rwin)
{
  
  gtk_label_set_text (GTK_LABEL (GTK_BIN (parent)->child), "Fixed");
  gtk_widget_set_sensitive (GTK_WIDGET (parent), FALSE);  
  gtk_label_set_text (GTK_LABEL (GTK_BIN (rwin->physical_param_fix[0])->child), "Fix");
  gtk_widget_set_sensitive (GTK_WIDGET (rwin->physical_param_fix[0]), TRUE);  
  widthfixed = FALSE;
  heightfixed = TRUE;
}


/*
 * the window aspect 
 */


void 
rectwaveguide_win (GtkWidget *parent)
     /*rectwaveguide_win (trans_gui *tg)*/
{
  short row;

  /* if there is a window that already exists kill it first */
  if (main_body_window != NULL)   {
    gtk_widget_destroy(main_body_window);
    g_free (twin);
    twin = g_malloc(sizeof(*twin));
  }

  setup_transgui(RECTWAVEGUIDE, parent, twin);  



  gtk_label_set_text (GTK_LABEL (GTK_BIN (twin->physical_param_fix[0])->child), "Fixed");
  gtk_widget_set_sensitive (GTK_WIDGET (twin->physical_param_fix[0]), FALSE);
  widthfixed = TRUE;
  heightfixed = FALSE;


  for (row = 0; row<=1; row++) {
    switch(row) {
    case 0: 
      gtk_signal_connect (GTK_OBJECT (twin->physical_param_fix[row]), "clicked",
			  GTK_SIGNAL_FUNC (fixwidth), twin);
      break;
    case 1:
      gtk_signal_connect (GTK_OBJECT (twin->physical_param_fix[row]), "clicked",
			  GTK_SIGNAL_FUNC (fixheight), twin);
      break;
    }
  }

  gtk_signal_connect (GTK_OBJECT (twin->Analbutton), "clicked",
		      GTK_SIGNAL_FUNC (analyze_rectwaveguide), twin); 

  gtk_signal_connect (GTK_OBJECT (twin->Synbutton), "clicked",
		      GTK_SIGNAL_FUNC (synthesize_rectwaveguide), twin); 

  if (statusint == INCONSISTENT)  {
    analyze_rectwaveguide(parent, (trans_win *) twin);
  }


  for (row = 0; row<=1; row++){
    gtk_signal_connect (GTK_OBJECT (twin->electrical_param_text[row]), "changed",
			GTK_SIGNAL_FUNC (setstatus), twin);
  }

}


