Skip to content

Commit 4c6eaad

Browse files
esaruohoclaude
andcommitted
Add MOSFET channel-length modulation and scope trail persistence
- Add lambda parameter to MosfetElm for channel-length modulation (#155). In saturation: Ids = 0.5*beta*(Vgs-Vt)^2*(1+lambda*Vds), Gds = 0.5*beta*(Vgs-Vt)^2*lambda. Default 0 (ideal). - Expose lambda in JfetElm edit dialog (n < 3 threshold). - Add configurable XY trail persistence slider in scope dialog (#157). trailLen 0-49 = varying fade speed, 50 = infinite (no fade). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c1167bc commit 4c6eaad

File tree

4 files changed

+61
-22
lines changed

4 files changed

+61
-22
lines changed

src/com/lushprojects/circuitjs1/client/JfetElm.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ void getInfo(String arr[]) {
137137
getFetInfo(arr, "JFET");
138138
}
139139
public EditInfo getEditInfo(int n) {
140-
if (n < 2)
140+
if (n < 3)
141141
return super.getEditInfo(n);
142142
return null;
143143
}

src/com/lushprojects/circuitjs1/client/MosfetElm.java

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class MosfetElm extends CircuitElm implements MouseWheelHandler {
4141
double vt;
4242
// beta = 1/(RdsON*(Vgs-Vt))
4343
double beta;
44+
// channel-length modulation parameter (1/V), 0 = ideal
45+
double lambda;
4446
static int globalFlags;
4547
Diode diodeB1, diodeB2;
4648
double diodeCurrent1, diodeCurrent2, bodyCurrent;
@@ -69,6 +71,7 @@ public MosfetElm(int xa, int ya, int xb, int yb, int f,
6971
try {
7072
vt = new Double(st.nextToken()).doubleValue();
7173
beta = new Double(st.nextToken()).doubleValue();
74+
lambda = new Double(st.nextToken()).doubleValue();
7275
} catch (Exception e) {}
7376
globalFlags = flags & (FLAGS_GLOBAL);
7477
allocNodes(); // make sure volts[] has the right number of elements when hasBodyTerminal() is true
@@ -120,15 +123,18 @@ void dumpXml(Document doc, Element elem) {
120123
super.dumpXml(doc, elem);
121124
XMLSerializer.dumpAttr(elem, "vt", vt);
122125
XMLSerializer.dumpAttr(elem, "be", beta);
126+
if (lambda != 0)
127+
XMLSerializer.dumpAttr(elem, "lambda", lambda);
123128
}
124129

125130
void undumpXml(XMLDeserializer xml) {
126131
flags = 0;
127132
super.undumpXml(xml);
128133
vt = xml.parseDoubleAttr("vt", vt);
129134
beta = xml.parseDoubleAttr("be", beta);
135+
lambda = xml.parseDoubleAttr("lambda", 0);
130136
globalFlags = flags & (FLAGS_GLOBAL);
131-
allocNodes(); // make sure volts[] has the right number of elements when hasBodyTerminal() is true
137+
allocNodes(); // make sure volts[] has the right number of elements when hasBodyTerminal() is true
132138
}
133139

134140
int getDumpType() { return 'f'; }
@@ -423,11 +429,14 @@ void calculate(boolean finished) {
423429
Gds = beta*(vgs-vds-vt);
424430
mode = 1;
425431
} else {
426-
// saturation; Gds = 0
427-
gm = beta*(vgs-vt);
432+
// saturation; Gds from channel-length modulation
433+
double vgs_vt = vgs-vt;
434+
gm = beta*vgs_vt*(1+lambda*vds);
435+
ids = .5*beta*vgs_vt*vgs_vt*(1+lambda*vds);
436+
Gds = .5*beta*vgs_vt*vgs_vt*lambda;
428437
// use very small Gds to avoid nonconvergence
429-
Gds = 1e-8;
430-
ids = .5*beta*(vgs-vt)*(vgs-vt) + (vds-(vgs-vt))*Gds;
438+
if (Gds < 1e-8)
439+
Gds = 1e-8;
431440
mode = 2;
432441
}
433442

@@ -465,7 +474,9 @@ void calculate(boolean finished) {
465474
void getFetInfo(String arr[], String n) {
466475
arr[0] = Locale.LS(((pnp == -1) ? "p-" : "n-") + n);
467476
arr[0] += " (Vt=" + getVoltageText(pnp*vt);
468-
arr[0] += ", \u03b2=" + beta + ")";
477+
arr[0] += ", \u03b2=" + beta;
478+
if (lambda > 0) arr[0] += ", \u03bb=" + lambda;
479+
arr[0] += ")";
469480
arr[1] = ((pnp == 1) ? "Ids = " : "Isd = ") + getCurrentText(ids);
470481
arr[2] = "Vgs = " + getVoltageText(volts[0]-volts[pnp == -1 ? 2 : 1]);
471482
arr[3] = ((pnp == 1) ? "Vds = " : "Vsd = ") + getVoltageText(volts[2]-volts[1]);
@@ -493,27 +504,29 @@ public EditInfo getEditInfo(int n) {
493504
return new EditInfo("Threshold Voltage", pnp*vt, .01, 5);
494505
if (n == 1)
495506
return new EditInfo(EditInfo.makeLink("mosfet-beta.html", "Beta"), beta, .01, 5);
496-
if (n == 2) {
507+
if (n == 2)
508+
return new EditInfo("Channel-Length Modulation (1/V)", lambda, 0, 0).setDimensionless();
509+
if (n == 3) {
497510
EditInfo ei = new EditInfo("", 0, -1, -1);
498511
ei.checkbox = new Checkbox("Show Bulk", showBulk());
499512
return ei;
500513
}
501-
if (n == 3) {
514+
if (n == 4) {
502515
EditInfo ei = new EditInfo("", 0, -1, -1);
503516
ei.checkbox = new Checkbox("Swap D/S", (flags & FLAG_FLIP) != 0);
504517
return ei;
505518
}
506-
if (n == 4 && !showBulk()) {
519+
if (n == 5 && !showBulk()) {
507520
EditInfo ei = new EditInfo("", 0, -1, -1);
508521
ei.checkbox = new Checkbox("Digital Symbol", drawDigital());
509522
return ei;
510523
}
511-
if (n == 4 && showBulk()) {
524+
if (n == 5 && showBulk()) {
512525
EditInfo ei = new EditInfo("", 0, -1, -1);
513526
ei.checkbox = new Checkbox("Simulate Body Diode", (flags & FLAG_BODY_DIODE) != 0);
514527
return ei;
515528
}
516-
if (n == 5 && doBodyDiode()) {
529+
if (n == 6 && doBodyDiode()) {
517530
EditInfo ei = new EditInfo("", 0, -1, -1);
518531
ei.checkbox = new Checkbox("Body Terminal", (flags & FLAG_BODY_TERMINAL) != 0);
519532
return ei;
@@ -525,28 +538,30 @@ public void setEditValue(int n, EditInfo ei) {
525538
if (n == 0)
526539
vt = pnp*ei.value;
527540
if (n == 1 && ei.value > 0)
528-
beta = lastBeta = ei.value;
529-
if (n == 2) {
541+
beta = lastBeta = ei.value;
542+
if (n == 2 && ei.value >= 0)
543+
lambda = ei.value;
544+
if (n == 3) {
530545
globalFlags = (!ei.checkbox.getState()) ? (globalFlags|FLAG_HIDE_BULK) :
531546
(globalFlags & ~(FLAG_HIDE_BULK|FLAG_DIGITAL));
532547
// setPoints();
533548
ei.newDialog = true;
534549
}
535-
if (n == 3) {
550+
if (n == 4) {
536551
flags = (ei.checkbox.getState()) ? (flags | FLAG_FLIP) :
537552
(flags & ~FLAG_FLIP);
538553
// setPoints();
539554
}
540-
if (n == 4 && !showBulk()) {
555+
if (n == 5 && !showBulk()) {
541556
globalFlags = (ei.checkbox.getState()) ? (globalFlags|FLAG_DIGITAL) :
542557
(globalFlags & ~FLAG_DIGITAL);
543558
// setPoints();
544559
}
545-
if (n == 4 && showBulk()) {
560+
if (n == 5 && showBulk()) {
546561
flags = ei.changeFlag(flags, FLAG_BODY_DIODE);
547562
ei.newDialog = true;
548563
}
549-
if (n == 5) {
564+
if (n == 6) {
550565
flags = ei.changeFlag(flags, FLAG_BODY_TERMINAL);
551566
}
552567

src/com/lushprojects/circuitjs1/client/Scope.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ class Scope {
241241
Canvas imageCanvas;
242242
Context2d imageContext;
243243
int alphaCounter =0;
244+
int trailLen = 2; // controls XY trail fade speed (higher = longer persistence)
244245
// scopeTimeStep to check if sim timestep has changed from previous value when redrawing
245246
double scopeTimeStep;
246247
double scale[]; // Max value to scale the display to show - indexed for each value of UNITS - e.g. UNITS_V, UNITS_A etc.
@@ -806,8 +807,8 @@ void draw2d(Graphics g) {
806807
g.clipRect(0, 0, rect.width, rect.height);
807808

808809
alphaCounter++;
809-
810-
if (alphaCounter>2) {
810+
811+
if (trailLen < 50 && alphaCounter > trailLen) {
811812
// fade out plot
812813
alphaCounter=0;
813814
imageContext.setGlobalAlpha(0.01);
@@ -1918,6 +1919,8 @@ void dumpXml(Document doc, Element root) {
19181919

19191920
if (text != null)
19201921
xmlElm.setAttribute("x", text);
1922+
if (trailLen != 2)
1923+
XMLSerializer.dumpAttr(xmlElm, "tl", trailLen);
19211924
}
19221925

19231926
void undumpXml(XMLDeserializer xml) {
@@ -1933,6 +1936,7 @@ void undumpXml(XMLDeserializer xml) {
19331936
position = xml.parseIntAttr("p", 0);
19341937
manDivisions = xml.parseIntAttr("md", 8);
19351938
text = xml.parseStringAttr("x", (String)null);
1939+
trailLen = xml.parseIntAttr("tl", 2);
19361940
int i = 0;
19371941
for (Element elem: xml.getChildElements()) {
19381942
xml.parseChildElement(elem);

src/com/lushprojects/circuitjs1/client/ScopePropertiesDialog.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ public class ScopePropertiesDialog extends Dialog implements ValueChangeHandler<
5858
CheckBox elmInfoBox;
5959
TextBox labelTextBox, manualScaleTextBox, divisionsTextBox;
6060
Button applyButton, scaleUpButton, scaleDownButton;
61-
Scrollbar speedBar,positionBar;
61+
Scrollbar speedBar, positionBar, trailBar;
6262
Scope scope;
6363
Grid grid, vScaleGrid, hScaleGrid;
6464
int nx, ny;
65-
Label scopeSpeedLabel, manualScaleLabel,vScaleList, manualScaleId, positionLabel, divisionsLabel;
65+
Label scopeSpeedLabel, manualScaleLabel,vScaleList, manualScaleId, positionLabel, divisionsLabel, trailLabel;
6666
expandingLabel vScaleLabel, hScaleLabel;
6767
Vector <Button> chanButtons = new Vector <Button>();
6868
int plotSelection = 0;
@@ -424,6 +424,19 @@ public void execute() {
424424
viBox.addValueChangeHandler(this);
425425
addItemToGrid(grid, xyBox = new ScopeCheckBox(Locale.LS("Plot X/Y"), "plotxy"));
426426
xyBox.addValueChangeHandler(this);
427+
Grid trailGrid = new Grid(1, 3);
428+
trailGrid.setWidget(0, 0, new Label(Locale.LS("Trail Persistence")));
429+
trailBar = new Scrollbar(Scrollbar.HORIZONTAL, scope.trailLen, 1, 0, 51, new Command() {
430+
public void execute() {
431+
scope.trailLen = trailBar.getValue();
432+
setTrailLabel();
433+
}
434+
});
435+
trailGrid.setWidget(0, 1, trailBar);
436+
trailLabel = new Label("");
437+
trailGrid.setWidget(0, 2, trailLabel);
438+
fp.add(trailGrid);
439+
setTrailLabel();
427440
if (transistor) {
428441
addItemToGrid(grid, vceIcBox = new ScopeCheckBox(Locale.LS("Show Vce vs Ic"), "showvcevsic"));
429442
vceIcBox.addValueChangeHandler(this);
@@ -529,6 +542,13 @@ void updateRowVisibility() {
529542
void setScopeSpeedLabel() {
530543
scopeSpeedLabel.setText(CircuitElm.getUnitText(scope.calcGridStepX(), "s")+"/div");
531544
}
545+
546+
void setTrailLabel() {
547+
if (scope.trailLen >= 50)
548+
trailLabel.setText("max");
549+
else
550+
trailLabel.setText("" + scope.trailLen);
551+
}
532552

533553
void addItemToGrid(Grid g, FocusWidget scb) {
534554
g.setWidget(ny, nx, scb);

0 commit comments

Comments
 (0)