#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 2 11:22:31 2023
@author: mariano
"""
import sympy as sp
from os import path
import shutil
import time
from numbers import Real
from .general import pytc2_full_path, get_home_directory
############################################
#%% Variables para la interfaz con LTspice #
############################################
ltux = 16
"""
Unidades X para dibujar en la hoja de LTspice
"""
ltuy = 16
"""
Unidades Y para dibujar en la hoja de LTspice
"""
filename_eq_base = 'ltspice_equalizador_base'
"""
Archivo marco contenedor de las redes sintetizadas como ecualizadores/filtros
"""
cap_num = 1
"""
cuenta de capacitores
"""
res_num = 1
"""
cuenta de resistores
"""
ind_num = 1
"""
cuenta de inductores
"""
node_num = 1
"""
cuenta de nodos
"""
cur_x = 0
"""
cursor X para la localización de componentes
"""
cur_y = 0
"""
cursor Y para la localización de componentes
"""
lt_wire_length = 4 # ltux/ltuy unidades normalizadas
"""
tamaño estandard del cable
"""
#####
# Palabras clave del LTspice para disponer los componentes en el
# esquemático.
#
res_der_str = [ 'SYMBOL res {:d} {:d} R0\n', # posición absoluta X-Y en el esquemático
'WINDOW 0 48 43 Left 2\n', # posiciones relativas de etiquetas
'WINDOW 3 47 68 Left 2\n', # posiciones relativas de etiquetas
'SYMATTR InstName {:s}\n', # etiqueta que tendrá
'SYMATTR Value {:3.5f}\n' # valor que tendrá
]
"""
resistor en derivacion
"""
ind_der_str = [ 'SYMBOL ind {:d} {:d} R0\n', # posición absoluta X-Y en el esquemático
'WINDOW 0 47 34 Left 2\n', # posiciones relativas de etiquetas
'WINDOW 3 43 65 Left 2\n', # posiciones relativas de etiquetas
'SYMATTR InstName {:s}\n', # etiqueta que tendrá
'SYMATTR Value {:3.5f}\n' # valor que tendrá
]
"""
inductor en derivacion
"""
cap_der_str = [ 'SYMBOL cap {:d} {:d} R0\n', # posición absoluta X-Y en el esquemático
'WINDOW 0 48 18 Left 2\n', # posiciones relativas de etiquetas
'WINDOW 3 45 49 Left 2\n', # posiciones relativas de etiquetas
'SYMATTR InstName {:s}\n', # etiqueta que tendrá
'SYMATTR Value {:3.5f}\n' # valor que tendrá
]
"""
capacitor en derivacion
"""
res_ser_str = [ 'SYMBOL res {:d} {:d} R90\n', # posición absoluta X-Y en el esquemático
'WINDOW 0 -7 86 VBottom 2\n', # posiciones relativas de etiquetas
'WINDOW 3 -36 24 VTop 2\n', # posiciones relativas de etiquetas
'SYMATTR InstName {:s}\n', # etiqueta que tendrá
'SYMATTR Value {:3.5f}\n' # valor que tendrá
]
"""
resistor en serie
"""
ind_ser_str = [ 'SYMBOL ind {:d} {:d} R270\n', # posición absoluta X-Y en el esquemático
'WINDOW 0 40 19 VTop 2\n', # posiciones relativas de etiquetas
'WINDOW 3 67 100 VBottom 2\n', # posiciones relativas de etiquetas
'SYMATTR InstName {:s}\n', # etiqueta que tendrá
'SYMATTR Value {:3.5f}\n' # valor que tendrá
]
"""
inductor en serie
"""
cap_ser_str = [ 'SYMBOL cap {:d} {:d} R90\n', # posición absoluta X-Y en el esquemático
'WINDOW 0 -8 55 VBottom 2\n', # posiciones relativas de etiquetas
'WINDOW 3 -37 0 VTop 2\n', # posiciones relativas de etiquetas
'SYMATTR InstName {:s}\n', # etiqueta que tendrá
'SYMATTR Value {:3.5f}\n' # valor que tendrá
]
"""
capacitor en serie
"""
#############################################
#%% Funciones para dibujar redes en LTspice #
#############################################
[docs]def ltsp_nuevo_circuito(circ_name=None, circ_folder = None):
'''
Se genera un circuito nuevo en LTspice de nombre *circ_name*.
Parameters
----------
circ_name : string
Nombre del circuito.
circ_folder : str, opcional
Path a la carpeta donde se creará el archivo ASC y PLT de LTspice.
Returns
-------
circ_hdl : archivo de texto
Handle al archivo de texto de LTspice para continuar construyendo el
circuito.
Raises
------
TypeError
Si ZZ no es una instancia de sympy.Matrix.
See Also
--------
:func:`ltsp_capa_derivacion`
:func:`ltsp_ind_serie`
Examples
--------
>>> from pytc2.ltspice import ltsp_nuevo_circuito, ltsp_etiquetar_nodo, ltsp_ind_serie, ltsp_capa_derivacion, ltsp_etiquetar_nodo
>>> circ_hdl = ltsp_nuevo_circuito('prueba1')
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vi')
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_capa_derivacion(circ_hdl, 2.0)
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vo')
>>> R01 = 1.0
>>> R02 = 1.0
>>> circ_hdl.writelines('TEXT -48 304 Left 2 !.param RG={:3.3f} RL={:3.3f}'.format(R01, R02))
>>> circ_hdl.close()
[ Buscar el archivo "ltsp_prueba.asc" en LTspice ]
'''
if not isinstance(circ_name, (str, type(None)) ):
raise ValueError('El nombre del circuito debe ser un string u omitirse.')
if not isinstance(circ_folder, (str, type(None)) ):
raise ValueError('La carpeta circ_folder debe ser un path correcto u omitirse.')
global cap_num, res_num, ind_num, cur_x, cur_y
if circ_name is None:
timestr = time.strftime("%Y%m%d-%H%M%S")
circ_name = 'NN-' + timestr
if circ_folder is None:
circ_folder = get_home_directory()
else:
if path.exists(circ_folder):
raise ValueError('La carpeta circ_folder debe ser un path correcto u omitirse.')
circ_hdl = None
src_fname_asc = path.join(pytc2_full_path, filename_eq_base + '.asc' )
if path.isfile(src_fname_asc):
dst_fname_asc = 'pytc2ltspice_{:s}.asc'.format(circ_name)
dst_fname_asc = path.join(circ_folder, dst_fname_asc )
shutil.copy(src_fname_asc, dst_fname_asc)
# configuración de los gráficos standard S11 / S21
src_fname_plt = path.join(pytc2_full_path, filename_eq_base + '.plt' )
dst_fname_plt = 'pytc2ltspice_{:s}.plt'.format(circ_name)
dst_fname_plt = path.join(circ_folder, dst_fname_plt )
shutil.copy(src_fname_plt, dst_fname_plt)
circ_hdl = open(dst_fname_asc, 'a')
cap_num = 1
res_num = 1
ind_num = 1
cur_x = 0
cur_y = 0
else:
raise RuntimeError('El archivo {:s} no se encuentra. Contacte al desarrollador en: https://github.com/marianux/pytc2/issues'.format(src_fname_asc) )
return(circ_hdl)
[docs]def ltsp_capa_derivacion(circ_hdl, cap_value, cap_label=None):
'''
Incorpora un capacitor en derivación a un circuito en LTspice.
Parameters
----------
circ_hdl : archivo de texto LTspice
Handle al archivo LTspice.
cap_value : float o numéro simbólico
Valor del capacitor.
cap_label : string o None
Etiqueta para identificar al capacitor en el circuito.
Returns
-------
None
Raises
------
ValueError
Si cap_value no es numérico o el valor no es positivo.
See Also
--------
:func:`ltsp_capa_derivacion`
:func:`ltsp_ind_serie`
Examples
--------
>>> from pytc2.ltspice import ltsp_nuevo_circuito, ltsp_etiquetar_nodo, ltsp_ind_serie, ltsp_capa_derivacion, ltsp_etiquetar_nodo
>>> circ_hdl = ltsp_nuevo_circuito('prueba1')
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vi')
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_capa_derivacion(circ_hdl, 2.0)
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vo')
>>> R01 = 1.0
>>> R02 = 1.0
>>> circ_hdl.writelines('TEXT -48 304 Left 2 !.param RG={:3.3f} RL={:3.3f}'.format(R01, R02))
>>> circ_hdl.close()
[ Buscar el archivo "ltsp_prueba.asc" en LTspice ]
'''
if not ( isinstance(cap_value, (Real, sp.Number) ) and cap_value > 0 ):
raise ValueError('Se espera un valor numérico positivo para el capacitor.')
if not isinstance(cap_label, (str, type(None))):
raise ValueError('cap_label debe ser str o None.')
global cap_der_str, cap_num
if cap_label is None:
cap_label = 'C{:d}'.format(cap_num)
cap_num += 1
this_cap_str = cap_der_str.copy()
element_xy = [cur_x - ltux, cur_y + lt_wire_length*ltuy]
this_cap_str[0] = this_cap_str[0].format(element_xy[0], element_xy[1])
this_cap_str[3] = this_cap_str[3].format(cap_label)
this_cap_str[4] = this_cap_str[4].format(cap_value)
# conectamos el elemento en derivación con el cursor actual.
wire_str = 'WIRE {:d} {:d} {:d} {:d}\n'.format(cur_x, cur_y, element_xy[0] + ltux, element_xy[1] )
# y el otro extremo a referencia GND
gnd_str = 'FLAG {:d} {:d} 0\n'.format(element_xy[0] + ltux, element_xy[1] + 4*ltuy )
circ_hdl.writelines(wire_str)
circ_hdl.writelines(this_cap_str)
circ_hdl.writelines(gnd_str)
return()
[docs]def ltsp_ind_serie(circ_hdl, ind_value, ind_label=None):
'''
Incorpora un inductor en serie a un circuito en LTspice.
Parameters
----------
circ_hdl : archivo de texto LTspice
Handle al archivo LTspice.
ind_value : float o numéro simbólico
Valor del inductor.
ind_label : string o None
Etiqueta para identificar al inductor en el circuito.
Returns
-------
None
Raises
------
ValueError
Si cap_value no es numérico o el valor no es positivo.
See Also
--------
:func:`ltsp_capa_derivacion`
:func:`ltsp_ind_serie`
Examples
--------
>>> from pytc2.ltspice import ltsp_nuevo_circuito, ltsp_etiquetar_nodo, ltsp_ind_serie, ltsp_capa_derivacion, ltsp_etiquetar_nodo
>>> circ_hdl = ltsp_nuevo_circuito('prueba1')
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vi')
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_capa_derivacion(circ_hdl, 2.0)
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vo')
>>> R01 = 1.0
>>> R02 = 1.0
>>> circ_hdl.writelines('TEXT -48 304 Left 2 !.param RG={:3.3f} RL={:3.3f}'.format(R01, R02))
>>> circ_hdl.close()
[ Buscar el archivo "ltsp_prueba.asc" en LTspice ]
'''
if not ( isinstance(ind_value, (Real, sp.Number) ) and ind_value > 0 ):
raise ValueError('Se espera un valor numérico positivo para el inductor.')
if not isinstance(ind_label, (str, type(None))):
raise ValueError('ind_label debe ser str o None.')
global ind_ser_str, cap_num, cur_x, cur_y
if ind_label is None:
ind_label = 'C{:d}'.format(cap_num)
cap_num += 1
this_ind_str = ind_ser_str.copy()
element_xy = [cur_x + lt_wire_length*ltux, cur_y + ltuy]
this_ind_str[0] = this_ind_str[0].format(element_xy[0], element_xy[1])
this_ind_str[3] = this_ind_str[3].format(ind_label)
this_ind_str[4] = this_ind_str[4].format(ind_value)
# conectamos el elemento en serie con el cursor actual, y el otro extremo
# al siguiente elemento.
next_x = element_xy[0] + 6*ltux + lt_wire_length*ltux
next_y = element_xy[1] - ltuy
wire_str = ['WIRE {:d} {:d} {:d} {:d}\n'.format(cur_x, cur_y, element_xy[0] + ltux, element_xy[1] - ltuy),
'WIRE {:d} {:d} {:d} {:d}\n'.format(element_xy[0] + 6*ltux, element_xy[1] - ltuy, next_x, next_y) ]
# actualizamos cursor.
cur_x = next_x
cur_y = next_y
circ_hdl.writelines(wire_str)
circ_hdl.writelines(this_ind_str)
return()
[docs]def ltsp_etiquetar_nodo(circ_hdl, node_label=None):
'''
Asigna una etiqueta a un nodo de un circuito en LTspice.
Parameters
----------
circ_hdl : archivo de texto LTspice
Handle al archivo LTspice.
node_label : string o None
Etiqueta para identificar al nodo en el circuito.
Returns
-------
None
Raises
------
ValueError
Si cap_value no es numérico o el valor no es positivo.
See Also
--------
:func:`ltsp_capa_derivacion`
:func:`ltsp_ind_serie`
Examples
--------
>>> from pytc2.ltspice import ltsp_nuevo_circuito, ltsp_etiquetar_nodo, ltsp_ind_serie, ltsp_capa_derivacion, ltsp_etiquetar_nodo
>>> circ_hdl = ltsp_nuevo_circuito('prueba1')
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vi')
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_capa_derivacion(circ_hdl, 2.0)
>>> ltsp_ind_serie(circ_hdl, 1.0)
>>> ltsp_etiquetar_nodo(circ_hdl, node_label='vo')
>>> R01 = 1.0
>>> R02 = 1.0
>>> circ_hdl.writelines('TEXT -48 304 Left 2 !.param RG={:3.3f} RL={:3.3f}'.format(R01, R02))
>>> circ_hdl.close()
[ Buscar el archivo "ltsp_prueba.asc" en LTspice ]
'''
if not isinstance(node_label, (str, type(None))):
raise ValueError('node_label debe ser str o None.')
global cap_der_str, node_num, cur_x, cur_y
if node_label is None:
node_label = 'v{:d}'.format(node_num)
node_num += 1
flag_str = ['FLAG {:d} {:d} {:s}\n'.format(cur_x, cur_y, node_label) ]
circ_hdl.writelines(flag_str)
return()