/*  eer.c

This file is part of a program that implements a Software-Defined Radio.

Copyright (C) 2014 Warren Pratt, NR0V

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

The author can be reached by email at

warren@wpratt.com

*/

#include "comm.h"

PORT
EER create_eer (int run, int size, double* in, double* out, double* outM, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq)
{
    EER a = (EER) malloc0 (sizeof (eer));
    a->run = run;
    a->size = size;
    a->in = in;
    a->out = out;
    a->outM = outM;
    a->rate = rate;
    a->mgain = mgain;
    a->pgain = pgain;
    a->rundelays = rundelays;
    a->mdelay = mdelay;
    a->pdelay = pdelay;
    a->amiq = amiq;
    a->mdel = create_delay (
        a->rundelays,                               // run
        a->size,                                    // size
        a->outM,                                    // input buffer
        a->outM,                                    // output buffer
        a->rate,                                    // sample rate
        20.0e-09,                                   // delta (delay stepsize)
        a->mdelay);                                 // delay
    a->pdel = create_delay (
        a->rundelays,                               // run
        a->size,                                    // size
        a->out,                                     // input buffer
        a->out,                                     // output buffer
        a->rate,                                    // sample rate
        20.0e-09,                                   // delta (delay stepsize)
        a->pdelay);                                 // delay
    InitializeCriticalSectionAndSpinCount(&a->cs_update, 2500);

    a->legacy  = (double *) malloc0 (2048 * sizeof (complex));                                                      /////////////// legacy interface - remove
    a->legacyM = (double *) malloc0 (2048 * sizeof (complex));                                                      /////////////// legacy interface - remove

    return a;
}

PORT
void destroy_eer (EER a)
{
    DeleteCriticalSection (&a->cs_update);
    destroy_delay (a->pdel);
    destroy_delay (a->mdel);
    _aligned_free (a);
}

PORT
void flush_eer (EER a)
{
    flush_delay (a->mdel);
    flush_delay (a->pdel);
}

PORT
void xeer (EER a)
{
    EnterCriticalSection (&a->cs_update);
    if (a->run)
    {
        int i;
        double I, Q, mag;
        for (i = 0; i < a->size; i++)
        {
            I = a->in[2 * i + 0];
            Q = a->in[2 * i + 1];
            a->outM[2 * i + 0] = I * a->mgain;
            a->outM[2 * i + 1] = Q * a->mgain;
            switch (a->amiq)
            {
            case 0:     // send phase info only, magnitude is constant
                mag = sqrt (I * I + Q * Q);
                a->out [2 * i + 0] = a->pgain * I / mag;
                a->out [2 * i + 1] = a->pgain * Q / mag;
                break;
            case 1:     // send magnitude and phase information, I and Q
                a->out [2 * i + 0] = a->pgain * I;
                a->out [2 * i + 1] = a->pgain * Q;
                break;
            case 2:     // send envelope
                mag = sqrt (I * I + Q * Q);
                a->out [2 * i + 0] = a->out[2 * i + 1] = a->pgain * mag;
                break;
            }
        }
        xdelay (a->mdel);       // delay for outM
        xdelay (a->pdel);       // delay for out
    }
    else if (a->out != a->in)
        memcpy (a->out, a->in, a->size * sizeof (complex));
    LeaveCriticalSection (&a->cs_update);
}

/********************************************************************************************************
*                                                                                                       *
*                                       CALLS FOR EXTERNAL USE                                          *
*                                                                                                       *
********************************************************************************************************/

#define MAX_EXT_EERS    (2)                         // maximum number of EERs called from outside wdsp
__declspec (align (16)) EER peer[MAX_EXT_EERS];     // array of pointers for EERs used EXTERNAL to wdsp


PORT
void create_eerEXT (int id, int run, int size, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq)
{
    peer[id] = create_eer (run, size, 0, 0, 0, rate, mgain, pgain, rundelays, mdelay, pdelay, amiq);
}

PORT
void destroy_eerEXT (int id)
{
    destroy_eer (peer[id]);
}

PORT
void flush_eerEXT (int id)
{
    flush_eer (peer[id]);
}

