From f99a137eb31845d2fcd87943a78927b8c677d0be Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 29 Dec 2023 10:59:56 +0100 Subject: [PATCH 01/15] Patch for snprintf provided by Marco Atzeri --- src/xspice/icm/digital/d_cosim/cfunc.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xspice/icm/digital/d_cosim/cfunc.mod b/src/xspice/icm/digital/d_cosim/cfunc.mod index fb653a47d..e85d9e5f7 100644 --- a/src/xspice/icm/digital/d_cosim/cfunc.mod +++ b/src/xspice/icm/digital/d_cosim/cfunc.mod @@ -39,7 +39,7 @@ char *dlerror(void) // Lifted from dev.c. if (rc == 0) { /* FormatMessage failed */ (void) sprintf(errstr, errstr_fmt, (unsigned long) GetLastError()); } else { - snprintf(errstr, sizeof errstr, lpMsgBuf); + snprintf(errstr, sizeof errstr, errstr_fmt, lpMsgBuf); LocalFree(lpMsgBuf); } return errstr; From 1ad639d90a667de47035e0d805722ceac82a3e31 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 29 Dec 2023 11:01:01 +0100 Subject: [PATCH 02/15] Add d_cosim to MS project file --- visualc/xspice/digital.vcxproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/visualc/xspice/digital.vcxproj b/visualc/xspice/digital.vcxproj index bd4d42487..53616fa1d 100644 --- a/visualc/xspice/digital.vcxproj +++ b/visualc/xspice/digital.vcxproj @@ -339,6 +339,8 @@ + + @@ -398,4 +400,4 @@ - + \ No newline at end of file From ee531ae3bd7081d00c7452d0b0022dbd1ca2d6ce Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 29 Dec 2023 16:52:15 +0100 Subject: [PATCH 03/15] Remove as it is oild and redundant --- compile_linux_klu.sh | 76 -------------------------------------------- 1 file changed, 76 deletions(-) delete mode 100755 compile_linux_klu.sh diff --git a/compile_linux_klu.sh b/compile_linux_klu.sh deleted file mode 100755 index cc848778b..000000000 --- a/compile_linux_klu.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash -# ngspice-klu build script for Linux, release or debug version, 64 bit -# compile_linux_klu.sh -# https://ieeexplore.ieee.org/document/6226278 - -# Procedure: -# Install gcc, bison, flex, libtool, autoconf, automake, -# libx11 and libx11-dev (headers), libXaw and libXaw-dev, libreadline and dev -# xmu, xet, xt, libxft, libxrender, libfreetype, libfontconfig -# for details please see the ngspice manual, chapt. 32.1. -# Declare 'compile_linux_klu.sh' executable and start compiling with -# './compile_linux_klu.sh' or './compile_linux_klu.sh d' from the ngspice directory. -# Options: -# XSPICE (--enable-xspice) may be selected at will. -# --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info. -# OSDI (--enable-osdi) is not yet supported by KLU - -# ngspice as shared library: -# Replace --with-x by --with-ngshared in line ../configure ... . -# Add (optionally) --enable-relpath to avoid absolute paths when searching for code models. -# It might be necessary to uncomment and run ./autogen.sh . - -SECONDS=0 - -# We need to remove all remnants of a previous compile -if test "$1" = "d"; then - rm -f -r debug - mkdir debug - if [ $? -ne 0 ]; then echo "mkdir debug failed"; exit 1 ; fi -else - rm -f -r release - mkdir release - if [ $? -ne 0 ]; then echo "mkdir release failed"; exit 1 ; fi -fi - -# If compiling sources from tarball, you may comment out the following two lines: -./autogen.sh -if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi - -echo -if test "$1" = "d"; then - cd debug - if [ $? -ne 0 ]; then echo "cd debug failed"; exit 1 ; fi - echo "configuring for 64 bit debug" - echo - ../configure --with-x --enable-cider --with-readline=yes --enable-openmp --enable-xspice --enable-klu --enable-predictor --enable-osdi CFLAGS="-g -m64 -O0 -Wall -Wno-unused-but-set-variable" LDFLAGS="-m64 -g" -else - cd release - if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi - echo "configuring for 64 bit release" - echo - ../configure --with-x --enable-cider --with-readline=yes --enable-openmp --enable-xspice --enable-klu --enable-predictor --enable-osdi --disable-debug CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" -fi -if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi - -echo -# make clean is required for properly making the code models -echo "cleaning (see make_clean.log)" -make clean 2>&1 -j8 | tee make_clean.log -exitcode=${PIPESTATUS[0]} -if [ $exitcode -ne 0 ]; then echo "make clean failed"; exit 1 ; fi -echo "compiling (see make.log)" -make 2>&1 -j8 | tee make.log -exitcode=${PIPESTATUS[0]} -if [ $exitcode -ne 0 ]; then echo "make failed"; exit 1 ; fi -# Install to /usr/local -echo "installing (see make_install.log)" -make install 2>&1 | tee make_install.log -exitcode=${PIPESTATUS[0]} -if [ $exitcode -ne 0 ]; then echo "make install failed"; exit 1 ; fi - -ELAPSED="Elapsed compile time: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec" -echo -echo $ELAPSED -echo "success" -exit 0 From 797af31e524f2de4e0a4178415e78a574ebeb0e2 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 29 Dec 2023 16:52:57 +0100 Subject: [PATCH 04/15] Remove ADMS, add KLU --- compile_cyg_make_short_check_64.sh | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/compile_cyg_make_short_check_64.sh b/compile_cyg_make_short_check_64.sh index 54d0868d2..668fd5218 100644 --- a/compile_cyg_make_short_check_64.sh +++ b/compile_cyg_make_short_check_64.sh @@ -11,12 +11,10 @@ # './compile_cyg_auto.sh' # Options: -# --adms and --enable-adms will install extra HICUM, EKV and MEXTRAM models via the -# adms interface. -# Please see http://ngspice.sourceforge.net/admshowto.html for more info on adms. -# CIDER, XSPICE, and OpenMP may be selected at will. +# CIDER, XSPICE, OSDI, KLU, and OpenMP may be selected at will. # --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info. # --enable-oldapps will make ngnutmeg ngsconvert ngproc2mod ngmultidec ngmakeidx in addition to ngspice +# --enable-shortcheck will provide a fast 'make check' by checking only BSIM3 and BSIM4 if [ ! -d "release64_cyg" ]; then mkdir release64_cyg @@ -27,18 +25,11 @@ fi ./autogen.sh if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi -# Alternatively, if compiling sources from CVS, and want to add adms created devices, -# you may need to uncomment the following two lines (and don't forget to add adms option -# to the ../configure statement): -#./autogen.sh --adms -#if [ $? -ne 0 ]; then echo "./autogen.sh --adms failed"; exit 1 ; fi - echo cd release64_cyg if [ $? -ne 0 ]; then echo "cd release64_cyg failed"; exit 1 ; fi echo -# You may add --enable-adms to the following command for adding adms generated devices -../configure --with-x=yes --with-readline=yes --disable-debug --enable-cider --enable-openmp --enable-xspice --enable-osdi --enable-predictor --enable-shortcheck CFLAGS="-O2 -m64" LDFLAGS="-s -m64" +../configure --with-x=yes --with-readline=yes --disable-debug --enable-cider --enable-openmp --enable-xspice --enable-osdi --enable-klu --enable-predictor --enable-shortcheck CFLAGS="-O2 -m64" LDFLAGS="-s -m64" #../configure --with-x=no --with-readline=yes --disable-debug --enable-xspice --enable-cider --enable-openmp if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi From ace235da049de402e206e6f9e77b8903bbd39d01 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 29 Dec 2023 16:53:39 +0100 Subject: [PATCH 05/15] Remove ADMS from build process --- configure.ac | 56 ++------------------------------ src/Makefile.am | 3 +- src/spicelib/devices/Makefile.am | 3 +- 3 files changed, 4 insertions(+), 58 deletions(-) diff --git a/configure.ac b/configure.ac index 0cda21fc6..07c30a30e 100644 --- a/configure.ac +++ b/configure.ac @@ -154,9 +154,9 @@ AC_ARG_ENABLE([osdi], AC_ARG_ENABLE([cider], [AS_HELP_STRING([--enable-cider], [Enable CIDER enhancements])]) -# --enable-adms: define ADMS in the code. This is for the adms Verilog-A compiler support +# --enable-adms: ADMS is no longer supported AC_ARG_ENABLE([adms], - [AS_HELP_STRING([--enable-adms], [Enable ADMS code models, (experimental)])]) + [AS_HELP_STRING([--enable-adms], [ADMS is no longer supported])]) # --enable-pss: enable PSS Analysis AC_ARG_ENABLE([pss], @@ -1227,58 +1227,6 @@ AM_CONDITIONAL([NUMDEV_WANTED], [test "x$enable_cider" = xyes]) AM_CONDITIONAL([PSS_WANTED], [test "x$enable_pss" = xyes]) AM_CONDITIONAL([SENSE2_WANTED], [test "x$enable_sense2" = xyes]) -# adms option -if test "x$enable_adms" = xyes ; then - AC_MSG_RESULT([********************************** -* ADMS support is experimental * -**********************************]) - AC_CHECK_PROGS([ADMSXML], [admsXml admsXml.exe]) - - if test "x$ADMSXML" = x; then - AC_MSG_ERROR([If you want Verilog-A models you should install admsXml]) - fi - AC_DEFINE([ADMS], [1], [Support for Verilog-A models]) - - VLADEVDIR=" adms/bsimbulk \ - adms/bsimcmg \ - adms/ekv \ - adms/hicum0 \ - adms/mextram \ - adms/psp102 \ - adms/psp103 \ - adms/r2_cmc " - -# The makefiles for adms (to be added to AC_CONFIG_FILES by ./autogen.sh --adms) -#VLAMKF src/spicelib/devices/adms/bsimbulk/Makefile -#VLAMKF src/spicelib/devices/adms/bsimcmg/Makefile -#VLAMKF src/spicelib/devices/adms/ekv/Makefile -#VLAMKF src/spicelib/devices/adms/hicum0/Makefile -#VLAMKF src/spicelib/devices/adms/mextram/Makefile -#VLAMKF src/spicelib/devices/adms/psp102/Makefile -#VLAMKF src/spicelib/devices/adms/psp103/Makefile -#VLAMKF src/spicelib/devices/adms/r2_cmc/Makefile - - NOTVLADEVDIR="" - - VLADEV=" spicelib/devices/adms/bsimbulk/libbsimbulk.la \ - spicelib/devices/adms/bsimcmg/libbsimcmg.la \ - spicelib/devices/adms/ekv/libekv.la \ - spicelib/devices/adms/hicum0/libhicum0.la \ - spicelib/devices/adms/mextram/libbjt504t.la \ - spicelib/devices/adms/psp102/libpsp102.la \ - spicelib/devices/adms/psp103/libpsp103.la \ - spicelib/devices/adms/r2_cmc/libr2_cmc.la " - -else - - VLADEVDIR="" - NOTVLADEVDIR="adms/admst" - -fi - -AC_SUBST([VLADEVDIR]) -AC_SUBST([VLADEV]) -AC_SUBST([NOTVLADEVDIR]) # NDEV option if test "x$enable_ndev" = xyes; then diff --git a/src/Makefile.am b/src/Makefile.am index 55cf63a35..62a3536a2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,8 +97,7 @@ DYNAMIC_DEVICELIBS = \ spicelib/devices/vccs/libvccs.la \ spicelib/devices/vcvs/libvcvs.la \ spicelib/devices/vdmos/libvdmos.la \ - spicelib/devices/vsrc/libvsrc.la \ - @VLADEV@ + spicelib/devices/vsrc/libvsrc.la diff --git a/src/spicelib/devices/Makefile.am b/src/spicelib/devices/Makefile.am index bc6674611..a97f7c26c 100644 --- a/src/spicelib/devices/Makefile.am +++ b/src/spicelib/devices/Makefile.am @@ -51,8 +51,7 @@ SUBDIRS = \ vccs \ vcvs \ vdmos \ - vsrc \ - @VLADEVDIR@ + vsrc if NDEV_WANTED SUBDIRS += ndev From dd7b9ff27367ba65120e05e76f41da991ec0949b Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 30 Dec 2023 22:52:41 +0100 Subject: [PATCH 06/15] Avoid memory crash when reading old VDMOS models. Enable both old and current model format. --- src/frontend/inpcom.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 026d43661..32d57d22b 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -8004,13 +8004,19 @@ static int inp_vdmos_model(struct card *deck) if (cut_line) { wl_append_word(&wl, &wl, copy_substring(curr_line, cut_line)); wlb = wl; - if (strstr(cut_line, "pchan")) { + if (search_plain_identifier(cut_line, "pchan")) { wl_append_word(NULL, &wl, copy("vdmosp (")); } + else if (search_plain_identifier(cut_line, "nchan")) { + wl_append_word(NULL, &wl, copy("vdmosn (")); + } else { wl_append_word(NULL, &wl, copy("vdmosn (")); } - cut_line = cut_line + 5; + cut_line = cut_line + 6; /* skip VDMOS */ + + if (ciprefix("nchan", cut_line) || ciprefix("pchan", cut_line)) + cut_line = cut_line + 5; /* old VDMOS model, skip nchan, pchan */ cut_line = skip_ws(cut_line); if (*cut_line == '(') @@ -8018,13 +8024,16 @@ static int inp_vdmos_model(struct card *deck) new_line = NULL; while (cut_line && *cut_line) { token = gettok_model(&cut_line); - if (!ciprefix("pchan", token) && !ciprefix("ron=", token) && + if (token && *token != '\0' && + !ciprefix("pchan", token) && !ciprefix("ron=", token) && !ciprefix("vds=", token) && !ciprefix("qg=", token) && !ciprefix("mfg=", token) && !ciprefix("nchan", token)) wl_append_word(NULL, &wl, token); - else + else { tfree(token); - if (*cut_line == ')') { + break; + } + if (*cut_line == ')' || *cut_line == '\0') { wl_append_word(NULL, &wl, copy(")")); break; } From 88844ba74d9181832bd72d5dc23b7cd81336cf77 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 31 Dec 2023 13:32:45 +0100 Subject: [PATCH 07/15] Prevent vectors carrying the E POLY source current from being disaggregated --- src/frontend/variable.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/variable.c b/src/frontend/variable.c index 5bc2c1f93..205ea969b 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -861,6 +861,10 @@ wordlist *cp_variablesubst(wordlist *wlist) while ((s_dollar = strchr(wl->wl_word + i, '$')) != NULL) { + /* Prevent vectors carrying the E POLY source current from being disaggregated */ + if (ciprefix("a$poly$", wl->wl_word)) + break; + int prefix_len = (int) (s_dollar - wl->wl_word); char *tail = span_var_expr(s_dollar + 1); From ace932e1b912451c44343e6284088053a3a0f99b Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 31 Dec 2023 13:51:05 +0100 Subject: [PATCH 08/15] Patches provided by Carsten Schoenert: Misspellings, File access --- src/frontend/trannoise/FastNorm3.c | 2 +- src/osdi/osdiinit.c | 2 +- src/osdi/osdisetup.c | 4 ++-- src/xspice/icm/digital/d_osc/cfunc.mod | 2 +- src/xspice/icm/digital/d_pwm/cfunc.mod | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frontend/trannoise/FastNorm3.c b/src/frontend/trannoise/FastNorm3.c index 28c97fca2..6ad414f5a 100644 --- a/src/frontend/trannoise/FastNorm3.c +++ b/src/frontend/trannoise/FastNorm3.c @@ -92,7 +92,7 @@ c but avoids most of the well-known defects of this type of generator c by, in effect, generating x[n+k] from x[n] as defined by the c sequence above, where k is chosen randomly in 1 ... 128 with the c help of a subsidiary Tauseworth-type generator. -c For the positve integer generator irandm, the less +c For the positive integer generator irandm, the less c significant digits are more random than is usual for a Lehmer c generator. The last n<31 digits do not repeat with a period of 2^n. c This is also true of the unsigned integer generator urandm, but less diff --git a/src/osdi/osdiinit.c b/src/osdi/osdiinit.c index cbfa5cf31..b647de25e 100644 --- a/src/osdi/osdiinit.c +++ b/src/osdi/osdiinit.c @@ -54,7 +54,7 @@ static int write_param_info(IFparm **dst, const OsdiDescriptor *descr, break; default: errRtn = "get_osdi_info"; - errMsg = tprintf("Unkown OSDI type %d for parameter %s!", + errMsg = tprintf("Unknown OSDI type %d for parameter %s!", para->flags & PARA_TY_MASK, para->name[0]); return -1; } diff --git a/src/osdi/osdisetup.c b/src/osdi/osdisetup.c index 4614292a3..77860e21f 100644 --- a/src/osdi/osdisetup.c +++ b/src/osdi/osdisetup.c @@ -43,11 +43,11 @@ static int handle_init_info(OsdiInitInfo info, const OsdiDescriptor *descr) { break; } default: - printf("Unkown OSDO init error code %d!\n", err->code); + printf("Unknown OSDO init error code %d!\n", err->code); } } free(info.errors); - errMsg = tprintf("%i errors occurred during initalization", info.num_errors); + errMsg = tprintf("%i errors occurred during initialization", info.num_errors); return (E_PRIVATE); } diff --git a/src/xspice/icm/digital/d_osc/cfunc.mod b/src/xspice/icm/digital/d_osc/cfunc.mod index e4361e783..8a598bd89 100644 --- a/src/xspice/icm/digital/d_osc/cfunc.mod +++ b/src/xspice/icm/digital/d_osc/cfunc.mod @@ -99,7 +99,7 @@ void cm_d_osc(ARGS) table[i].ctl = PARAM(cntl_array[i]); table[i].freq = PARAM(freq_array[i]); if (table[i].freq <= 0) { - cm_message_printf("Error: frequency %g is not positve, " + cm_message_printf("Error: frequency %g is not positive, " "value replaced by 1e-16.", table[i].freq); table[i].freq = 1.0e-16; diff --git a/src/xspice/icm/digital/d_pwm/cfunc.mod b/src/xspice/icm/digital/d_pwm/cfunc.mod index 908a2b5b3..b3e47fb61 100644 --- a/src/xspice/icm/digital/d_pwm/cfunc.mod +++ b/src/xspice/icm/digital/d_pwm/cfunc.mod @@ -106,7 +106,7 @@ void cm_d_pwm(ARGS) table[i].ctl = PARAM(cntl_array[i]); table[i].dc = PARAM(dc_array[i]); if (table[i].dc <= 0) { - cm_message_printf("Error: duty cycle %g is not positve, " + cm_message_printf("Error: duty cycle %g is not positive, " "value replaced by 0.01.", table[i].dc); table[i].dc = 0.01; From 88d0727d3f77833289679fb438d3d9c0dbac8cab Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 6 Jan 2024 11:51:11 +0100 Subject: [PATCH 09/15] In PS compat mode we only have 2 nodes in a diode, but still allow self heating diode with ngspice syntax. --- src/frontend/inpcom.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 32d57d22b..922393c5c 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -4853,6 +4853,10 @@ int get_number_terminals(char *c) return 2; break; case 'd': + /* PS: D <(+) node> <(-) node> [area value], + but still allow self heating diode with ngspice syntax. */ + if (newcompat.ps && !search_plain_identifier(c, "thermal")) + return 2; i = 0; /* find the first token with "off" or "=" in the line*/ while ((i < 10) && (*c != '\0')) { From aaaad70991cea77efe39a78b2aff451dff3e30f1 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 6 Jan 2024 23:09:27 +0100 Subject: [PATCH 10/15] Previous AM was buggy in V(I)SRC: Update to amplitude modulation with corrected equation. --- src/spicelib/devices/isrc/isrcload.c | 39 ++++++++++++++++------------ src/spicelib/devices/vsrc/vsrcload.c | 39 ++++++++++++++++------------ 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/spicelib/devices/isrc/isrcload.c b/src/spicelib/devices/isrc/isrcload.c index f947686c9..832c84598 100644 --- a/src/spicelib/devices/isrc/isrcload.c +++ b/src/spicelib/devices/isrc/isrcload.c @@ -239,38 +239,45 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) case AM: { - double VA, FC, MF, VO, TD; - double PHASEC, PHASES; + double VO, VA, FM, MD, FC, TD, PHASEM, PHASEC; double phasec; - double phases; + double phasem; - PHASEC = here->ISRCfunctionOrder > 5 - ? here->ISRCcoeffs[5] : 0.0; - PHASES = here->ISRCfunctionOrder > 6 + PHASEC = here->ISRCfunctionOrder > 6 ? here->ISRCcoeffs[6] : 0.0; + PHASEM = here->ISRCfunctionOrder > 7 + ? here->ISRCcoeffs[7] : 0.0; /* compute phases in radians */ phasec = PHASEC * M_PI / 180.0; - phases = PHASES * M_PI / 180.0; + phasem = PHASEM * M_PI / 180.0; - VA = here->ISRCcoeffs[0]; - VO = here->ISRCcoeffs[1]; - MF = here->ISRCfunctionOrder > 2 + VO = here->ISRCcoeffs[0]; + VA = here->ISRCcoeffs[1]; + FM = here->ISRCfunctionOrder > 2 && here->ISRCcoeffs[2] ? here->ISRCcoeffs[2] : (1/ckt->CKTfinalTime); - FC = here->ISRCfunctionOrder > 3 - ? here->ISRCcoeffs[3] : 0.0; - TD = here->ISRCfunctionOrder > 4 - && here->ISRCcoeffs[4] + MD = here->ISRCfunctionOrder > 3 + ? here->ISRCcoeffs[3] : 0.5; + FC = here->ISRCfunctionOrder > 4 ? here->ISRCcoeffs[4] : 0.0; + TD = here->ISRCfunctionOrder > 5 + && here->ISRCcoeffs[5] + ? here->ISRCcoeffs[5] : 0.0; + + /* limit the modulation depth */ + if (MD > 1) + MD = 1; + else if (MD < 0) + MD = 0; time -= TD; if (time <= 0) { value = 0; } else { /* compute waveform value */ - value = VA * (VO + sin(2.0 * M_PI * MF * time + phases )) * - sin(2.0 * M_PI * FC * time + phases); + value = VO + VA * (1 + MD * sin(2.0 * M_PI * FM * time + phasem)) * + sin(2.0 * M_PI * FC * time + phasec); } } break; diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index a93687087..ed172652d 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -261,38 +261,45 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) case AM: { - double VA, FC, MF, VO, TD; - double PHASEC, PHASES; + double VO, VA, FM, MD, FC, TD, PHASEM, PHASEC; double phasec; - double phases; + double phasem; - PHASEC = here->VSRCfunctionOrder > 5 - ? here->VSRCcoeffs[5] : 0.0; - PHASES = here->VSRCfunctionOrder > 6 + PHASEM = here->VSRCfunctionOrder > 6 ? here->VSRCcoeffs[6] : 0.0; + PHASEC = here->VSRCfunctionOrder > 7 + ? here->VSRCcoeffs[7] : 0.0; /* compute phases in radians */ phasec = PHASEC * M_PI / 180.0; - phases = PHASES * M_PI / 180.0; + phasem = PHASEM * M_PI / 180.0; - VA = here->VSRCcoeffs[0]; - VO = here->VSRCcoeffs[1]; - MF = here->VSRCfunctionOrder > 2 + VO = here->VSRCcoeffs[0]; + VA = here->VSRCcoeffs[1]; + FM = here->VSRCfunctionOrder > 2 && here->VSRCcoeffs[2] ? here->VSRCcoeffs[2] : (1/ckt->CKTfinalTime); - FC = here->VSRCfunctionOrder > 3 - ? here->VSRCcoeffs[3] : 0.0; - TD = here->VSRCfunctionOrder > 4 - && here->VSRCcoeffs[4] + MD = here->VSRCfunctionOrder > 3 + ? here->VSRCcoeffs[3] : 0.5; + FC = here->VSRCfunctionOrder > 4 ? here->VSRCcoeffs[4] : 0.0; + TD = here->VSRCfunctionOrder > 5 + && here->VSRCcoeffs[5] + ? here->VSRCcoeffs[5] : 0.0; + + /* limit the modulation depth */ + if (MD > 1) + MD = 1; + else if (MD < 0) + MD = 0; time -= TD; if (time <= 0) { value = 0; } else { /* compute waveform value */ - value = VA * (VO + sin(2.0 * M_PI * MF * time + phases )) * - sin(2.0 * M_PI * FC * time + phases); + value = VO + VA * (1 + MD * sin(2.0 * M_PI * FM * time + phasem)) * + sin(2.0 * M_PI * FC * time + phasec); } } break; From a4ae81ec065d4d64e95ebb40cca5df5af7da8f66 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 6 Jan 2024 23:14:26 +0100 Subject: [PATCH 11/15] We are developing towards ngspice-43 --- configure.ac | 2 +- src/include/ngspice/sharedspice.h | 2 +- visualc/src/include/ngspice/config.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 07c30a30e..48b776c4c 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ # problem to the user. AC_PREREQ([2.59]) -m4_define([ngspice_major_version], [42]) +m4_define([ngspice_major_version], [42+]) m4_define([ngspice_minor_version], [0]) m4_define([ngspice_version], [ngspice_major_version]) diff --git a/src/include/ngspice/sharedspice.h b/src/include/ngspice/sharedspice.h index 3f37f6be2..2b9ac5b5f 100644 --- a/src/include/ngspice/sharedspice.h +++ b/src/include/ngspice/sharedspice.h @@ -107,7 +107,7 @@ are of type bool if sharedspice.h is used externally. */ #ifndef NGSPICE_PACKAGE_VERSION -#define NGSPICE_PACKAGE_VERSION "42" +#define NGSPICE_PACKAGE_VERSION "42+" #endif /* we have NG_BOOL instead of BOOL */ #ifndef HAS_NG_BOOL diff --git a/visualc/src/include/ngspice/config.h b/visualc/src/include/ngspice/config.h index 43e4aa7ad..98da3ab21 100644 --- a/visualc/src/include/ngspice/config.h +++ b/visualc/src/include/ngspice/config.h @@ -15,7 +15,7 @@ #define PACKAGE "ngspice" /* Version number of package */ -#define VERSION "42" +#define VERSION "42+" /* Define the directory for executables */ #define NGSPICEBINDIR "../bin" From 4d0e65b3b6f442a9a08f57624f4b407aa1545d8e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 7 Jan 2024 11:56:56 +0100 Subject: [PATCH 12/15] Extend the error message --- src/spicelib/parser/sperror.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spicelib/parser/sperror.c b/src/spicelib/parser/sperror.c index 915afc79c..d669260d6 100644 --- a/src/spicelib/parser/sperror.c +++ b/src/spicelib/parser/sperror.c @@ -42,7 +42,7 @@ const char *SPerror(int type) msg = "no such terminal on this device"; break; case E_BADPARM: - msg = "no such parameter on this device"; + msg = "no such parameter on this device or parameter is missing"; break; case E_NOMEM: msg = "out of memory"; From 5dbb25f66768f44148520f9fbfca7218f010d08c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 7 Jan 2024 11:59:33 +0100 Subject: [PATCH 13/15] Set new default values. Restructure the code a bit. Two parameters for AM are required (offset and amplitude). Even if this sounds needless, changing it would require a major effort. --- src/spicelib/devices/isrc/isrcload.c | 28 +++++++++++++--------------- src/spicelib/devices/vsrc/vsrcload.c | 25 ++++++++++++------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/spicelib/devices/isrc/isrcload.c b/src/spicelib/devices/isrc/isrcload.c index 832c84598..a3267d0c5 100644 --- a/src/spicelib/devices/isrc/isrcload.c +++ b/src/spicelib/devices/isrc/isrcload.c @@ -240,9 +240,20 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) case AM: { double VO, VA, FM, MD, FC, TD, PHASEM, PHASEC; - double phasec; - double phasem; + double phasec, phasem; + VO = here->ISRCcoeffs[0]; + VA = here->ISRCcoeffs[1]; + FM = here->ISRCfunctionOrder > 2 + && here->ISRCcoeffs[2] + ? here->ISRCcoeffs[2] : (5. / ckt->CKTfinalTime); + MD = here->ISRCfunctionOrder > 3 + ? here->ISRCcoeffs[3] : 0.5; + FC = here->ISRCfunctionOrder > 4 + ? here->ISRCcoeffs[4] : (500. / ckt->CKTfinalTime); + TD = here->ISRCfunctionOrder > 5 + && here->ISRCcoeffs[5] + ? here->ISRCcoeffs[5] : 0.0; PHASEC = here->ISRCfunctionOrder > 6 ? here->ISRCcoeffs[6] : 0.0; PHASEM = here->ISRCfunctionOrder > 7 @@ -252,19 +263,6 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) phasec = PHASEC * M_PI / 180.0; phasem = PHASEM * M_PI / 180.0; - VO = here->ISRCcoeffs[0]; - VA = here->ISRCcoeffs[1]; - FM = here->ISRCfunctionOrder > 2 - && here->ISRCcoeffs[2] - ? here->ISRCcoeffs[2] : (1/ckt->CKTfinalTime); - MD = here->ISRCfunctionOrder > 3 - ? here->ISRCcoeffs[3] : 0.5; - FC = here->ISRCfunctionOrder > 4 - ? here->ISRCcoeffs[4] : 0.0; - TD = here->ISRCfunctionOrder > 5 - && here->ISRCcoeffs[5] - ? here->ISRCcoeffs[5] : 0.0; - /* limit the modulation depth */ if (MD > 1) MD = 1; diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index ed172652d..f7821b39a 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -265,6 +265,18 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) double phasec; double phasem; + VO = here->VSRCcoeffs[0]; + VA = here->VSRCcoeffs[1]; + FM = here->VSRCfunctionOrder > 2 + && here->VSRCcoeffs[2] + ? here->VSRCcoeffs[2] : (5. / ckt->CKTfinalTime); + MD = here->VSRCfunctionOrder > 3 + ? here->VSRCcoeffs[3] : 0.5; + FC = here->VSRCfunctionOrder > 4 + ? here->VSRCcoeffs[4] : (500. / ckt->CKTfinalTime); + TD = here->VSRCfunctionOrder > 5 + && here->VSRCcoeffs[5] + ? here->VSRCcoeffs[5] : 0.0; PHASEM = here->VSRCfunctionOrder > 6 ? here->VSRCcoeffs[6] : 0.0; PHASEC = here->VSRCfunctionOrder > 7 @@ -274,19 +286,6 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) phasec = PHASEC * M_PI / 180.0; phasem = PHASEM * M_PI / 180.0; - VO = here->VSRCcoeffs[0]; - VA = here->VSRCcoeffs[1]; - FM = here->VSRCfunctionOrder > 2 - && here->VSRCcoeffs[2] - ? here->VSRCcoeffs[2] : (1/ckt->CKTfinalTime); - MD = here->VSRCfunctionOrder > 3 - ? here->VSRCcoeffs[3] : 0.5; - FC = here->VSRCfunctionOrder > 4 - ? here->VSRCcoeffs[4] : 0.0; - TD = here->VSRCfunctionOrder > 5 - && here->VSRCcoeffs[5] - ? here->VSRCcoeffs[5] : 0.0; - /* limit the modulation depth */ if (MD > 1) MD = 1; From e6d6f8e92801ee0a1c5d644e3786df2488a3d3ff Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 8 Jan 2024 13:21:43 +0100 Subject: [PATCH 14/15] Update to V/I sources, SFFM and AM Enable more AM functions. Unify the settings of both sources. --- src/spicelib/devices/isrc/isrcload.c | 86 ++++++++++++---------- src/spicelib/devices/vsrc/vsrcload.c | 106 +++++++++++++++------------ 2 files changed, 106 insertions(+), 86 deletions(-) diff --git a/src/spicelib/devices/isrc/isrcload.c b/src/spicelib/devices/isrc/isrcload.c index a3267d0c5..d88cc4072 100644 --- a/src/spicelib/devices/isrc/isrcload.c +++ b/src/spicelib/devices/isrc/isrcload.c @@ -205,76 +205,84 @@ ISRCload(GENmodel *inModel, CKTcircuit *ckt) case SFFM: { - double VO, VA, FC, MDI, FS; - double PHASEC, PHASES; + double VO, VA, FM, MDI, FC, TD, PHASEM, PHASEC; double phasec; - double phases; - - PHASEC = here->ISRCfunctionOrder > 5 - ? here->ISRCcoeffs[5] : 0.0; - PHASES = here->ISRCfunctionOrder > 6 - ? here->ISRCcoeffs[6] : 0.0; - - /* compute phases in radians */ - phasec = PHASEC * M_PI / 180.0; - phases = PHASES * M_PI / 180.0; + double phasem; + static bool warn1 = FALSE, warn2 = FALSE; VO = here->ISRCcoeffs[0]; VA = here->ISRCcoeffs[1]; - FC = here->ISRCfunctionOrder > 2 - && here->ISRCcoeffs[2] - ? here->ISRCcoeffs[2] : (1/ckt->CKTfinalTime); + FM = here->ISRCfunctionOrder > 2 + ? here->ISRCcoeffs[2] : (5./ckt->CKTfinalTime); MDI = here->ISRCfunctionOrder > 3 - ? here->ISRCcoeffs[3] : 0.0; - FS = here->ISRCfunctionOrder > 4 + ? here->ISRCcoeffs[3] : 90.0; + FC = here->ISRCfunctionOrder > 4 && here->ISRCcoeffs[4] - ? here->ISRCcoeffs[4] : (1/ckt->CKTfinalTime); + ? here->ISRCcoeffs[4] : (500./ckt->CKTfinalTime); + TD = here->ISRCfunctionOrder > 5 + ? here->ISRCcoeffs[5] : 0; + PHASEM = here->ISRCfunctionOrder > 5 + ? here->ISRCcoeffs[5] : 0.0; + PHASEC = here->ISRCfunctionOrder > 6 + ? here->ISRCcoeffs[6] : 0.0; + + /* limit the modulation index */ + if (MDI > FC / FM) { + MDI = FC / FM; + if (!warn1){ + fprintf(stderr, "Warning: MDI in %s limited to FC/FM\n", here->gen.GENname); + warn1 = TRUE; + } + } + else if (MDI < 0) { + MDI = 0; + if (!warn2) { + fprintf(stderr, "Warning: MDI in %s set to 0\n", here->gen.GENname); + warn2 = TRUE; + } + } + + /* compute phases in radians */ + phasec = PHASEC * M_PI / 180.0; + phasem = PHASEM * M_PI / 180.0; /* compute waveform value */ value = VO + VA * sin((2.0 * M_PI * FC * time + phasec) + - MDI * sin(2.0 * M_PI * FS * time + phases)); + MDI * sin(2.0 * M_PI * FM * time + phasem)); } break; case AM: { - double VO, VA, FM, MD, FC, TD, PHASEM, PHASEC; + double VO, VMO, VMA, FM, FC, TD, PHASEM, PHASEC; double phasec, phasem; VO = here->ISRCcoeffs[0]; - VA = here->ISRCcoeffs[1]; - FM = here->ISRCfunctionOrder > 2 - && here->ISRCcoeffs[2] - ? here->ISRCcoeffs[2] : (5. / ckt->CKTfinalTime); - MD = here->ISRCfunctionOrder > 3 - ? here->ISRCcoeffs[3] : 0.5; + VMO = here->ISRCcoeffs[1]; + VMA = here->ISRCfunctionOrder > 2 + ? here->ISRCcoeffs[2] : 1.; + FM = here->ISRCfunctionOrder > 3 + ? here->ISRCcoeffs[3] : (5. / ckt->CKTfinalTime); FC = here->ISRCfunctionOrder > 4 - ? here->ISRCcoeffs[4] : (500. / ckt->CKTfinalTime); - TD = here->ISRCfunctionOrder > 5 - && here->ISRCcoeffs[5] - ? here->ISRCcoeffs[5] : 0.0; - PHASEC = here->ISRCfunctionOrder > 6 + ? here->ISRCcoeffs[4] : (500. / ckt->CKTfinalTime); + TD = here->ISRCfunctionOrder > 5 + ? here->ISRCcoeffs[5] : 0.0; + PHASEM = here->ISRCfunctionOrder > 6 ? here->ISRCcoeffs[6] : 0.0; - PHASEM = here->ISRCfunctionOrder > 7 + PHASEC = here->ISRCfunctionOrder > 7 ? here->ISRCcoeffs[7] : 0.0; /* compute phases in radians */ phasec = PHASEC * M_PI / 180.0; phasem = PHASEM * M_PI / 180.0; - /* limit the modulation depth */ - if (MD > 1) - MD = 1; - else if (MD < 0) - MD = 0; - time -= TD; if (time <= 0) { value = 0; } else { /* compute waveform value */ - value = VO + VA * (1 + MD * sin(2.0 * M_PI * FM * time + phasem)) * + value = VO + (VMO + VMA * sin(2.0 * M_PI * FM * time + phasem)) * sin(2.0 * M_PI * FC * time + phasec); } } diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index f7821b39a..54183aaa5 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -227,55 +227,73 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) case SFFM: { - double VO, VA, FC, MDI, FS; - double PHASEC, PHASES; - double phasec; - double phases; - - PHASEC = here->VSRCfunctionOrder > 5 - ? here->VSRCcoeffs[5] : 0.0; - PHASES = here->VSRCfunctionOrder > 6 - ? here->VSRCcoeffs[6] : 0.0; - - /* compute phases in radians */ - phasec = PHASEC * M_PI / 180.0; - phases = PHASES * M_PI / 180.0; - - VO = here->VSRCcoeffs[0]; - VA = here->VSRCcoeffs[1]; - FC = here->VSRCfunctionOrder > 2 - && here->VSRCcoeffs[2] - ? here->VSRCcoeffs[2] : (1/ckt->CKTfinalTime); - MDI = here->VSRCfunctionOrder > 3 - ? here->VSRCcoeffs[3] : 0.0; - FS = here->VSRCfunctionOrder > 4 - && here->VSRCcoeffs[4] - ? here->VSRCcoeffs[4] : (1/ckt->CKTfinalTime); - - /* compute waveform value */ - value = VO + VA * - sin((2.0 * M_PI * FC * time + phasec) + - MDI * sin(2.0 * M_PI * FS * time + phases)); - } - break; - - case AM: { - - double VO, VA, FM, MD, FC, TD, PHASEM, PHASEC; + double VO, VA, FM, MDI, FC, TD, PHASEM, PHASEC; double phasec; double phasem; + static bool warn1 = FALSE, warn2 = FALSE; VO = here->VSRCcoeffs[0]; VA = here->VSRCcoeffs[1]; FM = here->VSRCfunctionOrder > 2 - && here->VSRCcoeffs[2] - ? here->VSRCcoeffs[2] : (5. / ckt->CKTfinalTime); - MD = here->VSRCfunctionOrder > 3 - ? here->VSRCcoeffs[3] : 0.5; + ? here->VSRCcoeffs[2] : (5./ckt->CKTfinalTime); + MDI = here->VSRCfunctionOrder > 3 + ? here->VSRCcoeffs[3] : 90.0; /* 0.9 * FC / FM */ + FC = here->VSRCfunctionOrder > 4 + && here->VSRCcoeffs[4] /* test if not 0 */ + ? here->VSRCcoeffs[4] : (500./ckt->CKTfinalTime); + TD = here->VSRCfunctionOrder > 5 + ? here->VSRCcoeffs[5] : 0; + PHASEM = here->VSRCfunctionOrder > 6 + ? here->VSRCcoeffs[6] : 0.0; + PHASEC = here->VSRCfunctionOrder > 7 + ? here->VSRCcoeffs[7] : 0.0; + + /* compute phases in radians */ + phasec = PHASEC * M_PI / 180.0; + phasem = PHASEM * M_PI / 180.0; + + /* limit the modulation index */ + if (MDI > FC / FM) { + MDI = FC / FM; + if (!warn1){ + fprintf(stderr, "Warning: MDI in %s limited to FC/FM\n", here->gen.GENname); + warn1 = TRUE; + } + } + else if (MDI < 0) { + MDI = 0; + if (!warn2) { + fprintf(stderr, "Warning: MDI in %s set to 0\n", here->gen.GENname); + warn2 = TRUE; + } + } + + time -= TD; + if (time <= 0) { + value = 0; + } + else { + /* compute waveform value */ + value = VO + VA * + sin((2.0 * M_PI * FC * time + phasec) + + MDI * sin(2.0 * M_PI * FM * time + phasem)); + } + } + break; + case AM: { + + double VO, VMO, VMA, FM, FC, TD, PHASEM, PHASEC; + double phasem, phasec; + + VO = here->VSRCcoeffs[0]; + VMO = here->VSRCcoeffs[1]; + VMA = here->VSRCfunctionOrder > 2 + ? here->VSRCcoeffs[2] : 1.; + FM = here->VSRCfunctionOrder > 3 + ? here->VSRCcoeffs[3] : (5. / ckt->CKTfinalTime); FC = here->VSRCfunctionOrder > 4 ? here->VSRCcoeffs[4] : (500. / ckt->CKTfinalTime); TD = here->VSRCfunctionOrder > 5 - && here->VSRCcoeffs[5] ? here->VSRCcoeffs[5] : 0.0; PHASEM = here->VSRCfunctionOrder > 6 ? here->VSRCcoeffs[6] : 0.0; @@ -286,18 +304,12 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) phasec = PHASEC * M_PI / 180.0; phasem = PHASEM * M_PI / 180.0; - /* limit the modulation depth */ - if (MD > 1) - MD = 1; - else if (MD < 0) - MD = 0; - time -= TD; if (time <= 0) { value = 0; } else { /* compute waveform value */ - value = VO + VA * (1 + MD * sin(2.0 * M_PI * FM * time + phasem)) * + value = VO + (VMO + VMA * sin(2.0 * M_PI * FM * time + phasem)) * sin(2.0 * M_PI * FC * time + phasec); } } From fb76eb5e12db88bffaa59c55d47ab3c80d1bbbfc Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 8 Jan 2024 13:24:01 +0100 Subject: [PATCH 15/15] Example for V/I sources, SFFM and AM --- examples/various/v-i-sources-am-ffm.cir | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 examples/various/v-i-sources-am-ffm.cir diff --git a/examples/various/v-i-sources-am-ffm.cir b/examples/various/v-i-sources-am-ffm.cir new file mode 100644 index 000000000..855b4da80 --- /dev/null +++ b/examples/various/v-i-sources-am-ffm.cir @@ -0,0 +1,65 @@ +* FFM and AM, independen voltage or current source + +* select 1 for current source, or 0 for voltage source +.param is = 0 + +* AM(VO, VMO, VMA, FM, FC, TD, PHASEM, PHASEC) + +* am modulation, modulation depth 0.9 (MD=VMA/VMO) +.if (is) +I1 0 1 AM 0.5 2 1.8 1k 100k 1m 0 0 +RI 1 0 1 +.else +V1 1 0 AM 0.5 2 1.8 1k 100k 1m 0 0 +.endif + +*Double side band suppressed carrier +.if (is) +I3 0 20 AM 0 0 1 1k 100k 1m 0 0 +RI3 20 0 1 +.else +V3 20 0 AM 0 0 1 1k 100k 1m 0 0 +.endif + +* SFFM(VO, VA, FM, MDI, FC, TD, PHASEM, PHASEC) +.if (is) +I2 0 10 SFFM 0.1 2 200 45 10k 1m 0 0 +RI2 0 10 1 +.else +V2 10 0 SFFM 0.1 2 200 45 10k 1m 0 0 ; MDI=FC/FM*0.9 +.endif + +*** diode detector for AM +D1 1 2 DMOD +.model DMOD D + +C1 2 0 5n +R1 2 0 10k +C2 2 3 2n +R2 3 0 1Meg +*** + +* Do we know a simple detector for FM ? + +.tran 500n 64m + +.control +run +rusage +set xbrushwidth=2 +plot v(1) title 'AM modulation 1kHz in 100kHz, mdepth=0.9' +plot v(1) xlimit 45m 50m title 'AM modulation 1kHz in 100kHz, mdepth=0.9' +plot v(2) v(3) title 'AM modulation, output of diode detector' +plot v(2) v(3) xlimit 45m 50m title 'AM modulation, output of diode detector' +plot v(10) title 'Frequency modulation' +plot v(10) xlimit 36m 41m title 'Frequency modulation' +plot v(20) xlimit 36m 41m title 'Double side band suppressed carrier modulation' +linearize +fft v(1) v(3) v(10) v(20) +plot mag(v(1)) xlimit 90k 110k title 'AM modulation 1kHz in 100kHz, mdepth=0.9' +plot mag(v(3)) xlimit 0k 5k title 'AM modulation, output of diode detector' +plot mag(v(10)) xlimit 0k 25k title 'Frequency modulation' +plot mag(v(20)) xlimit 90k 110k title 'Double side band suppressed carrier modulation' +.endc + +.end