You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/src/electronics/simulation/diode.cpp

199 lines
3.8 KiB

/***************************************************************************
* Copyright (C) 2003-2004 by David Saxton *
* david@bluehaze.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. *
***************************************************************************/
#include <vector>
#include "diode.h"
#include "elementset.h"
#include "matrix.h"
#include <cmath>
//BEGIN class Diode Settings
DiodeSettings::DiodeSettings()
{
reset();
}
void DiodeSettings::reset()
{
I_S = 1e-15;
N = 1.0;
V_B = 4.7;
// R = 0.001;
}
//END class Diode Settings
//BEGIN class Diode
Diode::Diode()
: NonLinear()
{
m_numCNodes = 2;
g_new = g_old = I_new = I_old = V_prev = 0.0;
}
Diode::~Diode()
{
}
void Diode::add_map()
{
if (!b_status)
return;
if ( !p_cnode[0]->isGround )
{
p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false );
}
if ( !p_cnode[1]->isGround )
{
p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false );
}
if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround )
{
p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_unstable, false );
p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_unstable, false );
}
}
void Diode::add_initial_dc()
{
g_new = g_old = I_new = I_old = V_prev = 0.0;
update_dc();
}
double Diode::current() const
{
if (!b_status)
return 0.0;
double I;
calcIg( p_cnode[0]->v - p_cnode[1]->v, & I, 0 );
return I;
}
void Diode::updateCurrents()
{
if (!b_status)
return;
m_cnodeI[1] = current();
m_cnodeI[0] = -m_cnodeI[1];
}
void Diode::update_dc()
{
if (!b_status)
return;
calc_eq();
A_g( 0, 0 ) += g_new - g_old;
A_g( 1, 1 ) += g_new - g_old;
A_g( 0, 1 ) -= g_new - g_old;
A_g( 1, 0 ) -= g_new - g_old;
b_i( 0 ) -= I_new - I_old;
b_i( 1 ) += I_new - I_old;
g_old = g_new;
I_old = I_new;
}
#ifndef MIN
# define MIN(x,y) (((x) < (y)) ? (x) : (y))
#endif
void Diode::calc_eq()
{
double I_S = m_diodeSettings.I_S;
double N = m_diodeSettings.N;
double V_B = m_diodeSettings.V_B;
// double R = m_diodeSettings.R;
double v = p_cnode[0]->v - p_cnode[1]->v;
// adjust voltage to help convergence
double V_crit = diodeCriticalVoltage( I_S, N * V_T );
if (V_B != 0 && v < MIN (0, -V_B + 10 * N * V_T))
{
double V = -(v + V_B);
V = diodeVoltage( V, -(V_prev + V_B), V_T * N, V_crit );
v = -(V + V_B);
}
else
v = diodeVoltage( v, V_prev, V_T * N, V_crit );
V_prev = v;
double I_D;
calcIg( v, & I_D, & g_new );
I_new = I_D - (v * g_new);
}
void Diode::calcIg( double V, double * I_D, double * g ) const
{
double I_S = m_diodeSettings.I_S;
double N = m_diodeSettings.N;
double V_B = m_diodeSettings.V_B;
// double R = m_diodeSettings.R;
double gtiny = (V < - 10 * V_T * N && V_B != 0) ? I_S : 0;
if ( V >= (-3 * N * V_T) )
{
if ( g )
*g = diodeConductance( V, I_S, V_T * N ) + gtiny;
*I_D = diodeCurrent( V, I_S, V_T * N ) + (gtiny * V);
}
else if ( V_B == 0 || V >= -V_B )
{
double a = (3 * N * V_T) / (V * M_E);
a = a * a * a;
*I_D = (-I_S * (1 + a)) + (gtiny * V);
if ( g )
*g = ((I_S * 3 * a) / V) + gtiny;
}
else
{
double a = exp( -(V_B + V) / N / V_T );
*I_D = (-I_S * a) + (gtiny * V);
if ( g )
*g = I_S * a / V_T / N + gtiny;
}
}
void Diode::setDiodeSettings( const DiodeSettings & settings )
{
m_diodeSettings = settings;
if (p_eSet)
p_eSet->setCacheInvalidated();
}
//END class Diode