From 6f55bd55e2ca86dac061b40ea6568bf6845c84df Mon Sep 17 00:00:00 2001 From: cjt Date: Sat, 4 Feb 2012 10:26:47 -0500 Subject: [PATCH] update to schematic tool --- js/cktsim.js | 88 +++++++++++++++++++++++++++++++++++++++++++++---- js/schematic.js | 74 ++++++++++++++++++++++++++++------------- 2 files changed, 134 insertions(+), 28 deletions(-) diff --git a/js/cktsim.js b/js/cktsim.js index 9a9fbe351f..cbbcb21a1d 100755 --- a/js/cktsim.js +++ b/js/cktsim.js @@ -97,9 +97,11 @@ cktsim = (function() { // load circuit from JSON netlist (see schematic.js) Circuit.prototype.load_netlist = function(netlist) { // set up mapping for ground node always called '0' in JSON netlist - this.node_map['0'] = this.gnd_node(); + var gnd_label = '0'; + this.node_map[gnd_label] = this.gnd_node(); // process each component in the JSON netlist (see schematic.js for format) + var found_ground = false; for (var i = netlist.length - 1; i >= 0; --i) { var component = netlist[i]; var type = component[0]; @@ -118,6 +120,7 @@ cktsim = (function() { var connections = component[3]; for (var j = connections.length - 1; j >= 0; --j) { var node = connections[j]; + if(node == gnd_label) found_ground = true; var index = this.node_map[node]; if (index == undefined) index = this.node(node,T_VOLTAGE); connections[j] = index; @@ -137,12 +140,18 @@ cktsim = (function() { else if (type == 'i') // current source this.i(connections[0],connections[1],properties['value'],name); else if (type == 'o') // op amp - this.opamp(connections[0],connections[1],connections[2],properties['A'],name); + this.opamp(connections[0],connections[1],connections[2],connections[3],properties['A'],name); else if (type == 'n') // n fet this.n(connections[0],connections[1],connections[2],properties['W/L'],name); else if (type == 'p') // p fet this.p(connections[0],connections[1],connections[2],properties['W/L'],name); } + if(found_ground == false) { // No ground on schematic + alert('Please make at least one connection to ground (inverted T symbol)'); + return false; + } + return true; + } // if converges: updates this.solution, this.soln_max, returns iter count @@ -187,6 +196,8 @@ cktsim = (function() { // DC analysis Circuit.prototype.dc = function() { + + // Allocation matrices for linear part, etc. this.finalize(); // Load up the linear part. @@ -624,6 +635,17 @@ cktsim = (function() { return this.add_device(d, name); } + Circuit.prototype.opamp = function(np,nn,no,ng,A,name) { + // try to convert string value into numeric value, barf if we can't + if ((typeof A) == 'string') { + ratio = parse_number(A,undefined); + if (A === undefined) return undefined; + } + var branch = this.node(undefined,T_CURRENT); + var d = new Opamp(np,nn,no,ng,branch,A,name); + return this.add_device(d, name); + } + Circuit.prototype.n = function(d,g,s, ratio, name) { // try to convert string value into numeric value, barf if we can't if ((typeof ratio) == 'string') { @@ -1090,6 +1112,15 @@ cktsim = (function() { src.value = function(t) { return v; } // closure } + // post-processing for impulse sources + // impulse(height,width) + else if (src.fun == 'impulse') { + var h = arg_value(src.args,0,1); // default height: 1 + var w = Math.abs(arg_value(src.args,2,1e-9)); // default width: 1ns + src.args = [h,w]; // remember any defaulted values + pwl_source(src,[0,0,w/2,h,w,0],false); + } + // post-processing for step sources // step(v_init,v_plateau,t_delay,t_rise) else if (src.fun == 'step') { @@ -1263,8 +1294,8 @@ cktsim = (function() { // MNA stamp for independent voltage source ckt.add_to_Gl(this.branch,this.npos,1.0); ckt.add_to_Gl(this.branch,this.nneg,-1.0); - ckt.add_to_Gl(this.npos,this.branch,1.0); - ckt.add_to_Gl(this.nneg,this.branch,-1.0); + ckt.add_to_Gl(this.npos,this.branch,-1.0); + ckt.add_to_Gl(this.nneg,this.branch,1.0); } // Source voltage added to b. @@ -1448,9 +1479,9 @@ cktsim = (function() { // MNA stamp for inductor linear part // L on diag of C because L di/dt = v(n1) - v(n2) ckt.add_to_Gl(this.n1,this.branch,1); - ckt.add_to_Gl(this.branch,this.n1,1); + ckt.add_to_Gl(this.branch,this.n1,-1); ckt.add_to_Gl(this.n2,this.branch,-1); - ckt.add_to_Gl(this.branch,this.n2,-1); + ckt.add_to_Gl(this.branch,this.n2,1); ckt.add_to_C(this.branch,this.branch,this.value) } @@ -1464,6 +1495,51 @@ cktsim = (function() { Inductor.prototype.load_tran = function(ckt) { } + + + /////////////////////////////////////////////////////////////////////////////// + // + // Simple Voltage-Controlled Voltage Source Op Amp model + // + /////////////////////////////////////////////////////////////////////////////// + + function Opamp(np,nn,no,ng,branch,A,name) { + Device.call(this); + this.np = np; + this.nn = nn; + this.no = no; + this.ng = ng; + this.branch = branch; + this.gain = A; + this.name = name; + } + + Opamp.prototype = new Device(); + Opamp.prototype.constructor = Opamp; + + Opamp.prototype.load_linear = function(ckt) { + // MNA stamp for VCVS: 1/A(v(no) - v(ng)) - (v(np)-v(nn))) = 0. + var invA = 1.0/this.gain; + ckt.add_to_Gl(this.no,this.branch,1); + ckt.add_to_Gl(this.ng,this.branch,-1); + ckt.add_to_Gl(this.branch,this.no,-invA); + ckt.add_to_Gl(this.branch,this.ng,invA); + ckt.add_to_Gl(this.branch,this.np,1); + ckt.add_to_Gl(this.branch,this.nn,-1); + } + + Opamp.prototype.load_dc = function(ckt,soln,rhs) { + // Op-amp is linear. + } + + Opamp.prototype.load_ac = function(ckt) { + } + + Opamp.prototype.load_tran = function(ckt) { + } + + + /////////////////////////////////////////////////////////////////////////////// // // Simplified MOS FET with no bulk connection and no body effect. diff --git a/js/schematic.js b/js/schematic.js index 15997a4988..af694040c6 100644 --- a/js/schematic.js +++ b/js/schematic.js @@ -634,9 +634,10 @@ schematic = (function() { // create a circuit from the netlist var ckt = new cktsim.Circuit(); - ckt.load_netlist(netlist); - - return ckt; + if (ckt.load_netlist(netlist)) + return ckt; + else + return null; } Schematic.prototype.dc_analysis = function() { @@ -645,6 +646,7 @@ schematic = (function() { this.redraw_background(); var ckt = this.extract_circuit(); + if (ckt === null) return; // run the analysis this.operating_point = ckt.dc(); @@ -712,6 +714,7 @@ schematic = (function() { Schematic.prototype.ac_analysis = function(npts,fstart,fstop,ac_source_name) { // run the analysis var ckt = this.extract_circuit(); + if (ckt === null) return; var results = ckt.ac(npts,fstart,fstop,ac_source_name); // save a copy of the results for submission @@ -731,11 +734,28 @@ schematic = (function() { var y_values = []; // list of [color, result_array] var probes = this.find_probes(); + var probe_maxv = []; + var probe_color = []; + + // Check for proble with near zero transfer function and warn + for (var i = probes.length - 1; i >= 0; --i) { + probe_color[i] = probes[i][0]; + var label = probes[i][1]; + var v = results[label]; + probe_maxv[i] = array_max(v); // magnitudes always > 0 + } + var all_max = array_max(probe_maxv); + for (var i = probes.length - 1; i >= 0; --i) { + if ((probe_maxv[i] / all_max) < 1.0e-10) { + alert('Near zero ac response, remove ' + probe_color[i] + ' probe'); + return; + } + } + for (var i = probes.length - 1; i >= 0; --i) { var color = probes[i][0]; var label = probes[i][1]; var v = results[label]; - // convert values into dB relative to source amplitude var v_max = 1; for (var j = v.length - 1; j >= 0; --j) @@ -774,6 +794,8 @@ schematic = (function() { this.dialog('Transient Analysis',content,function(content) { var sch = content.sch; var ckt = sch.extract_circuit(); + if (ckt === null) return; + // retrieve parameters, remember for next time sch.tran_npts = content.fields[npts_lbl].value; @@ -1943,25 +1965,27 @@ schematic = (function() { var x1 = (index == 0) ? x_values[0] : x_values[index-1]; var x2 = x_values[index]; - // for each plot, interpolate and output value at intersection with marker - c.textAlign = 'left'; - var tx = graph.left_margin + 4; - var ty = graph.top_margin; - for (var plot = 0; plot < graph.y_values.length; plot++) { - var values = graph.y_values[plot][1]; + if (x2 != undefined) { + // for each plot, interpolate and output value at intersection with marker + c.textAlign = 'left'; + var tx = graph.left_margin + 4; + var ty = graph.top_margin; + for (var plot = 0; plot < graph.y_values.length; plot++) { + var values = graph.y_values[plot][1]; - // interpolate signal value at graph_x using values[index-1] and values[index] - var y1 = (index == 0) ? values[0] : values[index-1]; - var y2 = values[index]; - var y = y1; - if (graph_x != x1) y += (graph_x - x1)*(y2 - y1)/(x2 - x1); + // interpolate signal value at graph_x using values[index-1] and values[index] + var y1 = (index == 0) ? values[0] : values[index-1]; + var y2 = values[index]; + var y = y1; + if (graph_x != x1) y += (graph_x - x1)*(y2 - y1)/(x2 - x1); - // annotate plot with value of signal at marker - c.fillStyle = element_style; - c.fillText('\u2588\u2588\u2588\u2588\u2588',tx-3,ty); - c.fillStyle = probe_colors_rgb[graph.y_values[plot][0]]; - c.fillText(engineering_notation(y,3,false),tx,ty); - ty += 14; + // annotate plot with value of signal at marker + c.fillStyle = element_style; + c.fillText('\u2588\u2588\u2588\u2588\u2588',tx-3,ty); + c.fillStyle = probe_colors_rgb[graph.y_values[plot][0]]; + c.fillText(engineering_notation(y,3,false),tx,ty); + ty += 14; + } } } } @@ -3086,7 +3110,8 @@ schematic = (function() { this.add_connection(0,0); // + this.add_connection(0,16); // - this.add_connection(48,8); // output - this.bounding_box = [0,-8,48,24]; + this.add_connection(24,32); // ground + this.bounding_box = [0,-8,48,32]; this.update_coords(); } OpAmp.prototype = new Component(); @@ -3104,7 +3129,9 @@ schematic = (function() { // inputs and output this.draw_line(c,0,0,8,0); this.draw_line(c,0,16,8,16); + this.draw_text(c,'gnd',37,18,property_size); this.draw_line(c,40,8,48,8); + this.draw_line(c,24,16,24,32); // + and - this.draw_line(c,10,0,16,0); this.draw_line(c,13,-3,13,3); @@ -3176,6 +3203,9 @@ schematic = (function() { source_functions = { 'dc': ['DC value'], + 'impulse': ['Height', + 'Width (secs)'], + 'step': ['Initial value', 'Plateau value', 'Delay until step (secs)',