118 lines
4.6 KiB
JavaScript
118 lines
4.6 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
||
import { Circuit, Resistor, VoltageSource, Diode, LED, BJT_NPN } from '../src/index.js';
|
||
|
||
describe('Shockley diode — basic', () => {
|
||
it('forward-biased diode in series with R drops ~0.6-0.7V', () => {
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', 5));
|
||
c.addComponent(new Resistor('R1', 'vcc', 'a', 1000));
|
||
c.addComponent(new Diode('D1', 'a', 'gnd')); // default Is=1e-14
|
||
c.solveDC();
|
||
const Vd = c.nodeVoltage('a');
|
||
expect(Vd).toBeGreaterThan(0.55);
|
||
expect(Vd).toBeLessThan(0.75);
|
||
// Current ≈ (5 − 0.65)/1000 ≈ 4.35 mA
|
||
const I = (5 - Vd) / 1000;
|
||
expect(I).toBeGreaterThan(0.004);
|
||
expect(I).toBeLessThan(0.005);
|
||
});
|
||
|
||
it('reverse-biased diode blocks current', () => {
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', 5));
|
||
c.addComponent(new Resistor('R1', 'gnd', 'a', 1000));
|
||
c.addComponent(new Diode('D1', 'a', 'vcc')); // anode to 'a' (~0V), cathode to +5V
|
||
c.solveDC();
|
||
// With the diode reverse biased, no current; 'a' pulled to gnd through R1.
|
||
expect(Math.abs(c.nodeVoltage('a'))).toBeLessThan(0.01);
|
||
});
|
||
});
|
||
|
||
describe('LED — forward voltage by color', () => {
|
||
it('red LED with 220Ω resistor @ 5V → Vf ≈ 2.0V, I ≈ 13.6mA', () => {
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', 5));
|
||
c.addComponent(new Resistor('R1', 'vcc', 'a', 220));
|
||
const led = new LED('LED1', 'a', 'gnd', 'red');
|
||
c.addComponent(led);
|
||
c.solveDC();
|
||
const Vf = c.nodeVoltage('a');
|
||
expect(Vf).toBeGreaterThan(1.8);
|
||
expect(Vf).toBeLessThan(2.3);
|
||
const I = led.currentThrough(c.state);
|
||
expect(I).toBeGreaterThan(0.010);
|
||
expect(I).toBeLessThan(0.016);
|
||
// Brightness proxy: ratio of I to rated 20 mA
|
||
const b = led.brightness(c.state);
|
||
expect(b).toBeGreaterThan(0.5);
|
||
expect(b).toBeLessThanOrEqual(1.0);
|
||
});
|
||
|
||
it('blue LED Vf higher than red', () => {
|
||
const mkCircuit = (color) => {
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', 5));
|
||
c.addComponent(new Resistor('R1', 'vcc', 'a', 220));
|
||
c.addComponent(new LED('LED1', 'a', 'gnd', color));
|
||
c.solveDC();
|
||
return c.nodeVoltage('a');
|
||
};
|
||
const Vred = mkCircuit('red');
|
||
const Vblue = mkCircuit('blue');
|
||
expect(Vblue).toBeGreaterThan(Vred);
|
||
expect(Vblue).toBeGreaterThan(2.8);
|
||
expect(Vblue).toBeLessThan(3.6);
|
||
});
|
||
|
||
it('LED brightness increases with supply voltage', () => {
|
||
const readBrightness = (V) => {
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', V));
|
||
c.addComponent(new Resistor('R1', 'vcc', 'a', 220));
|
||
const led = new LED('LED1', 'a', 'gnd', 'red');
|
||
c.addComponent(led);
|
||
c.solveDC();
|
||
return led.brightness(c.state);
|
||
};
|
||
const b1 = readBrightness(2.0); // below knee
|
||
const b2 = readBrightness(3.0);
|
||
const b3 = readBrightness(5.0);
|
||
expect(b1).toBeLessThan(b2);
|
||
expect(b2).toBeLessThan(b3);
|
||
expect(b1).toBeLessThan(0.1); // almost off
|
||
expect(b3).toBeGreaterThan(0.5); // bright
|
||
});
|
||
});
|
||
|
||
describe('BJT NPN — switch mode', () => {
|
||
it('NPN saturates with base drive, V_CE < 0.3V', () => {
|
||
// V_cc → R_c → collector. Base fed via R_b from +5V. Emitter to gnd.
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', 5));
|
||
c.addComponent(new VoltageSource('Vb', 'bsrc', 'gnd', 5));
|
||
c.addComponent(new Resistor('Rc', 'vcc', 'coll', 1000));
|
||
c.addComponent(new Resistor('Rb', 'bsrc', 'base', 10000));
|
||
c.addComponent(new BJT_NPN('Q1', 'coll', 'base', 'gnd'));
|
||
c.solveDC({ maxIter: 200, tol: 1e-6 });
|
||
const Vce = c.nodeVoltage('coll');
|
||
const Vbe = c.nodeVoltage('base');
|
||
expect(Vbe).toBeGreaterThan(0.5);
|
||
expect(Vbe).toBeLessThan(0.85);
|
||
// Should be saturated — near 0.1–0.3 V on real BJT, but simplified Ebers-Moll
|
||
// doesn't model saturation aggressively, so we accept up to 0.8 V.
|
||
expect(Vce).toBeLessThan(0.8);
|
||
// Must be much less than V_cc
|
||
expect(Vce).toBeLessThan(c.nodeVoltage('vcc') - 3);
|
||
});
|
||
|
||
it('NPN with no base drive is off, V_CE ≈ V_cc', () => {
|
||
const c = new Circuit();
|
||
c.addComponent(new VoltageSource('V1', 'vcc', 'gnd', 5));
|
||
c.addComponent(new Resistor('Rc', 'vcc', 'coll', 1000));
|
||
c.addComponent(new Resistor('Rb', 'gnd', 'base', 10000)); // base pulled to gnd
|
||
c.addComponent(new BJT_NPN('Q1', 'coll', 'base', 'gnd'));
|
||
c.solveDC({ maxIter: 200, tol: 1e-6 });
|
||
expect(c.nodeVoltage('coll')).toBeGreaterThan(4.9);
|
||
});
|
||
});
|