mirror of
https://github.com/holub/mame
synced 2025-06-01 10:31:48 +03:00

- Also fixes bzone regression. - Contexts have disappeared now. All modules use class level private variables now. - Reorganized code and added more include files. - This is still work in progress. It is working though.
1408 lines
39 KiB
C
1408 lines
39 KiB
C
/************************************************************************
|
|
*
|
|
* MAME - Discrete sound system emulation library
|
|
*
|
|
* Written by Keith Wilkins (mame@esplexo.co.uk)
|
|
*
|
|
* (c) K.Wilkins 2000
|
|
*
|
|
***********************************************************************
|
|
*
|
|
* DST_CRFILTER - Simple CR filter & also highpass filter
|
|
* DST_FILTER1 - Generic 1st order filter
|
|
* DST_FILTER2 - Generic 2nd order filter
|
|
* DST_OP_AMP_FILT - Op Amp filter circuits
|
|
* DST_RC_CIRCUIT_1 - RC charge/discharge circuit
|
|
* DST_RCDISC - Simple discharging RC
|
|
* DST_RCDISC2 - Simple charge R1/C, discharge R0/C
|
|
* DST_RCDISC3 - Simple charge R1/c, discharge R0*R1/(R0+R1)/C
|
|
* DST_RCDISC4 - Various charge/discharge circuits
|
|
* DST_RCDISC5 - Diode in series with R//C
|
|
* DST_RCDISC_MOD - RC triggered by logic and modulated
|
|
* DST_RCFILTER - Simple RC filter & also lowpass filter
|
|
* DST_RCFILTER_SW - Usage of node_description values for switchable RC filter
|
|
* DST_RCINTEGRATE - Two diode inputs, transistor and a R/C charge
|
|
* discharge network
|
|
* DST_SALLEN_KEY - Sallen-Key filter circuit
|
|
*
|
|
************************************************************************/
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_CRFILTER - Usage of node_description values for CR filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
* input[4] - Voltage reference. Usually 0V.
|
|
*
|
|
************************************************************************/
|
|
#define DST_CRFILTER__IN DISCRETE_INPUT(0)
|
|
#define DST_CRFILTER__R DISCRETE_INPUT(1)
|
|
#define DST_CRFILTER__C DISCRETE_INPUT(2)
|
|
#define DST_CRFILTER__VREF DISCRETE_INPUT(3)
|
|
|
|
DISCRETE_STEP(dst_crfilter)
|
|
{
|
|
if (UNEXPECTED(m_has_rc_nodes))
|
|
{
|
|
double rc = DST_CRFILTER__R * DST_CRFILTER__C;
|
|
if (rc != m_rc)
|
|
{
|
|
m_rc = rc;
|
|
m_exponent = RC_CHARGE_EXP_CLASS(rc);
|
|
}
|
|
}
|
|
|
|
double v_out = DST_CRFILTER__IN - m_vCap;
|
|
double v_diff = v_out - DST_CRFILTER__VREF;
|
|
this->output[0] = v_out;
|
|
m_vCap += v_diff * m_exponent;
|
|
}
|
|
|
|
DISCRETE_RESET(dst_crfilter)
|
|
{
|
|
m_has_rc_nodes = this->input_is_node() & 0x6;
|
|
m_rc = DST_CRFILTER__R * DST_CRFILTER__C;
|
|
m_exponent = RC_CHARGE_EXP_CLASS(m_rc);
|
|
m_vCap = 0;
|
|
this->output[0] = DST_CRFILTER__IN;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_FILTER1 - Generic 1st order filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Frequency value (initialization only)
|
|
* input[3] - Filter type (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_FILTER1__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_FILTER1__IN DISCRETE_INPUT(1)
|
|
#define DST_FILTER1__FREQ DISCRETE_INPUT(2)
|
|
#define DST_FILTER1__TYPE DISCRETE_INPUT(3)
|
|
|
|
static void calculate_filter1_coefficients(discrete_base_node *node, double fc, double type,
|
|
struct discrete_filter_coeff &coeff)
|
|
{
|
|
double den, w, two_over_T;
|
|
|
|
/* calculate digital filter coefficents */
|
|
/*w = 2.0*M_PI*fc; no pre-warping */
|
|
w = node->sample_rate()*2.0*tan(M_PI*fc/node->sample_rate()); /* pre-warping */
|
|
two_over_T = 2.0*node->sample_rate();
|
|
|
|
den = w + two_over_T;
|
|
coeff.a1 = (w - two_over_T)/den;
|
|
if (type == DISC_FILTER_LOWPASS)
|
|
{
|
|
coeff.b0 = coeff.b1 = w/den;
|
|
}
|
|
else if (type == DISC_FILTER_HIGHPASS)
|
|
{
|
|
coeff.b0 = two_over_T/den;
|
|
coeff.b1 = -(coeff.b0);
|
|
}
|
|
else
|
|
{
|
|
node->device->discrete_log("calculate_filter1_coefficients() - Invalid filter type for 1st order filter.");
|
|
}
|
|
}
|
|
|
|
DISCRETE_STEP(dst_filter1)
|
|
{
|
|
double gain = 1.0;
|
|
|
|
if (DST_FILTER1__ENABLE == 0.0)
|
|
{
|
|
gain = 0.0;
|
|
}
|
|
|
|
this->output[0] = -m_fc.a1*m_fc.y1 + m_fc.b0*gain*DST_FILTER1__IN + m_fc.b1*m_fc.x1;
|
|
|
|
m_fc.x1 = gain*DST_FILTER1__IN;
|
|
m_fc.y1 = this->output[0];
|
|
}
|
|
|
|
DISCRETE_RESET(dst_filter1)
|
|
{
|
|
calculate_filter1_coefficients(this, DST_FILTER1__FREQ, DST_FILTER1__TYPE, m_fc);
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_FILTER2 - Generic 2nd order filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Frequency value (initialization only)
|
|
* input[3] - Damping value (initialization only)
|
|
* input[4] - Filter type (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_FILTER2__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_FILTER2__IN DISCRETE_INPUT(1)
|
|
#define DST_FILTER2__FREQ DISCRETE_INPUT(2)
|
|
#define DST_FILTER2__DAMP DISCRETE_INPUT(3)
|
|
#define DST_FILTER2__TYPE DISCRETE_INPUT(4)
|
|
|
|
static void calculate_filter2_coefficients(discrete_base_node *node,
|
|
double fc, double d, double type,
|
|
struct discrete_filter_coeff &coeff)
|
|
{
|
|
double w; /* cutoff freq, in radians/sec */
|
|
double w_squared;
|
|
double den; /* temp variable */
|
|
double two_over_T = 2 * node->sample_rate();
|
|
double two_over_T_squared = two_over_T * two_over_T;
|
|
|
|
/* calculate digital filter coefficents */
|
|
/*w = 2.0*M_PI*fc; no pre-warping */
|
|
w = node->sample_rate() * 2.0 * tan(M_PI * fc / node->sample_rate()); /* pre-warping */
|
|
w_squared = w * w;
|
|
|
|
den = two_over_T_squared + d*w*two_over_T + w_squared;
|
|
|
|
coeff.a1 = 2.0 * (-two_over_T_squared + w_squared) / den;
|
|
coeff.a2 = (two_over_T_squared - d * w * two_over_T + w_squared) / den;
|
|
|
|
if (type == DISC_FILTER_LOWPASS)
|
|
{
|
|
coeff.b0 = coeff.b2 = w_squared/den;
|
|
coeff.b1 = 2.0 * (coeff.b0);
|
|
}
|
|
else if (type == DISC_FILTER_BANDPASS)
|
|
{
|
|
coeff.b0 = d * w * two_over_T / den;
|
|
coeff.b1 = 0.0;
|
|
coeff.b2 = -(coeff.b0);
|
|
}
|
|
else if (type == DISC_FILTER_HIGHPASS)
|
|
{
|
|
coeff.b0 = coeff.b2 = two_over_T_squared / den;
|
|
coeff.b1 = -2.0 * (coeff.b0);
|
|
}
|
|
else
|
|
{
|
|
node->device->discrete_log("calculate_filter2_coefficients() - Invalid filter type for 2nd order filter.");
|
|
}
|
|
}
|
|
|
|
DISCRETE_STEP(dst_filter2)
|
|
{
|
|
double gain = 1.0;
|
|
|
|
if (DST_FILTER2__ENABLE == 0.0)
|
|
{
|
|
gain = 0.0;
|
|
}
|
|
|
|
this->output[0] = -m_fc.a1 * m_fc.y1 - m_fc.a2 * m_fc.y2 +
|
|
m_fc.b0 * gain * DST_FILTER2__IN + m_fc.b1 * m_fc.x1 + m_fc.b2 * m_fc.x2;
|
|
|
|
m_fc.x2 = m_fc.x1;
|
|
m_fc.x1 = gain * DST_FILTER2__IN;
|
|
m_fc.y2 = m_fc.y1;
|
|
m_fc.y1 = this->output[0];
|
|
}
|
|
|
|
DISCRETE_RESET(dst_filter2)
|
|
{
|
|
calculate_filter2_coefficients(this, DST_FILTER2__FREQ, DST_FILTER2__DAMP, DST_FILTER2__TYPE,
|
|
m_fc);
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_OP_AMP_FILT - Op Amp filter circuit RC filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - IN0 node
|
|
* input[2] - IN1 node
|
|
* input[3] - Filter Type
|
|
*
|
|
* also passed discrete_op_amp_filt_info structure
|
|
*
|
|
* Mar 2004, D Renaud.
|
|
************************************************************************/
|
|
#define DST_OP_AMP_FILT__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_OP_AMP_FILT__INP1 DISCRETE_INPUT(1)
|
|
#define DST_OP_AMP_FILT__INP2 DISCRETE_INPUT(2)
|
|
#define DST_OP_AMP_FILT__TYPE DISCRETE_INPUT(3)
|
|
|
|
DISCRETE_STEP(dst_op_amp_filt)
|
|
{
|
|
DISCRETE_DECLARE_INFO(discrete_op_amp_filt_info)
|
|
|
|
double i, v = 0;
|
|
|
|
if (DST_OP_AMP_FILT__ENABLE)
|
|
{
|
|
if (m_is_norton)
|
|
{
|
|
v = DST_OP_AMP_FILT__INP1 - OP_AMP_NORTON_VBE;
|
|
if (v < 0) v = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Millman the input voltages. */
|
|
i = m_iFixed;
|
|
switch (m_type)
|
|
{
|
|
case DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A:
|
|
i += (DST_OP_AMP_FILT__INP1 - DST_OP_AMP_FILT__INP2) / info->r1;
|
|
if (info->r2 != 0)
|
|
i += (m_vP - DST_OP_AMP_FILT__INP2) / info->r2;
|
|
if (info->r3 != 0)
|
|
i += (m_vN - DST_OP_AMP_FILT__INP2) / info->r3;
|
|
break;
|
|
default:
|
|
i += (DST_OP_AMP_FILT__INP1 - m_vRef) / info->r1;
|
|
if (info->r2 != 0)
|
|
i += (DST_OP_AMP_FILT__INP2 - m_vRef) / info->r2;
|
|
break;
|
|
}
|
|
v = i * m_rTotal;
|
|
}
|
|
|
|
switch (m_type)
|
|
{
|
|
case DISC_OP_AMP_FILTER_IS_LOW_PASS_1:
|
|
m_vC1 += (v - m_vC1) * m_exponentC1;
|
|
this->output[0] = m_vC1 * m_gain + info->vRef;
|
|
break;
|
|
|
|
case DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A:
|
|
m_vC1 += (v - m_vC1) * m_exponentC1;
|
|
this->output[0] = m_vC1 * m_gain + DST_OP_AMP_FILT__INP2;
|
|
break;
|
|
|
|
case DISC_OP_AMP_FILTER_IS_HIGH_PASS_1:
|
|
this->output[0] = (v - m_vC1) * m_gain + info->vRef;
|
|
m_vC1 += (v - m_vC1) * m_exponentC1;
|
|
break;
|
|
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_1:
|
|
this->output[0] = (v - m_vC2);
|
|
m_vC2 += (v - m_vC2) * m_exponentC2;
|
|
m_vC1 += (this->output[0] - m_vC1) * m_exponentC1;
|
|
this->output[0] = m_vC1 * m_gain + info->vRef;
|
|
break;
|
|
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON:
|
|
m_vC1 += (v - m_vC1) * m_exponentC1;
|
|
m_vC2 += (m_vC1 - m_vC2) * m_exponentC2;
|
|
v = m_vC2;
|
|
this->output[0] = v - m_vC3;
|
|
m_vC3 += (v - m_vC3) * m_exponentC3;
|
|
i = this->output[0] / m_rTotal;
|
|
this->output[0] = (m_iFixed - i) * info->rF;
|
|
break;
|
|
|
|
case DISC_OP_AMP_FILTER_IS_HIGH_PASS_0 | DISC_OP_AMP_IS_NORTON:
|
|
this->output[0] = v - m_vC1;
|
|
m_vC1 += (v - m_vC1) * m_exponentC1;
|
|
i = this->output[0] / m_rTotal;
|
|
this->output[0] = (m_iFixed - i) * info->rF;
|
|
break;
|
|
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M:
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M | DISC_OP_AMP_IS_NORTON:
|
|
this->output[0] = -m_fc.a1 * m_fc.y1 - m_fc.a2 * m_fc.y2 +
|
|
m_fc.b0 * v + m_fc.b1 * m_fc.x1 + m_fc.b2 * m_fc.x2 +
|
|
m_vRef;
|
|
m_fc.x2 = m_fc.x1;
|
|
m_fc.x1 = v;
|
|
m_fc.y2 = m_fc.y1;
|
|
break;
|
|
}
|
|
|
|
/* Clip the output to the voltage rails.
|
|
* This way we get the original distortion in all it's glory.
|
|
*/
|
|
if (this->output[0] > m_vP) this->output[0] = m_vP;
|
|
if (this->output[0] < m_vN) this->output[0] = m_vN;
|
|
m_fc.y1 = this->output[0] - m_vRef;
|
|
}
|
|
else
|
|
this->output[0] = 0;
|
|
|
|
}
|
|
|
|
DISCRETE_RESET(dst_op_amp_filt)
|
|
{
|
|
DISCRETE_DECLARE_INFO(discrete_op_amp_filt_info)
|
|
|
|
/* Convert the passed filter type into an int for easy use. */
|
|
m_type = (int)DST_OP_AMP_FILT__TYPE & DISC_OP_AMP_FILTER_TYPE_MASK;
|
|
m_is_norton = (int)DST_OP_AMP_FILT__TYPE & DISC_OP_AMP_IS_NORTON;
|
|
|
|
if (m_is_norton)
|
|
{
|
|
m_vRef = 0;
|
|
m_rTotal = info->r1;
|
|
if (m_type == (DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON))
|
|
m_rTotal += info->r2 + info->r3;
|
|
|
|
/* Setup the current to the + input. */
|
|
m_iFixed = (info->vP - OP_AMP_NORTON_VBE) / info->r4;
|
|
|
|
/* Set the output max. */
|
|
m_vP = info->vP - OP_AMP_NORTON_VBE;
|
|
m_vN = info->vN;
|
|
}
|
|
else
|
|
{
|
|
m_vRef = info->vRef;
|
|
/* Set the output max. */
|
|
m_vP = info->vP - OP_AMP_VP_RAIL_OFFSET;
|
|
m_vN = info->vN;
|
|
|
|
/* Work out the input resistance. It is all input and bias resistors in parallel. */
|
|
m_rTotal = 1.0 / info->r1; /* There has to be an R1. Otherwise the table is wrong. */
|
|
if (info->r2 != 0) m_rTotal += 1.0 / info->r2;
|
|
if (info->r3 != 0) m_rTotal += 1.0 / info->r3;
|
|
m_rTotal = 1.0 / m_rTotal;
|
|
|
|
m_iFixed = 0;
|
|
|
|
m_rRatio = info->rF / (m_rTotal + info->rF);
|
|
m_gain = -info->rF / m_rTotal;
|
|
}
|
|
|
|
switch (m_type)
|
|
{
|
|
case DISC_OP_AMP_FILTER_IS_LOW_PASS_1:
|
|
case DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A:
|
|
m_exponentC1 = RC_CHARGE_EXP_CLASS(info->rF * info->c1);
|
|
m_exponentC2 = 0;
|
|
break;
|
|
case DISC_OP_AMP_FILTER_IS_HIGH_PASS_1:
|
|
m_exponentC1 = RC_CHARGE_EXP_CLASS(m_rTotal * info->c1);
|
|
m_exponentC2 = 0;
|
|
break;
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_1:
|
|
m_exponentC1 = RC_CHARGE_EXP_CLASS(info->rF * info->c1);
|
|
m_exponentC2 = RC_CHARGE_EXP_CLASS(m_rTotal * info->c2);
|
|
break;
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M | DISC_OP_AMP_IS_NORTON:
|
|
if (info->r2 == 0)
|
|
m_rTotal = info->r1;
|
|
else
|
|
m_rTotal = RES_2_PARALLEL(info->r1, info->r2);
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M:
|
|
{
|
|
double fc = 1.0 / (2 * M_PI * sqrt(m_rTotal * info->rF * info->c1 * info->c2));
|
|
double d = (info->c1 + info->c2) / sqrt(info->rF / m_rTotal * info->c1 * info->c2);
|
|
double gain = -info->rF / m_rTotal * info->c2 / (info->c1 + info->c2);
|
|
|
|
calculate_filter2_coefficients(this, fc, d, DISC_FILTER_BANDPASS, m_fc);
|
|
m_fc.b0 *= gain;
|
|
m_fc.b1 *= gain;
|
|
m_fc.b2 *= gain;
|
|
|
|
if (m_is_norton)
|
|
m_vRef = (info->vP - OP_AMP_NORTON_VBE) / info->r3 * info->rF;
|
|
else
|
|
m_vRef = info->vRef;
|
|
|
|
break;
|
|
}
|
|
case DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON:
|
|
m_exponentC1 = RC_CHARGE_EXP_CLASS(RES_2_PARALLEL(info->r1, info->r2 + info->r3 + info->r4) * info->c1);
|
|
m_exponentC2 = RC_CHARGE_EXP_CLASS(RES_2_PARALLEL(info->r1 + info->r2, info->r3 + info->r4) * info->c2);
|
|
m_exponentC3 = RC_CHARGE_EXP_CLASS((info->r1 + info->r2 + info->r3 + info->r4) * info->c3);
|
|
break;
|
|
case DISC_OP_AMP_FILTER_IS_HIGH_PASS_0 | DISC_OP_AMP_IS_NORTON:
|
|
m_exponentC1 = RC_CHARGE_EXP_CLASS(info->r1 * info->c1);
|
|
break;
|
|
}
|
|
|
|
/* At startup there is no charge on the caps and output is 0V in relation to vRef. */
|
|
m_vC1 = 0;
|
|
m_vC1b = 0;
|
|
m_vC2 = 0;
|
|
m_vC3 = 0;
|
|
|
|
this->output[0] = info->vRef;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RC_CIRCUIT_1 - RC charge/discharge circuit
|
|
*
|
|
************************************************************************/
|
|
#define DST_RC_CIRCUIT_1__IN0 DISCRETE_INPUT(0)
|
|
#define DST_RC_CIRCUIT_1__IN1 DISCRETE_INPUT(1)
|
|
#define DST_RC_CIRCUIT_1__R DISCRETE_INPUT(2)
|
|
#define DST_RC_CIRCUIT_1__C DISCRETE_INPUT(3)
|
|
|
|
#define CD4066_R_ON 270
|
|
|
|
DISCRETE_STEP( dst_rc_circuit_1 )
|
|
{
|
|
if (DST_RC_CIRCUIT_1__IN0 == 0)
|
|
if (DST_RC_CIRCUIT_1__IN1 == 0)
|
|
/* cap is floating and does not change charge */
|
|
/* output is pulled to ground */
|
|
this->output[0] = 0;
|
|
else
|
|
{
|
|
/* cap is discharged */
|
|
m_v_cap -= m_v_cap * m_exp_2;
|
|
this->output[0] = m_v_cap * m_v_drop;
|
|
}
|
|
else
|
|
if (DST_RC_CIRCUIT_1__IN1 == 0)
|
|
{
|
|
/* cap is charged */
|
|
m_v_cap += (5.0 - m_v_cap) * m_exp_1;
|
|
/* output is pulled to ground */
|
|
this->output[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
/* cap is charged slightly less */
|
|
m_v_cap += (m_v_charge_1_2 - m_v_cap) * m_exp_1_2;
|
|
this->output[0] = m_v_cap * m_v_drop;
|
|
}
|
|
}
|
|
|
|
DISCRETE_RESET( dst_rc_circuit_1 )
|
|
{
|
|
/* the charging voltage across the cap based on in2*/
|
|
m_v_drop = RES_VOLTAGE_DIVIDER(CD4066_R_ON, CD4066_R_ON + DST_RC_CIRCUIT_1__R);
|
|
m_v_charge_1_2 = 5.0 * m_v_drop;
|
|
m_v_cap = 0;
|
|
|
|
/* precalculate charging exponents */
|
|
/* discharge cap - in1 = 0, in2 = 1*/
|
|
m_exp_2 = RC_CHARGE_EXP_CLASS((CD4066_R_ON + DST_RC_CIRCUIT_1__R) * DST_RC_CIRCUIT_1__C);
|
|
/* charge cap - in1 = 1, in2 = 0 */
|
|
m_exp_1 = RC_CHARGE_EXP_CLASS(CD4066_R_ON * DST_RC_CIRCUIT_1__C);
|
|
/* charge cap - in1 = 1, in2 = 1 */
|
|
m_exp_1_2 = RC_CHARGE_EXP_CLASS(RES_2_PARALLEL(CD4066_R_ON, CD4066_R_ON + DST_RC_CIRCUIT_1__R) * DST_RC_CIRCUIT_1__C);
|
|
|
|
/* starts at 0 until cap starts charging */
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC - Usage of node_description values for RC discharge
|
|
* (inverse slope of DST_RCFILTER)
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISC__IN DISCRETE_INPUT(1)
|
|
#define DST_RCDISC__R DISCRETE_INPUT(2)
|
|
#define DST_RCDISC__C DISCRETE_INPUT(3)
|
|
|
|
DISCRETE_STEP(dst_rcdisc)
|
|
{
|
|
switch (m_state)
|
|
{
|
|
case 0: /* waiting for trigger */
|
|
if(DST_RCDISC__ENABLE)
|
|
{
|
|
m_state = 1;
|
|
m_t = 0;
|
|
}
|
|
this->output[0] = 0;
|
|
break;
|
|
|
|
case 1:
|
|
if (DST_RCDISC__ENABLE)
|
|
{
|
|
this->output[0] = DST_RCDISC__IN * exp(m_t / m_exponent0);
|
|
m_t += this->sample_time();
|
|
} else
|
|
{
|
|
m_state = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcdisc)
|
|
{
|
|
this->output[0] = 0;
|
|
|
|
m_state = 0;
|
|
m_t = 0;
|
|
m_exponent0=-1.0 * DST_RCDISC__R * DST_RCDISC__C;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC2 - Usage of node_description values for RC discharge
|
|
* Has switchable charge resistor/input
|
|
*
|
|
* input[0] - Switch input value
|
|
* input[1] - input[0] value
|
|
* input[2] - Resistor0 value (initialization only)
|
|
* input[3] - input[1] value
|
|
* input[4] - Resistor1 value (initialization only)
|
|
* input[5] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC2__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISC2__IN0 DISCRETE_INPUT(1)
|
|
#define DST_RCDISC2__R0 DISCRETE_INPUT(2)
|
|
#define DST_RCDISC2__IN1 DISCRETE_INPUT(3)
|
|
#define DST_RCDISC2__R1 DISCRETE_INPUT(4)
|
|
#define DST_RCDISC2__C DISCRETE_INPUT(5)
|
|
|
|
DISCRETE_STEP(dst_rcdisc2)
|
|
{
|
|
double diff;
|
|
|
|
/* Works differently to other as we are always on, no enable */
|
|
/* exponential based in difference between input/output */
|
|
|
|
diff = ((DST_RCDISC2__ENABLE == 0) ? DST_RCDISC2__IN0 : DST_RCDISC2__IN1) - this->output[0];
|
|
diff = diff - (diff * ((DST_RCDISC2__ENABLE == 0) ? m_exponent0 : m_exponent1));
|
|
this->output[0] += diff;
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcdisc2)
|
|
{
|
|
this->output[0] = 0;
|
|
|
|
m_state = 0;
|
|
m_t = 0;
|
|
m_exponent0 = RC_DISCHARGE_EXP_CLASS(DST_RCDISC2__R0 * DST_RCDISC2__C);
|
|
m_exponent1 = RC_DISCHARGE_EXP_CLASS(DST_RCDISC2__R1 * DST_RCDISC2__C);
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC3 - Usage of node_description values for RC discharge
|
|
*
|
|
*
|
|
* input[0] - Enable
|
|
* input[1] - input value
|
|
* input[2] - Resistor0 value (initialization only)
|
|
* input[4] - Resistor1 value (initialization only)
|
|
* input[5] - Capacitor Value (initialization only)
|
|
* input[6] - Diode Junction voltage (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC3__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISC3__IN DISCRETE_INPUT(1)
|
|
#define DST_RCDISC3__R1 DISCRETE_INPUT(2)
|
|
#define DST_RCDISC3__R2 DISCRETE_INPUT(3)
|
|
#define DST_RCDISC3__C DISCRETE_INPUT(4)
|
|
#define DST_RCDISC3__DJV DISCRETE_INPUT(5)
|
|
|
|
DISCRETE_STEP(dst_rcdisc3)
|
|
{
|
|
double diff;
|
|
|
|
/* Exponential based in difference between input/output */
|
|
|
|
if(DST_RCDISC3__ENABLE)
|
|
{
|
|
diff = DST_RCDISC3__IN - this->output[0];
|
|
if (m_v_diode > 0)
|
|
{
|
|
if (diff > 0)
|
|
{
|
|
diff = diff * m_exponent0;
|
|
}
|
|
else if (diff < -m_v_diode)
|
|
{
|
|
diff = diff * m_exponent1;
|
|
}
|
|
else
|
|
{
|
|
diff = diff * m_exponent0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (diff < 0)
|
|
{
|
|
diff = diff * m_exponent0;
|
|
}
|
|
else if (diff > -m_v_diode)
|
|
{
|
|
diff = diff * m_exponent1;
|
|
}
|
|
else
|
|
{
|
|
diff = diff * m_exponent0;
|
|
}
|
|
}
|
|
this->output[0] += diff;
|
|
}
|
|
else
|
|
{
|
|
this->output[0] = 0;
|
|
}
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcdisc3)
|
|
{
|
|
this->output[0] = 0;
|
|
|
|
m_state = 0;
|
|
m_t = 0;
|
|
m_v_diode = DST_RCDISC3__DJV;
|
|
m_exponent0 = RC_CHARGE_EXP_CLASS(DST_RCDISC3__R1 * DST_RCDISC3__C);
|
|
m_exponent1 = RC_CHARGE_EXP_CLASS(RES_2_PARALLEL(DST_RCDISC3__R1, DST_RCDISC3__R2) * DST_RCDISC3__C);
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC4 - Various charge/discharge circuits
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - R1 Resistor value (initialization only)
|
|
* input[2] - R2 Resistor value (initialization only)
|
|
* input[4] - C1 Capacitor Value (initialization only)
|
|
* input[4] - vP power source (initialization only)
|
|
* input[4] - circuit type (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC4__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISC4__IN DISCRETE_INPUT(1)
|
|
#define DST_RCDISC4__R1 DISCRETE_INPUT(2)
|
|
#define DST_RCDISC4__R2 DISCRETE_INPUT(3)
|
|
#define DST_RCDISC4__R3 DISCRETE_INPUT(4)
|
|
#define DST_RCDISC4__C1 DISCRETE_INPUT(5)
|
|
#define DST_RCDISC4__VP DISCRETE_INPUT(6)
|
|
#define DST_RCDISC4__TYPE DISCRETE_INPUT(7)
|
|
|
|
DISCRETE_STEP(dst_rcdisc4)
|
|
{
|
|
int inp1 = (DST_RCDISC4__IN == 0) ? 0 : 1;
|
|
|
|
if (DST_RCDISC4__ENABLE == 0)
|
|
{
|
|
this->output[0] = 0;
|
|
return;
|
|
}
|
|
|
|
switch (m_type)
|
|
{
|
|
case 1:
|
|
case 3:
|
|
m_vC1 += ((m_v[inp1] - m_vC1) * m_exp[inp1]);
|
|
this->output[0] = m_vC1;
|
|
break;
|
|
}
|
|
|
|
/* clip output */
|
|
if (this->output[0] > m_max_out) this->output[0] = m_max_out;
|
|
if (this->output[0] < 0) this->output[0] = 0;
|
|
}
|
|
|
|
DISCRETE_RESET( dst_rcdisc4)
|
|
{
|
|
double v, i, r, rT;
|
|
|
|
m_type = 0;
|
|
/* some error checking. */
|
|
if (DST_RCDISC4__R1 <= 0 || DST_RCDISC4__R2 <= 0 || DST_RCDISC4__C1 <= 0 || (DST_RCDISC4__R3 <= 0 && m_type == 1))
|
|
{
|
|
this->device->discrete_log("Invalid component values in NODE_%d.\n", this->index());
|
|
return;
|
|
}
|
|
if (DST_RCDISC4__VP < 3)
|
|
{
|
|
this->device->discrete_log("vP must be >= 3V in NODE_%d.\n", this->index());
|
|
return;
|
|
}
|
|
if (DST_RCDISC4__TYPE < 1 || DST_RCDISC4__TYPE > 3)
|
|
{
|
|
this->device->discrete_log("Invalid circuit type in NODE_%d.\n", this->index());
|
|
return;
|
|
}
|
|
|
|
m_vC1 = 0;
|
|
/* store type as integer */
|
|
m_type = (int)DST_RCDISC4__TYPE;
|
|
/* setup the maximum op-amp output. */
|
|
m_max_out = DST_RCDISC4__VP - OP_AMP_VP_RAIL_OFFSET;
|
|
|
|
switch (m_type)
|
|
{
|
|
case 1:
|
|
/* We will simulate this as a voltage divider with 2 states depending
|
|
* on the input. But we have to take the diodes into account.
|
|
*/
|
|
v = DST_RCDISC4__VP - .5; /* diode drop */
|
|
|
|
/* When the input is 1, both R1 & R3 are basically in parallel. */
|
|
r = RES_2_PARALLEL(DST_RCDISC4__R1, DST_RCDISC4__R3);
|
|
rT = DST_RCDISC4__R2 + r;
|
|
i = v / rT;
|
|
m_v[1] = i * r + .5;
|
|
rT = RES_2_PARALLEL(DST_RCDISC4__R2, r);
|
|
m_exp[1] = RC_CHARGE_EXP_CLASS(rT * DST_RCDISC4__C1);
|
|
|
|
/* When the input is 0, R1 is out of circuit. */
|
|
rT = DST_RCDISC4__R2 + DST_RCDISC4__R3;
|
|
i = v / rT;
|
|
m_v[0] = i * DST_RCDISC4__R3 + .5;
|
|
rT = RES_2_PARALLEL(DST_RCDISC4__R2, DST_RCDISC4__R3);
|
|
m_exp[0] = RC_CHARGE_EXP_CLASS(rT * DST_RCDISC4__C1);
|
|
break;
|
|
|
|
case 3:
|
|
/* We will simulate this as a voltage divider with 2 states depending
|
|
* on the input. The 1k pullup is in parallel with the internal TTL
|
|
* resistance, so we will just use .5k in series with R1.
|
|
*/
|
|
r = 500.0 + DST_RCDISC4__R1;
|
|
m_v[1] = RES_VOLTAGE_DIVIDER(r, DST_RCDISC4__R2) * (5.0 - 0.5);
|
|
rT = RES_2_PARALLEL(r, DST_RCDISC4__R2);
|
|
m_exp[1] = RC_CHARGE_EXP_CLASS(rT * DST_RCDISC4__C1);
|
|
|
|
/* When the input is 0, R1 is out of circuit. */
|
|
m_v[0] = 0;
|
|
m_exp[0] = RC_CHARGE_EXP_CLASS(DST_RCDISC4__R2 * DST_RCDISC4__C1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC5 - Diode in series with R//C
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC5__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISC5__IN DISCRETE_INPUT(1)
|
|
#define DST_RCDISC5__R DISCRETE_INPUT(2)
|
|
#define DST_RCDISC5__C DISCRETE_INPUT(3)
|
|
|
|
DISCRETE_STEP( dst_rcdisc5)
|
|
{
|
|
double diff,u;
|
|
|
|
/* Exponential based in difference between input/output */
|
|
|
|
u = DST_RCDISC5__IN - 0.7; /* Diode drop */
|
|
if( u < 0)
|
|
u = 0;
|
|
|
|
diff = u - m_v_cap;
|
|
|
|
if(DST_RCDISC5__ENABLE)
|
|
{
|
|
if(diff < 0)
|
|
diff = diff * m_exponent0;
|
|
|
|
m_v_cap += diff;
|
|
this->output[0] = m_v_cap;
|
|
}
|
|
else
|
|
{
|
|
if(diff > 0)
|
|
m_v_cap = u;
|
|
|
|
this->output[0] = 0;
|
|
}
|
|
}
|
|
|
|
DISCRETE_RESET( dst_rcdisc5)
|
|
{
|
|
this->output[0] = 0;
|
|
|
|
m_state = 0;
|
|
m_t = 0;
|
|
m_v_cap = 0;
|
|
m_exponent0 = RC_CHARGE_EXP_CLASS(DST_RCDISC5__R * DST_RCDISC5__C);
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC_MOD - RC triggered by logic and modulated
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value 1
|
|
* input[2] - input value 2
|
|
* input[3] - Resistor 1 value (initialization only)
|
|
* input[4] - Resistor 2 value (initialization only)
|
|
* input[5] - Resistor 3 value (initialization only)
|
|
* input[6] - Resistor 4 value (initialization only)
|
|
* input[7] - Capacitor Value (initialization only)
|
|
* input[8] - Voltage Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC_MOD__IN1 DISCRETE_INPUT(0)
|
|
#define DST_RCDISC_MOD__IN2 DISCRETE_INPUT(1)
|
|
#define DST_RCDISC_MOD__R1 DISCRETE_INPUT(2)
|
|
#define DST_RCDISC_MOD__R2 DISCRETE_INPUT(3)
|
|
#define DST_RCDISC_MOD__R3 DISCRETE_INPUT(4)
|
|
#define DST_RCDISC_MOD__R4 DISCRETE_INPUT(5)
|
|
#define DST_RCDISC_MOD__C DISCRETE_INPUT(6)
|
|
#define DST_RCDISC_MOD__VP DISCRETE_INPUT(7)
|
|
|
|
DISCRETE_STEP(dst_rcdisc_mod)
|
|
{
|
|
double diff, v_cap, u, vD;
|
|
int mod_state, mod1_state, mod2_state;
|
|
|
|
/* Exponential based in difference between input/output */
|
|
v_cap = m_v_cap;
|
|
|
|
mod1_state = DST_RCDISC_MOD__IN1 > 0.5;
|
|
mod2_state = DST_RCDISC_MOD__IN2 > 0.6;
|
|
mod_state = (mod2_state << 1) + mod1_state;
|
|
|
|
u = mod1_state ? 0 : DST_RCDISC_MOD__VP;
|
|
/* Clamp */
|
|
diff = u - v_cap;
|
|
vD = diff * m_vd_gain[mod_state];
|
|
if (vD < -0.6)
|
|
{
|
|
diff = u + 0.6 - v_cap;
|
|
diff -= diff * m_exp_low[mod1_state];
|
|
v_cap += diff;
|
|
this->output[0] = mod2_state ? 0 : -0.6;
|
|
}
|
|
else
|
|
{
|
|
diff -= diff * m_exp_high[mod_state];
|
|
v_cap += diff;
|
|
/* neglecting current through R3 drawn by next8 node */
|
|
this->output[0] = mod2_state ? 0: (u - v_cap) * m_gain[mod1_state];
|
|
}
|
|
m_v_cap = v_cap;
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcdisc_mod)
|
|
{
|
|
double rc[2], rc2[2];
|
|
|
|
/* pre-calculate fixed values */
|
|
/* DST_RCDISC_MOD__IN1 <= 0.5 */
|
|
rc[0] = DST_RCDISC_MOD__R1 + DST_RCDISC_MOD__R2;
|
|
if (rc[0] < 1) rc[0] = 1;
|
|
m_exp_low[0] = RC_DISCHARGE_EXP_CLASS(DST_RCDISC_MOD__C * rc[0]);
|
|
m_gain[0] = RES_VOLTAGE_DIVIDER(rc[0], DST_RCDISC_MOD__R4);
|
|
/* DST_RCDISC_MOD__IN1 > 0.5 */
|
|
rc[1] = DST_RCDISC_MOD__R2;
|
|
if (rc[1] < 1) rc[1] = 1;
|
|
m_exp_low[1] = RC_DISCHARGE_EXP_CLASS(DST_RCDISC_MOD__C * rc[1]);
|
|
m_gain[1] = RES_VOLTAGE_DIVIDER(rc[1], DST_RCDISC_MOD__R4);
|
|
/* DST_RCDISC_MOD__IN2 <= 0.6 */
|
|
rc2[0] = DST_RCDISC_MOD__R4;
|
|
/* DST_RCDISC_MOD__IN2 > 0.6 */
|
|
rc2[1] = RES_2_PARALLEL(DST_RCDISC_MOD__R3, DST_RCDISC_MOD__R4);
|
|
/* DST_RCDISC_MOD__IN1 <= 0.5 && DST_RCDISC_MOD__IN2 <= 0.6 */
|
|
m_exp_high[0] = RC_DISCHARGE_EXP_CLASS(DST_RCDISC_MOD__C * (rc[0] + rc2[0]));
|
|
m_vd_gain[0] = RES_VOLTAGE_DIVIDER(rc[0], rc2[0]);
|
|
/* DST_RCDISC_MOD__IN1 > 0.5 && DST_RCDISC_MOD__IN2 <= 0.6 */
|
|
m_exp_high[1] = RC_DISCHARGE_EXP_CLASS(DST_RCDISC_MOD__C * (rc[1] + rc2[0]));
|
|
m_vd_gain[1] = RES_VOLTAGE_DIVIDER(rc[1], rc2[0]);
|
|
/* DST_RCDISC_MOD__IN1 <= 0.5 && DST_RCDISC_MOD__IN2 > 0.6 */
|
|
m_exp_high[2] = RC_DISCHARGE_EXP_CLASS(DST_RCDISC_MOD__C * (rc[0] + rc2[1]));
|
|
m_vd_gain[2] = RES_VOLTAGE_DIVIDER(rc[0], rc2[1]);
|
|
/* DST_RCDISC_MOD__IN1 > 0.5 && DST_RCDISC_MOD__IN2 > 0.6 */
|
|
m_exp_high[3] = RC_DISCHARGE_EXP_CLASS(DST_RCDISC_MOD__C * (rc[1] + rc2[1]));
|
|
m_vd_gain[3] = RES_VOLTAGE_DIVIDER(rc[1], rc2[1]);
|
|
|
|
m_v_cap = 0;
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCFILTER - Usage of node_description values for RC filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
* input[4] - Voltage reference. Usually 0V.
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCFILTER__VIN DISCRETE_INPUT(0)
|
|
#define DST_RCFILTER__R DISCRETE_INPUT(1)
|
|
#define DST_RCFILTER__C DISCRETE_INPUT(2)
|
|
#define DST_RCFILTER__VREF DISCRETE_INPUT(3)
|
|
|
|
DISCRETE_STEP(dst_rcfilter)
|
|
{
|
|
if (EXPECTED(m_is_fast))
|
|
this->output[0] += ((DST_RCFILTER__VIN - this->output[0]) * m_exponent);
|
|
else
|
|
{
|
|
if (UNEXPECTED(m_has_rc_nodes))
|
|
{
|
|
double rc = DST_RCFILTER__R * DST_RCFILTER__C;
|
|
if (rc != m_rc)
|
|
{
|
|
m_rc = rc;
|
|
m_exponent = RC_CHARGE_EXP_CLASS(rc);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Next Value = PREV + (INPUT_VALUE - PREV)*(1-(EXP(-TIMEDELTA/RC))) */
|
|
/************************************************************************/
|
|
|
|
m_vCap += ((DST_RCFILTER__VIN - this->output[0]) * m_exponent);
|
|
this->output[0] = m_vCap + DST_RCFILTER__VREF;
|
|
}
|
|
}
|
|
|
|
|
|
DISCRETE_RESET(dst_rcfilter)
|
|
{
|
|
m_has_rc_nodes = this->input_is_node() & 0x6;
|
|
m_rc = DST_RCFILTER__R * DST_RCFILTER__C;
|
|
m_exponent = RC_CHARGE_EXP_CLASS(m_rc);
|
|
m_vCap = 0;
|
|
this->output[0] = 0;
|
|
/* FIXME --> we really need another class here */
|
|
if (!m_has_rc_nodes && DST_RCFILTER__VREF == 0)
|
|
m_is_fast = 1;
|
|
else
|
|
m_is_fast = 0;
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCFILTER_SW - Usage of node_description values for switchable RC filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
* input[4] - Voltage reference. Usually 0V.
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCFILTER_SW__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCFILTER_SW__VIN DISCRETE_INPUT(1)
|
|
#define DST_RCFILTER_SW__SWITCH DISCRETE_INPUT(2)
|
|
#define DST_RCFILTER_SW__R DISCRETE_INPUT(3)
|
|
#define DST_RCFILTER_SW__C(x) DISCRETE_INPUT(4+x)
|
|
|
|
/* 74HC4066 : 15
|
|
* 74VHC4066 : 15
|
|
* UTC4066 : 270 @ 5VCC, 80 @ 15VCC
|
|
* CD4066BC : 270 (Fairchild)
|
|
*
|
|
* The choice below makes scramble sound about "right". For future error reports,
|
|
* we need the exact type of switch and at which voltage (5, 12?) it is operated.
|
|
*/
|
|
#define CD4066_ON_RES (40)
|
|
|
|
// FIXME: This needs optimization !
|
|
DISCRETE_STEP(dst_rcfilter_sw)
|
|
{
|
|
int i;
|
|
int bits = (int)DST_RCFILTER_SW__SWITCH;
|
|
double us = 0;
|
|
double vIn = DST_RCFILTER_SW__VIN;
|
|
|
|
if (EXPECTED(DST_RCFILTER_SW__ENABLE))
|
|
{
|
|
switch (bits)
|
|
{
|
|
case 0:
|
|
this->output[0] = vIn;
|
|
break;
|
|
case 1:
|
|
m_vCap[0] += (vIn - m_vCap[0]) * m_exp0;
|
|
this->output[0] = m_vCap[0] + (vIn - m_vCap[0]) * m_factor;
|
|
break;
|
|
case 2:
|
|
m_vCap[1] += (vIn - m_vCap[1]) * m_exp1;
|
|
this->output[0] = m_vCap[1] + (vIn - m_vCap[1]) * m_factor;
|
|
break;
|
|
default:
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (( bits & (1 << i)) != 0)
|
|
us += m_vCap[i];
|
|
}
|
|
this->output[0] = m_f1[bits] * vIn + m_f2[bits] * us;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (( bits & (1 << i)) != 0)
|
|
m_vCap[i] += (this->output[0] - m_vCap[i]) * m_exp[i];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->output[0] = 0;
|
|
}
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcfilter_sw)
|
|
{
|
|
int i, bits;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
m_vCap[i] = 0;
|
|
m_exp[i] = RC_CHARGE_EXP_CLASS( CD4066_ON_RES * DST_RCFILTER_SW__C(i));
|
|
}
|
|
|
|
for (bits=0; bits < 15; bits++)
|
|
{
|
|
double rs = 0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (( bits & (1 << i)) != 0)
|
|
rs += DST_RCFILTER_SW__R;
|
|
}
|
|
m_f1[bits] = RES_VOLTAGE_DIVIDER(rs, CD4066_ON_RES);
|
|
m_f2[bits] = DST_RCFILTER_SW__R / (CD4066_ON_RES + rs);
|
|
}
|
|
|
|
|
|
/* fast cases */
|
|
m_exp0 = RC_CHARGE_EXP_CLASS((CD4066_ON_RES + DST_RCFILTER_SW__R) * DST_RCFILTER_SW__C(0));
|
|
m_exp1 = RC_CHARGE_EXP_CLASS((CD4066_ON_RES + DST_RCFILTER_SW__R) * DST_RCFILTER_SW__C(1));
|
|
m_factor = RES_VOLTAGE_DIVIDER(DST_RCFILTER_SW__R, CD4066_ON_RES);
|
|
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCINTEGRATE - Two diode inputs, transistor and a R/C charge
|
|
* discharge network
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value 1
|
|
* input[2] - input value 2
|
|
* input[3] - Resistor 1 value (initialization only)
|
|
* input[4] - Resistor 2 value (initialization only)
|
|
* input[5] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCINTEGRATE__IN1 DISCRETE_INPUT(0)
|
|
#define DST_RCINTEGRATE__R1 DISCRETE_INPUT(1)
|
|
#define DST_RCINTEGRATE__R2 DISCRETE_INPUT(2)
|
|
#define DST_RCINTEGRATE__R3 DISCRETE_INPUT(3)
|
|
#define DST_RCINTEGRATE__C DISCRETE_INPUT(4)
|
|
#define DST_RCINTEGRATE__VP DISCRETE_INPUT(5)
|
|
#define DST_RCINTEGRATE__TYPE DISCRETE_INPUT(6)
|
|
|
|
/* Ebers-Moll large signal model
|
|
* Couriersud:
|
|
* The implementation avoids all iterative approaches in order not to burn cycles
|
|
* We will calculate Ic from vBE and use this as an indication where to go.
|
|
* The implementation may oscillate if you change the weighting factors at the
|
|
* end.
|
|
*
|
|
* This implementation is not perfect, but does it's job in dkong'
|
|
*/
|
|
|
|
/* reverse saturation current */
|
|
#define IES 7e-15
|
|
#define ALPHAT 0.99
|
|
#define KT 0.026
|
|
#define EM_IC(x) (ALPHAT * IES * exp( (x) / KT - 1.0 ))
|
|
|
|
DISCRETE_STEP( dst_rcintegrate)
|
|
{
|
|
double diff, u, iQ, iQc, iC, RG, vE;
|
|
double vP;
|
|
|
|
u = DST_RCINTEGRATE__IN1;
|
|
vP = DST_RCINTEGRATE__VP;
|
|
|
|
if ( u - 0.7 < m_vCap * m_gain_r1_r2)
|
|
{
|
|
/* discharge .... */
|
|
diff = 0.0 - m_vCap;
|
|
iC = m_c_exp1 * diff; /* iC */
|
|
diff -= diff * m_exp_exponent1;
|
|
m_vCap += diff;
|
|
iQ = 0;
|
|
vE = m_vCap * m_gain_r1_r2;
|
|
RG = vE / iC;
|
|
}
|
|
else
|
|
{
|
|
/* charging */
|
|
diff = (vP - m_vCE) * m_f - m_vCap;
|
|
iC = 0.0 - m_c_exp0 * diff; /* iC */
|
|
diff -= diff * m_exp_exponent0;
|
|
m_vCap += diff;
|
|
iQ = iC + (iC * DST_RCINTEGRATE__R1 + m_vCap) / DST_RCINTEGRATE__R2;
|
|
RG = (vP - m_vCE) / iQ;
|
|
vE = (RG - DST_RCINTEGRATE__R3) / RG * (vP - m_vCE);
|
|
}
|
|
|
|
|
|
u = DST_RCINTEGRATE__IN1;
|
|
if (u > 0.7 + vE)
|
|
vE = u - 0.7;
|
|
iQc = EM_IC(u - vE);
|
|
m_vCE = MIN(vP - 0.1, vP - RG * iQc);
|
|
|
|
/* Avoid oscillations
|
|
* The method tends to largely overshoot - no wonder without
|
|
* iterative solution approximation
|
|
*/
|
|
|
|
m_vCE = MAX(m_vCE, 0.1 );
|
|
m_vCE = 0.1 * m_vCE + 0.9 * (vP - vE - iQ * DST_RCINTEGRATE__R3);
|
|
|
|
switch (m_type)
|
|
{
|
|
case DISC_RC_INTEGRATE_TYPE1:
|
|
this->output[0] = m_vCap;
|
|
break;
|
|
case DISC_RC_INTEGRATE_TYPE2:
|
|
this->output[0] = vE;
|
|
break;
|
|
case DISC_RC_INTEGRATE_TYPE3:
|
|
this->output[0] = MAX(0, vP - iQ * DST_RCINTEGRATE__R3);
|
|
break;
|
|
}
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcintegrate)
|
|
{
|
|
double r;
|
|
double dt = this->sample_time();
|
|
|
|
m_type = DST_RCINTEGRATE__TYPE;
|
|
|
|
m_vCap = 0;
|
|
m_vCE = 0;
|
|
|
|
/* pre-calculate fixed values */
|
|
m_gain_r1_r2 = RES_VOLTAGE_DIVIDER(DST_RCINTEGRATE__R1, DST_RCINTEGRATE__R2);
|
|
|
|
r = DST_RCINTEGRATE__R1 / DST_RCINTEGRATE__R2 * DST_RCINTEGRATE__R3 + DST_RCINTEGRATE__R1 + DST_RCINTEGRATE__R3;
|
|
|
|
m_f = RES_VOLTAGE_DIVIDER(DST_RCINTEGRATE__R3, DST_RCINTEGRATE__R2);
|
|
m_exponent0 = -1.0 * r * m_f * DST_RCINTEGRATE__C;
|
|
m_exponent1 = -1.0 * (DST_RCINTEGRATE__R1 + DST_RCINTEGRATE__R2) * DST_RCINTEGRATE__C;
|
|
m_exp_exponent0 = exp(dt / m_exponent0);
|
|
m_exp_exponent1 = exp(dt / m_exponent1);
|
|
m_c_exp0 = DST_RCINTEGRATE__C / m_exponent0 * m_exp_exponent0;
|
|
m_c_exp1 = DST_RCINTEGRATE__C / m_exponent1 * m_exp_exponent1;
|
|
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_SALLEN_KEY - Sallen-Key filter circuit
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - IN0 node
|
|
* input[3] - Filter Type
|
|
*
|
|
* also passed discrete_op_amp_filt_info structure
|
|
*
|
|
* 2008, couriersud
|
|
************************************************************************/
|
|
#define DST_SALLEN_KEY__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_SALLEN_KEY__INP0 DISCRETE_INPUT(1)
|
|
#define DST_SALLEN_KEY__TYPE DISCRETE_INPUT(2)
|
|
|
|
DISCRETE_STEP(dst_sallen_key)
|
|
{
|
|
double gain = 1.0;
|
|
|
|
if (DST_SALLEN_KEY__ENABLE == 0.0)
|
|
{
|
|
gain = 0.0;
|
|
}
|
|
|
|
this->output[0] = -m_fc.a1 * m_fc.y1 - m_fc.a2 * m_fc.y2 +
|
|
m_fc.b0 * gain * DST_SALLEN_KEY__INP0 + m_fc.b1 * m_fc.x1 + m_fc.b2 * m_fc.x2;
|
|
|
|
m_fc.x2 = m_fc.x1;
|
|
m_fc.x1 = gain * DST_SALLEN_KEY__INP0;
|
|
m_fc.y2 = m_fc.y1;
|
|
m_fc.y1 = this->output[0];
|
|
}
|
|
|
|
DISCRETE_RESET(dst_sallen_key)
|
|
{
|
|
DISCRETE_DECLARE_INFO(discrete_op_amp_filt_info)
|
|
|
|
double freq, q;
|
|
|
|
switch ((int) DST_SALLEN_KEY__TYPE)
|
|
{
|
|
case DISC_SALLEN_KEY_LOW_PASS:
|
|
freq = 1.0 / ( 2.0 * M_PI * sqrt(info->c1 * info->c2 * info->r1 * info->r2));
|
|
q = sqrt(info->c1 * info->c2 * info->r1 * info->r2) / (info->c2 * (info->r1 + info->r2));
|
|
break;
|
|
default:
|
|
fatalerror("Unknown sallen key filter type");
|
|
}
|
|
|
|
calculate_filter2_coefficients(this, freq, 1.0 / q, DISC_FILTER_LOWPASS, m_fc);
|
|
this->output[0] = 0;
|
|
}
|
|
|
|
|
|
/* !!!!!!!!!!! NEW FILTERS for testing !!!!!!!!!!!!!!!!!!!!! */
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCFILTERN - Usage of node_description values for RC filter
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCFILTERN__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCFILTERN__IN DISCRETE_INPUT(1)
|
|
#define DST_RCFILTERN__R DISCRETE_INPUT(2)
|
|
#define DST_RCFILTERN__C DISCRETE_INPUT(3)
|
|
|
|
#if 0
|
|
DISCRETE_RESET(dst_rcfilterN)
|
|
{
|
|
#if 0
|
|
double f=1.0/(2*M_PI* DST_RCFILTERN__R * DST_RCFILTERN__C);
|
|
|
|
/* !!!!!!!!!!!!!! CAN'T CHEAT LIKE THIS !!!!!!!!!!!!!!!! */
|
|
/* Put this stuff in a context */
|
|
|
|
this->input[2] = f;
|
|
this->input[3] = DISC_FILTER_LOWPASS;
|
|
|
|
/* Use first order filter */
|
|
dst_filter1_reset(node);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISCN - Usage of node_description values for RC discharge
|
|
* (inverse slope of DST_RCFILTER)
|
|
*
|
|
* input[0] - Enable input value
|
|
* input[1] - input value
|
|
* input[2] - Resistor value (initialization only)
|
|
* input[3] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISCN__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISCN__IN DISCRETE_INPUT(1)
|
|
#define DST_RCDISCN__R DISCRETE_INPUT(2)
|
|
#define DST_RCDISCN__C DISCRETE_INPUT(3)
|
|
|
|
DISCRETE_RESET(dst_rcdiscN)
|
|
{
|
|
#if 0
|
|
double f = 1.0 / (2 * M_PI * DST_RCDISCN__R * DST_RCDISCN__C);
|
|
|
|
/* !!!!!!!!!!!!!! CAN'T CHEAT LIKE THIS !!!!!!!!!!!!!!!! */
|
|
/* Put this stuff in a context */
|
|
|
|
this->input[2] = f;
|
|
this->input[3] = DISC_FILTER_LOWPASS;
|
|
|
|
/* Use first order filter */
|
|
dst_filter1_reset(node);
|
|
#endif
|
|
}
|
|
|
|
DISCRETE_STEP(dst_rcdiscN)
|
|
{
|
|
double gain = 1.0;
|
|
|
|
if (DST_RCDISCN__ENABLE == 0.0)
|
|
{
|
|
gain = 0.0;
|
|
}
|
|
|
|
/* A rise in the input signal results in an instant charge, */
|
|
/* else discharge through the RC to zero */
|
|
if (gain* DST_RCDISCN__IN > m_x1)
|
|
this->output[0] = gain* DST_RCDISCN__IN;
|
|
else
|
|
this->output[0] = -m_a1*m_y1;
|
|
|
|
m_x1 = gain* DST_RCDISCN__IN;
|
|
m_y1 = this->output[0];
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* DST_RCDISC2N - Usage of node_description values for RC discharge
|
|
* Has switchable charge resistor/input
|
|
*
|
|
* input[0] - Switch input value
|
|
* input[1] - input[0] value
|
|
* input[2] - Resistor0 value (initialization only)
|
|
* input[3] - input[1] value
|
|
* input[4] - Resistor1 value (initialization only)
|
|
* input[5] - Capacitor Value (initialization only)
|
|
*
|
|
************************************************************************/
|
|
#define DST_RCDISC2N__ENABLE DISCRETE_INPUT(0)
|
|
#define DST_RCDISC2N__IN0 DISCRETE_INPUT(1)
|
|
#define DST_RCDISC2N__R0 DISCRETE_INPUT(2)
|
|
#define DST_RCDISC2N__IN1 DISCRETE_INPUT(3)
|
|
#define DST_RCDISC2N__R1 DISCRETE_INPUT(4)
|
|
#define DST_RCDISC2N__C DISCRETE_INPUT(5)
|
|
|
|
|
|
DISCRETE_STEP(dst_rcdisc2N)
|
|
{
|
|
double input = ((DST_RCDISC2N__ENABLE == 0) ? DST_RCDISC2N__IN0 : DST_RCDISC2N__IN1);
|
|
|
|
if (DST_RCDISC2N__ENABLE == 0)
|
|
this->output[0] = -m_fc0.a1*m_y1 + m_fc0.b0*input + m_fc0.b1 * m_x1;
|
|
else
|
|
this->output[0] = -m_fc1.a1*m_y1 + m_fc1.b0*input + m_fc1.b1*m_x1;
|
|
|
|
m_x1 = input;
|
|
m_y1 = this->output[0];
|
|
}
|
|
|
|
DISCRETE_RESET(dst_rcdisc2N)
|
|
{
|
|
double f1,f2;
|
|
|
|
f1 = 1.0 / (2 * M_PI * DST_RCDISC2N__R0 * DST_RCDISC2N__C);
|
|
f2 = 1.0 / (2 * M_PI * DST_RCDISC2N__R1 * DST_RCDISC2N__C);
|
|
|
|
calculate_filter1_coefficients(this, f1, DISC_FILTER_LOWPASS, m_fc0);
|
|
calculate_filter1_coefficients(this, f2, DISC_FILTER_LOWPASS, m_fc1);
|
|
|
|
/* Initialize the object */
|
|
this->output[0] = 0;
|
|
}
|