/// <summary> /// Configures submatrices so that <paramref name="opAmp"/> is considered to work in saturation, however output remains at 0 - op-amps have /// to be manually activated, for example using <see cref="ActivateSaturatedOpAmp(AdmittanceMatrix, IOpAmpDescription)"/>. /// </summary> /// <param name="matrix"></param> /// <param name="opAmp"></param> private void ConfigureOpAmpForSaturation(AdmittanceMatrix matrix, IOpAmpDescription opAmp) { // Indices of op-amps nodes var nodes = _OpAmpsNodes[opAmp]; // And its index var index = _IndexedComponentsIndices[opAmp]; // If the non-inverting input is not grounded, reset its entry in the _C matrix if (nodes.NonInvertingInput != ReferenceNode) { matrix._C[index, nodes.NonInvertingInput] = 0; } // If the inverting input is not grounded, reset its entry in the _C matrix if (nodes.InvertingInput != ReferenceNode) { matrix._C[index, nodes.InvertingInput] = 0; } // And the entry in _C corresponding to the output node to 1 // It is important that, when non-inverting input is connected directly to the output, the entry in _B // corresponding to that node is 1 (and not 0 like the if above would set it). Because this assigning is done after // the one for non-inverting input no special conditions are necessary however it's very important to remeber about // it if (when) this method is modified matrix._C[index, nodes.Output] = 1; }
/// <summary> /// Configures submatrices so that <paramref name="opAmp"/> is considered to work in active operation (output is between /// supply voltages) /// </summary> /// <param name="matrix"></param> /// <param name="opAmp"></param> private void ConfigureOpAmpForActiveOperation(AdmittanceMatrix matrix, IOpAmpDescription opAmp) { // Get nodes of the op-amp var nodes = _OpAmpsNodes[opAmp]; // As well as its index var index = _IndexedComponentsIndices[opAmp]; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // Very important: in case the non-inverting input and output are short-circuited (they are the same node) then // // the value of 1 should be entered into the corresponding cell in _C array. This comes from the fact that having // // -OpenLoopGain and OpenLoogGain in the _C in the same row enforces potentials at both inputs to be equal. However // // if the non-inverting input is shorted then the OpenLoopGain value has no place in _C and 1 from the output should // // be put there. // // If the inverting input is shorted with the output then the OpenLoopGain should be put int the corresponding cell // // instead of 1. That's because the initial assumption that V+ = V- is made and if Vout = V- (the same node) then // // The OpenLoopGain has to be used to guarantee both voltages will be equal // // (so matrix looks like : ... -k ... k ... | 0 which boils down to kV- = kV+ which means V- = V+) // // That's why it is very important that if (when) this method is modified the rules presented above are obeyed. // // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // If there exists a node to which TerminalA (non-inverting input) is connected // (it's possible it may not exist due to removed ground node) if (nodes.NonInvertingInput != ReferenceNode) { // Fill the entry in the row corresponding to the op-amp (plus starting row) // and column corresponding to the node (positive terminal) with -OpenLoopGain matrix._C[index, nodes.NonInvertingInput] = -opAmp.OpenLoopGain; } // If there exists a node to which TerminalB (inverting input) is connected // (it's possible it may not exist due to removed ground node) if (nodes.InvertingInput != ReferenceNode) { // Fill the entry in the row corresponding to the op-amp (plus starting row) // and column corresponding to the node (positive terminal) with OpenLoopGain matrix._C[index, nodes.InvertingInput] = opAmp.OpenLoopGain; } // If the output is not shorted with the inverting input if (nodes.Output != nodes.InvertingInput) { matrix._C[index, nodes.Output] = 1; } // Fill the entry in the row corresponding to the op-amp (plus starting row) // and column corresponding to the node (positive terminal) with 1 matrix._E[index] = 0; }
/// <summary> /// Configures <see cref="IOpAmp"/> for operation in <paramref name="operationMode"/> in <paramref name="matrix"/>. Saturated op-amps remain /// inactive after this call - they will appear to be saturated but saturation voltage will be equal to 0. Op-amps have to be manually /// activated, for example using <see cref="ActivateSaturatedOpAmps(AdmittanceMatrix)"/> /// </summary> /// <param name="matrix"></param> /// <param name="opAmpIndex"></param> /// <param name="operationMode"></param> private void ConfigureOpAmpOperation(AdmittanceMatrix matrix, IOpAmpDescription opAmp, OpAmpOperationMode operationMode) { // Call appropriate method based on operation mode switch (_OpAmpOperation[opAmp]) { case OpAmpOperationMode.Active: { ConfigureOpAmpForActiveOperation(matrix, opAmp); } break; case OpAmpOperationMode.PositiveSaturation: case OpAmpOperationMode.NegativeSaturation: { ConfigureOpAmpForSaturation(matrix, opAmp); } break; default: { throw new Exception("Unhandled case"); } } }
/// <summary> /// Activates saturated op-amp - modifies matrix E with saturation voltage in entry corresponding to that op-amp. Does not check if /// the op-amp is in fact saturated - assumes that caller checked that. /// </summary> /// <param name="matrix"></param> /// <param name="opAmp"></param> private void ActivateSaturatedOpAmp(AdmittanceMatrix matrix, IOpAmpDescription opAmp) => // Depending on which supply was exceeded, set the value of the op-amp output to either positive or negative // supply voltage, depending on saturaiton (if determined that it is in either saturation). // Modify entry in E matrix corresponding to index of the op-amp. matrix._E[_IndexedComponentsIndices[opAmp]] = _OpAmpOperation[opAmp] == OpAmpOperationMode.PositiveSaturation ? opAmp.PositiveSupplyVoltage : opAmp.NegativeSupplyVoltage;