├── .gitignore ├── .vscode └── settings.json ├── AppliancesDetector ├── Appliances.py ├── Filters.py ├── Sonda.py └── __init__.py ├── CurrentSensor.py ├── ExampleSubcircuit.py ├── Models ├── AD8619.cir ├── BasicOpamp.cir └── TL084.cir ├── PySpice-library.code-workspace ├── PySpiceDvTools ├── Filters.py ├── LTSpiceServer.py ├── LTSpiceServerXVII.py ├── Loads.py ├── SkFilters.py └── __init__.py ├── README.md ├── RunSimulation.py ├── SKFilterDesign.py ├── SKFilterTest.py ├── SearchR.py ├── SimulacionesFiltersBP.py ├── SimulacionesFiltersHPLP.py ├── SimulacionesPython.py ├── SimulacionesPythonLT.py └── spinit /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | /__Previews 254 | **/__pycache__ 255 | *.pyproj 256 | /temp.* 257 | /Docs 258 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.pylintEnabled": false 3 | } -------------------------------------------------------------------------------- /AppliancesDetector/Appliances.py: -------------------------------------------------------------------------------- 1 | from PySpice.Spice.Netlist import SubCircuitFactory 2 | from PySpice.Unit import * 3 | 4 | from PySpiceDvTools.Loads import * 5 | 6 | """ 7 | ILabels = {'Microondas 1200Wh' 'Nevera 250Wh' 'Pequeño 15Wh' 'Horno 4000Wh' 'Picos 20A'}; 8 | listaRl = [27.5 130.625 2942.5 7.305 6.445]; 9 | listaLl = [0.5 2 5 0.5 0.5]; 10 | """ 11 | 12 | class MicroOndas1200(BasicNonLinearLoad): 13 | """MicroOndas 1200w""" 14 | __name__ = 'MicroOndas1200' 15 | 16 | def __init__(self, diodeModel=None): 17 | super().__init__(diodeModel=diodeModel,r= 27.5, l=0.5) 18 | 19 | class Nevera250(BasicNonLinearLoad): 20 | """Nevera 250w""" 21 | __name__ = 'Nevera250' 22 | 23 | def __init__(self, diodeModel=None): 24 | super().__init__(diodeModel=diodeModel,r= 130.625, l=2) 25 | 26 | 27 | class Pequeno15(BasicNonLinearLoad): 28 | """Pequeño 15Wh""" 29 | __name__ = 'Pequeno15' 30 | 31 | def __init__(self, diodeModel=None): 32 | super().__init__(diodeModel=diodeModel,r= 2942.5, l=5) 33 | 34 | class Horno4000(BasicNonLinearLoad): 35 | """Horno 4000Wh""" 36 | __name__ = 'Horno4000' 37 | 38 | def __init__(self, diodeModel=None): 39 | super().__init__(diodeModel=diodeModel,r= 7.305, l=0.5) 40 | 41 | class Picos20A(BasicNonLinearLoad): 42 | """Picos 20A""" 43 | __name__ = 'Picos20A' 44 | 45 | def __init__(self, diodeModel=None): 46 | super().__init__(diodeModel=diodeModel,r= 6.445, l=0.5) 47 | 48 | -------------------------------------------------------------------------------- /AppliancesDetector/Filters.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PySpice.Spice.Netlist import SubCircuitFactory 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | 12 | class Adder(SubCircuitFactory): 13 | __nodes__ = ('In50', 'In150', 'In250', 'Out', 'Ref', 'V+','V-') 14 | __name__ = 'Adder' 15 | 16 | def __init__(self, opAmpModel=None): 17 | super().__init__() 18 | self.X('1',opAmpModel,'Ref','N001','V+','V-','Out') 19 | self.R('1','In50','N001',kilo(6)) 20 | self.R('2','In150','N001',kilo(3)) 21 | self.R('3','In250','N001',kilo(2)) 22 | self.R('4','Out','N001',kilo(2)) -------------------------------------------------------------------------------- /AppliancesDetector/Sonda.py: -------------------------------------------------------------------------------- 1 | from PySpice.Spice.Netlist import SubCircuitFactory 2 | from PySpice.Unit import * 3 | 4 | class Sonda(SubCircuitFactory): 5 | __name__ = 'Sonda' 6 | __nodes__ = ('CIn', 'COut','VSense', 'VRef') 7 | 8 | def __init__(self, 9 | turn_ratio=1/500, 10 | primary_inductance=100e-6, 11 | coupling=1, 12 | RShunt=68, 13 | ): 14 | 15 | super().__init__() 16 | secondary_inductance = primary_inductance / float(turn_ratio**2) 17 | primary_inductor = self.L('primary', 'CIn', 'COut', primary_inductance) 18 | secondary_inductor = self.L('secondary', 'VSense', 'VRef', secondary_inductance) 19 | self.CoupledInductor('coupling', primary_inductor.name, secondary_inductor.name, coupling) 20 | self.R('shunt','VSense', 'VRef',RShunt) 21 | 22 | class VoltageReference(SubCircuitFactory): 23 | __name__ = 'VoltageReference' 24 | __nodes__ = ('Vin','Vref', 'Vgnd' ) 25 | 26 | def __init__ (self, opAmpModel=None): 27 | super().__init__() 28 | self.X('1',opAmpModel,'N001','VRef','VIn','Vgnd','VRef') 29 | self.R('1','VIn','N001',kilo(100)) 30 | self.R('2','Vgnd','N001',kilo(100)) 31 | -------------------------------------------------------------------------------- /AppliancesDetector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielvilas/PySpice-Library/999c3850fd69bab58fc44f39571b292aaeb626ab/AppliancesDetector/__init__.py -------------------------------------------------------------------------------- /CurrentSensor.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | from PySpiceDvTools.Loads import * 13 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 14 | from AppliancesDetector.Appliances import * 15 | from AppliancesDetector.Sonda import * 16 | from PySpiceDvTools.SkFilters import * 17 | from PySpiceDvTools.Filters import CascadeFilter 18 | from scipy.fftpack import fft,fftfreq 19 | from scipy.signal import resample 20 | 21 | circuit = Circuit('Sensor Sim') 22 | 23 | model = 'BasicOpamp' 24 | 25 | circuit.include('Models/BasicOpamp.cir') 26 | circuit.include('Models/AD8619.cir') 27 | circuit.include('Models/TL084.cir') 28 | 29 | microwave= MicroOndas1200() 30 | sonda = Sonda() 31 | vRef = VoltageReference (opAmpModel=model) 32 | 33 | filterL = SKLowPassFilter(opAmpModel=model,R1=470,R2=kilo(3), C1=micro(1),C2=nano(100),name='sklpfi_400') 34 | filterH = SKHighPassFilter(opAmpModel=model, R1=kilo(1), R2=kilo(33), C1=micro(4.7),C2=nano(100),name='skhpfi_40') 35 | filter2o = CascadeFilter(fa=filterL,fb=filterH,name='wide_40_400') 36 | 37 | circuit.subcircuit(microwave) 38 | circuit.subcircuit(sonda) 39 | circuit.subcircuit(vRef) 40 | circuit.subcircuit(filter2o) 41 | filter2o.attach(circuit) 42 | 43 | circuit.SinusoidalVoltageSource('1', 'A', circuit.gnd, amplitude=220, frequency=50) 44 | circuit.V('2','5V',circuit.gnd,'5') 45 | circuit.X('1', microwave.name, 'A', 'SIn') 46 | circuit.X('2', vRef.name, '5V', 'VRef',circuit.gnd) 47 | circuit.X('3', sonda.name, 'SIn',circuit.gnd,'VSense','VRef') 48 | circuit.X('4',filter2o.name,'VSense','VFilter','VRef','5V',circuit.gnd) 49 | 50 | print (circuit) 51 | simulator = circuit.simulator() 52 | #enableLtSpice(simulator, spice_command='/Applications/LTspice.app/Contents/MacOS/LTspice') 53 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 54 | 55 | current = analysis['V1'] 56 | aimax = np.amax(current.data) 57 | aimin = np.amin(current.data) 58 | print ('Max Current: ',aimax) 59 | print ('Min Current: ',aimin) 60 | 61 | figure1 = plt.figure(1, (20, 10)) 62 | plt.subplot(321) 63 | plt.plot(analysis.time, current, '-') 64 | plt.grid() 65 | plt.title('Current') 66 | plt.xlabel('time') 67 | plt.ylabel('Amps') 68 | plt.axhline(y=aimax,color='red') 69 | plt.axhline(y=aimin,color='red') 70 | yticks, ytlabels =plt.yticks() 71 | yticks[-1]=aimax 72 | yticks[-2]=aimin 73 | plt.yticks(yticks) 74 | plt.subplot(322) 75 | nTime=np.linspace(0.0, 1.0, num=5000); 76 | yf = np.interp(nTime,analysis.time,current) 77 | yf = fft(yf) 78 | x =fftfreq(yf.size,1.0/yf.size) 79 | plt.plot(x[1:500], np.abs(yf[1:500]), '-') 80 | plt.grid() 81 | plt.title('Frec Current Sensed PF ({})'.format(model)) 82 | plt.xlabel('Freq') 83 | plt.ylabel('Pos-Filter') 84 | 85 | Vsense = analysis['VSense'] 86 | plt.subplot(323) 87 | plt.plot(analysis.time, Vsense, '-') 88 | plt.grid() 89 | plt.title('Current Sensed') 90 | plt.xlabel('time') 91 | plt.ylabel('Voltaje') 92 | 93 | plt.subplot(324) 94 | nTime=np.linspace(0.0, 1.0, num=5000); 95 | yf = np.interp(nTime,analysis.time,Vsense) 96 | yf = fft(yf) 97 | x =fftfreq(yf.size,1.0/yf.size) 98 | plt.plot(x[1:500], np.abs(yf[1:500]), '-') 99 | plt.grid() 100 | plt.title('Frec Current Sensed PF ({})'.format(model)) 101 | plt.xlabel('Freq') 102 | plt.ylabel('Pos-Filter') 103 | 104 | Vsense = analysis['VFilter'] 105 | plt.subplot(325) 106 | plt.plot(analysis.time,Vsense, '-') 107 | plt.grid() 108 | plt.title('Current Sensed PF ({})'.format(model)) 109 | plt.xlabel('time') 110 | plt.ylabel('Pos-Filter') 111 | 112 | plt.subplot(326) 113 | nTime=np.linspace(0.0, 1.0, num=5000); 114 | yf = np.interp(nTime,analysis.time,Vsense) 115 | yf = fft(yf) 116 | x =fftfreq(yf.size,1.0/yf.size) 117 | plt.plot(x[1:500], np.abs(yf[1:500]), '-') 118 | plt.grid() 119 | plt.title('Frec Current Sensed PF ({})'.format(model)) 120 | plt.xlabel('Freq') 121 | plt.ylabel('Pos-Filter') 122 | 123 | plt.show() 124 | -------------------------------------------------------------------------------- /ExampleSubcircuit.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | from PySpiceDvTools.Loads import * 13 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 14 | circuit = Circuit('NonLineal Load Sim') 15 | 16 | 17 | ''' 18 | V1 A 0 SINE(0 220 50) 19 | D1 0 N001 Def 20 | D2 A N001 Def 21 | D3 N003 A Def 22 | D4 N003 0 Deg 23 | R1 N001 N002 27.5 24 | L1 N002 N003 0.5 25 | .MODEL Def D 26 | ''' 27 | 28 | circuit.SinusoidalVoltageSource('1', 'A', circuit.gnd, amplitude=220, frequency=50) 29 | 30 | subcir= BasicNonLinearLoad(r=27.5,l=0.5) 31 | circuit.subcircuit(subcir) 32 | circuit.X('1', 'BasicNonLinearLoad', 'A', circuit.gnd) 33 | 34 | 35 | print(circuit) 36 | 37 | print(subcir.getRValue()) 38 | print(subcir.getLValue()) 39 | 40 | 41 | simulator = circuit.simulator() 42 | #enableLtSpice(simulator) 43 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 44 | 45 | current = analysis['V1'] 46 | aimax = np.amax(current.data) 47 | aimin = np.amin(current.data) 48 | print ('Max Current: ',aimax) 49 | print ('Min Current: ',aimin) 50 | 51 | figure1 = plt.figure(1, (20, 10)) 52 | plt.subplot(211) 53 | plt.plot(analysis.time, current, '-') 54 | plt.grid() 55 | plt.title('Current') 56 | plt.xlabel('time') 57 | plt.ylabel('Amps') 58 | plt.axhline(y=aimax,color='red') 59 | plt.axhline(y=aimin,color='red') 60 | yticks, ytlabels =plt.yticks() 61 | yticks[-1]=aimax 62 | yticks[-2]=aimin 63 | plt.yticks(yticks) 64 | 65 | valsr = np.linspace(10,40,61) 66 | valsc05 = [] 67 | valsc10 = [] 68 | 69 | for val in valsr: 70 | print(val) 71 | subcir.setLValue(0.5) 72 | subcir.setRValue(val) 73 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 74 | current = analysis['V1'] 75 | max = np.amax(current.data) 76 | valsc05 = np.append(valsc05, max) 77 | subcir.setLValue(1) 78 | subcir.setRValue(val) 79 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 80 | current = analysis['V1'] 81 | max = np.amax(current.data) 82 | valsc10 = np.append(valsc10, max) 83 | 84 | plt.subplot(212) 85 | plt.plot(valsr,valsc05,valsr, valsc10) 86 | plt.title('Max Current') 87 | plt.xlabel('Resistance') 88 | plt.ylabel('Amps') 89 | plt.show() 90 | -------------------------------------------------------------------------------- /Models/AD8619.cir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielvilas/PySpice-Library/999c3850fd69bab58fc44f39571b292aaeb626ab/Models/AD8619.cir -------------------------------------------------------------------------------- /Models/BasicOpamp.cir: -------------------------------------------------------------------------------- 1 | * Basic Op from ecircuits 2 | * Description: Amplifier 3 | * END Notes 4 | * 5 | * Node Assignments 6 | * noninverting input 7 | * | inverting input 8 | * | | positive supply 9 | * | | | negative supply 10 | * | | | | output 11 | * | | | | | 12 | * | | | | | 13 | .SUBCKT BasicOpAmp 1 2 99 100 6 14 | RIN 1 2 10MEG 15 | * DC Gain = 100K And pole1 = 100Hz 16 | * Unitiy Gain = DCGAIN * POLE! = 100MHZ 17 | EGAIN 3 0 1 2 100K 18 | RP1 3 4 1K 19 | CP1 4 0 1.591uf 20 | EBUFFER 5 0 4 0 1 21 | ROUT 5 6 10 22 | .ENDS 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Models/TL084.cir: -------------------------------------------------------------------------------- 1 | * TL084 OPERATIONAL AMPLIFIER "MACROMODEL" SUBCIRCUIT 2 | * CREATED USING PARTS RELEASE 4.01 ON 06/16/89 AT 13:08 3 | * (REV N/A) SUPPLY VOLTAGE: +/-15V 4 | * CONNECTIONS: NON-INVERTING INPUT 5 | * | INVERTING INPUT 6 | * | | POSITIVE POWER SUPPLY 7 | * | | | NEGATIVE POWER SUPPLY 8 | * | | | | OUTPUT 9 | * | | | | | 10 | .SUBCKT TL084 1 2 3 4 5 11 | * 12 | C1 11 12 3.498E-12 13 | C2 6 7 15.00E-12 14 | DC 5 53 DX 15 | DE 54 5 DX 16 | DLP 90 91 DX 17 | DLN 92 90 DX 18 | DP 4 3 DX 19 | EGND 99 0 POLY(2) (3,0) (4,0) 0 .5 .5 20 | FB 7 99 POLY(5) VB VC VE VLP VLN 0 4.715E6 -5E6 5E6 5E6 -5E6 21 | GA 6 0 11 12 282.8E-6 22 | GCM 0 6 10 99 8.942E-9 23 | ISS 3 10 DC 195.0E-6 24 | HLIM 90 0 VLIM 1K 25 | J1 11 2 10 JX 26 | J2 12 1 10 JX 27 | R2 6 9 100.0E3 28 | RD1 4 11 3.536E3 29 | RD2 4 12 3.536E3 30 | RO1 8 5 150 31 | RO2 7 99 150 32 | RP 3 4 2.143E3 33 | RSS 10 99 1.026E6 34 | VB 9 0 DC 0 35 | VC 3 53 DC 2.200 36 | VE 54 4 DC 2.200 37 | VLIM 7 8 DC 0 38 | VLP 91 0 DC 25 39 | VLN 0 92 DC 25 40 | .MODEL DX D(IS=800.0E-18) 41 | .MODEL JX PJF(IS=15.00E-12 BETA=270.1E-6 VTO=-1) 42 | .ENDS 43 |  -------------------------------------------------------------------------------- /PySpice-library.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /PySpiceDvTools/Filters.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PySpice.Spice.Netlist import SubCircuitFactory 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | 12 | class NarrowBandPassFilterInverted(SubCircuitFactory): 13 | _Gain = None 14 | _Fc = None 15 | _BW = None 16 | _C = None 17 | 18 | _Q = None 19 | _R3 = None 20 | _Req = None 21 | _a = None 22 | _R2 = None 23 | _R1 = None 24 | 25 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 26 | 27 | def _calc_values(self): 28 | """ 29 | Q=Fc/Bw 30 | 𝑅3=𝑄/(𝜋 𝐶 𝐹𝑐) 31 | 𝑅𝑒𝑞=𝑅3/(4 𝑄^2) 32 | 𝛼=(2𝑄^2)/𝐺 33 | 𝑅2=𝑅𝑒𝑞*((1+𝛼)/𝛼) 34 | 𝑅1=𝛼𝑅2 35 | """ 36 | self._Q =self._Fc/self._BW 37 | self._R3 = self._Q/(np.pi*self._C*self._Fc) 38 | self._Req=self._R3/(4*pow(self._Q,2) ) 39 | self._a=(2*pow(self._Q,2))/self._Gain 40 | self._R2=self._Req*(1+self._a)/self._a 41 | self._R1=self._a*self._R2 42 | 43 | def __init__(self, opAmpModel=None, C=nano(100), Gain=1, Fc=50, BW=10, name = None): 44 | super().__init__() 45 | 46 | if opAmpModel == None: 47 | raise NotImplementedError("This Filters needs An Operational Model") 48 | 49 | self._C=float(C) 50 | self._Gain =float(Gain) 51 | self._Fc=float(Fc) 52 | self._BW=float(BW) 53 | 54 | if name == None: 55 | name = 'nbpfi_{}_{}_{}'.format(Fc,BW,Gain) 56 | self.__name__= name 57 | self._name = name 58 | self._calc_values() 59 | self.X('1',opAmpModel,'Ref','N002','V+','V-','Out') 60 | self.C('1','N001','Out',self._C) 61 | self.C('2','N001','N002',self._C) 62 | self.R('1','N001','In',self._R1) 63 | self.R('2','V-','N001',self._R2) 64 | self.R('3','N002','Out',self._R3) 65 | 66 | class HighPassFilterInverted(SubCircuitFactory): 67 | _Gain = None 68 | _Fc = None 69 | _C = None 70 | 71 | _R2 = None 72 | _R1 = None 73 | 74 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 75 | 76 | def _calc_values(self): 77 | """ 78 | R1=1/(2πFc*C1 ) 79 | R2=G*R1 80 | """ 81 | self._R1 = 1/(2*np.pi*self._C*self._Fc) 82 | self._R2=self._Gain*self._R1 83 | 84 | 85 | def __init__(self, opAmpModel=None, C=nano(100), Gain=1, Fc=50, name = None): 86 | super().__init__() 87 | 88 | if opAmpModel == None: 89 | raise NotImplementedError("This Filters needs An Operational Model") 90 | 91 | self._C=float(C) 92 | self._Gain =float(Gain) 93 | self._Fc=float(Fc) 94 | 95 | if name == None: 96 | name = 'hpfi_{}_{}'.format(Fc,Gain) 97 | self.__name__= name 98 | self._name = name 99 | self._calc_values() 100 | self.X('1',opAmpModel,'Ref','N002','V+','V-','Out') 101 | self.C('1','N001','N002',self._C) 102 | self.R('1','N001','In',self._R1) 103 | self.R('2','N002','Out',self._R2) 104 | 105 | class LowPassFilterInverted(SubCircuitFactory): 106 | _Gain = None 107 | _Fc = None 108 | _C = None 109 | 110 | _R2 = None 111 | _R1 = None 112 | 113 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 114 | 115 | def _calc_values(self): 116 | """ 117 | R1=1/(2πFc*C1 ) 118 | R2=R1/G 119 | """ 120 | self._R1 = 1/(2*np.pi*self._C*self._Fc) 121 | self._R2=self._R1/self._Gain 122 | 123 | 124 | def __init__(self, opAmpModel=None, C=nano(100), Gain=1, Fc=350, name = None): 125 | super().__init__() 126 | 127 | if opAmpModel == None: 128 | raise NotImplementedError("This Filters needs An Operational Model") 129 | 130 | self._C=float(C) 131 | self._Gain =float(Gain) 132 | self._Fc=float(Fc) 133 | 134 | if name == None: 135 | name = 'lpfi_{}_{}'.format(Fc,Gain) 136 | self.__name__= name 137 | self._name = name 138 | self._calc_values() 139 | self.X('1',opAmpModel,'Ref','N001','V+','V-','Out') 140 | self.C('1','N001','Out',self._C) 141 | self.R('1','N001','Out',self._R1) 142 | self.R('2','In','N002',self._R2) 143 | self.C('2','N002','N001',micro(10)) 144 | 145 | class CascadeFilter(SubCircuitFactory): 146 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 147 | _fa=None 148 | _fb=None 149 | 150 | def attach(self, circuit): 151 | circuit.subcircuit(self) 152 | circuit.subcircuit(self._fa) 153 | circuit.subcircuit(self._fb) 154 | 155 | def __init__(self, fa=None, fb=None, name=None): 156 | super().__init__() 157 | self.__name__ = name 158 | self._name=name 159 | 160 | self._fa=fa 161 | self._fb=fb 162 | 163 | self.X('1',self._fa.name,'In','N001','Ref','V+','V-') 164 | self.X('2',self._fb.name,'N001','Out','Ref','V+','V-') 165 | 166 | class WideBandPassFilterInverted(CascadeFilter): 167 | def __init__(self, opAmpModel=None, C=nano(100), Gain=1, Finf=50, Fsup=350, name = None): 168 | 169 | if opAmpModel == None: 170 | raise NotImplementedError("This Filters needs An Operational Model") 171 | 172 | if name == None: 173 | name = 'wbpfi_{}_{}_{}'.format(Finf,Fsup,Gain) 174 | lp=LowPassFilterInverted(opAmpModel=opAmpModel,C=C,Gain=Gain,Fc=Fsup) 175 | hp=HighPassFilterInverted(opAmpModel=opAmpModel,C=C,Gain=Gain,Fc=Finf) 176 | super().__init__(fa=lp,fb=hp,name=name) 177 | 178 | 179 | -------------------------------------------------------------------------------- /PySpiceDvTools/LTSpiceServer.py: -------------------------------------------------------------------------------- 1 | """This module provides an interface to run ngspice in server mode and get back the simulation 2 | output. 3 | 4 | When ngspice runs in server mode, it writes on the standard output an header and then the simulation 5 | output in binary format. At the end of the simulation, it writes on the standard error a line of 6 | the form: 7 | 8 | .. code:: 9 | 10 | @@@ \d+ \d+ 11 | 12 | where the second number is the number of points of the simulation. Due to the iterative and 13 | adaptive nature of a transient simulation, the number of points is only known at the end. 14 | 15 | Any line starting with "Error" in the standard output indicates an error in the simulation process. 16 | The line "run simulation(s) aborted" in the standard error indicates the simulation aborted. 17 | 18 | Any line starting with *Warning* in the standard error indicates non critical error in the 19 | simulation process. 20 | 21 | """ 22 | 23 | #################################################################################################### 24 | 25 | import logging 26 | import re 27 | import os 28 | import numpy as np 29 | 30 | #################################################################################################### 31 | 32 | from PySpice.Spice.RawFile import RawFile, Variable 33 | from PySpice.Spice.Server import SpiceServer 34 | 35 | #################################################################################################### 36 | 37 | _module_logger = logging.getLogger(__name__) 38 | 39 | #################################################################################################### 40 | 41 | class LtSpiceServer(SpiceServer): 42 | 43 | """This class wraps the execution of ngspice in server mode and convert the output to a Python data 44 | structure. 45 | 46 | Example of usage:: 47 | 48 | spice_server = SpiceServer(spice_command='/path/to/ngspice') 49 | raw_file = spice_server(spice_input) 50 | 51 | It returns a :obj:`PySpice.Spice.RawFile` instance. 52 | 53 | """ 54 | 55 | _logger = _module_logger.getChild('LTSpiceServer') 56 | 57 | ############################################## 58 | def __init__(self, spice_command='scad3.exe'): 59 | 60 | self._spice_command = spice_command 61 | 62 | def _parse_log(self, logName): 63 | 64 | """Parse logfile for warnings and return the number of points.""" 65 | 66 | #Read in log file and check for errors 67 | file = open(logName,"r") 68 | log_lines = file.readlines() 69 | 70 | for line in log_lines: 71 | if 'Warning' in line: 72 | self._logger.warning(line[len('Warning :'):]) 73 | elif "Error" in line: 74 | raise NameError("Simulation aborted\n" + line) 75 | file.close() 76 | def _decode_number_of_points(self, line): 77 | 78 | """Decode the number of points in the given line.""" 79 | 80 | match = re.match(r'No. Points: +(\d+)', line) 81 | if match is not None: 82 | return int(match.group(1)) 83 | else: 84 | raise NameError("Cannot decode the number of points") 85 | 86 | def _parse_stdout(self, stdout): 87 | 88 | """Parse stdout for errors.""" 89 | 90 | # self._logger.debug('\n' + stdout) 91 | number_of_points = None 92 | error_found = False 93 | # UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 870: invalid start byte 94 | # lines = stdout.decode('utf-8').splitlines() 95 | lines = stdout.splitlines() 96 | for line_index, line in enumerate(lines): 97 | if line.startswith(b'Error '): 98 | error_found = True 99 | self._logger.error('\n' + line.decode('utf-8') + '\n' + lines[line_index+1].decode('utf-8')) 100 | if line.startswith(b'No. Points:'): 101 | number_of_points = self._decode_number_of_points(line.decode('utf-8')) 102 | if error_found: 103 | raise NameError("Errors was found by Spice") 104 | return number_of_points 105 | ############################################## 106 | def __call__(self, spice_input): 107 | 108 | """Run SPICE in server mode as a subprocess for the given input and return a 109 | :obj:`PySpice.RawFile.RawFile` instance. 110 | 111 | """ 112 | 113 | self._logger.info("Start the ltspice subprocess") 114 | 115 | file= open('temp.net','w') 116 | file.write(str(spice_input)) 117 | file.close() 118 | 119 | #command = "{} -b -ascii {}".format(self._spice_command,'temp.net') 120 | command = "{} -b {}".format(self._spice_command,'temp.net') 121 | self._logger.info("Command is "+command) 122 | os.system(command) 123 | 124 | self._parse_log('temp.log') 125 | 126 | file = open ('temp.raw','rb') 127 | stdout = file.read() 128 | number_of_points =self._parse_stdout(stdout) 129 | 130 | if number_of_points is None: 131 | raise NameError("The number of points was not found in the standard error buffer," 132 | " ngspice returned:\n" + 133 | stderr) 134 | 135 | return LtRawFile(stdout, number_of_points) 136 | 137 | class LtRawFile(RawFile): 138 | def __init__(self, stdout, number_of_points): 139 | super().__init__(stdout, number_of_points) 140 | def _read_header(self, stdout): 141 | 142 | """ Parse the header """ 143 | 144 | binary_line = b'Binary:\n' 145 | binary_location = stdout.find(binary_line) 146 | if binary_location < 0: 147 | raise NameError('Cannot locate binary data') 148 | raw_data_start = binary_location + len(binary_line) 149 | # self._logger.debug('\n' + stdout[:raw_data_start].decode('utf-8')) 150 | header_lines = stdout[:binary_location].splitlines() 151 | raw_data = stdout[raw_data_start:] 152 | header_line_iterator = iter(header_lines) 153 | 154 | ##Todo esto esta en el .log PDTE 155 | #self.circuit = self._read_header_field_line(header_line_iterator, 'Circuit') 156 | #self.temperature = self._read_header_line(header_line_iterator, 'Doing analysis at TEMP') 157 | #self.warnings = [self._read_header_field_line(header_line_iterator, 'Warning') 158 | # for i in range(stdout.count(b'Warning'))] 159 | #for warning in self.warnings: 160 | # self._logger.warn(warning) 161 | 162 | self.title = self._read_header_field_line(header_line_iterator, 'Title') 163 | self.date = self._read_header_field_line(header_line_iterator, 'Date') 164 | self.plot_name = self._read_header_field_line(header_line_iterator, 'Plotname') 165 | self.flags = self._read_header_field_line(header_line_iterator, 'Flags') 166 | self.number_of_variables = int(self._read_header_field_line(header_line_iterator, 'No. Variables')) 167 | self._read_header_field_line(header_line_iterator, 'No. Points') 168 | self._read_header_field_line(header_line_iterator, 'Offset') 169 | self._read_header_field_line(header_line_iterator, 'Command') 170 | self._read_header_field_line(header_line_iterator, 'Variables', has_value=False) 171 | 172 | self.variables = {} 173 | for i in range(self.number_of_variables): 174 | line = (next(header_line_iterator)).decode('utf-8') 175 | self._logger.debug(line) 176 | items = [x.strip() for x in line.split('\t') if x] 177 | # 0 frequency frequency grid=3 178 | index, name, unit = items[:3] 179 | name=str(name).lower() 180 | self.variables[name] = Variable(index, name, unit) 181 | # self._read_header_field_line(header_line_iterator, 'Binary', has_value=False) 182 | 183 | return raw_data 184 | 185 | def _read_variable_data(self, raw_data): 186 | 187 | """ Read the raw data and set the variable values. """ 188 | 189 | if('real' in self.flags): 190 | number_of_columns = self.number_of_variables 191 | elif('complex' in self.flags): 192 | number_of_columns = 2*self.number_of_variables 193 | else: 194 | raise NotImplementedError 195 | 196 | if('Transient' in self.plot_name): #Tran 197 | number_of_columns = self.number_of_variables+1 198 | input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='float32') 199 | input_data = input_data.reshape((self.number_of_points, number_of_columns)) 200 | input_data = input_data.transpose() 201 | time = input_data [0:2] 202 | tmpdata= time.transpose().flatten().tostring() 203 | time=np.fromstring(tmpdata, count=self.number_of_points, dtype='float64') 204 | time=np.absolute(time) 205 | input_data = input_data [1:] 206 | input_data[0]=time 207 | else: 208 | input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='float64') 209 | input_data = input_data.reshape((self.number_of_points, number_of_columns)) 210 | input_data = input_data.transpose() 211 | #input_data = input_data [1:] 212 | #np.savetxt('raw.txt', input_data) 213 | if 'complex' in self.flags: 214 | raw_data = input_data 215 | input_data = np.array(raw_data[0::2], dtype='complex64') 216 | input_data.imag = raw_data[1::2] 217 | for variable in self.variables.values(): 218 | variable.data = input_data[variable.index] 219 | 220 | def _read_variable_data_assci(self, raw_data): 221 | 222 | """ Read the raw data and set the variable values. """ 223 | 224 | if('real' in self.flags): 225 | number_of_columns = self.number_of_variables+1 226 | elif('complex' in self.flags): 227 | number_of_columns = 2*self.number_of_variables+1 228 | else: 229 | raise NotImplementedError 230 | print (np.version.version) 231 | tmpdata=raw_data.replace(b'\r\n',b'\t') 232 | tmpdata=tmpdata.replace(b'\t\t',b'\t') 233 | tmpdata=tmpdata.replace(b',',b'\t') 234 | 235 | input_data = np.fromstring(tmpdata.decode('utf-8'), count=number_of_columns*self.number_of_points, dtype='f8',sep='\t') 236 | input_data = input_data.reshape((self.number_of_points, number_of_columns)) 237 | input_data = input_data.transpose() 238 | input_data = input_data [1:] 239 | np.savetxt('raw.txt', input_data) 240 | if 'complex' in self.flags: 241 | raw_data = input_data 242 | input_data = np.array(raw_data[0::2], dtype='complex64') 243 | input_data.imag = raw_data[1::2] 244 | for variable in self.variables.values(): 245 | variable.data = input_data[variable.index] 246 | 247 | #"c:\\Program Files (x86)\\LTC\\LTspiceIV\\scad3.exe" 248 | # c:\\tmp\\EWB\\LTSpcieIV\\scad3.exe 249 | def enableLtSpice(simulator, spice_command='"c:\\Program Files (x86)\\LTC\\LTspiceIV\\scad3.exe"'): 250 | simulator._options.pop('filetype') 251 | simulator._options.pop('NOINIT') 252 | simulator._spice_server= LtSpiceServer(spice_command=spice_command) -------------------------------------------------------------------------------- /PySpiceDvTools/LTSpiceServerXVII.py: -------------------------------------------------------------------------------- 1 | """This module provides an interface to run ngspice in server mode and get back the simulation 2 | output. 3 | 4 | When ngspice runs in server mode, it writes on the standard output an header and then the simulation 5 | output in binary format. At the end of the simulation, it writes on the standard error a line of 6 | the form: 7 | 8 | .. code:: 9 | 10 | @@@ \d+ \d+ 11 | 12 | where the second number is the number of points of the simulation. Due to the iterative and 13 | adaptive nature of a transient simulation, the number of points is only known at the end. 14 | 15 | Any line starting with "Error" in the standard output indicates an error in the simulation process. 16 | The line "run simulation(s) aborted" in the standard error indicates the simulation aborted. 17 | 18 | Any line starting with *Warning* in the standard error indicates non critical error in the 19 | simulation process. 20 | 21 | """ 22 | 23 | #################################################################################################### 24 | 25 | import logging 26 | import re 27 | import os 28 | import numpy as np 29 | 30 | #################################################################################################### 31 | 32 | from PySpice.Spice.RawFile import RawFile, Variable 33 | from PySpice.Spice.Server import SpiceServer 34 | 35 | #################################################################################################### 36 | 37 | _module_logger = logging.getLogger(__name__) 38 | 39 | #################################################################################################### 40 | 41 | class LtSpiceServerXVII(SpiceServer): 42 | 43 | """This class wraps the execution of ngspice in server mode and convert the output to a Python data 44 | structure. 45 | 46 | Example of usage:: 47 | 48 | spice_server = SpiceServer(spice_command='/path/to/ngspice') 49 | raw_file = spice_server(spice_input) 50 | 51 | It returns a :obj:`PySpice.Spice.RawFile` instance. 52 | 53 | """ 54 | 55 | _logger = _module_logger.getChild('LTSpiceServer') 56 | 57 | ############################################## 58 | def __init__(self, spice_command='XVIIx64.exe'): 59 | 60 | self._spice_command = spice_command 61 | 62 | def _parse_log(self, logName): 63 | 64 | """Parse logfile for warnings and return the number of points.""" 65 | 66 | #Read in log file and check for errors 67 | file = open(logName,"r") 68 | log_lines = file.readlines() 69 | 70 | for line in log_lines: 71 | if 'Warning' in line: 72 | self._logger.warning(line[len('Warning :'):]) 73 | elif "Error" in line: 74 | raise NameError("Simulation aborted\n" + line) 75 | file.close() 76 | def _decode_number_of_points(self, line): 77 | 78 | """Decode the number of points in the given line.""" 79 | 80 | match = re.match(r'No. Points: +(\d+)', line) 81 | if match is not None: 82 | return int(match.group(1)) 83 | else: 84 | raise NameError("Cannot decode the number of points") 85 | 86 | def _parse_stdout(self, stdout): 87 | 88 | """Parse stdout for errors.""" 89 | 90 | # self._logger.debug('\n' + stdout) 91 | number_of_points = None 92 | error_found = False 93 | # UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 870: invalid start byte 94 | # lines = stdout.decode('utf-8').splitlines() 95 | lines = stdout.splitlines() 96 | for line_index, line in enumerate(lines): 97 | #print(line) 98 | if line.startswith(b'\x00'): 99 | line = line[1:] 100 | #print(line.decode('utf-16')) 101 | line = line.decode('utf-16').encode('utf-8') 102 | if line.startswith(b'Error '): 103 | error_found = True 104 | self._logger.error('\n' + line.decode('utf-8') + '\n' + lines[line_index+1].decode('utf-8')) 105 | if line.startswith(b'No. Points:'): 106 | number_of_points = self._decode_number_of_points(line.decode('utf-8')) 107 | if line.startswith(b'Binary:'): 108 | break 109 | if error_found: 110 | raise NameError("Errors was found by Spice") 111 | return number_of_points 112 | ############################################## 113 | def __call__(self, spice_input): 114 | 115 | """Run SPICE in server mode as a subprocess for the given input and return a 116 | :obj:`PySpice.RawFile.RawFile` instance. 117 | 118 | """ 119 | 120 | self._logger.info("Start the ltspice subprocess") 121 | 122 | file= open('temp.net','w') 123 | file.write(str(spice_input)) 124 | file.close() 125 | 126 | #command = "{} -b -ascii {}".format(self._spice_command,'temp.net') 127 | command = "{} -b {}".format(self._spice_command,'temp.net') 128 | self._logger.info("Command is "+command) 129 | os.system(command) 130 | 131 | self._parse_log('temp.log') 132 | 133 | file = open ('temp.raw','rb') 134 | stdout = file.read() 135 | number_of_points =self._parse_stdout(stdout) 136 | 137 | if number_of_points is None: 138 | raise NameError("The number of points was not found in the standard error buffer," 139 | " ngspice returned:\n" + 140 | stdout) 141 | 142 | return LtRawFile(stdout, number_of_points) 143 | 144 | class LtRawFile(RawFile): 145 | def __init__(self, stdout, number_of_points): 146 | super().__init__(stdout, number_of_points) 147 | def _read_header(self, stdout): 148 | 149 | """ Parse the header """ 150 | 151 | binary_line = 'Binary:\n'.encode('utf-16') 152 | binary_line = binary_line[2:] 153 | binary_location = stdout.find(binary_line) 154 | if binary_location < 0: 155 | raise NameError('Cannot locate binary data') 156 | raw_data_start = binary_location + len(binary_line) 157 | # self._logger.debug('\n' + stdout[:raw_data_start].decode('utf-8')) 158 | header_lines = stdout[:binary_location].decode('utf-16').encode('utf-8').splitlines() 159 | raw_data = stdout[raw_data_start:] 160 | header_line_iterator = iter(header_lines) 161 | 162 | ##Todo esto esta en el .log PDTE 163 | #self.circuit = self._read_header_field_line(header_line_iterator, 'Circuit') 164 | #self.temperature = self._read_header_line(header_line_iterator, 'Doing analysis at TEMP') 165 | #self.warnings = [self._read_header_field_line(header_line_iterator, 'Warning') 166 | # for i in range(stdout.count(b'Warning'))] 167 | #for warning in self.warnings: 168 | # self._logger.warn(warning) 169 | 170 | self.title = self._read_header_field_line(header_line_iterator, 'Title') 171 | self.date = self._read_header_field_line(header_line_iterator, 'Date') 172 | self.plot_name = self._read_header_field_line(header_line_iterator, 'Plotname') 173 | self.flags = self._read_header_field_line(header_line_iterator, 'Flags') 174 | self.number_of_variables = int(self._read_header_field_line(header_line_iterator, 'No. Variables')) 175 | self._read_header_field_line(header_line_iterator, 'No. Points') 176 | self._read_header_field_line(header_line_iterator, 'Offset') 177 | self._read_header_field_line(header_line_iterator, 'Command') 178 | self._read_header_field_line(header_line_iterator, 'Variables', has_value=False) 179 | 180 | self.variables = {} 181 | for i in range(self.number_of_variables): 182 | line = (next(header_line_iterator)).decode('utf-8') 183 | self._logger.debug(line) 184 | items = [x.strip() for x in line.split('\t') if x] 185 | # 0 frequency frequency grid=3 186 | index, name, unit = items[:3] 187 | name=str(name).lower() 188 | self.variables[name] = Variable(index, name, unit) 189 | # self._read_header_field_line(header_line_iterator, 'Binary', has_value=False) 190 | 191 | return raw_data 192 | 193 | def _read_variable_data(self, raw_data): 194 | 195 | """ Read the raw data and set the variable values. """ 196 | 197 | if('real' in self.flags): 198 | number_of_columns = self.number_of_variables 199 | elif('complex' in self.flags): 200 | number_of_columns = 2*self.number_of_variables 201 | else: 202 | raise NotImplementedError 203 | 204 | if('Transient' in self.plot_name): #Tran 205 | number_of_columns = self.number_of_variables+1 206 | input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='float32') 207 | input_data = input_data.reshape((self.number_of_points, number_of_columns)) 208 | input_data = input_data.transpose() 209 | time = input_data [0:2] 210 | tmpdata= time.transpose().flatten().tostring() 211 | time=np.fromstring(tmpdata, count=self.number_of_points, dtype='float64') 212 | time=np.absolute(time) 213 | input_data = input_data [1:] 214 | input_data[0]=time 215 | else: 216 | input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='float64') 217 | input_data = input_data.reshape((self.number_of_points, number_of_columns)) 218 | input_data = input_data.transpose() 219 | #input_data = input_data [1:] 220 | #np.savetxt('raw.txt', input_data) 221 | if 'complex' in self.flags: 222 | raw_data = input_data 223 | input_data = np.array(raw_data[0::2], dtype='complex64') 224 | input_data.imag = raw_data[1::2] 225 | for variable in self.variables.values(): 226 | variable.data = input_data[variable.index] 227 | 228 | def _read_variable_data_assci(self, raw_data): 229 | 230 | """ Read the raw data and set the variable values. """ 231 | 232 | if('real' in self.flags): 233 | number_of_columns = self.number_of_variables+1 234 | elif('complex' in self.flags): 235 | number_of_columns = 2*self.number_of_variables+1 236 | else: 237 | raise NotImplementedError 238 | print (np.version.version) 239 | tmpdata=raw_data.replace(b'\r\n',b'\t') 240 | tmpdata=tmpdata.replace(b'\t\t',b'\t') 241 | tmpdata=tmpdata.replace(b',',b'\t') 242 | 243 | input_data = np.fromstring(tmpdata.decode('utf-8'), count=number_of_columns*self.number_of_points, dtype='f8',sep='\t') 244 | input_data = input_data.reshape((self.number_of_points, number_of_columns)) 245 | input_data = input_data.transpose() 246 | input_data = input_data [1:] 247 | np.savetxt('raw.txt', input_data) 248 | if 'complex' in self.flags: 249 | raw_data = input_data 250 | input_data = np.array(raw_data[0::2], dtype='complex64') 251 | input_data.imag = raw_data[1::2] 252 | for variable in self.variables.values(): 253 | variable.data = input_data[variable.index] 254 | 255 | #"c:\\Program Files (x86)\\LTC\\LTspiceIV\\scad3.exe" 256 | # c:\\tmp\\EWB\\LTSpcieIV\\scad3.exe 257 | def enableLtSpiceServerXVII(simulator, spice_command='"c:\\Program Files\\LTC\\LTspiceXVII\\XVIIx64.exe"'): 258 | simulator._options.pop('filetype') 259 | simulator._options.pop('NOINIT') 260 | simulator._spice_server= LtSpiceServerXVII(spice_command=spice_command) -------------------------------------------------------------------------------- /PySpiceDvTools/Loads.py: -------------------------------------------------------------------------------- 1 | from PySpice.Spice.Netlist import SubCircuitFactory 2 | from PySpice.Unit import * 3 | 4 | 5 | class NonLinearLoad(SubCircuitFactory): 6 | """Non Linear Load Subcircuit""" 7 | __name__ = 'NonLinearLoad' 8 | __nodes__ = ('Live', 'Neutral') 9 | __diodeModel__= None 10 | ############################################## 11 | def __init__(self, 12 | diodeModel=None 13 | ): 14 | 15 | super().__init__() 16 | if(diodeModel==None): 17 | diodeModel = 'DefaultD' 18 | self.model('DefaultD', 'D') 19 | __diodeModel__=diodeModel 20 | 21 | self.D('1','Neutral','NPOS', model=diodeModel) 22 | self.D('2','Live','NPOS',model=diodeModel) 23 | self.D('3','NNEG','Live',model=diodeModel) 24 | self.D('4','NNEG','Neutral',model=diodeModel) 25 | 26 | class BasicNonLinearLoad(NonLinearLoad): 27 | """Non Linear Load Subcircuit""" 28 | __name__ = 'BasicNonLinearLoad' 29 | 30 | def __init__(self, r, l, 31 | diodeModel=None 32 | ): 33 | 34 | super().__init__(diodeModel=diodeModel) 35 | self.R('1','NPOS','N001',r) 36 | self.L('1','N001','NNEG',l) 37 | 38 | def setRValue(self,r): 39 | self['R1'].resistance=r 40 | def getRValue(self): 41 | return self['R1'].resistance 42 | def setLValue(self,l): 43 | self['L1'].inductance=l 44 | def getLValue(self): 45 | return self['L1'].inductance 46 | 47 | class RlcNonLinearLoad(NonLinearLoad): 48 | """Non Linear Load Subcircuit""" 49 | __name__ = 'RlcNonLinearLoad' 50 | 51 | def __init__(self, r, l, c, 52 | diodeModel=None 53 | ): 54 | 55 | super().__init__(diodeModel=diodeModel) 56 | self.R('1','NPOS','N001',r) 57 | self.L('1','N001','NNEG',l) 58 | self.C('1','NPOS','NNEG',c) 59 | 60 | def setRValue(self,r): 61 | self['R1'].resistance=r 62 | def getRValue(self): 63 | return self['R1'].resistance 64 | def setLValue(self,l): 65 | self['L1'].inductance=l 66 | def getLValue(self): 67 | return self['L1'].inductance 68 | def setCValue(self,c): 69 | self['C1'].capacitance=l 70 | def getCValue(self): 71 | return self['C1'].capacitance 72 | 73 | class RlcNonLinearLoadFullSerie(NonLinearLoad): 74 | """Non Linear Load Subcircuit""" 75 | __name__ = 'RlcNonLinearLoadFullSerie' 76 | 77 | def __init__(self, r, l, c, 78 | diodeModel=None 79 | ): 80 | 81 | super().__init__(diodeModel=diodeModel) 82 | self.R('1','NPOS','N001',r) 83 | self.L('1','N001','N002',l) 84 | self.C('1','N002','NNEG',c) 85 | 86 | def setRValue(self,r): 87 | self['R1'].resistance=r 88 | def getRValue(self): 89 | return self['R1'].resistance 90 | def setLValue(self,l): 91 | self['L1'].inductance=l 92 | def getLValue(self): 93 | return self['L1'].inductance 94 | def setCValue(self,c): 95 | self['C1'].capacitance=l 96 | def getCValue(self): 97 | return self['C1'].capacitance 98 | 99 | 100 | class RlcNonLinearLoadFullParallel(NonLinearLoad): 101 | """Non Linear Load Subcircuit""" 102 | __name__ = 'RlcNonLinearLoadFullParallel' 103 | 104 | def __init__(self, r, l, c, 105 | diodeModel=None 106 | ): 107 | 108 | super().__init__(diodeModel=diodeModel) 109 | self.R('1','NPOS','NNEG',r) 110 | self.L('1','NPOS','NNEG',l) 111 | self.C('1','NPOS','NNEG',c) 112 | 113 | def setRValue(self,r): 114 | self['R1'].resistance=r 115 | def getRValue(self): 116 | return self['R1'].resistance 117 | def setLValue(self,l): 118 | self['L1'].inductance=l 119 | def getLValue(self): 120 | return self['L1'].inductance 121 | def setCValue(self,c): 122 | self['C1'].capacitance=l 123 | def getCValue(self): 124 | return self['C1'].capacitance -------------------------------------------------------------------------------- /PySpiceDvTools/SkFilters.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PySpice.Spice.Netlist import SubCircuitFactory 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | 12 | class NarrowBandPassFilterInverted(SubCircuitFactory): 13 | _Gain = None 14 | _Fc = None 15 | _BW = None 16 | _C = None 17 | 18 | _Q = None 19 | _R3 = None 20 | _Req = None 21 | _a = None 22 | _R2 = None 23 | _R1 = None 24 | 25 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 26 | 27 | def _calc_values(self): 28 | """ 29 | Q=Fc/Bw 30 | 𝑅3=𝑄/(𝜋 𝐶 𝐹𝑐) 31 | 𝑅𝑒𝑞=𝑅3/(4 𝑄^2) 32 | 𝛼=(2𝑄^2)/𝐺 33 | 𝑅2=𝑅𝑒𝑞*((1+𝛼)/𝛼) 34 | 𝑅1=𝛼𝑅2 35 | """ 36 | self._Q =self._Fc/self._BW 37 | self._R3 = self._Q/(np.pi*self._C*self._Fc) 38 | self._Req=self._R3/(4*pow(self._Q,2) ) 39 | self._a=(2*pow(self._Q,2))/self._Gain 40 | self._R2=self._Req*(1+self._a)/self._a 41 | self._R1=self._a*self._R2 42 | 43 | def __init__(self, opAmpModel=None, C=nano(100), Gain=1, Fc=50, BW=10, name = None): 44 | super().__init__() 45 | 46 | if opAmpModel == None: 47 | raise NotImplementedError("This Filters needs An Operational Model") 48 | 49 | self._C=float(C) 50 | self._Gain =float(Gain) 51 | self._Fc=float(Fc) 52 | self._BW=float(BW) 53 | 54 | if name == None: 55 | name = 'nbpfi_{}_{}_{}'.format(Fc,BW,Gain) 56 | self.__name__= name 57 | self.name = name 58 | self._calc_values() 59 | self.X('1',opAmpModel,'Ref','N002','V+','V-','Out') 60 | self.C('1','N001','Out',self._C) 61 | self.C('2','N001','N002',self._C) 62 | self.R('1','N001','In',self._R1) 63 | self.R('2','V-','N001',self._R2) 64 | self.R('3','N002','Out',self._R3) 65 | 66 | class SKHighPassFilter(SubCircuitFactory): 67 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 68 | 69 | _Gain = None 70 | _Fc = None 71 | _C1 = None 72 | _C2 = None 73 | 74 | _R2 = None 75 | _R1 = None 76 | _R3 = None 77 | _R4 = None 78 | 79 | _Q = None 80 | 81 | def __init__(self, opAmpModel=None, R1=None, R2= None, R3=None, R4=None, C1=None,C2=None, name = None): 82 | super().__init__() 83 | if opAmpModel == None: 84 | raise NotImplementedError("This Filters needs An Operational Model") 85 | 86 | if self._C1==None: 87 | self._C1=C1 88 | if self._C2==None: 89 | self._C2=C2 90 | 91 | if self._R1==None: 92 | self._R1=R1 93 | if self._R2==None: 94 | self._R2=R2 95 | if self._R3==None: 96 | self._R3=R3 97 | if self._R4==None: 98 | self._R4=R4 99 | if self.__name__==None: 100 | self.__name__=name 101 | self._name = name 102 | if self.__name__==None: 103 | self.__name__='skhpfi' 104 | self._name = name 105 | 106 | self.R('1','N001','Out',self._R1) 107 | self.R('2','N002','Ref',self._R2) 108 | self.C('1','N001','In',self._C1) 109 | self.C('2','N002','N001',self._C2) 110 | 111 | if (R3 == None) & (R4 == None): 112 | self._Gain=1; 113 | # + - Vp Vn out 114 | self.X('1',opAmpModel,'N002','Out','V+','V-','Out') 115 | elif(R3 != None) & (R4 != None): 116 | self.R('3','N003','Ref',self._R3) 117 | self.R('4','N003','Out',self._R4) 118 | self.X('1',opAmpModel,'N002','N003','V+','V-','Out') 119 | self._Gain=1+R4/R3 120 | else: 121 | raise NotImplementedError("R3 and R4 goes in tandem") 122 | 123 | 124 | class SKHighPassFilterInverted(SKHighPassFilter): 125 | def _calc_values(self): 126 | """ 127 | K= - 1, Solve R 128 | C1=C2 129 | 130 | Gain = 1 131 | W0=2πFc; 132 | R1= 1/(2Q W0 C) 133 | R2= 2Q/(W0 C) 134 | """ 135 | w0=2*np.pi*self._Fc 136 | self._R1 = 1/(2*self._Q*w0*self._C1) 137 | self._R2=(2*self._Q)/(self._C1 * w0) 138 | 139 | 140 | def __init__(self, opAmpModel=None, C=nano(100), Fc=50, Q=1, name = None): 141 | 142 | if opAmpModel == None: 143 | raise NotImplementedError("This Filters needs An Operational Model") 144 | Gain=1; 145 | self._C1=float(C) 146 | self._C2=float(C) 147 | self._Gain =float(Gain) 148 | self._Fc=float(Fc) 149 | self._Q=float(Q) 150 | 151 | if name == None: 152 | name = 'skhpfi_{}_{}'.format(Fc,Gain) 153 | self.__name__= name 154 | self._name = name 155 | self._calc_values() 156 | super().__init__(opAmpModel=opAmpModel) 157 | 158 | 159 | class SKLowPassFilter(SubCircuitFactory): 160 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 161 | 162 | _Gain = None 163 | _Fc = None 164 | _C1 = None 165 | _C2 = None 166 | 167 | _R2 = None 168 | _R1 = None 169 | _R3 = None 170 | _R4 = None 171 | 172 | _Q = None 173 | 174 | def __init__(self, opAmpModel=None, R1=None, R2= None, R3=None, R4=None, C1=None,C2=None, name = None): 175 | super().__init__() 176 | if opAmpModel == None: 177 | raise NotImplementedError("This Filters needs An Operational Model") 178 | 179 | if self._C1==None: 180 | self._C1=C1 181 | if self._C2==None: 182 | self._C2=C2 183 | 184 | if self._R1==None: 185 | self._R1=R1 186 | if self._R2==None: 187 | self._R2=R2 188 | if self._R3==None: 189 | self._R3=R3 190 | if self._R4==None: 191 | self._R4=R4 192 | if self.__name__==None: 193 | self.__name__=name 194 | self._name = name 195 | if self.__name__==None: 196 | self.__name__='sklpfi' 197 | self._name = name 198 | 199 | self.R('1','N001','In',self._R1) 200 | self.R('2','N002','N001',self._R2) 201 | self.C('1','N001','Out',self._C1) 202 | self.C('2','N002','Ref',self._C2) 203 | 204 | if (R3 == None) & (R4 == None): 205 | self._Gain=1; 206 | # + - Vp Vn out 207 | self.X('1',opAmpModel,'N002','Out','V+','V-','Out') 208 | elif(R3 != None) & (R4 != None): 209 | self.R('3','N003','Ref',self._R3) 210 | self.R('4','N003','Out',self._R4) 211 | self.X('1',opAmpModel,'N002','N003','V+','V-','Out') 212 | self._Gain=1+R4/R3 213 | else: 214 | raise NotImplementedError("R3 and R4 goes in tandem") 215 | 216 | 217 | class SKLowPassFilterInverted(SKLowPassFilter): 218 | 219 | def _calc_values(self): 220 | """ 221 | K= - 1, Solve C 222 | Gain = 1 223 | W0=2πFc; 224 | C1= (Q/W0)*(1/R1+1/R2) 225 | C2= 1/(Q W0 (R1+R2)) 226 | """ 227 | w0=2*np.pi*self._Fc 228 | self._C1=(self._Q/w0)*(1/self._R1+1/self._R2) 229 | self._C2=1/(self._Q*w0 *(self._R1+self._R2)) 230 | 231 | 232 | def __init__(self, opAmpModel=None, R1=kilo(100), R2= kilo(100), Fc=350, Q=1, name = None): 233 | self._R1=float(R1) 234 | self._R2=float(R2) 235 | self._Fc=float(Fc) 236 | self._Q=float(Q) 237 | if name == None: 238 | name = 'sklpfi_{}_{}'.format(Fc,1) 239 | self.__name__= name 240 | self._name = name 241 | self._calc_values() 242 | super().__init__(opAmpModel=opAmpModel) 243 | 244 | 245 | class SKWideBandPassFilterInverted(SubCircuitFactory): 246 | __nodes__ = ('In', 'Out', 'Ref', 'V+','V-') 247 | _lp=None 248 | _hp=None 249 | 250 | def attach(self, circuit): 251 | circuit.subcircuit(self) 252 | circuit.subcircuit(self._lp) 253 | circuit.subcircuit(self._hp) 254 | 255 | def __init__(self, opAmpModel=None, C=nano(100), R1=kilo(100), R2= kilo(100), Finf=50, Fsup=350, name = None): 256 | super().__init__() 257 | Gain=1 258 | if opAmpModel == None: 259 | raise NotImplementedError("This Filters needs An Operational Model") 260 | 261 | if name == None: 262 | name = 'skwbpfi_{}_{}_{}'.format(Finf,Fsup,Gain) 263 | self.__name__= name 264 | self._name = name 265 | self._lp=SKLowPassFilterInverted(opAmpModel=opAmpModel, R1=R1, R2=R2,Fc=Fsup) 266 | self._hp=SKHighPassFilterInverted(opAmpModel=opAmpModel,C=C,Fc=Finf) 267 | self.X('1',self._hp.name,'In','N001','Ref','V+','V-') 268 | self.X('2',self._lp.name,'N001','Out','Ref','V+','V-') 269 | 270 | -------------------------------------------------------------------------------- /PySpiceDvTools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielvilas/PySpice-Library/999c3850fd69bab58fc44f39571b292aaeb626ab/PySpiceDvTools/__init__.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PySpice-Library 2 | Library of componentes for PySpice 3 | 4 | ## How To install 5 | ''' 6 | pip3 install numpy 7 | pip3 install PySpice 8 | pip3 install matplotlib 9 | ''' -------------------------------------------------------------------------------- /RunSimulation.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | from PySpiceDvTools.Loads import * 13 | from AppliancesDetector.Appliances import * 14 | from AppliancesDetector.Sonda import * 15 | 16 | circuit = Circuit('Sensor Sim') 17 | circuit.SinusoidalVoltageSource('1', 'A', circuit.gnd, amplitude=220, frequency=50) 18 | 19 | subcir= MicroOndas1200() 20 | circuit.subcircuit(subcir) 21 | circuit.X('1', subcir.name, 'A', 'SIn') 22 | subcir = Sonda() 23 | circuit.subcircuit(subcir) 24 | circuit.X('2', subcir.name, 'SIn',circuit.gnd,'VSense',circuit.gnd) 25 | 26 | print (circuit) 27 | simulator = circuit.simulator() 28 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 29 | 30 | current = analysis['V1'] 31 | aimax = np.amax(current.data) 32 | aimin = np.amin(current.data) 33 | print ('Max Current: ',aimax) 34 | print ('Min Current: ',aimin) 35 | 36 | figure1 = plt.figure(1, (20, 10)) 37 | plt.subplot(211) 38 | plt.plot(analysis.time, current, '-') 39 | plt.grid() 40 | plt.title('Current') 41 | plt.xlabel('time') 42 | plt.ylabel('Amps') 43 | plt.axhline(y=aimax,color='red') 44 | plt.axhline(y=aimin,color='red') 45 | yticks, ytlabels =plt.yticks() 46 | yticks[-1]=aimax 47 | yticks[-2]=aimin 48 | plt.yticks(yticks) 49 | 50 | Vsense = analysis['VSense'] 51 | plt.subplot(212) 52 | plt.plot(analysis.time, Vsense, '-') 53 | plt.grid() 54 | plt.title('Current Sensed') 55 | plt.xlabel('time') 56 | plt.ylabel('Voltaje') 57 | plt.show() -------------------------------------------------------------------------------- /SKFilterDesign.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | 13 | from PySpice.Plot.BodeDiagram import bode_diagram 14 | 15 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 16 | from PySpiceDvTools.Filters import * 17 | from PySpiceDvTools.SkFilters import * 18 | #from AppliancesDetector.Filters import * 19 | 20 | def createCircuit(filter1o,filter2o): 21 | circuit = Circuit('Filter') 22 | circuit.include('Models/BasicOpamp.cir') 23 | circuit.include('Models/AD8619.cir') 24 | circuit.include('Models/TL084.cir') 25 | circuit.subcircuit(filter1o) 26 | circuit.subcircuit(filter2o) 27 | 28 | circuit.V('1','5V',circuit.gnd,'5') 29 | circuit.V('2','VRef',circuit.gnd,'2.5') 30 | circuit.SinusoidalVoltageSource('In', 'In', 'VRef', amplitude=1) 31 | circuit.X('1',filter1o.name,'In','out1o','VRef','5V',circuit.gnd) 32 | circuit.X('2',filter2o.name,'In','out2o','VRef','5V',circuit.gnd) 33 | 34 | print(circuit) 35 | return circuit 36 | 37 | def simulateAndPrint(figure1, ax1, ax2, circuit, fc0, fc1=None): 38 | simulator = circuit.simulator() 39 | # enableLtSpice(simulator, spice_command='/Applications/LTspice.app/Contents/MacOS/LTspice') 40 | 41 | analysis = simulator.ac(start_frequency=10@u_Hz, stop_frequency=5@u_kHz, number_of_points=500, variation='dec') 42 | 43 | print('Simulated, Bode plotting...') 44 | 45 | bode_diagram(axes=(figure1.add_subplot(ax1), figure1.add_subplot(ax2)), 46 | frequency=analysis.frequency, 47 | gain=20*np.log10(np.absolute(analysis.out1o)), 48 | #gain=np.absolute(analysis.out50), 49 | phase=np.angle(analysis.out1o, deg=False), 50 | marker='', 51 | color='blue', 52 | linestyle='-', 53 | ) 54 | bode_diagram(axes=(figure1.add_subplot(ax1), figure1.add_subplot(ax2)), 55 | frequency=analysis.frequency, 56 | gain=20*np.log10(np.absolute(analysis.out2o)), 57 | #gain=np.absolute(analysis.out50), 58 | phase=np.angle(analysis.out2o, deg=False), 59 | marker='', 60 | color='red', 61 | linestyle='-', 62 | ) 63 | figure1.add_subplot(ax1) 64 | plt.axvline(x=fc0, linewidth=0.5, color='k') 65 | if fc1 != None: 66 | plt.axvline(x=fc1, linewidth=0.5, color='k') 67 | figure1.add_subplot(ax2) 68 | plt.axvline(x=fc0, linewidth=0.5, color='k') 69 | if fc1 != None: 70 | plt.axvline(x=fc1, linewidth=0.5, color='k') 71 | 72 | def testModel(model): 73 | figure1 = plt.figure(1, (20, 10)) 74 | plt.title('Bode Diagram of Salen Key vs 1 Order ({})'.format(model)) 75 | 76 | filterL1 = SKLowPassFilterInverted(opAmpModel=model, Fc=350) 77 | filterL2 = SKLowPassFilter(opAmpModel=model,R1=470,R2=kilo(3), C1=micro(1),C2=nano(100),name='sklpfi_400') 78 | circuit = createCircuit(filterL1,filterL2) 79 | simulateAndPrint(figure1,231,234, circuit,350) 80 | 81 | filterH1 = SKHighPassFilterInverted(opAmpModel=model, Fc=50) 82 | filterH2 = SKHighPassFilter(opAmpModel=model, R1=kilo(1), R2=kilo(33), C1=micro(4.7),C2=nano(100),name='skhpfi_40') 83 | circuit = createCircuit(filterH1,filterH2) 84 | simulateAndPrint(figure1,232,235,circuit,50) 85 | 86 | filter1o = CascadeFilter(fa=filterL1,fb=filterH1,name='wide_50_350') 87 | filter2o = CascadeFilter(fa=filterL2,fb=filterH2,name='wide_40_400') 88 | circuit = createCircuit(filter1o,filter2o) 89 | filter1o.attach(circuit) 90 | filter2o.attach(circuit) 91 | simulateAndPrint(figure1,233,236,circuit,50,350) 92 | 93 | plt.tight_layout() 94 | plt.show() 95 | testModel(model='BasicOpamp') 96 | testModel(model='AD8619') 97 | testModel(model='TL084') 98 | -------------------------------------------------------------------------------- /SKFilterTest.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | 13 | from PySpice.Plot.BodeDiagram import bode_diagram 14 | 15 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 16 | from PySpiceDvTools.Filters import * 17 | from PySpiceDvTools.SkFilters import * 18 | #from AppliancesDetector.Filters import * 19 | 20 | def createCircuit(filter1o,filter2o): 21 | circuit = Circuit('Filter') 22 | circuit.include('Models/BasicOpamp.cir') 23 | circuit.include('Models/AD8619.cir') 24 | circuit.include('Models/TL084.cir') 25 | circuit.subcircuit(filter1o) 26 | circuit.subcircuit(filter2o) 27 | 28 | circuit.V('1','5V',circuit.gnd,'5') 29 | circuit.V('2','VRef',circuit.gnd,'2.5') 30 | circuit.SinusoidalVoltageSource('In', 'In', 'VRef', amplitude=1) 31 | circuit.X('1',filter1o.name,'In','out1o','VRef','5V',circuit.gnd) 32 | circuit.X('2',filter2o.name,'In','out2o','VRef','5V',circuit.gnd) 33 | 34 | print(circuit) 35 | return circuit 36 | 37 | def simulateAndPrint(figure1, ax1, ax2, circuit, fc0, fc1=None): 38 | simulator = circuit.simulator() 39 | # enableLtSpice(simulator, spice_command='/Applications/LTspice.app/Contents/MacOS/LTspice') 40 | 41 | analysis = simulator.ac(start_frequency=10@u_Hz, stop_frequency=5@u_kHz, number_of_points=500, variation='dec') 42 | 43 | print('Simulated, Bode plotting...') 44 | 45 | bode_diagram(axes=(figure1.add_subplot(ax1), figure1.add_subplot(ax2)), 46 | frequency=analysis.frequency, 47 | gain=20*np.log10(np.absolute(analysis.out1o)), 48 | #gain=np.absolute(analysis.out50), 49 | phase=np.angle(analysis.out1o, deg=False), 50 | marker='', 51 | color='blue', 52 | linestyle='-', 53 | ) 54 | bode_diagram(axes=(figure1.add_subplot(ax1), figure1.add_subplot(ax2)), 55 | frequency=analysis.frequency, 56 | gain=20*np.log10(np.absolute(analysis.out2o)), 57 | #gain=np.absolute(analysis.out50), 58 | phase=np.angle(analysis.out2o, deg=False), 59 | marker='', 60 | color='red', 61 | linestyle='-', 62 | ) 63 | figure1.add_subplot(ax1) 64 | plt.axvline(x=fc0, linewidth=0.5, color='k') 65 | if fc1 != None: 66 | plt.axvline(x=fc1, linewidth=0.5, color='k') 67 | figure1.add_subplot(ax2) 68 | plt.axvline(x=fc0, linewidth=0.5, color='k') 69 | if fc1 != None: 70 | plt.axvline(x=fc1, linewidth=0.5, color='k') 71 | 72 | def testModel(model): 73 | figure1 = plt.figure(1, (20, 10)) 74 | plt.title('Bode Diagram of Salen Key vs 1 Order ({})'.format(model)) 75 | 76 | filter1o = LowPassFilterInverted(opAmpModel=model, Fc=350) 77 | filter2o = SKLowPassFilterInverted(opAmpModel=model, Fc=350) 78 | circuit = createCircuit(filter1o,filter2o) 79 | simulateAndPrint(figure1,231,234, circuit,350) 80 | 81 | filter1o = HighPassFilterInverted(opAmpModel=model, Fc=50) 82 | filter2o = SKHighPassFilterInverted(opAmpModel=model, Fc=50) 83 | circuit = createCircuit(filter1o,filter2o) 84 | simulateAndPrint(figure1,232,235,circuit,50) 85 | 86 | filter1o = WideBandPassFilterInverted(opAmpModel=model, Finf=50, Fsup=350) 87 | filter2o = SKWideBandPassFilterInverted(opAmpModel=model, Finf=50, Fsup=350) 88 | circuit = createCircuit(filter1o,filter2o) 89 | filter1o.attach(circuit) 90 | filter2o.attach(circuit) 91 | simulateAndPrint(figure1,233,236,circuit,50,350) 92 | 93 | plt.tight_layout() 94 | plt.show() 95 | testModel(model='BasicOpamp') 96 | testModel(model='AD8619') 97 | testModel(model='TL084') 98 | -------------------------------------------------------------------------------- /SearchR.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | import PySpice.Logging.Logging as Logging 5 | logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | from PySpiceDvTools.Loads import * 13 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 14 | 15 | circuit = Circuit('Sensor Sim') 16 | circuit.SinusoidalVoltageSource('1', 'A', circuit.gnd, amplitude=220, frequency=50) 17 | 18 | #subcir= RlcNonLinearLoadFullSerie(r=50,l=.35,c=nano(35)) 19 | subcir= RlcNonLinearLoadFullParallel(r=200,l=150,c=pico(200)) 20 | circuit.subcircuit(subcir) 21 | circuit.X('1', subcir.name, 'A', circuit.gnd) 22 | 23 | print (circuit) 24 | simulator = circuit.simulator() 25 | simulator._options['method']='gear' 26 | simulator._options['maxord']='6' 27 | simulator._options['reltol']='0.1' 28 | simulator._options['pivrel']='0.1' 29 | simulator._options['abstol']='1e-10' 30 | analysis = simulator.transient(step_time=100@u_us, end_time=5.2@u_s, start_time=5@u_s) 31 | 32 | current = analysis['V1'] 33 | aimax = np.amax(current.data) 34 | aimin = np.amin(current.data) 35 | print ('Max Current: ',aimax) 36 | print ('Min Current: ',aimin) 37 | 38 | figure1 = plt.figure(1, (20, 10)) 39 | #plt.subplot(211) 40 | plt.plot(analysis.time, current, '-') 41 | plt.grid() 42 | plt.title('Current') 43 | plt.xlabel('time') 44 | plt.ylabel('Amps') 45 | plt.axhline(y=aimax,color='red') 46 | plt.axhline(y=aimin,color='red') 47 | yticks, ytlabels =plt.yticks() 48 | yticks[-1]=aimax 49 | yticks[-2]=aimin 50 | plt.yticks(yticks) 51 | 52 | plt.show() -------------------------------------------------------------------------------- /SimulacionesFiltersBP.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | 13 | from PySpice.Plot.BodeDiagram import bode_diagram 14 | 15 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 16 | from PySpiceDvTools.Filters import * 17 | from AppliancesDetector.Filters import * 18 | 19 | 20 | circuit = circuit = Circuit('Filter') 21 | #circuit.include('Models/BasicOpamp.cir') 22 | circuit.include('Models/AD8619.cir') 23 | filter50 = NarrowBandPassFilterInverted(opAmpModel='AD8619', Fc=50) 24 | circuit.subcircuit(filter50) 25 | 26 | filter150 = NarrowBandPassFilterInverted(opAmpModel='AD8619', Fc=150) 27 | circuit.subcircuit(filter150) 28 | 29 | filter250 = NarrowBandPassFilterInverted(opAmpModel='AD8619', Fc=250) 30 | circuit.subcircuit(filter250) 31 | 32 | adder = Adder(opAmpModel='AD8619') 33 | circuit.subcircuit(adder) 34 | 35 | 36 | circuit.V('1','5V',circuit.gnd,'5') 37 | circuit.V('2','VRef',circuit.gnd,'2.5') 38 | circuit.SinusoidalVoltageSource('In', 'In', circuit.gnd, amplitude=1) 39 | circuit.X('1',filter50.name,'In','out50','VRef','5V',circuit.gnd) 40 | circuit.X('2',filter150.name,'In','out150','VRef','5V',circuit.gnd) 41 | circuit.X('3',filter250.name,'In','out250','VRef','5V',circuit.gnd) 42 | circuit.X('4',adder.name,'out50','out150','out250','out','VRef','5V',circuit.gnd) 43 | 44 | 45 | print(circuit) 46 | 47 | _C=nano(100) 48 | 49 | simulator = circuit.simulator() 50 | #enableLtSpice(simulator) 51 | 52 | analysis = simulator.ac(start_frequency=10@u_Hz, stop_frequency=1@u_kHz, number_of_points=200, variation='dec') 53 | 54 | print('Simulated, Bode plotting...') 55 | 56 | figure1 = plt.figure(1, (20, 10)) 57 | plt.title("Bode Diagram of a Low-Pass RC Filter") 58 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 59 | frequency=analysis.frequency, 60 | gain=20*np.log10(np.absolute(analysis.out50)), 61 | phase=np.angle(analysis.out50, deg=False), 62 | marker='', 63 | color='blue', 64 | linestyle='-', 65 | ) 66 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 67 | frequency=analysis.frequency, 68 | gain=20*np.log10(np.absolute(analysis.out150)), 69 | phase=np.angle(analysis.out150, deg=False), 70 | marker='', 71 | color='green', 72 | linestyle='-', 73 | ) 74 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 75 | frequency=analysis.frequency, 76 | gain=20*np.log10(np.absolute(analysis.out250)), 77 | phase=np.angle(analysis.out250, deg=False), 78 | marker='', 79 | color='red', 80 | linestyle='-', 81 | ) 82 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 83 | frequency=analysis.frequency, 84 | gain=20*np.log10(np.absolute(analysis.out)), 85 | phase=np.angle(analysis.out, deg=False), 86 | marker='', 87 | color='magenta', 88 | linestyle='-', 89 | ) 90 | plt.tight_layout() 91 | plt.show() -------------------------------------------------------------------------------- /SimulacionesFiltersHPLP.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | #import PySpice.Logging.Logging as Logging 5 | #logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | 13 | from PySpice.Plot.BodeDiagram import bode_diagram 14 | 15 | #from PySpiceDvTools.LTSpiceServer import enableLtSpice 16 | from PySpiceDvTools.Filters import * 17 | from AppliancesDetector.Filters import * 18 | 19 | circuit = circuit = Circuit('Filter') 20 | circuit.include('Models/BasicOpamp.cir') 21 | circuit.include('Models/AD8619.cir') 22 | circuit.include('Models/TL084.cir') 23 | 24 | filter50 = HighPassFilterInverted(opAmpModel='AD8619',Fc=50) 25 | circuit.subcircuit(filter50) 26 | filter2 = LowPassFilterInverted(opAmpModel='AD8619', Fc=350) 27 | circuit.subcircuit(filter2) 28 | filter3 = WideBandPassFilterInverted(opAmpModel='AD8619') 29 | filter3.attach(circuit) 30 | 31 | circuit.V('1','5V',circuit.gnd,'5') 32 | circuit.V('2','VRef',circuit.gnd,'2.5') 33 | circuit.SinusoidalVoltageSource('In', 'In', circuit.gnd, amplitude=1) 34 | circuit.X('1',filter50.name,'In','out50','VRef','5V',circuit.gnd) 35 | circuit.X('2',filter2.name,'In','out350','VRef','5V',circuit.gnd) 36 | circuit.X('3',filter2.name,'out50','out','VRef','5V',circuit.gnd) 37 | circuit.X('4',filter3.name,'In','out2','VRef','5V',circuit.gnd) 38 | 39 | print(circuit) 40 | 41 | simulator = circuit.simulator() 42 | #enableLtSpice(simulator, spice_command='/Applications/LTspice.app/Contents/MacOS/LTspice') 43 | 44 | analysis = simulator.ac(start_frequency=10@u_Hz, stop_frequency=5@u_kHz, number_of_points=500, variation='dec') 45 | 46 | print('Simulated, Bode plotting...') 47 | 48 | 49 | figure1 = plt.figure(1, (20, 10)) 50 | plt.title("Bode Diagram of a Low-Pass RC Filter") 51 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 52 | frequency=analysis.frequency, 53 | gain=20*np.log10(np.absolute(analysis.out50)), 54 | #gain=np.absolute(analysis.out50), 55 | phase=np.angle(analysis.out50, deg=False), 56 | marker='', 57 | color='blue', 58 | linestyle='-', 59 | ) 60 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 61 | frequency=analysis.frequency, 62 | gain=20*np.log10(np.absolute(analysis.out350)), 63 | #gain=np.absolute(analysis.out2), 64 | phase=np.angle(analysis.out350, deg=False), 65 | marker='', 66 | color='red', 67 | linestyle='-', 68 | ) 69 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 70 | frequency=analysis.frequency, 71 | gain=20*np.log10(np.absolute(analysis.out)), 72 | #gain=np.absolute(analysis.out2), 73 | phase=np.angle(analysis.out, deg=False), 74 | marker='', 75 | color='magenta', 76 | linestyle='-', 77 | ) 78 | bode_diagram(axes=(plt.subplot(211), plt.subplot(212)), 79 | frequency=analysis.frequency, 80 | gain=20*np.log10(np.absolute(analysis.out2)), 81 | #gain=np.absolute(analysis.out2), 82 | phase=np.angle(analysis.out2, deg=False), 83 | marker='', 84 | color='green', 85 | linestyle='-', 86 | ) 87 | plt.tight_layout() 88 | plt.show() 89 | -------------------------------------------------------------------------------- /SimulacionesPython.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | import PySpice.Logging.Logging as Logging 5 | logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | 13 | circuit = Circuit('NonLineal Load Sim') 14 | 15 | 16 | ''' 17 | V1 A 0 SINE(0 220 50) 18 | D1 0 N001 Def 19 | D2 A N001 Def 20 | D3 N003 A Def 21 | D4 N003 0 Deg 22 | R1 N001 N002 27.5 23 | L1 N002 N003 0.5 24 | .MODEL Def D 25 | ''' 26 | 27 | circuit.SinusoidalVoltageSource('1', 'A', circuit.gnd, amplitude=220, frequency=50) 28 | circuit.D('1',circuit.gnd,'N001', model='Def') 29 | circuit.D('2','A','N001',model='Def') 30 | circuit.D('3','N003','A',model='Def') 31 | circuit.D('4','N003',circuit.gnd,model='Def') 32 | circuit.R('1','N001','N002',27.5) 33 | circuit.L('1','N002','N003',0.5) 34 | circuit.model('Def', 'D') 35 | 36 | 37 | print(circuit) 38 | 39 | simulator = circuit.simulator() 40 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 41 | 42 | current = analysis['V1'] 43 | aimax = np.amax(current.data) 44 | aimin = np.amin(current.data) 45 | print ('Max Current: ',aimax) 46 | print ('Min Current: ',aimin) 47 | 48 | figure1 = plt.figure(1, (20, 10)) 49 | plt.plot(analysis.time, current, '-') 50 | plt.grid() 51 | plt.title('Current') 52 | plt.xlabel('time') 53 | plt.ylabel('Amps') 54 | plt.axhline(y=aimax,color='red') 55 | plt.axhline(y=aimin,color='red') 56 | yticks, ytlabels =plt.yticks() 57 | yticks[-1]=aimax 58 | yticks[-1]=aimin 59 | plt.yticks(yticks) 60 | plt.show() 61 | -------------------------------------------------------------------------------- /SimulacionesPythonLT.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | import PySpice.Logging.Logging as Logging 5 | logger = Logging.setup_logging() 6 | 7 | from PySpice.Spice.Netlist import Circuit 8 | from PySpice.Unit import * 9 | from PySpice.Spice.BasicElement import * 10 | from PySpice.Spice.HighLevelElement import * 11 | from PySpice.Spice.Simulation import * 12 | # from PySpiceDvTools.LTSpiceServer import * 13 | 14 | circuit = Circuit('NonLineal Load Sim') 15 | 16 | 17 | ''' 18 | V1 A 0 SINE(0 220 50) 19 | D1 0 N001 Def 20 | D2 A N001 Def 21 | D3 N003 A Def 22 | D4 N003 0 Deg 23 | R1 N001 N002 27.5 24 | L1 N002 N003 0.5 25 | .MODEL Def D 26 | ''' 27 | 28 | circuit.SinusoidalVoltageSource('1', 'A', circuit.gnd, amplitude=220, frequency=50) 29 | circuit.D('1',circuit.gnd,'N001', model='Def') 30 | circuit.D('2','A','N001',model='Def') 31 | circuit.D('3','N003','A',model='Def') 32 | circuit.D('4','N003',circuit.gnd,model='Def') 33 | circuit.R('1','N001','N002',27.5) 34 | circuit.L('1','N002','N003',0.5) 35 | circuit.model('Def', 'D') 36 | 37 | print(circuit) 38 | 39 | simulator = circuit.simulator() 40 | #simulator._options.pop('filetype') 41 | #simulator._options.pop('NOINIT') 42 | #simulator._spice_server= LtSpiceServer() 43 | analysis = simulator.transient(step_time=1@u_ms, end_time=1@u_s) 44 | for node in analysis.nodes.values(): 45 | print('Node {}: V'.format(str(node))) 46 | for node in analysis.branches.values(): 47 | print('branch {} A'.format(str(node))) 48 | current = analysis.v1 49 | print(current.data) 50 | aimax = np.amax(current.data) 51 | aimin = np.amin(current.data) 52 | 53 | print ('Max Current: ',aimax) 54 | print ('Min Current: ',aimin) 55 | 56 | figure1 = plt.figure(1, (20, 10)) 57 | plt.plot(analysis.time, current.data, '-') 58 | plt.grid() 59 | plt.title('Current') 60 | plt.xlabel('time') 61 | plt.ylabel('Amps') 62 | plt.axhline(y=aimax,color='red') 63 | plt.axhline(y=aimin,color='red') 64 | yticks, ytlabels =plt.yticks() 65 | yticks[-1]=aimax 66 | yticks[-1]=aimin 67 | plt.yticks(yticks) 68 | plt.show() 69 | -------------------------------------------------------------------------------- /spinit: -------------------------------------------------------------------------------- 1 | * For SPICE2 POLYs, edit the below line to point to the location 2 | * of your codemodel. 3 | 4 | * codemodel C:/Spice/lib/ngspice/spice2poly.cm 5 | 6 | * The other codemodels 7 | * codemodel C:/Spice/lib/ngspice/analog.cm 8 | * codemodel C:/Spice/lib/ngspice/digital.cm 9 | * codemodel C:/Spice/lib/ngspice/xtradev.cm 10 | * codemodel C:/Spice/lib/ngspice/xtraevt.cm --------------------------------------------------------------------------------