mirror of
https://github.com/ra3xdh/qucs_s
synced 2025-03-28 21:13:26 +00:00
412 lines
12 KiB
C++
412 lines
12 KiB
C++
/***************************************************************************
|
|
libcomp.cpp
|
|
-------------
|
|
begin : Fri Jun 10 2005
|
|
copyright : (C) 2005 by Michael Margraf
|
|
email : michael.margraf@alumni.tu-berlin.de
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "libcomp.h"
|
|
#include "main.h"
|
|
#include "misc.h"
|
|
#include "node.h"
|
|
#include "extsimkernels/qucs2spice.h"
|
|
#include "extsimkernels/spicecompat.h"
|
|
|
|
|
|
#include <QTextStream>
|
|
#include <QDir>
|
|
#include <QRegularExpression>
|
|
#include <QDebug>
|
|
|
|
LibComp::LibComp()
|
|
{
|
|
Type = isComponent; // both analog and digital
|
|
Description = QObject::tr("Component taken from Qucs library");
|
|
|
|
Ports.append(new Port(0, 0)); // dummy port because of being device
|
|
|
|
Model = "Lib";
|
|
Name = "X";
|
|
SpiceModel = "X";
|
|
|
|
Props.append(new Property("Lib", "", true,
|
|
QObject::tr("name of qucs library file")));
|
|
Props.append(new Property("Comp", "", true,
|
|
QObject::tr("name of component in library")));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
Component* LibComp::newOne()
|
|
{
|
|
LibComp *p = new LibComp();
|
|
p->Props.first()->Value = Props.first()->Value;
|
|
p->Props.next()->Value = Props.next()->Value;
|
|
p->recreate(0);
|
|
return p;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Makes the schematic symbol subcircuit with the correct number
|
|
// of ports.
|
|
void LibComp::createSymbol()
|
|
{
|
|
tx = INT_MIN;
|
|
ty = INT_MIN;
|
|
if(loadSymbol() > 0) {
|
|
if(tx == INT_MIN) tx = x1+4;
|
|
if(ty == INT_MIN) ty = y2+4;
|
|
}
|
|
else {
|
|
// only paint a rectangle
|
|
Lines.append(new qucs::Line(-15, -15, 15, -15, QPen(Qt::darkBlue,2)));
|
|
Lines.append(new qucs::Line( 15, -15, 15, 15, QPen(Qt::darkBlue,2)));
|
|
Lines.append(new qucs::Line(-15, 15, 15, 15, QPen(Qt::darkBlue,2)));
|
|
Lines.append(new qucs::Line(-15, -15,-15, 15, QPen(Qt::darkBlue,2)));
|
|
|
|
x1 = -18; y1 = -18;
|
|
x2 = 18; y2 = 18;
|
|
|
|
tx = x1+4;
|
|
ty = y2+4;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Loads the section with name "Name" from library file into "Section".
|
|
int LibComp::loadSection(const QString& Name, QString& Section,
|
|
QStringList *Includes, QStringList *Attach)
|
|
{
|
|
QDir Directory(QucsSettings.LibDir);
|
|
QFile file(misc::properAbsFileName(Directory.absoluteFilePath(Props.first()->Value + ".lib"), containingSchematic));
|
|
if(!file.open(QIODevice::ReadOnly))
|
|
return -1;
|
|
|
|
QString libDefaultSymbol;
|
|
|
|
QTextStream ReadWhole(&file);
|
|
Section = ReadWhole.readAll();
|
|
file.close();
|
|
|
|
|
|
if(Section.left(14) != "<Qucs Library ") // wrong file type ?
|
|
return -2;
|
|
|
|
int Start, End = Section.indexOf(' ', 14);
|
|
if(End < 15) return -3;
|
|
QString Line = Section.mid(14, End-14); // extract version string
|
|
VersionTriplet LibVersion = VersionTriplet(Line);
|
|
if (LibVersion > QucsVersion) {// wrong version number ?
|
|
if (!QucsSettings.IgnoreFutureVersion) {
|
|
return -3;
|
|
}
|
|
}
|
|
|
|
if(Name == "Symbol") {
|
|
Start = Section.indexOf("\n<", 14); // library has default symbol
|
|
if(Start > 0)
|
|
if(Section.mid(Start+2, 14) == "DefaultSymbol>") {
|
|
Start += 16;
|
|
End = Section.indexOf("\n</DefaultSymbol>", Start);
|
|
if(End < 0) return -9;
|
|
libDefaultSymbol = Section.mid(Start, End-Start);
|
|
}
|
|
}
|
|
|
|
// search component
|
|
Line = "\n<Component " + Props.next()->Value + ">";
|
|
Start = Section.indexOf(Line);
|
|
if(Start < 0) return -4; // component not found
|
|
Start = Section.indexOf('\n', Start);
|
|
if(Start < 0) return -5; // file corrupt
|
|
Start++;
|
|
End = Section.indexOf("\n</Component>", Start);
|
|
if(End < 0) return -6; // file corrupt
|
|
Section = Section.mid(Start, End-Start+1);
|
|
|
|
// search model includes
|
|
if(Includes) {
|
|
int StartI, EndI;
|
|
StartI = Section.indexOf("<"+Name+"Includes");
|
|
if(StartI >= 0) { // includes found
|
|
StartI = Section.indexOf('"', StartI);
|
|
if(StartI < 0) return -10; // file corrupt
|
|
EndI = Section.indexOf('>', StartI);
|
|
if(EndI < 0) return -11; // file corrupt
|
|
StartI++; EndI--;
|
|
QString inc = Section.mid(StartI, EndI-StartI);
|
|
QStringList f = inc.split(QRegularExpression("\"\\s+\""));
|
|
for(QStringList::Iterator it = f.begin(); it != f.end(); ++it ) {
|
|
Includes->append(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
// search attached files
|
|
if(Attach) {
|
|
int StartI, EndI;
|
|
StartI = Section.indexOf("<"+Name+"Attach");
|
|
if(StartI >= 0) { // includes found
|
|
StartI = Section.indexOf('"', StartI);
|
|
if(StartI < 0) return -10; // file corrupt
|
|
EndI = Section.indexOf('>', StartI);
|
|
if(EndI < 0) return -11; // file corrupt
|
|
StartI++; EndI--;
|
|
QString inc = Section.mid(StartI, EndI-StartI);
|
|
QStringList f = inc.split(QRegularExpression("\"\\s+\""));
|
|
for(QStringList::Iterator it = f.begin(); it != f.end(); ++it ) {
|
|
Attach->append(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
// search model
|
|
Start = Section.indexOf("<"+Name+">");
|
|
if(Start < 0) {
|
|
if((Name == "Symbol") && (!libDefaultSymbol.isEmpty())) {
|
|
// component does not define its own symbol but the library defines a default symbol
|
|
Section = libDefaultSymbol;
|
|
return 0;
|
|
} else {
|
|
return -7; // symbol not found
|
|
}
|
|
}
|
|
Start = Section.indexOf('\n', Start);
|
|
if(Start < 0) return -8; // file corrupt
|
|
while(Section.at(++Start) == ' ') ;
|
|
End = Section.indexOf("</"+Name+">", Start);
|
|
if(End < 0) return -9; // file corrupt
|
|
|
|
// snip actual model
|
|
Section = Section.mid(Start, End-Start);
|
|
return 0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Loads the symbol for the subcircuit from the schematic file and
|
|
// returns the number of painting elements.
|
|
int LibComp::loadSymbol()
|
|
{
|
|
int z, Result;
|
|
QString FileString, Line;
|
|
z = loadSection("Symbol", FileString);
|
|
if(z < 0) {
|
|
if(z != -7) return z;
|
|
|
|
// If library component not defined as subcircuit, then load
|
|
// new component and transfer data to this component.
|
|
z = loadSection("Model", Line);
|
|
if(z < 0) return z;
|
|
|
|
Component *pc = getComponentFromName(Line);
|
|
if(pc == 0) return -20;
|
|
|
|
copyComponent(pc);
|
|
|
|
pc->Props.setAutoDelete(false);
|
|
delete pc;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
z = 0;
|
|
x1 = y1 = INT_MAX;
|
|
x2 = y2 = INT_MIN;
|
|
|
|
QTextStream stream(&FileString, QIODevice::ReadOnly);
|
|
while(!stream.atEnd()) {
|
|
Line = stream.readLine();
|
|
Line = Line.trimmed();
|
|
if(Line.isEmpty()) continue;
|
|
if(Line.at(0) != '<') return -11;
|
|
if(Line.at(Line.length()-1) != '>') return -12;
|
|
Line = Line.mid(1, Line.length()-2); // cut off start and end character
|
|
Result = analyseLine(Line, 2);
|
|
if(Result < 0) return -13; // line format error
|
|
z += Result;
|
|
}
|
|
|
|
x1 -= 4; x2 += 4; // enlarge component boundings a little
|
|
y1 -= 4; y2 += 4;
|
|
return z; // return number of ports
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
QString LibComp::getSubcircuitFile()
|
|
{
|
|
QDir Directory(QucsSettings.LibDir);
|
|
QString FileName = misc::properAbsFileName(Directory.absoluteFilePath(Props.first()->Value) + ".lib");
|
|
FileName.chop(4);
|
|
return FileName;
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
bool LibComp::createSubNetlist(QTextStream *stream, QStringList &FileList,
|
|
int type)
|
|
{
|
|
int r = -1;
|
|
QString FileString;
|
|
QStringList Includes;
|
|
if(type&1) {
|
|
r = loadSection("Model", FileString, &Includes);
|
|
} else if(type&2) {
|
|
r = loadSection("VHDLModel", FileString, &Includes);
|
|
} else if(type&4) {
|
|
r = loadSection("VerilogModel", FileString, &Includes);
|
|
} else if(type&8) {
|
|
r = loadSection("Spice",FileString, &Includes);
|
|
if (r<0) {
|
|
r = loadSection("Model", FileString, &Includes); // Ngspice
|
|
FileString = qucs2spice::convert_netlist(FileString);
|
|
}
|
|
} else if (type&16) {
|
|
r = loadSection("Spice",FileString, &Includes);
|
|
if (r<0) {
|
|
r = loadSection("Model", FileString, &Includes); // Ngspice
|
|
FileString = qucs2spice::convert_netlist(FileString,true);
|
|
}
|
|
}
|
|
if(r < 0) return false;
|
|
|
|
// also include files
|
|
int error = 0;
|
|
for(QStringList::Iterator it = Includes.begin();
|
|
it != Includes.end(); ++it ) {
|
|
QString s = getSubcircuitFile()+"/"+*it;
|
|
if(FileList.indexOf(s) >= 0) continue;
|
|
FileList.append(s);
|
|
|
|
// load file and stuff into stream
|
|
QFile file(s);
|
|
if(!file.open(QIODevice::ReadOnly)) {
|
|
error++;
|
|
} else {
|
|
QByteArray FileContent = file.readAll();
|
|
file.close();
|
|
//?stream->writeRawBytes(FileContent.data(), FileContent.size());
|
|
(*stream) << FileContent.data();
|
|
qDebug() << "hi from libcomp";
|
|
}
|
|
}
|
|
|
|
(*stream) << "\n" << FileString << "\n";
|
|
return error > 0 ? false : true;
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
QString LibComp::createType()
|
|
{
|
|
QString Type = misc::properFileName(Props.first()->Value);
|
|
return misc::properName(Type + "_" + Props.next()->Value);
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
QString LibComp::netlist()
|
|
{
|
|
QString s = "Sub:"+Name; // output as subcircuit
|
|
|
|
// output all node names
|
|
for (Port *p1 : Ports)
|
|
s += " "+p1->Connection->Name; // node names
|
|
|
|
// output property
|
|
s += " Type=\""+createType()+"\""; // type for subcircuit
|
|
|
|
// output user defined parameters
|
|
for(Property *pp = Props.at(2); pp != 0; pp = Props.next())
|
|
s += " "+pp->Name+"=\""+pp->Value+"\"";
|
|
|
|
return s + '\n';
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
QString LibComp::verilogCode(int)
|
|
{
|
|
QString s = " Sub_" + createType() + " " + Name + " (";
|
|
|
|
// output all node names
|
|
QListIterator<Port *> iport(Ports);
|
|
Port *pp = iport.next();
|
|
if(pp) s += pp->Connection->Name;
|
|
while (iport.hasNext()) {
|
|
pp = iport.next();
|
|
s += ", "+pp->Connection->Name; // node names
|
|
}
|
|
|
|
s += ");\n";
|
|
return s;
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
QString LibComp::vhdlCode(int)
|
|
{
|
|
QString s = " " + Name + ": entity Sub_" + createType() + " port map (";
|
|
|
|
// output all node names
|
|
QListIterator<Port *> iport(Ports);
|
|
Port *pp = iport.next();
|
|
if(pp) s += pp->Connection->Name;
|
|
while (iport.hasNext()) {
|
|
pp = iport.next();
|
|
s += ", "+pp->Connection->Name; // node names
|
|
}
|
|
|
|
s += ");\n";
|
|
return s;
|
|
}
|
|
|
|
QString LibComp::spice_netlist(bool)
|
|
{
|
|
QString s = SpiceModel + Name + " 0 "; // connect ground of subckt to circuit ground
|
|
for (Port *p1 : Ports)
|
|
s += " "+p1->Connection->Name; // node names
|
|
s += " " + createType();
|
|
|
|
// output user defined parameters
|
|
for(Property *pp = Props.at(2); pp != 0; pp = Props.next()) {
|
|
QString val = spicecompat::normalize_value(pp->Value);
|
|
s += " "+pp->Name+"="+val;
|
|
}
|
|
s +="\n";
|
|
|
|
return s;
|
|
}
|
|
|
|
QStringList LibComp::getAttachedIFS()
|
|
{
|
|
QString content;
|
|
QStringList includes,attach,ifs_lst;
|
|
ifs_lst.clear();
|
|
|
|
int r = loadSection("Spice",content,&includes,&attach);
|
|
if (r<0) return ifs_lst;
|
|
for (const QString& file : attach) {
|
|
if (file.endsWith(".ifs")) ifs_lst.append(getSubcircuitFile()+'/'+file);
|
|
}
|
|
return ifs_lst;
|
|
}
|
|
|
|
QStringList LibComp::getAttachedMOD()
|
|
{
|
|
QString content;
|
|
QStringList includes,attach,mod_lst;
|
|
mod_lst.clear();
|
|
|
|
int r = loadSection("Spice",content,&includes,&attach);
|
|
if (r<0) return mod_lst;
|
|
for (const QString& file : attach) {
|
|
if (file.endsWith(".mod")) mod_lst.append(getSubcircuitFile()+'/'+file);
|
|
}
|
|
return mod_lst;
|
|
}
|