PORT
void SetEERRun (int id, int run)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->run = run;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERAMIQ (int id, int amiq)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->amiq = amiq;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERMgain (int id, double gain)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->mgain = gain;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERPgain (int id, double gain)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->pgain = gain;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERRunDelays (int id, int run)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->rundelays = run;
    SetDelayRun (a->mdel, a->rundelays);
    SetDelayRun (a->pdel, a->rundelays);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERMdelay (int id, double delay)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->mdelay = delay;
    SetDelayValue (a->mdel, a->mdelay);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERPdelay (int id, double delay)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->pdelay = delay;
    SetDelayValue (a->pdel, a->pdelay);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERSize (int id, int size)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->size = size;
    SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
    SetDelayBuffs (a->pdel, a->size, a->out, a->out);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void SetEERSamplerate (int id, int rate)
{
    EER a = peer[id];
    EnterCriticalSection (&a->cs_update);
    a->rate = rate;
    destroy_delay (a->mdel);
    destroy_delay (a->pdel);
    a->mdel = create_delay (
        a->rundelays,                               // run
        a->size,                                    // size
        a->outM,                                    // input buffer
        a->outM,                                    // output buffer
        a->rate,                                    // sample rate
        20.0e-09,                                   // delta (delay stepsize)
        a->mdelay);                                 // delay
    a->pdel = create_delay (
        a->rundelays,                               // run
        a->size,                                    // size
        a->out,                                     // input buffer
        a->out,                                     // output buffer
        a->rate,                                    // sample rate
        20.0e-09,                                   // delta (delay stepsize)
        a->pdelay);                                 // delay
    LeaveCriticalSection (&a->cs_update);
}

/********************************************************************************************************
*                                                                                                       *
*                                           POINTER-BASED PROPERTIES                                    *
*                                                                                                       *
********************************************************************************************************/

PORT
void pSetEERRun (EER a, int run)
{
    EnterCriticalSection (&a->cs_update);
    a->run = run;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERAMIQ (EER a, int amiq)
{
    EnterCriticalSection (&a->cs_update);
    a->amiq = amiq;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERMgain (EER a, double gain)
{
    EnterCriticalSection (&a->cs_update);
    a->mgain = gain;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERPgain (EER a, double gain)
{
    EnterCriticalSection (&a->cs_update);
    a->pgain = gain;
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERRunDelays (EER a, int run)
{
    EnterCriticalSection (&a->cs_update);
    a->rundelays = run;
    SetDelayRun (a->mdel, a->rundelays);
    SetDelayRun (a->pdel, a->rundelays);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERMdelay (EER a, double delay)
{
    EnterCriticalSection (&a->cs_update);
    a->mdelay = delay;
    SetDelayValue (a->mdel, a->mdelay);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERPdelay (EER a, double delay)
{
    EnterCriticalSection (&a->cs_update);
    a->pdelay = delay;
    SetDelayValue (a->pdel, a->pdelay);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERSize (EER a, int size)
{
    EnterCriticalSection (&a->cs_update);
    a->size = size;
    SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
    SetDelayBuffs (a->pdel, a->size, a->out, a->out);
    LeaveCriticalSection (&a->cs_update);
}

PORT
void pSetEERSamplerate (EER a, int rate)
{
    EnterCriticalSection (&a->cs_update);
    a->rate = rate;
    destroy_delay (a->mdel);
    destroy_delay (a->pdel);
    a->mdel = create_delay (
        a->rundelays,                               // run
        a->size,                                    // size
        a->outM,                                    // input buffer
        a->outM,                                    // output buffer
        a->rate,                                    // sample rate
        20.0e-09,                                   // delta (delay stepsize)
        a->mdelay);                                 // delay
    a->pdel = create_delay (
        a->rundelays,                               // run
        a->size,                                    // size
        a->out,                                     // input buffer
        a->out,                                     // output buffer
        a->rate,                                    // sample rate
        20.0e-09,                                   // delta (delay stepsize)
        a->pdelay);                                 // delay
    LeaveCriticalSection (&a->cs_update);
}


/********************************************************************************************************
*                                                                                                       *
*                                          Legacy Interface                                             *
*                                                                                                       *
********************************************************************************************************/

PORT
void xeerEXTF (int id, float* inI, float* inQ, float* outI, float* outQ, float* outMI, float* outMQ, int mox, int size)
{
    EER a = peer[id];
    if (mox && a->run)
    {
        int i;
        a->in   = a->legacy;
        a->out  = a->legacy;
        a->outM = a->legacyM;
        a->size = size;
        SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
        SetDelayBuffs (a->pdel, a->size, a->out, a->out);
        for (i = 0; i < a->size; i++)
        {
            a->legacy[2 * i + 0] = (double)inI[i];
            a->legacy[2 * i + 1] = (double)inQ[i];
        }
        xeer (a);
        for (i = 0; i < a->size; i++)
        {
            outI[i]  = (float)a->legacy [2 * i + 0];
            outQ[i]  = (float)a->legacy [2 * i + 1];
            outMI[i] = (float)a->legacyM[2 * i + 0];
            outMQ[i] = (float)a->legacyM[2 * i + 1];
        }
    }
}
