# ========================================================================= # FILE: kicadtoNgspice.py # # USAGE: --- # # DESCRIPTION: This define all configuration used in Application. # # OPTIONS: --- # REQUIREMENTS: --- # BUGS: --- # NOTES: --- # AUTHOR: Fahim Khan, fahim.elex@gmail.com # MODIFIED: Rahul Paknikar, rahulp@iitb.ac.in # ORGANIZATION: eSim team at FOSSEE, IIT Bombay. # CREATED: Wednesday 04 March 2015 # REVISION: Friday 14 February 2020 # ========================================================================= import sys import os from PyQt4 import QtGui from .Processing import PrcocessNetlist from . import Analysis from . import Source from . import Model from . import DeviceModel from . import SubcircuitTab from . import Convert from . import TrackWidget import json class MainWindow(QtGui.QWidget): """ - This class create KicadtoNgspice window. - And Call Convert function if convert button is pressed. - The convert function takes all the value entered by user and create a final netlist "*.cir.out". - This final netlist is compatible with Ngspice. - clarg1 is the path to the .cir file - clarg2 is either None or "sub" depending on the analysis type """ def __init__(self, clarg1, clarg2=None): QtGui.QWidget.__init__(self) print("==================================") print("Kicad to Ngspice netlist converter ") print("==================================") global kicadNetlist, schematicInfo global infoline, optionInfo self.kicadFile = clarg1 self.clarg1 = clarg1 self.clarg2 = clarg2 # Create object of track widget # Track the dynamically created widget of KicadtoNgspice Window self.obj_track = TrackWidget.TrackWidget() # Clear Dictionary/List item of sub circuit and Ngspice model # Dictionary self.obj_track.subcircuitList.clear() self.obj_track.subcircuitTrack.clear() self.obj_track.model_entry_var.clear() # List self.obj_track.modelTrack[:] = [] # Object of Processing obj_proc = PrcocessNetlist() # Read the netlist, ie the .cir file kicadNetlist = obj_proc.readNetlist(self.kicadFile) # print("=============================================================") # print("Given Kicad Schematic Netlist Info :", kicadNetlist) # Construct parameter information param = obj_proc.readParamInfo(kicadNetlist) # Replace parameter with values netlist, infoline = obj_proc.preprocessNetlist(kicadNetlist, param) # print("=============================================================") # print("Schematic Info after processing Kicad Netlist: ", netlist) # Separate option and schematic information optionInfo, schematicInfo = obj_proc.separateNetlistInfo(netlist) # print("=============================================================") # print("OPTIONINFO in the Netlist", optionInfo) # List for storing source and its value global sourcelist, sourcelisttrack sourcelist = [] sourcelisttrack = [] schematicInfo, sourcelist = obj_proc.insertSpecialSourceParam( schematicInfo, sourcelist) # List storing model detail global modelList, outputOption,\ unknownModelList, multipleModelList, plotText modelList = [] outputOption = [] plotText = [] ( schematicInfo, outputOption, modelList, unknownModelList, multipleModelList, plotText ) = obj_proc.convertICintoBasicBlocks( schematicInfo, outputOption, modelList, plotText ) # print("=======================================") # print("Model available in the Schematic :", modelList) """ - Checking if any unknown model is used in schematic which is not recognized by NgSpice. - Also if the two model of same name is present under modelParamXML directory """ if unknownModelList: print("Unknown Model List is : ", unknownModelList) self.msg = QtGui.QErrorMessage() self.msg.setModal(True) self.msg.setWindowTitle("Unknown Models") self.content = "Your schematic contain unknown model " + \ ', '.join(unknownModelList) self.msg.showMessage(self.content) self.msg.exec_() elif multipleModelList: self.msg = QtGui.QErrorMessage() self.msg.setModal(True) self.msg.setWindowTitle("Multiple Models") self.mcontent = "Look like you have duplicate model in \ modelParamXML directory " + \ ', '.join(multipleModelList[0]) self.msg.showMessage(self.mcontent) self.msg.exec_() else: self.createMainWindow() def createMainWindow(self): """ - This function create main window of KiCad to Ngspice converter - Two components - createcreateConvertWidget - Convert button => callConvert """ self.vbox = QtGui.QVBoxLayout() self.hbox = QtGui.QHBoxLayout() self.hbox.addStretch(1) self.convertbtn = QtGui.QPushButton("Convert") self.convertbtn.clicked.connect(self.callConvert) self.hbox.addWidget(self.convertbtn) self.vbox.addWidget(self.createcreateConvertWidget()) self.vbox.addLayout(self.hbox) self.setLayout(self.vbox) self.setWindowTitle("Kicad To NgSpice Converter") self.show() def createcreateConvertWidget(self): """ - Contains the tabs for various convertor elements - Analysis => obj_analysis => Analysis.Analysis(`path_to_projFile`) - Source Details => obj_source => Source.Source(`sourcelist`,`sourcelisttrack`,`path_to_projFile`) - NgSpice Model => obj_model => Model.Model(`schematicInfo`,`modelList`,`path_to_projFile`) - Device Modelling => obj_devicemodel => DeviceModel.DeviceModel(`schematicInfo`,`path_to_projFile`) - Subcircuits => obj_subcircuitTab => SubcircuitTab.SubcircuitTab(`schematicInfo`,`path_to_projFile`) - Finally pass each of these objects, to widgets - convertWindow > mainLayout > tabWidgets > AnalysisTab, SourceTab ... """ global obj_analysis self.convertWindow = QtGui.QWidget() self.analysisTab = QtGui.QScrollArea() obj_analysis = Analysis.Analysis(self.clarg1) self.analysisTab.setWidget(obj_analysis) # self.analysisTabLayout = QtGui.QVBoxLayout(self.analysisTab.widget()) self.analysisTab.setWidgetResizable(True) global obj_source self.sourceTab = QtGui.QScrollArea() obj_source = Source.Source(sourcelist, sourcelisttrack, self.clarg1) self.sourceTab.setWidget(obj_source) # self.sourceTabLayout = QtGui.QVBoxLayout(self.sourceTab.widget()) self.sourceTab.setWidgetResizable(True) global obj_model self.modelTab = QtGui.QScrollArea() obj_model = Model.Model(schematicInfo, modelList, self.clarg1) self.modelTab.setWidget(obj_model) # self.modelTabLayout = QtGui.QVBoxLayout(self.modelTab.widget()) self.modelTab.setWidgetResizable(True) global obj_devicemodel self.deviceModelTab = QtGui.QScrollArea() obj_devicemodel = DeviceModel.DeviceModel(schematicInfo, self.clarg1) self.deviceModelTab.setWidget(obj_devicemodel) self.deviceModelTab.setWidgetResizable(True) global obj_subcircuitTab self.subcircuitTab = QtGui.QScrollArea() obj_subcircuitTab = SubcircuitTab.SubcircuitTab( schematicInfo, self.clarg1) self.subcircuitTab.setWidget(obj_subcircuitTab) self.subcircuitTab.setWidgetResizable(True) self.tabWidget = QtGui.QTabWidget() # self.tabWidget.TabShape(QtGui.QTabWidget.Rounded) self.tabWidget.addTab(self.analysisTab, "Analysis") self.tabWidget.addTab(self.sourceTab, "Source Details") self.tabWidget.addTab(self.modelTab, "NgSpice Model") self.tabWidget.addTab(self.deviceModelTab, "Device Modeling") self.tabWidget.addTab(self.subcircuitTab, "Subcircuits") self.mainLayout = QtGui.QVBoxLayout() self.mainLayout.addWidget(self.tabWidget) # self.mainLayout.addStretch(1) self.convertWindow.setLayout(self.mainLayout) self.convertWindow.show() return self.convertWindow def callConvert(self): """ - This function called when convert button clicked - Extracting data from the objs created above - Pushing this data to json, and dumping it finally - Written to a ..._Previous_Valuse.json file in the projDirectory - Finally, call createNetListFile, with the converted schematic """ global schematicInfo global analysisoutput global kicad store_schematicInfo = list(schematicInfo) (projpath, filename) = os.path.split(self.kicadFile) project_name = os.path.basename(projpath) # Opening previous value file pertaining to the selected project fw = open( os.path.join( projpath, project_name + "_Previous_Values.json"), 'w') # Creating a dictionary to map the json data json_data = {} # Writing analysis values json_data["analysis"] = {} json_data["analysis"]["ac"] = {} if obj_analysis.Lin.isChecked(): json_data["analysis"]["ac"]["Lin"] = "true" json_data["analysis"]["ac"]["Dec"] = "false" json_data["analysis"]["ac"]["Oct"] = "false" elif obj_analysis.Dec.isChecked(): json_data["analysis"]["ac"]["Lin"] = "false" json_data["analysis"]["ac"]["Dec"] = "true" json_data["analysis"]["ac"]["Oct"] = "false" if obj_analysis.Oct.isChecked(): json_data["analysis"]["ac"]["Lin"] = "false" json_data["analysis"]["ac"]["Dec"] = "false" json_data["analysis"]["ac"]["Oct"] = "true" json_data["analysis"]["ac"]["Start Frequency"] = str( obj_analysis.ac_entry_var[0].text()) json_data["analysis"]["ac"]["Stop Frequency"] = str( obj_analysis.ac_entry_var[1].text()) json_data["analysis"]["ac"]["No. of points"] = str( obj_analysis.ac_entry_var[2].text()) json_data["analysis"]["ac"]["Start Fre Combo"] = ( obj_analysis.ac_parameter[0] ) json_data["analysis"]["ac"]["Stop Fre Combo"] = ( obj_analysis.ac_parameter[1] ) json_data["analysis"]["dc"] = {} json_data["analysis"]["dc"]["Source 1"] = str( obj_analysis.dc_entry_var[0].text()) json_data["analysis"]["dc"]["Start"] = str( obj_analysis.dc_entry_var[1].text()) json_data["analysis"]["dc"]["Increment"] = str( obj_analysis.dc_entry_var[2].text()) json_data["analysis"]["dc"]["Stop"] = str( obj_analysis.dc_entry_var[3].text()) json_data["analysis"]["dc"]["Operating Point"] = str( self.obj_track.op_check[-1]) json_data["analysis"]["dc"]["Start Combo"] = ( obj_analysis.dc_parameter[0] ) json_data["analysis"]["dc"]["Increment Combo"] = ( obj_analysis.dc_parameter[1] ) json_data["analysis"]["dc"]["Stop Combo"] = ( obj_analysis.dc_parameter[2] ) json_data["analysis"]["dc"]["Source 2"] = str( obj_analysis.dc_entry_var[4].text()) json_data["analysis"]["dc"]["Start2"] = str( obj_analysis.dc_entry_var[5].text()) json_data["analysis"]["dc"]["Increment2"] = str( obj_analysis.dc_entry_var[6].text()) json_data["analysis"]["dc"]["Stop2"] = str( obj_analysis.dc_entry_var[7].text()) json_data["analysis"]["dc"]["Start Combo2"] = ( obj_analysis.dc_parameter[3] ) json_data["analysis"]["dc"]["Increment Combo2"] = ( obj_analysis.dc_parameter[4] ) json_data["analysis"]["dc"]["Stop Combo2"] = ( obj_analysis.dc_parameter[5] ) json_data["analysis"]["tran"] = {} json_data["analysis"]["tran"]["Start Time"] = str( obj_analysis.tran_entry_var[0].text()) json_data["analysis"]["tran"]["Step Time"] = str( obj_analysis.tran_entry_var[1].text()) json_data["analysis"]["tran"]["Stop Time"] = str( obj_analysis.tran_entry_var[2].text()) json_data["analysis"]["tran"]["Start Combo"] = ( obj_analysis.tran_parameter[0] ) json_data["analysis"]["tran"]["Step Combo"] = ( obj_analysis.tran_parameter[1] ) json_data["analysis"]["tran"]["Stop Combo"] = ( obj_analysis.tran_parameter[2] ) # Writing source values json_data["source"] = {} count = 1 for line in store_schematicInfo: words = line.split(' ') wordv = words[0] if wordv[0] == "v" or wordv[0] == "i": json_data["source"][wordv] = {} json_data["source"][wordv]["type"] = words[len(words) - 1] json_data["source"][wordv]["values"] = [] if words[len(words) - 1] == "ac": amp = {"Amplitude": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(amp) phase = {"Phase": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(phase) elif words[len(words) - 1] == "dc": value = {"Value": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(value) elif words[len(words) - 1] == "sine": offset = { "Offset Value": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(offset) amp = {"Amplitude": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(amp) freq = {"Freuency": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(freq) delay = {"Delay Time": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(delay) damp = { "Damping Factor": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(damp) elif words[len(words) - 1] == "pulse": initial = { "Initial Value": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(initial) pulse = { "Pulse Value": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(pulse) delay = {"Delay Time": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(delay) rise = {"Rise Time": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(rise) fall = {"Fall Time": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(fall) width = { "Pulse width": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(width) period = {"Period": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(period) elif words[len(words) - 1] == "pwl": pwl = { "Enter in pwl format": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(pwl) elif words[len(words) - 1] == "exp": initial = { "Initial Value": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(initial) pulsed = { "Pulsed Value": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(pulsed) rise = { "Rise Delay Time": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(rise) fall = {"Fall Time": str(obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(fall) fallConstant = { "Fall Time Constant": str( obj_source.entry_var[count].text())} count += 1 json_data["source"][wordv]["values"].append(fallConstant) else: pass # Writing Model values i = 0 json_data["model"] = {} for line in modelList: for rand_itr in obj_model.obj_trac.modelTrack: if rand_itr[2] == line[2] and rand_itr[3] == line[3]: start = rand_itr[7] end = rand_itr[8] i = start json_data["model"][line[3]] = {} json_data["model"][line[3]]["type"] = line[2] json_data["model"][line[3]]["values"] = [] for key, value in line[7].items(): if( hasattr(value, '__iter__') and i <= end and not isinstance(value, str) ): for item in value: fields = { item: str( obj_model.obj_trac.model_entry_var[i].text())} json_data["model"][line[3]]["values"].append(fields) i = i + 1 else: fields = { value: str( obj_model.obj_trac.model_entry_var[i].text())} json_data["model"][line[3]]["values"].append(fields) i = i + 1 # Writing Device Model values json_data["deviceModel"] = {} for device in obj_devicemodel.devicemodel_dict_beg: json_data["deviceModel"][device] = [] it = obj_devicemodel.devicemodel_dict_beg[device] end = obj_devicemodel.devicemodel_dict_end[device] while it <= end: json_data["deviceModel"][device].append( str(obj_devicemodel.entry_var[it].text())) it = it + 1 # Writing Subcircuit values json_data["subcircuit"] = {} for subckt in obj_subcircuitTab.subcircuit_dict_beg: json_data["subcircuit"][subckt] = [] it = obj_subcircuitTab.subcircuit_dict_beg[subckt] end = obj_subcircuitTab.subcircuit_dict_end[subckt] while it <= end: json_data["subcircuit"][subckt].append( str(obj_subcircuitTab.entry_var[it].text())) it = it + 1 # json dumped and written to previous value file for the project write_data = json.dumps(json_data) fw.write(write_data) # Create Convert object with the source details & the schematic details print("=============================================================") print("SOURCE LIST TRACK") print(self.obj_track.sourcelisttrack["ITEMS"]) print("SOURCE ENTRY VAR") print(self.obj_track.source_entry_var["ITEMS"]) print("SCHEMATIC INFO") print(store_schematicInfo) print("=============================================================") self.obj_convert = Convert.Convert( self.obj_track.sourcelisttrack["ITEMS"], self.obj_track.source_entry_var["ITEMS"], store_schematicInfo, self.clarg1 ) try: # Adding Source Value to Schematic Info store_schematicInfo = self.obj_convert.addSourceParameter() print("=========================================================") print("Netlist After Adding Source details :", store_schematicInfo) # Adding Model Value to store_schematicInfo store_schematicInfo = self.obj_convert.addModelParameter( store_schematicInfo) print("=========================================================") print("Netlist After Adding Ngspice Model :", store_schematicInfo) # Adding Device Library to SchematicInfo store_schematicInfo = self.obj_convert.addDeviceLibrary( store_schematicInfo, self.kicadFile) print("=========================================================") print( "Netlist After Adding Device Model Library :", store_schematicInfo) # Adding Subcircuit Library to SchematicInfo store_schematicInfo = self.obj_convert.addSubcircuit( store_schematicInfo, self.kicadFile) print("=========================================================") print("Netlist After Adding subcircuits :", store_schematicInfo) analysisoutput = self.obj_convert.analysisInsertor( self.obj_track.AC_entry_var["ITEMS"], self.obj_track.DC_entry_var["ITEMS"], self.obj_track.TRAN_entry_var["ITEMS"], self.obj_track.set_CheckBox["ITEMS"], self.obj_track.AC_Parameter["ITEMS"], self.obj_track.DC_Parameter["ITEMS"], self.obj_track.TRAN_Parameter["ITEMS"], self.obj_track.AC_type["ITEMS"], self.obj_track.op_check ) print("=========================================================") print("Analysis OutPut ", analysisoutput) # Calling netlist file generation function print("=========================================================") print("STORE SCHEMATIC INFO") print(store_schematicInfo) print("=========================================================") self.createNetlistFile(store_schematicInfo, plotText) self.msg = "The Kicad to Ngspice Conversion completed " self.msg += "successfully!" QtGui.QMessageBox.information( self, "Information", self.msg, QtGui.QMessageBox.Ok ) except Exception as e: print("Exception Message: ", e) print("There was error while converting kicad to ngspice") self.close() # Generate .sub file from .cir.out file if it is a subcircuit subPath = os.path.splitext(self.kicadFile)[0] # If sub argument passed, create subCircuit file as well if self.clarg2 == "sub": self.createSubFile(subPath) def createNetlistFile(self, store_schematicInfo, plotText): """ - Creating .cir.out file - If analysis file present uses that and extract - Simulator - Initial - Analysis - Finally add the following components to .cir.out file - SimulatorOption - InitialCondOption - Store_SchematicInfo - AnalysisOption - In the end add control statements and allv, alli, end statements """ print("=============================================================") print("Creating Final netlist") # To avoid writing optionInfo twice in final netlist store_optionInfo = list(optionInfo) # checking if analysis files is present (projpath, filename) = os.path.split(self.kicadFile) analysisFileLoc = os.path.join(projpath, "analysis") if os.path.exists(analysisFileLoc): try: f = open(analysisFileLoc) # Read data data = f.read() # Close the file f.close() except BaseException: print("Error While opening Project Analysis file.\ Please check it") sys.exit() else: # print("========================================================") print(analysisFileLoc + " does not exist") sys.exit() # Adding analysis file info to optionInfo analysisData = data.splitlines() for eachline in analysisData: eachline = eachline.strip() if len(eachline) > 1: if eachline[0] == '.': store_optionInfo.append(eachline) analysisOption = [] initialCondOption = [] simulatorOption = [] # includeOption=[] # Don't know why to use it # model = [] # Don't know why to use it for eachline in store_optionInfo: words = eachline.split() option = words[0] if (option == '.ac' or option == '.dc' or option == '.disto' or option == '.noise' or option == '.op' or option == '.pz' or option == '.sens' or option == '.tf' or option == '.tran'): analysisOption.append(eachline + '\n') elif (option == '.save' or option == '.print' or option == '.plot' or option == '.four'): eachline = eachline.strip('.') outputOption.append(eachline + '\n') elif (option == '.nodeset' or option == '.ic'): initialCondOption.append(eachline + '\n') elif option == '.option': simulatorOption.append(eachline + '\n') # elif (option=='.include' or option=='.lib'): # includeOption.append(eachline+'\n') # elif (option=='.model'): # model.append(eachline+'\n') elif option == '.end': continue # Start creating final netlist cir.out file outfile = self.kicadFile + ".out" out = open(outfile, "w") out.writelines(infoline) out.writelines('\n') sections = [ simulatorOption, initialCondOption, store_schematicInfo, analysisOption] for section in sections: if len(section) == 0: continue else: for line in section: out.writelines('\n') out.writelines(line) out.writelines('\n* Control Statements \n') out.writelines('.control\n') out.writelines('run\n') # out.writelines(outputOption) out.writelines('print allv > plot_data_v.txt\n') out.writelines('print alli > plot_data_i.txt\n') for item in plotText: out.writelines(item + '\n') out.writelines('.endc\n') out.writelines('.end\n') out.close() def createSubFile(self, subPath): """ - To create subcircuit file - Extract data from .cir.out file """ self.project = subPath self.projName = os.path.basename(self.project) if os.path.exists(self.project + ".cir.out"): try: f = open(self.project + ".cir.out") except BaseException: print("Error in opening .cir.out file.") else: # print("=========================================================") print( self.projName + ".cir.out does not exist. Please create a spice netlist.") # Read the data from file data = f.read() # Close the file f.close() newNetlist = [] netlist = iter(data.splitlines()) for eachline in netlist: eachline = eachline.strip() if len(eachline) < 1: continue words = eachline.split() if eachline[2] == 'u': if words[len(words) - 1] == "port": subcktInfo = ".subckt " + self.projName + " " for i in range(2, len(words) - 1): subcktInfo += words[i] + " " continue if( words[0] == ".end" or words[0] == ".ac" or words[0] == ".dc" or words[0] == ".tran" or words[0] == '.disto' or words[0] == '.noise' or words[0] == '.op' or words[0] == '.pz' or words[0] == '.sens' or words[0] == '.tf' ): continue elif words[0] == ".control": while words[0] != ".endc": eachline = next(netlist) eachline = eachline.strip() if len(eachline) < 1: continue words = eachline.split() else: newNetlist.append(eachline) outfile = self.project + ".sub" out = open(outfile, "w") out.writelines("* Subcircuit " + self.projName) out.writelines('\n') out.writelines(subcktInfo) out.writelines('\n') for i in range(len(newNetlist), 0, -1): newNetlist.insert(i, '\n') out.writelines(newNetlist) out.writelines('\n') out.writelines('.ends ' + self.projName) # print("=============================================================") print("The subcircuit has been written in " + self.projName + ".sub")