128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
||
import { runNetlist } from './helpers/testSolver';
|
||
|
||
describe('ngspice — diode', () => {
|
||
it('1N4148-style diode forward drop is 0.55–0.80V @ ~4 mA', { timeout: 30_000 }, async () => {
|
||
const netlist = `Diode forward
|
||
V1 vcc 0 DC 5
|
||
R1 vcc a 1k
|
||
D1 a 0 DMOD
|
||
.model DMOD D(Is=2.52n N=1.752 Rs=0.568 Ibv=0.1u Bv=100)
|
||
.op
|
||
.end`;
|
||
const { dcValue } = await runNetlist(netlist);
|
||
const Va = dcValue('v(a)');
|
||
expect(Va).toBeGreaterThan(0.55);
|
||
expect(Va).toBeLessThan(0.8);
|
||
});
|
||
|
||
it('full-wave bridge rectifier outputs ~|Vin| − 2·Vf', { timeout: 30_000 }, async () => {
|
||
const netlist = `Bridge rectifier
|
||
V1 a b SIN(0 6 50)
|
||
D1 a p DMOD
|
||
D2 b p DMOD
|
||
D3 n a DMOD
|
||
D4 n b DMOD
|
||
R1 p n 1k
|
||
.model DMOD D(Is=1e-14 N=1)
|
||
.tran 0.1m 40m
|
||
.end`;
|
||
const { vec } = await runNetlist(netlist);
|
||
const t = vec('time') as number[];
|
||
const vp = vec('v(p)') as number[];
|
||
const vn = vec('v(n)') as number[];
|
||
let peakOut = -Infinity;
|
||
let minOut = Infinity;
|
||
for (let i = 0; i < t.length; i++) {
|
||
if (t[i] < 20e-3) continue;
|
||
const d = vp[i] - vn[i];
|
||
if (d > peakOut) peakOut = d;
|
||
if (d < minOut) minOut = d;
|
||
}
|
||
expect(peakOut).toBeGreaterThan(4.2);
|
||
expect(peakOut).toBeLessThan(5.4);
|
||
expect(minOut).toBeGreaterThan(-0.1);
|
||
});
|
||
});
|
||
|
||
describe('ngspice — BJT', () => {
|
||
it(
|
||
'common-emitter amplifier inverts and amplifies a small signal',
|
||
{ timeout: 30_000 },
|
||
async () => {
|
||
const netlist = `Common-emitter
|
||
Vcc vcc 0 DC 12
|
||
Vin in 0 SIN(0 0.01 1k)
|
||
Cin in b 1u
|
||
RB1 vcc b 47k
|
||
RB2 b 0 10k
|
||
RC vcc c 4.7k
|
||
RE e 0 1k
|
||
CE e 0 100u
|
||
Q1 c b e Q2N2222
|
||
Cout c out 1u
|
||
Rout out 0 100k
|
||
.model Q2N2222 NPN(Is=1e-14 Bf=200 Vaf=75)
|
||
.tran 10u 6m
|
||
.end`;
|
||
const { vec } = await runNetlist(netlist);
|
||
const t = vec('time') as number[];
|
||
const vin = vec('v(in)') as number[];
|
||
const vout = vec('v(out)') as number[];
|
||
let maxIn = 0;
|
||
let maxOut = 0;
|
||
let minOut = Infinity;
|
||
for (let i = 0; i < t.length; i++) {
|
||
if (t[i] < 3e-3) continue;
|
||
if (Math.abs(vin[i]) > maxIn) maxIn = Math.abs(vin[i]);
|
||
if (vout[i] > maxOut) maxOut = vout[i];
|
||
if (vout[i] < minOut) minOut = vout[i];
|
||
}
|
||
const gain = (maxOut - minOut) / (2 * maxIn);
|
||
expect(gain).toBeGreaterThan(30);
|
||
},
|
||
);
|
||
});
|
||
|
||
describe('ngspice — MOSFET', () => {
|
||
it('N-MOS switch: V_GS > Vth pulls drain to ground', { timeout: 30_000 }, async () => {
|
||
const netlist = `N-MOS switch
|
||
Vcc vcc 0 DC 5
|
||
Vgate gate 0 DC 5
|
||
RL vcc drain 1k
|
||
M1 drain gate 0 0 NMOS_L1 L=1u W=100u
|
||
.model NMOS_L1 NMOS(Level=1 Vto=1.0 Kp=50u Lambda=0.01)
|
||
.op
|
||
.end`;
|
||
const { dcValue } = await runNetlist(netlist);
|
||
expect(dcValue('v(drain)')).toBeLessThan(1.0);
|
||
});
|
||
|
||
it('N-MOS switch: V_GS < Vth leaves drain near V_dd', { timeout: 30_000 }, async () => {
|
||
const netlist = `N-MOS off
|
||
Vcc vcc 0 DC 5
|
||
Vgate gate 0 DC 0
|
||
RL vcc drain 1k
|
||
M1 drain gate 0 0 NMOS_L1 L=1u W=100u
|
||
.model NMOS_L1 NMOS(Level=1 Vto=1.0 Kp=50u Lambda=0.01)
|
||
.op
|
||
.end`;
|
||
const { dcValue } = await runNetlist(netlist);
|
||
expect(dcValue('v(drain)')).toBeGreaterThan(4.9);
|
||
});
|
||
});
|
||
|
||
describe('ngspice — op-amp (behavioral E-source)', () => {
|
||
it('inverting amplifier: V_out = −(R_f/R_in) · V_in', { timeout: 30_000 }, async () => {
|
||
const netlist = `Inverting amp
|
||
Vin in 0 DC 0.2
|
||
Rin in n 1k
|
||
Rf n out 10k
|
||
Eopa out 0 0 n 1e6
|
||
.op
|
||
.end`;
|
||
const { dcValue } = await runNetlist(netlist);
|
||
expect(dcValue('v(out)')).toBeCloseTo(-2.0, 2);
|
||
});
|
||
});
|