#include "ngspice/cm.h"
extern void cm_seegen(Mif_Private_t *);
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================

FILE seegenerator/cfunc.mod

Public Domain

Universty Duisburg-Essen
Duisburg, Germany
Project Flowspace

AUTHORS                      

    19 May 2025 Holger Vogt


MODIFICATIONS   



SUMMARY

    This file contains the model-specific routines used to
    functionally describe the see (single event effects) generator code model.


INTERFACES       

    FILE                 ROUTINE CALLED     


REFERENCED FILES

    Inputs from and outputs to ARGS structure.
                     

NON-STANDARD FEATURES

    NONE

===============================================================================*/

/*=== INCLUDE FILES ====================*/


                                      

/*=== CONSTANTS ========================*/




/*=== MACROS ===========================*/



  
/*=== LOCAL VARIABLES & TYPEDEFS =======*/                         

static void
cm_seegen_callback(Mif_Private_t *mif_private, Mif_Callback_Reason_t reason)
{
    switch (reason) {
        case MIF_CB_DESTROY: {
            double *last_t_value =  mif_private->inst_var[0]->element[0].pvalue;
            if (last_t_value)
                free(last_t_value);
             mif_private->inst_var[0]->element[0].pvalue = NULL;
            int *pulse_number =  mif_private->inst_var[1]->element[0].pvalue;
            if (pulse_number)
                free(pulse_number);
             mif_private->inst_var[1]->element[0].pvalue = NULL;
            double *last_ctrl =  mif_private->inst_var[2]->element[0].pvalue;
            if (last_ctrl)
                free(last_ctrl);
             mif_private->inst_var[2]->element[0].pvalue = NULL;
            break;
        }
    }
}
    
           
/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/




                   
/*==============================================================================

FUNCTION void cm_seegen()

AUTHORS                      

    19 May 2025 Holger Vogt

SUMMARY

    This function implements the see generator code model.

INTERFACES       

    FILE                 ROUTINE CALLED     

    CMutil.c             void cm_smooth_corner(); 
                         void cm_smooth_discontinuity();
                         void cm_climit_fcn()

RETURNED VALUE
    
    Returns inputs and outputs via ARGS structure.

GLOBAL VARIABLES
    
    NONE

NON-STANDARD FEATURES

    model source: 
    Ygor Quadros de Aguiar, Frédéric Wrobel. Jean-Luc Autran, Rubén García Alía
    Single-Event Effects, from Space to Accelerator Environments
    Springer 2025

==============================================================================*/

/*=== CM_SEEGEN ROUTINE ===*/

void cm_seegen(Mif_Private_t *mif_private)  /* structure holding parms, 
                                       inputs, outputs, etc.     */
{
    double tfall;            /* pulse fall time */
    double trise;            /* pulse rise time */
    double tdelay;           /* delay until first pulse */
    double inull;            /* max. current of pulse */
    double let;              /* linear energy transfer */
    double cdepth;           /* charge collection depth */
    double angle;            /* particle entrance angle */
    double tperiod;          /* pulse repetition period */
    double ctrlthres;        /* control voltage threshold */
    double ctrl;             /* control input */
    double out;              /* output current */
    double *last_t_value;    /* static storage of next pulse time */
    int *pulse_number;       /* static storage of next pulse time */
    double *last_ctrl;       /* static storage of last ctrl value */
    double tcurr = mif_private->circuit.time;     /* current simulation time */

    if (mif_private->circuit.anal_type == MIF_AC) {
        return;
    }

    /* Retrieve frequently used parameters... */

    tfall = mif_private->param[0]->element[0].rvalue;
    trise = mif_private->param[1]->element[0].rvalue;
    tdelay = mif_private->param[2]->element[0].rvalue;
    tperiod = mif_private->param[4]->element[0].rvalue;
    inull = mif_private->param[3]->element[0].rvalue;
    let = mif_private->param[6]->element[0].rvalue;
    cdepth = mif_private->param[7]->element[0].rvalue;
    angle = mif_private->param[8]->element[0].rvalue;
    ctrlthres = mif_private->param[5]->element[0].rvalue;

    if (mif_private->conn[0]->is_null)
        ctrl = 1;
    else
        ctrl = mif_private->conn[0]->port[0]->input.rvalue;

    if (mif_private->circuit.init==1) {

        *(mif_private->callback) = cm_seegen_callback;

        /* Allocate storage for last_t_value */
        mif_private->inst_var[0]->element[0].pvalue = (double *) malloc(sizeof(double));
        last_t_value = (double *) mif_private->inst_var[0]->element[0].pvalue;
        /* no start if ctrl is set */
        if (mif_private->conn[0]->is_null)
            *last_t_value = tdelay;
        else
            *last_t_value = 1e12;
        mif_private->inst_var[1]->element[0].pvalue = (int *) malloc(sizeof(int));
        pulse_number = (int *) mif_private->inst_var[1]->element[0].pvalue;
        *pulse_number = 1;
        mif_private->inst_var[2]->element[0].pvalue = (double *) malloc(sizeof(double));
        last_ctrl = (double *) mif_private->inst_var[2]->element[0].pvalue;
        *last_ctrl = ctrl;
        /* set breakpoints at first pulse start and pulse maximum times */
        double tatmax = *last_t_value + tfall * trise * log(trise/tfall) / (trise - tfall);
        cm_analog_set_perm_bkpt(*last_t_value);
        cm_analog_set_perm_bkpt(tatmax);
    }
    else {

        last_t_value = (double *) mif_private->inst_var[0]->element[0].pvalue;
        pulse_number = (int *) mif_private->inst_var[1]->element[0].pvalue;
        last_ctrl = (double *) mif_private->inst_var[2]->element[0].pvalue;

        /* reset the pulse sequence, to start anew upon a rising ctrl */
        if (*last_ctrl < ctrlthres && ctrl >= ctrlthres) {
            *last_t_value = tcurr + tdelay;
            *pulse_number = 1;
        }
        *last_ctrl = ctrl;

        /* the double exponential current pulse function */
        if (tcurr < *last_t_value)
            out = 0;
        else {
            if (inull == 0) {
                double LETeff = let/cos(angle);
                double Qc = 1.035e-14 * LETeff * cdepth;
                inull = Qc / (tfall - trise);
            }
            out = inull * (exp(-(tcurr-*last_t_value)/tfall) - exp(-(tcurr-*last_t_value)/trise));
        }
        if (tcurr > *last_t_value + tperiod * 0.9) {
            /* return some info */
            cm_message_printf("port name: out, node pair no.: %d, \nnode names: %s, %s, pulse time: %e",
                *pulse_number, cm_get_node_name("out", *pulse_number - 1), 
                cm_get_neg_node_name("out", *pulse_number - 1), *last_t_value);
            /* set the time for the next pulse */
            *last_t_value = *last_t_value + tperiod;
            /* set breakpoints at new pulse start and pulse maximum times */
            double tatmax = *last_t_value + tfall * trise * log(trise/tfall) / (trise - tfall);
            cm_analog_set_perm_bkpt(*last_t_value);
            cm_analog_set_perm_bkpt(tatmax);
            (*pulse_number)++;
            if (*pulse_number > mif_private->conn[2]->size) {
                if (mif_private->param[9]->element[0].bvalue == FALSE)
                    *pulse_number = 1;
                else
                    *last_t_value = 1e12; /* stop any output */
            }
        }
        if (*pulse_number - 1 < mif_private->conn[2]->size) {
              mif_private->conn[2]->port[*pulse_number-1]->output.rvalue = out;
            mif_private->conn[1]->port[0]->output.rvalue = out;
        }
    }
}



