
/*
*     File getofdm.c
*    
*     Author PA0MBO - M.BOS
*     DATE Feb 21st 2009
*
*     implements more or less the get_ofdm_symbol 
*     MATLAB routine from diorama-1.1.1
*
*     
*/

/*************************************************************************
*
*                           PA0MBO
*
*    COPYRIGHT (C)  2009  M.Bos 
*
*    This file is part of the distribution package RXAMADRM
*
*    This package is free software and you can redistribute is
*    and/or modify it under the terms of the GNU General Public License
*
*    More details can be found in the accompanying file COPYING
*************************************************************************/



#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fftw3.h>
#include "drmproto.h"
#include "appglobal.h"

#define PI (atan(1.0)*4.0)
int getofdm( /*@null@ */ float *rs, float time_offset_fractional_init,
             float freq_offset_init, float delta_time_offset_I_init, int Ts,
             int Tu, /*@null@ */ float Zi[], /*@null@ */ float *out,
             int init, int TIME_SYNC_ENABLE, int FREQ_SYNC_ENABLE)
{
  float EPSILON = 1.0E-10;
  float max_theta = 10.0;
  //  float max_time_offset_ctrl = 10.0;
  //  float max_delta_time_offset = 2.0;
  float kP_small_timing_controller = 0.01;
  float kP_large_timing_controller = 0.01;
  float threshold_timing_small_large = 2.0;
  float kI_timing_controller = 0.00020;
  float kP_small_freq_controller = 0.750;
  float threshold_freq_small_large = 0.75;
  float kI_freq_controller = 0.0008;
  int Tg, Tgh;
  float phi_freq_correction_last;
  float delta_time_offset_I;
  float delta_time_offset_P;
  float dfreq_offset_I;
  float freq_offset, time_offset_fractional;
  int i;
  float temp1[] = { 0.0, 0.0 }, temp2, temp3, temp4;
  float temp[] = { 0.0, 0.0 };
  float temp5[] = { 0.0, 0.0 };
  float temp6, temp7;
  float temp8[] = { 0.0, 0.0 };
  float temp9, temp10;
  float theta_plus, theta_minus, delta_theta;
  float time_offset_ctrl;
  float delta_time_offset;
  int delta_time_offset_integer;
  float epsilon_ML, freq_offset_ctrl, dfreq_offset_P;
  float kP_large_freq_controller, dfreq_offset;
  float tmptheta, term1;
  static float *exp_temp;
  static float *out1;

  static drmComplex s[288], S[288];	/* 288 = max Tu */
  static fftwf_plan p = NULL;

  if (init == 1)

    {

      /* malloc space for arrays */
      if ((exp_temp = (float *)malloc(Tu * 2 * sizeof(float))) == NULL)

        {
          printf("cannot malloc space for exp_temp in get_ofdm_symbol\n");
          exit(EXIT_FAILURE);
        }
      if ((out1 = (float *)malloc(Tu * 2 * sizeof(float))) == NULL)

        {
          printf("cannot malloc space for out1 in get_ofdm_symbol\n");
          exit(EXIT_FAILURE);
        }
      if (p != NULL)

        {
          fftwf_destroy_plan(p);
        }
      p = fftwf_plan_dft_1d(Tu,(fftwf_complex *)s,(fftwf_complex *)S,FFTW_FORWARD, FFTW_ESTIMATE);
      return (0);
    }

  else

    {

      /* fixed parameters */
      EPSILON = 1.0E-10;
      max_theta = 5.0;
      //      max_time_offset_ctrl = 10.0;
      //      max_delta_time_offset = 2.0;
      kP_small_timing_controller = 0.01;
      kP_large_timing_controller = 0.01;
      threshold_timing_small_large = 2.0;
      kI_timing_controller = 0.00005;
      kP_small_freq_controller = 0.05;
      kP_large_freq_controller = 0.75;
      threshold_freq_small_large = 0.5;
      kI_freq_controller = 0.0008;
      Tg = Ts - Tu;
      Tgh = (int) floor(Tg / 2 + 0.5);
      if (Zi[0] < 0.0)

        {
          phi_freq_correction_last = 0.0;
          delta_time_offset_I = delta_time_offset_I_init;
          dfreq_offset_I = 0.0;
          freq_offset = freq_offset_init;
          time_offset_fractional = time_offset_fractional_init;
        }

      else

        {
          phi_freq_correction_last = Zi[1];
          delta_time_offset_I = Zi[2];
          dfreq_offset_I = Zi[3];
          freq_offset = Zi[4];
          time_offset_fractional = Zi[5];
        }
      if (TIME_SYNC_ENABLE == 1)

        {
          temp1[0] = 0.0;
          temp1[1] = 0.0;
          temp2 = 0.0;
          temp3 = 0.0;
          temp5[0] = 0.0;
          temp5[1] = 0.0;
          temp6 = 0.0;
          temp7 = 0.0;
          temp8[0] = 0.0;
          temp8[1] = 0.0;
          temp9 = 0.0;
          temp10 = 0.0;
          for (i = 0; i < Tg + 2; i++)

            {
//              logfile->addToAux(QString("tm1 %1 %2 %3 %4 %5").arg(rs[i * 2] ).arg(rs[(Tu + i) * 2]).arg(rs[i * 2 + 1]).arg(rs[(Tu + i) * 2 + 1]).arg(temp1[0]));
              temp1[0] += rs[i * 2] * rs[(Tu + i) * 2] + rs[i * 2 + 1] * rs[(Tu + i) * 2 + 1];	/* real part */
              temp1[1] += -rs[i * 2] * rs[(Tu + i) * 2 + 1] + rs[i * 2 + 1] * rs[(Tu + i) * 2];	/* imag part */
              temp2 += rs[i * 2] * rs[i * 2] + rs[i * 2 + 1] * rs[i * 2 + 1];
              temp3 += rs[(i + Tu) * 2] * rs[(i + Tu) * 2] + rs[(i + Tu) * 2 + 1] * rs[(i + Tu) * 2 + 1];
            }
          temp4 = (float) (EPSILON + 0.5 * (temp2 + temp3));
//          logfile->addToAux(QString("tmp %1 %2 %3 %4 %5").arg(temp1[0]).arg(temp1[1]).arg(temp2).arg(temp3).arg(temp4));

          /* time offset measurement : theta */
          for (i = 0; i < 5; i++)

            {
              temp5[0] += rs[i * 2] * rs[(Tu + i) * 2] + rs[i * 2 + 1] * rs[(Tu + i) * 2 + 1];	/* 1-5 * Tu_1_5 */
              temp5[1] += -rs[i * 2] * rs[(Tu + i) * 2 + 1] + rs[i * 2 +1] * rs[(Tu + i) * 2];
              temp6 += rs[i * 2] * rs[i * 2] + rs[i * 2 + 1] * rs[i * 2 + 1];	/* 1-5 * 1-5' */
              temp7 += rs[(i + Tu) * 2] * rs[(i + Tu) * 2] + rs[(i + Tu) * 2 + 1] * rs[(i + Tu) * 2 + 1];	/* Tu_1_5 * Tu_1_5' */ /* pa0mbo check for OK */
              temp8[0] += rs[(i + Tg - 3) * 2] * rs[(i + Tu + Tg - 3) * 2] + rs[(i + Tg - 3) * 2 + 1] * rs[(i + Tu + Tg - 3) * 2 + 1];	/* Tg -2 +2 * Tg + Tu -2 +2 */
              temp8[1] += -rs[(i + Tg - 3) * 2] * rs[(i + Tu + Tg - 3) * 2 + 1] + rs[(i + Tg - 3) * 2 + 1] + rs[(i + Tu + Tg - 3) * 2];
              temp9 += rs[(i + Tg - 3) * 2] * rs[(i + Tg - 3) * 2] + rs[(i + Tg - 3) * 2 + 1] * rs[(i + Tg - 3) * 2 + 1];	/* Tg-2+2 * Tg-2+2 */
              temp10 += rs[(i + Tg + Tu - 3) * 2] * rs[(i + Tg + Tu - 3) * 2] + rs[(i + Tg + Tu - 3) * 2 + 1] * rs[(i + Tg + Tu - 3) * 2 + 1];	/* TG+Tu-2+2 * Tg+Tu-2+2 */
            }
          theta_plus = (float) sqrt((temp1[0] - temp5[0]) * (temp1[0] - temp5[0]) + (temp1[1] - temp5[1]) * (temp1[1] - temp5[1]));
          theta_plus -= (0.5 * (-temp6 - temp7));
          theta_minus = (float) sqrt((temp1[0] - temp8[0]) * (temp1[0] - temp8[0]) + (temp1[1] - temp8[1]) * (temp1[1] - temp8[1]));
          theta_minus -= (0.5 * (temp9 - temp10));
          delta_theta = (theta_plus - theta_minus) * Tgh / temp4;

          /* now limit the delta_theta value */
          if (delta_theta < -max_theta)  delta_theta = -max_theta;
          if (delta_theta > max_theta)   delta_theta = max_theta;

          /* P-I controller for theta */
          time_offset_ctrl = delta_theta - time_offset_fractional;
          delta_time_offset_I += kI_timing_controller * time_offset_ctrl;
          delta_time_offset_P = kP_large_timing_controller * time_offset_ctrl +
              threshold_timing_small_large * (kP_small_timing_controller -kP_large_timing_controller) *
              tanh(time_offset_ctrl / threshold_timing_small_large);
          delta_time_offset = delta_time_offset_P + delta_time_offset_I + time_offset_fractional;
          delta_time_offset_integer = (int) floor(delta_time_offset + 0.5);
//          logfile->addToAux(QString("dt %1 %2 %3 %4 %5").arg(time_offset_ctrl).arg(delta_time_offset_I).arg(delta_time_offset_P).arg(delta_time_offset).arg(delta_time_offset_integer));

          /* now limit delta_time_offset_integer */
          if (delta_time_offset_integer < -1) delta_time_offset_integer = -1;
          if (delta_time_offset_integer > 1)  delta_time_offset_integer = 1;
          time_offset_fractional =  delta_time_offset - delta_time_offset_integer;

          /* printf("delta_time_offset_integer as used in phi_freq_corr %d phifcl %g \n",
       delta_time_offset_integer, phi_freq_correction_last); */
          phi_freq_correction_last += (delta_time_offset_integer / Tu) * freq_offset;

        }

      else

        {
          delta_time_offset_integer = 0;
          time_offset_fractional = 0.0;
        }

      /*  printf("in getofdm delta_time_offset_integer is %d time_offs_fract %g\n",
         delta_time_offset_integer, time_offset_fractional);   */
      if (FREQ_SYNC_ENABLE == 1)

        {
          temp[0] = 0.0;
          temp[1] = 0.0;
          for (i = 0; i < Tg; i++)

            {
              temp[0] +=
                  rs[(i + 1 + delta_time_offset_integer) * 2] * rs[(i + 1 +
                                                                    delta_time_offset_integer
                                                                    + Tu) * 2] +
                  rs[(i + 1 + delta_time_offset_integer) * 2 +
                  1] * rs[(i + 1 + delta_time_offset_integer + Tu) * 2 + 1];
              temp[1] +=
                  rs[(i + 1 + delta_time_offset_integer) * 2 +
                  1] * rs[(i + 1 + delta_time_offset_integer + Tu) * 2] -
                  rs[(i + 1 + delta_time_offset_integer) * 2] * rs[(i + 1 +
                                                                    delta_time_offset_integer
                                                                    + Tu) * 2 +
                  1];
            }
          epsilon_ML = (float) (atan2(temp[1], temp[0]));

          /* filter epsilon_ML */
          freq_offset_ctrl =
              fmodf(epsilon_ML - freq_offset + PI + 100.0 * PI, 2 * PI) - PI;
          dfreq_offset_I += kI_freq_controller * freq_offset_ctrl;
          dfreq_offset_P =
              kP_large_freq_controller * freq_offset_ctrl +
              threshold_freq_small_large * (kP_small_freq_controller -
                                            kP_large_freq_controller) *
              tanh(freq_offset_ctrl / threshold_freq_small_large);
          dfreq_offset = dfreq_offset_P + dfreq_offset_I;
          freq_offset += dfreq_offset;
        }

      else

        {

          /* freq_offset =0.0  */
        }

      /* printf("in getofdm freq_offset = %g\n", freq_offset); */

      /* get symbol and correct frequency */
      for (i = 0; i < Tu; i++)

        {
          tmptheta = (freq_offset / Tu) * i + phi_freq_correction_last;
          exp_temp[i * 2] = (float) cos(tmptheta);
          exp_temp[i * 2 + 1] = (float) sin(tmptheta);
        }

      for (i = 0; i < Tu; i++)
        {
          (s[i]).re =
              rs[(1 + delta_time_offset_integer + Tgh +
                  i) * 2] * exp_temp[i * 2] - rs[(1 +
                                                  delta_time_offset_integer +
                                                  Tgh + i) * 2 +
              1] * exp_temp[i * 2 + 1];
          (s[i]).im =
              rs[(1 + delta_time_offset_integer + Tgh +
                  i) * 2] * exp_temp[i * 2 + 1] + rs[(1 +
                                                      delta_time_offset_integer
                                                      + Tgh + i) * 2 +
              1] * exp_temp[i * 2];
        }
      phi_freq_correction_last = (float) fmod(phi_freq_correction_last + (float) Ts / (float) Tu * freq_offset, (float)(2.0 * PI));

      /* Now do fft and output symbol */
      fftwf_execute(p);
      for (i = 0; i <= Tu / 2; i++)

        {
          term1 = (float) (i * 2.0 * PI * (Tgh + time_offset_fractional) / Tu);	/* Euler */
          exp_temp[i * 2] = (float) cos(term1);
          exp_temp[i * 2 + 1] = (float) sin(term1);
        }
      /* now calc out */
      for (i = 0; i < Tu / 2; i++)

        {
          out1[i * 2] = (float) (((S[(Tu - 1 - i)]).re) * exp_temp[(i + 1) * 2] + ((S[(Tu - 1 - i)]).im) * exp_temp[(i + 1) * 2 + 1]);	/* real = ac+bd */
          out1[i * 2 + 1] = (float) (((S[(Tu - 1 - i)]).im) * exp_temp[(i + 1) * 2] - ((S[(Tu - 1 - i)]).re) * exp_temp[(i + 1) * 2 + 1]);	/* imag bc -ad */
        }
      /* Now flip out1 to out */
      for (i = 0; i < Tu / 2; i++)

        {
          out[i * 2] = out1[(Tu / 2 - 1 - i) * 2];
          out[i * 2 + 1] = out1[(Tu / 2 - i - 1) * 2 + 1];
          out[(Tu / 2 + i) * 2] =
              (float) (((S[i]).re) * exp_temp[i * 2] -
                       ((S[i]).im) * exp_temp[i * 2 + 1]);
          out[(Tu / 2 + i) * 2 + 1] =
              (float) (((S[i]).im) * exp_temp[i * 2] +
                       ((S[i]).re) * exp_temp[i * 2 + 1]);
        } Zi[0] = 1.0;
      Zi[1] = phi_freq_correction_last;
      Zi[2] = delta_time_offset_I;
      Zi[3] = dfreq_offset_I;
      Zi[4] = freq_offset;
      Zi[5] = time_offset_fractional;
      return (delta_time_offset_integer);
    }
}
