Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ac simulation #4076

Draft
wants to merge 45 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c00d04f
Update Bug.md
failiz Feb 5, 2024
0d58769
fixes bug with u simbol. The u symbol should be replaced by a u in ng…
failiz Mar 8, 2023
115ba42
split different options of the spice circuit in sepate lines. Added i…
failiz Mar 12, 2023
600a144
towards transitoty simulations (still work in progress)
failiz Mar 13, 2023
7de4eb7
allow to change properties of the pulse and sinusoidal power supplies
failiz Mar 22, 2023
46bccb8
modified resources.xml to adapt to the new moduleIDs of the new power…
failiz Sep 20, 2023
fc03cb0
added number of cycles as a property to change in pulse power supplies
failiz Sep 20, 2023
be8fac1
fixed bug in loop, removed setElementId to show all the svg elements
failiz Sep 20, 2023
93eedcc
working towards the aligning the oscilloscope signals with the oscill…
failiz Sep 29, 2023
8db3f72
towards multiple oscilloscope in the simulation. Added offset marks t…
failiz Oct 3, 2023
5c2924f
if the negative terminal of a power supply is not connected and there…
failiz Oct 3, 2023
00abb32
handles the 4 channels and adds noise to the signals if the com probe…
failiz Oct 3, 2023
1a4378a
better name for a property
failiz Oct 3, 2023
af0c891
improved oscilloscope in sch view and added axis labels
failiz Oct 4, 2023
a36833f
added axis scale in the oscilloscope for the sch view
failiz Oct 5, 2023
105fd7b
added control of the horizontal simulation to the analysis without lo…
failiz Oct 5, 2023
af0414f
added handling of horizontal positions for multiple oscilloscopes
failiz Oct 6, 2023
433a51a
allow to introduce negative numbers in the properties of parts. This …
failiz Oct 7, 2023
dab2f73
Show the result of the simulation at the end time, not at the startin…
failiz Oct 7, 2023
6e2c3db
In sch view, added the name of the axes to the oscilloscope.
failiz Oct 10, 2023
47ac612
updated position of the labels and axes in the oscilloscope when simu…
failiz Oct 10, 2023
af9e5f6
added properties for the triangular wavegenerator
failiz Oct 10, 2023
b848df3
added some examples for the transitory analysis
failiz Oct 10, 2023
8e1440a
added 555 example
failiz Dec 4, 2023
082736d
Finish wiring of the BB view of the 555 example
failiz Dec 5, 2023
8e7dcd3
added two sketches as examples of the transient simulation
failiz Dec 5, 2023
3ed26cb
fixed oscilloscope text string (too many arguments)
failiz Dec 7, 2023
bfbacda
fixed bug when properties contain capital letters. If so, the propert…
failiz Dec 7, 2023
d258cf5
imprved handling of properties with capital letters
failiz Dec 7, 2023
7684fbd
improved simulation error dialog. Now, there is a scroll area for the…
failiz Dec 7, 2023
a10fdb5
show the right channel in the axis of the oscilloscope (SCH view)
failiz Dec 7, 2023
ef83cec
added properties of the transformer so that users can modifiy its pro…
failiz Dec 7, 2023
3dead91
fixed error when a property does not have symbol: If there is no symb…
failiz Dec 7, 2023
bcf5960
calculate the com voltage at the oscilloscope when there is a wire co…
failiz Dec 8, 2023
cccd30e
First implementation using an animation of the results of the simulat…
failiz Dec 9, 2023
2701e68
now all parts get voltages and currents depending on simulation time
failiz Dec 10, 2023
5d3fc49
Revert elcos
Feb 13, 2024
560f296
Added functionality to set sim frequency and save it to project.
Dec 15, 2023
a70ce54
added the simulation properties to the project properties menu
failiz Feb 18, 2024
4fa9783
added simulator message (sim time) at the top right corner when simul…
failiz Feb 20, 2024
9eb7edb
Added sawtooth generator and modified properties of the triangular si…
failiz Apr 6, 2024
1712697
changed ac poewer supply to sine, added generic pulse power supply an…
failiz Apr 7, 2024
234eb9a
make the transitory simulation not blocking. Results will be shown wh…
failiz Apr 7, 2024
1c56001
added exception for the oscilloscope as it does not have a pcb view
failiz Apr 7, 2024
39b0dad
Load analog code model to have access to current limiting devices for…
failiz May 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/simulation/ngspice_simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ std::vector<double> NgSpiceSimulator::getVecInfo(const std::string& vecName) {

std::vector<double> realValues;
if (vecInfo->v_realdata) {
realValues.push_back(vecInfo->v_realdata[0]);
std::cout << "getVecInfo: data" << vecInfo->v_length << std::endl;
for(int i=0; i<vecInfo->v_length; i++)
realValues.push_back(vecInfo->v_realdata[i]);
return realValues;
}

Expand Down
146 changes: 145 additions & 1 deletion src/simulation/simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Simulator::Simulator(MainWindow *mainWindow) : QObject(mainWindow) {

QSettings settings;
int enabled = settings.value("simulatorEnabled", 0).toInt();
enable(enabled);
enable(true);
Copy link
Member

@KjellMorgenstern KjellMorgenstern Feb 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is now save to remove that setting entirely. If there are no objections, I'll to that for 1.0.3

While on it, will it make sense to have a switch between DC and transient analysis?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with removing the enable simulator setting.

m_simulating = false;

}
Expand Down Expand Up @@ -194,6 +194,21 @@ void Simulator::simulate() {
QSet<ItemBase *> itemBases;
QString spiceNetlist = m_mainWindow->getSpiceNetlist("Simulator Netlist", netList, itemBases);

//Select the type of analysis based on if there is an oscilloscope in the simulation
foreach (ItemBase * item, itemBases) {
if(item->family().toLower().contains("oscilloscope")) {
//TODO: Use TextUtils::convertFromPowerPrefixU function
double time_div = TextUtils::convertFromPowerPrefix(item->getProperty("time/div"), "s");
std::cout << "time/div: " << item->getProperty("time/div").toStdString() << " " << time_div << std::endl;
double maxSimTime = time_div * 10;
QString tranAnalysis = QString(".TRAN %1 %2").arg(maxSimTime/100).arg(maxSimTime);
spiceNetlist.replace(".OP", tranAnalysis);
//TODO: Handle several oscilloscopes
break;
}
}


Copy link
Member

@KjellMorgenstern KjellMorgenstern Feb 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably answers my above question.
However there might be many use cases for transient analysis without an oscilloscope.
We could add a little arrow on the play button, like many other buttons in the menu bar have:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as soon as there is an oscilloscope, the transitory analysis is used. Before the trans analysis only made sense with the oscilloscope as there was no animation. Since I added the animation, users could use the LEDs in the breadboard to see the effects on their circuits.
So, yes, maybe it is a good idea to add the trans analysis as an option in the simulate button. However, in that case, how do we specify the simulation time?
Could you implement the button? The last time I played with adding buttons took me ages to make it correctly.

std::cout << "Netlist: " << spiceNetlist.toStdString() << std::endl;

//std::cout << "-----------------------------------" <<std::endl;
Expand Down Expand Up @@ -307,6 +322,8 @@ void Simulator::simulate() {
}
std::cout << "No fatal error found, continuing..." <<std::endl;

m_simulator->command("bg_halt");

//The spice simulation has finished, iterate over each part being simulated and update it (if it is necessary).
//This loops is in charge of:
// * update the multimeters screen
Expand Down Expand Up @@ -358,6 +375,10 @@ void Simulator::simulate() {
updatePotentiometer(part);
continue;
}
if (family.contains("oscilloscope")) {
updateOscilloscope(part);
continue;
}


}
Expand Down Expand Up @@ -568,6 +589,46 @@ double Simulator::calculateVoltage(ConnectorItem * c0, ConnectorItem * c1) {
return volt0-volt1;
}

std::vector<double> Simulator::voltageVector(ConnectorItem * c0) {
int net0 = m_connector2netHash.value(c0);
std::cout << "calculateVoltageVector: ";
QString net0str = QString("v(%1)").arg(net0);

auto timeInfo = m_simulator->getVecInfo(QString("time").toStdString());
if (net0 != 0) {
std::cout << "calculateVoltageVector: ";
}
return m_simulator->getVecInfo(net0str.toStdString());
}

QString Simulator::generateSvgPath(std::vector<double> proveVector, std::vector<double> comVector, QString nameId, double v_div, double v_offset) {
std::cout << "VOLTAGE VALUES " << nameId.toStdString() << ": ";
QString svg;
if (!nameId.isEmpty())
svg += QString("<path id='%1' d='").arg(nameId);
else
svg += QString("<path d='");

double vScale = -12.5/v_div;
double y_0 = 50; // the center of the screen


for (int i = 0; std::min( proveVector.size(), comVector.size() ); i++) {
double voltage = proveVector[i] - comVector[i];
if (i == 0) {
svg.append("M 0 " + QString::number( (voltage + v_offset) * vScale + y_0, 'f', 3) + " ");
} else {
svg.append("L " + QString::number(i, 'f', 3) + " " + QString::number((voltage + v_offset) * vScale + y_0, 'f', 3) + " ");
}
std::cout << voltage << ' ';
}
svg += "' fill='red' stroke='black' stroke-width='10'/> \n";

std::cout << std::endl;
return svg;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this and other methods might be better placed in items/oscilloscope.cpp or similar.
So, we'd create an Oscilloscope class, that can display probe and comVectors ... in the future it could also hold code to act on buttons and knobs.

Is there a specific reason it has to be here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I also thought about that several times. I can create an oscilloscope item and move that code there.

}

/**
* Returns the symbol of a part´s property. It is needed to be able to remove the symbol from the value of the property.
* @param[in] part The part that has a property
Expand Down Expand Up @@ -1206,3 +1267,86 @@ void Simulator::updateMultimeter(ItemBase * part) {
return;
}
}

/**
* Updates and checks a oscilloscope. If the ground connection is not connected, plots a noisy signal.
* Calculates the parameter to measure and updates the display of the multimeter.
* @param[in] part An oscilloscope that is going to be checked and updated.
*/
void Simulator::updateOscilloscope(ItemBase * part) {
std::cout << "updateOscilloscope: " << std::endl;
QString nChannels = part->getProperty("channels").toLower();
ConnectorItem * comProbe = nullptr, * v1Probe = nullptr, * v2Probe = nullptr, * v3Probe = nullptr, * v4Probe = nullptr;
QList<ConnectorItem *> probes = part->cachedConnectorItems();
foreach(ConnectorItem * ci, probes) {
if(ci->connectorSharedName().toLower().compare("com probe") == 0) comProbe = ci;
if(ci->connectorSharedName().toLower().compare("v1 probe") == 0) v1Probe = ci;
if(ci->connectorSharedName().toLower().compare("v2 probe") == 0) v2Probe = ci;
if(ci->connectorSharedName().toLower().compare("v3 probe") == 0) v3Probe = ci;
if(ci->connectorSharedName().toLower().compare("v4 probe") == 0) v4Probe = ci;
}
if(!comProbe || !v1Probe || !v2Probe || !v3Probe || !v4Probe)
return;

if(!v1Probe->connectedToWires() && !v2Probe->connectedToWires() && !v3Probe->connectedToWires() && !v4Probe->connectedToWires()) {
std::cout << "Oscilloscope does not have any wire connected to the probe terminals. " << std::endl;
return;
}


if(comProbe->connectedToWires() && v1Probe->connectedToWires()) {
std::cout << "Oscilloscope probe v1 connected. " << std::endl;
auto v1 = voltageVector(v1Probe);
//auto vCom = voltageVector(comProbe);
std::vector<double> vCom(v1.size(), 0.0);

//TODO: use convertFromPowerPrefixU
double vols_div = TextUtils::convertFromPowerPrefix(part->getProperty("volts/div"), "V");
double v1_offset = TextUtils::convertFromPowerPrefix(part->getProperty("v1 offset"), "V");

QString svg = QString("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n%5"
"<svg xmlns:svg='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg' "
"version='1.2' baseProfile='tiny' "
"x='0in' y='0in' width='%1in' height='%2in' "
"viewBox='0 0 %3 %4' >\n"
)
.arg(125)
.arg(100)
.arg(125)
.arg(100)
.arg(TextUtils::CreatedWithFritzingXmlComment);
svg += generateSvgPath(v1, vCom, "v1-path", vols_div, v1_offset);
svg += "</svg>";

QGraphicsSvgItem * graph = new QGraphicsSvgItem(part);
QSvgRenderer *graphRender = new QSvgRenderer(svg.toUtf8());
if(graphRender->isValid())
std::cout << "SVG Graph is VALID " << std::endl;
else
std::cout << "SVG Graph is NOT VALID " << std::endl;
std::cout << "SVG: " << svg.toStdString() << std::endl;
graph->setSharedRenderer(graphRender);
graph->setElementId("graph");
graph->setZValue(std::numeric_limits<double>::max());

//There are issues as the size of the text changes depending on the display settings in windows
//This hack scales the text to match the appropiate value
QRectF schOscilloscopeBoundingBox = part->boundingRect();
QRectF schBoundingBox = graph->boundingRect();

//Set the text to be a 80% percent of the multimeter´s width and 50% in sch view
//graph->setScale((0.5*schOscilloscopeBoundingBox.width())/schBoundingBox.width());

//Update the boundiong box after scaling them
//schBoundingBox = graph->mapRectToParent(graph->boundingRect());

//Center the text
//graph->setPos(QPointF((schOscilloscopeBoundingBox.width()-schBoundingBox.width())/2
// ,0.13*schOscilloscopeBoundingBox.height()));
graph->setPos(QPointF(5.0,5.0));

part->addSimulationGraphicsItem(graph);

}

}
3 changes: 3 additions & 0 deletions src/simulation/simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public slots:
QString getSymbol(ItemBase*, QString);
double getVectorValueOrDefault(const std::string & vecName, double defaultValue);
double calculateVoltage(ConnectorItem *, ConnectorItem *);
std::vector<double> voltageVector(ConnectorItem *);
QString generateSvgPath(std::vector<double>, std::vector<double>, QString, double, double);
double getCurrent(ItemBase*, QString subpartName="");
double getTransistorCurrent(QString spicePartName, TransistorLeg leg);
double getPower(ItemBase*, QString subpartName="");
Expand All @@ -75,6 +77,7 @@ public slots:
void updateDiode(ItemBase *);
void updateLED(ItemBase *);
void updateMultimeter(ItemBase *);
void updateOscilloscope(ItemBase *);
void updateResistor(ItemBase *);
void updatePotentiometer(ItemBase *);
void updateDcMotor(ItemBase *);
Expand Down