You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

93 lines
2.7 KiB

"""
AC Analysis Example
This example demonstrates using the pyngspice Python extension
to perform AC analysis (frequency sweep) on an RC filter.
"""
try:
from pyngspice import SimRunner, RawRead
except ImportError:
print("pyngspice extension not installed. Run: pip install -e .")
exit(1)
import numpy as np
import os
# RC filter netlist with AC analysis
netlist_content = """
* RC Low-Pass Filter - AC Analysis
* Shows magnitude and phase response
V1 in 0 AC 1
R1 in out 1k
C1 out 0 100n
.ac dec 100 1 100k
.end
"""
def main():
output_dir = "./output"
os.makedirs(output_dir, exist_ok=True)
netlist_file = os.path.join(output_dir, "ac_filter.net")
with open(netlist_file, "w") as f:
f.write(netlist_content)
print("Running AC analysis...")
runner = SimRunner(output_folder=output_dir)
raw_file, log_file = runner.run_now(netlist_file)
raw = RawRead(raw_file)
print(f"Analysis type: {raw.analysis_type}")
print(f"Complex data: {raw.is_complex}")
print(f"\nTraces: {raw.get_trace_names()}")
# Get frequency and output voltage (complex)
freq = raw.get_trace("frequency").get_wave(0)
v_out_complex = raw.get_trace("V(out)").get_wave_complex(0)
# Calculate magnitude in dB and phase in degrees
magnitude_db = 20 * np.log10(np.abs(v_out_complex))
phase_deg = np.angle(v_out_complex, deg=True)
# Find -3dB cutoff frequency
idx_3db = np.argmin(np.abs(magnitude_db - (-3)))
f_cutoff = freq[idx_3db]
print(f"\n-3dB cutoff frequency: {f_cutoff:.2f} Hz")
print(f"Theoretical: {1/(2*np.pi*1e3*100e-9):.2f} Hz")
# Plot if matplotlib available
try:
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
ax1.semilogx(freq, magnitude_db)
ax1.axhline(-3, color='r', linestyle='--', alpha=0.5, label='-3dB')
ax1.axvline(f_cutoff, color='r', linestyle='--', alpha=0.5)
ax1.set_ylabel('Magnitude (dB)')
ax1.set_title('RC Low-Pass Filter Frequency Response')
ax1.legend()
ax1.grid(True, which='both', alpha=0.3)
ax2.semilogx(freq, phase_deg)
ax2.axhline(-45, color='r', linestyle='--', alpha=0.5, label='-45°')
ax2.set_xlabel('Frequency (Hz)')
ax2.set_ylabel('Phase (degrees)')
ax2.legend()
ax2.grid(True, which='both', alpha=0.3)
plt.tight_layout()
plot_file = os.path.join(output_dir, "ac_filter.png")
plt.savefig(plot_file, dpi=150)
print(f"\nPlot saved to: {plot_file}")
plt.show()
except ImportError:
print("\nInstall matplotlib for plotting: pip install matplotlib")
if __name__ == "__main__":
main()