diff options
Diffstat (limited to 'graphics/pgf')
-rw-r--r-- | graphics/pgf/contrib/bodeplot/README.md | 5 | ||||
-rw-r--r-- | graphics/pgf/contrib/bodeplot/bodeplot-2024-02-06.sty | 1112 | ||||
-rw-r--r-- | graphics/pgf/contrib/bodeplot/bodeplot.dtx | 46 | ||||
-rw-r--r-- | graphics/pgf/contrib/bodeplot/bodeplot.ins | 4 | ||||
-rw-r--r-- | graphics/pgf/contrib/bodeplot/bodeplot.pdf | bin | 1099158 -> 1099922 bytes |
5 files changed, 1141 insertions, 26 deletions
diff --git a/graphics/pgf/contrib/bodeplot/README.md b/graphics/pgf/contrib/bodeplot/README.md index ba00b9e9ee..037319a23a 100644 --- a/graphics/pgf/contrib/bodeplot/README.md +++ b/graphics/pgf/contrib/bodeplot/README.md @@ -7,8 +7,9 @@ Author: Rushikesh Kamalapurkar ([rlkamalapurkar@gmail.com](mailto:rlkamalapurkar License: [LPPL-1.3c](https://github.com/rlkamalapurkar/bodeplot/blob/main/LICENSE) ## Limitations - 1. TF commands are wrapped between -180 and 180 degrees in `pgf` mode. - 2. Version 1.0.8 and newer store `gnuplot` temporary files in the working directory. Use class option `declutter` to restore pre-v1.0.8 behavior. Option `declutter` can cause errors if used with a `tikzexternalize` prefix. + 1 Before version 1.2, in `pgf` mode, the package set `trig format plots` to `rad` globally. Version 1.2 onwards, this option is passed to each `addplot` command individually so it does not affect non-`bodeplot` plots. To roll back to pre-v1.2 behavior, load the package with `\usepackage[pgf]{bodeplot}[=2024-02-06]`. + 2. TF commands are wrapped between -180 and 180 degrees in `pgf` mode. + 3. Version 1.0.8 and newer store `gnuplot` temporary files in the working directory. Use class option `declutter` to restore pre-v1.0.8 behavior. Option `declutter` can cause errors if used with a `tikzexternalize` prefix. ## Compilation instructions 1) `latex bodeplot.ins` to generate `bodeplot.sty` diff --git a/graphics/pgf/contrib/bodeplot/bodeplot-2024-02-06.sty b/graphics/pgf/contrib/bodeplot/bodeplot-2024-02-06.sty new file mode 100644 index 0000000000..df2f610361 --- /dev/null +++ b/graphics/pgf/contrib/bodeplot/bodeplot-2024-02-06.sty @@ -0,0 +1,1112 @@ +%% +%% This is file `bodeplot.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% bodeplot.dtx (with options: `package') +%% This is a generated file. +%% Copyright (C) 2021 by Rushikesh Kamalapurkar +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either +%% version 1.3c of this license or (at your option) any later +%% version. The latest version of this license is in: +%% http://www.latex-project.org/lppl.txt +%% and version 1.3c or later is part of all distributions of +%% LaTeX version 2006/05/20 or later. +\NeedsTeXFormat{LaTeX2e}[2006/05/20] +\ProvidesPackage{bodeplot} +\RequirePackage{pdftexcmds} +\RequirePackage{ifplatform} +\RequirePackage{xparse} +\RequirePackage{pgfplots} + \pgfplotsset{compat=1.18} + \usepgfplotslibrary{groupplots} + +\newif\if@pgfarg\@pgfargfalse +\DeclareOption{pgf}{ + \@pgfargtrue +} +\newif\if@declutterarg\@declutterargfalse +\DeclareOption{declutter}{ + \@declutterargtrue +} +\newif\if@radarg\@radargfalse +\DeclareOption{rad}{ + \@radargtrue +} +\newif\if@hzarg\@hzargfalse +\DeclareOption{Hz}{ + \@hzargtrue +} +\ProcessOptions\relax +\newcommand{\n@mod}[2]{(#1)-((round((#1)/(#2)))*(#2))} +\newcommand{\n@mod@p}[2]{(#1)-((floor((#1)/(#2)))*(#2))} +\newcommand{\n@mod@n}[2]{(#1)-((floor((#1)/(#2))+1)*(#2))} +\if@pgfarg + \newcommand{\n@pow}[2]{(#1)^(#2)} + \pgfplotsset{ + trig format plots=rad + } +\else + \newcommand{\n@pow}[2]{(#1)**(#2)} + \newcounter{gnuplot@id} + \setcounter{gnuplot@id}{0} + \if@declutterarg + \edef\bodeplot@prefix{gnuplot/\jobname} + \else + \edef\bodeplot@prefix{\jobname} + \fi + \tikzset{ + gnuplot@prefix/.style={ + id=\arabic{gnuplot@id}, + prefix=\bodeplot@prefix + } + } + \ifwindows\else + \if@declutterarg + \immediate\write18{mkdir -p gnuplot} + \fi + \fi +\fi +\newif\if@babel\@babelfalse +\AtBeginDocument{% + \@ifpackageloaded{babel}{% + \@babeltrue + \let\shorthand@list\@empty + \def\do#1{% + \begingroup + \lccode`\~=`#1\relax + \lowercase{\ifbabelshorthand~{\g@addto@macro\shorthand@list{~}}{}} + \endgroup + } + \dospecials + }{} +} +\pgfplotsset{ + bode@style/.style = { + label style={font=\footnotesize}, + tick label style={font=\footnotesize}, + grid=both, + major grid style={color=gray!80}, + minor grid style={color=gray!20}, + x label style={at={(ticklabel cs:0.5)},anchor=near ticklabel}, + y label style={at={(ticklabel cs:0.5)},anchor=near ticklabel}, + scale only axis, + samples=200, + width=5cm, + log basis x=10 + } +} +\pgfplotsset{freq@filter/.style = {}} +\def\freq@scale{1} +\pgfplotsset{freq@label/.style = {xlabel = {Frequency (rad/s)}}} +\pgfplotsset{ph@x@label/.style = {xlabel={Phase (deg)}}} +\pgfplotsset{ph@y@label/.style = {ylabel={Phase (deg)}}} +\def\ph@scale{180/pi} +\if@radarg + \pgfplotsset{ph@y@label/.style = {ylabel={Phase (rad)}}} + \pgfplotsset{ph@x@label/.style = {xlabel={Phase (rad)}}} + \def\ph@scale{1} +\fi +\if@hzarg + \def\freq@scale{2*pi} + \pgfplotsset{freq@label/.style = {xlabel = {Frequency (Hz)}}} + \if@pgfarg + \pgfplotsset{freq@filter/.style = {x filter/.expression={x-log10(2*pi)}}} + \fi +\fi +\tikzset{ + phase unit/.initial={deg}, + phase unit/.default={deg}, + phase unit/.is choice, + phase unit/deg/.code={ + \renewcommand{\ph@scale}{180/pi} + \pgfplotsset{ph@x@label/.style = {xlabel={Phase (deg)}}} + \pgfplotsset{ph@y@label/.style = {ylabel={Phase (deg)}}} + }, + phase unit/rad/.code={ + \renewcommand{\ph@scale}{1} + \pgfplotsset{ph@y@label/.style = {ylabel={Phase (rad)}}} + \pgfplotsset{ph@x@label/.style = {xlabel={Phase (rad)}}} + }, + frequency unit/.initial={rad}, + frequency unit/.default={rad}, + frequency unit/.is choice, + frequency unit/Hz/.code={ + \renewcommand{\freq@scale}{2*pi} + \pgfplotsset{freq@label/.style = {xlabel = {Frequency (Hz)}}} + \if@pgfarg + \pgfplotsset{freq@filter/.style = {x filter/.expression={x-log10(2*pi)}}} + \fi + }, + frequency unit/rad/.code={ + \renewcommand{\freq@scale}{1} + \pgfplotsset{freq@label/.style = {xlabel = {Frequency (rad/s)}}} + } +} +\def\get@interval@start#1:#2\@nil{#1} +\def\get@interval@end#1:#2\@nil{#2} +\newcommand*{\MagK}[2]{(20*log10(abs(#1)))} +\newcommand*{\MagKAsymp}{\MagK} +\newcommand*{\MagKLin}{\MagK} +\newcommand*{\PhK}[2]{((#1<0?-pi:0)*\ph@scale)} +\newcommand*{\PhKAsymp}{\PhK} +\newcommand*{\PhKLin}{\PhK} +\newcommand*{\MagDel}[2]{0} +\newcommand*{\PhDel}[2]{(-#1*t*\ph@scale)} +\newcommand*{\MagPole}[2] + {(-20*log10(sqrt(\n@pow{#1}{2} + \n@pow{t - (#2)}{2})))} +\newcommand*{\MagPoleLin}[2]{(t < sqrt(\n@pow{#1}{2} + \n@pow{#2}{2}) ? + -20*log10(sqrt(\n@pow{#1}{2} + \n@pow{#2}{2})) : + -20*log10(t) + )} +\newcommand*{\MagPoleAsymp}{\MagPoleLin} +\newcommand*{\PhPole}[2]{((#1 > 0 ? (#2 > 0 ? + (\n@mod@p{-atan2((t - (#2)),-(#1))}{2*pi}) : + (-atan2((t - (#2)),-(#1)))) : + (-atan2((t - (#2)),-(#1))))*\ph@scale)} +\newcommand*{\PhPoleLin}[2]{ + ((abs(#1)+abs(#2) == 0 ? -pi/2 : + (t < (sqrt(\n@pow{#1}{2} + \n@pow{#2}{2}) / + (\n@pow{10}{sqrt(\n@pow{#1}{2}/(\n@pow{#1}{2} + \n@pow{#2}{2}))})) ? + (-atan2(-(#2),-(#1))) : + (t >= (sqrt(\n@pow{#1}{2} + \n@pow{#2}{2}) * + (\n@pow{10}{sqrt(\n@pow{#1}{2}/(\n@pow{#1}{2} + \n@pow{#2}{2}))})) ? + (#2>0?(#1>0?3*pi/2:-pi/2):-pi/2) : + (-atan2(-(#2),-(#1)) + (log10(t/(sqrt(\n@pow{#1}{2} + \n@pow{#2}{2}) / + (\n@pow{10}{sqrt(\n@pow{#1}{2}/(\n@pow{#1}{2} + + \n@pow{#2}{2}))}))))*((#2>0?(#1>0?3*pi/2:-pi/2):-pi/2) + atan2(-(#2),-(#1)))/ + (log10(\n@pow{10}{sqrt((4*\n@pow{#1}{2})/ + (\n@pow{#1}{2} + \n@pow{#2}{2}))}))))))*\ph@scale)} +\newcommand*{\PhPoleAsymp}[2]{((t < (sqrt(\n@pow{#1}{2} + \n@pow{#2}{2})) ? + (-atan2(-(#2),-(#1))) : + (#2>0?(#1>0?3*pi/2:-pi/2):-pi/2))*\ph@scale)} +\newcommand*{\MagZero}{0-\MagPole} +\newcommand*{\MagZeroLin}{0-\MagPoleLin} +\newcommand*{\MagZeroAsymp}{0-\MagPoleAsymp} +\newcommand*{\PhZero}{0-\PhPole} +\newcommand*{\PhZeroLin}{0-\PhPoleLin} +\newcommand*{\PhZeroAsymp}{0-\PhPoleAsymp} +\newcommand*{\MagCSPoles}[2]{(-20*log10(sqrt(\n@pow{\n@pow{#2}{2} + - \n@pow{t}{2}}{2} + \n@pow{2*#1*#2*t}{2})))} +\newcommand*{\MagCSPolesLin}[2]{(t < #2 ? -40*log10(#2) : - 40*log10(t))} +\newcommand*{\MagCSPolesAsymp}{\MagCSPolesLin} +\newcommand*{\PhCSPoles}[2]{((-atan2((2*(#1)*(#2)*t),(\n@pow{#2}{2} + - \n@pow{t}{2})))*\ph@scale)} +\newcommand*{\PhCSPolesLin}[2]{((t < (#2 / (\n@pow{10}{abs(#1)})) ? + 0 : + (t >= (#2 * (\n@pow{10}{abs(#1)})) ? + (#1>0 ? -pi : pi) : + (#1>0 ? (-pi*(log10(t*(\n@pow{10}{#1})/#2))/(2*#1)) : + (pi*(log10(t*(\n@pow{10}{abs(#1)})/#2))/(2*abs(#1))))))*\ph@scale)} +\newcommand*{\PhCSPolesAsymp}[2]{((#1>0?(t<#2?0:-pi):(t<#2?0:pi))*\ph@scale)} +\newcommand*{\MagCSZeros}{0-\MagCSPoles} +\newcommand*{\MagCSZerosLin}{0-\MagCSPolesLin} +\newcommand*{\MagCSZerosAsymp}{0-\MagCSPolesAsymp} +\newcommand*{\PhCSZeros}{0-\PhCSPoles} +\newcommand*{\PhCSZerosLin}{0-\PhCSPolesLin} +\newcommand*{\PhCSZerosAsymp}{0-\PhCSPolesAsymp} +\newcommand*{\MagCSPolesPeak}[3][]{ + \draw[#1,->] (axis cs:{#3},{-40*log10(#3)}) -- + (axis cs:{#3},{-40*log10(#3)-20*log10(2*abs(#2))}) +} +\newcommand*{\MagCSZerosPeak}[3][]{ + \draw[#1,->] (axis cs:{#3},{40*log10(#3)}) -- + (axis cs:{#3},{40*log10(#3)+20*log10(2*abs(#2))}) +} +\newcommand*{\MagSOPoles}[2]{ + (-20*log10(sqrt(\n@pow{#2 - \n@pow{t}{2}}{2} + \n@pow{#1*t}{2})))} +\newcommand*{\MagSOPolesLin}[2]{ + (t < sqrt(abs(#2)) ? -20*log10(abs(#2)) : - 40*log10(t))} +\newcommand*{\MagSOPolesAsymp}{\MagSOPolesLin} +\newcommand*{\PhSOPoles}[2]{((-atan2((#1)*t,((#2) - \n@pow{t}{2})))*\ph@scale)} +\newcommand*{\PhSOPolesLin}[2]{((#2>0 ? + \PhCSPolesLin{(#1/(2*sqrt(#2)))}{(sqrt(#2))} : + (#1>0 ? -pi : pi)))} +\newcommand*{\PhSOPolesAsymp}[2]{((#2>0 ? + \PhCSPolesAsymp{(#1/(2*sqrt(#2)))}{(sqrt(#2))} : + (#1>0 ? -pi : pi)))} +\newcommand*{\MagSOZeros}{0-\MagSOPoles} +\newcommand*{\MagSOZerosLin}{0-\MagSOPolesLin} +\newcommand*{\MagSOZerosAsymp}{0-\MagSOPolesAsymp} +\newcommand*{\PhSOZeros}{0-\PhSOPoles} +\newcommand*{\PhSOZerosLin}{0-\PhSOPolesLin} +\newcommand*{\PhSOZerosAsymp}{0-\PhSOPolesAsymp} +\newcommand*{\MagSOPolesPeak}[3][]{ + \draw[#1,->] (axis cs:{sqrt(abs(#3))},{-20*log10(abs(#3))}) -- + (axis cs:{sqrt(abs(#3))},{-20*log10(abs(#3)) - + 20*log10(abs(#2/sqrt(abs(#3))))}); +} +\newcommand*{\MagSOZerosPeak}[3][]{ + \draw[#1,->] (axis cs:{sqrt(abs(#3))},{20*log10(abs(#3))}) -- + (axis cs:{sqrt(abs(#3))},{20*log10(abs(#3)) + + 20*log10(abs(#2/sqrt(abs(#3))))}); +} +\newcommand{\BodeZPK}[4][approx/true]{ + \parse@opt{#1} + \gdef\func@mag{} + \gdef\func@ph{} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \build@ZPK@plot{\func@mag}{\func@ph}{\opt@approx}{#2} + \edef\temp@cmd{\noexpand\begin{groupplot}[ + bode@style, + xmin=#3, + xmax=#4, + domain=#3*\freq@scale:#4*\freq@scale, + height=2.5cm, + xmode=log, + group style = {group size = 1 by 2,vertical sep=0.25cm}, + \opt@group + ]} + \temp@cmd + \edef\temp@mag@cmd{\noexpand\nextgroupplot [ylabel={Gain (dB)}, xmajorticks=false, \optmag@axes] + \noexpand\addplot [freq@filter, variable=t, thick, \optmag@plot]} + \edef\temp@ph@cmd{\noexpand\nextgroupplot [ph@y@label, freq@label, \optph@axes] + \noexpand\addplot [freq@filter, variable=t, thick, \optph@plot]} + \if@pgfarg + \temp@mag@cmd {\func@mag}; + \optmag@commands + \temp@ph@cmd {\func@ph}; + \optph@commands + \else + \stepcounter{gnuplot@id} + \temp@mag@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set xrange [#3*\freq@scale:#4*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot \func@mag; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2); + }; + \optmag@commands + \stepcounter{gnuplot@id} + \temp@ph@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set xrange [#3*\freq@scale:#4*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot \func@ph; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2); + }; + \optph@commands + \fi + \end{groupplot} + \end{tikzpicture} +} +\AtBeginDocument{% + \if@babel + \let\Orig@BodeZPK\BodeZPK + \renewcommand{\BodeZPK}{% + \expandafter\shorthandoff\expandafter{\shorthand@list} + \BodeZPK@Shorthandoff + } + \newcommand{\BodeZPK@Shorthandoff}[4][]{% + \Orig@BodeZPK[#1]{#2}{#3}{#4} + \expandafter\shorthandon\expandafter{\shorthand@list} + } + \fi +} +\newcommand{\BodeTF}[4][]{ + \parse@opt{#1} + \gdef\func@mag{} + \gdef\func@ph{} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \build@TF@plot{\func@mag}{\func@ph}{#2} + \edef\temp@cmd{\noexpand\begin{groupplot}[ + bode@style, + xmin=#3, + xmax=#4, + domain=#3*\freq@scale:#4*\freq@scale, + height=2.5cm, + xmode=log, + group style = {group size = 1 by 2,vertical sep=0.25cm}, + \opt@group + ]} + \temp@cmd + \edef\temp@mag@cmd{\noexpand\nextgroupplot [ylabel={Gain (dB)}, xmajorticks=false, \optmag@axes] + \noexpand\addplot [freq@filter, variable=t, thick, \optmag@plot]} + \edef\temp@ph@cmd{\noexpand\nextgroupplot [ph@y@label, freq@label, \optph@axes] + \noexpand\addplot [freq@filter, variable=t, thick, \optph@plot]} + \if@pgfarg + \temp@mag@cmd {\func@mag}; + \optmag@commands + \temp@ph@cmd {\n@mod{\func@ph}{2*pi*\ph@scale}}; + \optph@commands + \else + \stepcounter{gnuplot@id} + \temp@mag@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set xrange [#3*\freq@scale:#4*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot \func@mag; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2); + }; + \optmag@commands + \stepcounter{gnuplot@id} + \temp@ph@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set trange [#3*\freq@scale:#4*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot '+' using (t) : ((\func@ph)/(\ph@scale)) smooth unwrap; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2*\ph@scale); + }; + \optph@commands + \fi + \end{groupplot} + \end{tikzpicture} +} +\AtBeginDocument{ + \if@babel + \let\Orig@BodeTF\BodeTF + \renewcommand{\BodeTF}{% + \expandafter\shorthandoff\expandafter{\shorthand@list} + \BodeTF@Shorthandoff + } + \newcommand{\BodeTF@Shorthandoff}[4][]{% + \Orig@BodeTF[#1]{#2}{#3}{#4} + \expandafter\shorthandon\expandafter{\shorthand@list} + } + \fi +} +\newcommand{\addBodeZPKPlots}[3][true/{}]{ + \foreach \approx/\opt in {#1} { + \gdef\plot@macro{} + \gdef\temp@macro{} + \ifnum\pdf@strcmp{#2}{phase}=0 + \build@ZPK@plot{\temp@macro}{\plot@macro}{\approx}{#3} + \else + \build@ZPK@plot{\plot@macro}{\temp@macro}{\approx}{#3} + \fi + \if@pgfarg + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, thick, \opt]} + \temp@cmd {\plot@macro}; + \else + \stepcounter{gnuplot@id} + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, \opt]} + \temp@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set xrange [\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot \plot@macro; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2); + }; + \fi + } +} +\newcommand{\addBodeTFPlot}[3][thick]{ + \gdef\plot@macro{} + \gdef\temp@macro{} + \ifnum\pdf@strcmp{#2}{phase}=0 + \build@TF@plot{\temp@macro}{\plot@macro}{#3} + \else + \build@TF@plot{\plot@macro}{\temp@macro}{#3} + \fi + \if@pgfarg + \ifnum\pdf@strcmp{#2}{phase}=0 + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd {\n@mod{\plot@macro}{2*pi}}; + \else + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd {\plot@macro}; + \fi + \else + \stepcounter{gnuplot@id} + \ifnum\pdf@strcmp{#2}{phase}=0 + \addplot [variable=t, #1] gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set trange [\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot '+' using (t) : ((\plot@macro)/(\ph@scale)) smooth unwrap; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2*\ph@scale); + }; + \else + \addplot [variable=t, #1] gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set xrange [\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot \plot@macro; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2); + }; + \fi + \fi +} +\newcommand{\addBodeComponentPlot}[2][thick]{ + \if@pgfarg + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd {#2}; + \else + \stepcounter{gnuplot@id} + \addplot [variable=t, #1] gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set dummy t; + set logscale x 10; + set xrange [\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale]; + set samples \pgfkeysvalueof{/pgfplots/samples}; + plot #2; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($1/(\freq@scale)):($2); + }; + \fi +} +\AtBeginDocument{% + \if@babel + \AddToHook{env/BodePhPlot/begin}{\expandafter\shorthandoff\expandafter{\shorthand@list}} + \AddToHook{env/BodePhPlot/end}{\expandafter\shorthandon\expandafter{\shorthand@list}} + \fi +} +\NewDocumentEnvironment{BodePhPlot}{O{}mm+b}{ + \parse@env@opt{#1} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \edef\temp@cmd{\noexpand\begin{semilogxaxis}[ + ph@y@label, + freq@label, + bode@style, + xmin={#2}, + xmax={#3}, + domain=#2:#3, + height=2.5cm, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + #4 + \end{semilogxaxis} + \end{tikzpicture} +}{} +\AtBeginDocument{% + \if@babel + \AddToHook{env/BodeMagPlot/begin}{\expandafter\shorthandoff\expandafter{\shorthand@list}} + \AddToHook{env/BodeMagPlot/end}{\expandafter\shorthandon\expandafter{\shorthand@list}} + \fi +} +\NewDocumentEnvironment{BodeMagPlot}{O{}mm+b}{ + \parse@env@opt{#1} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \edef\temp@cmd{\noexpand\begin{semilogxaxis}[ + bode@style, + freq@label, + xmin={#2}, + xmax={#3}, + domain=#2:#3, + height=2.5cm, + ylabel={Gain (dB)}, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + #4 + \end{semilogxaxis} + \end{tikzpicture} +}{} +\AtBeginDocument{% + \if@babel + \AddToHook{env/BodePlot/begin}{\expandafter\shorthandoff\expandafter{\shorthand@list}} + \AddToHook{env/BodePlot/end}{\expandafter\shorthandon\expandafter{\shorthand@list}} + \fi +} +\NewDocumentEnvironment{BodePlot}{O{}mm+b}{ + \parse@env@opt{#1} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \edef\temp@cmd{\noexpand\begin{semilogxaxis}[ + bode@style, + freq@label, + xmin={#2}, + xmax={#3}, + domain=#2:#3, + height=2.5cm, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + #4 + \end{semilogxaxis} + \end{tikzpicture} +}{} +\newcommand*{\add@feature}[3]{ + \ifcat$\detokenize\expandafter{#1}$ + \xdef#1{\unexpanded\expandafter{#1 0+#2}} + \else + \xdef#1{\unexpanded\expandafter{#1+#2}} + \fi + \foreach \y [count=\n] in #3 { + \xdef#1{\unexpanded\expandafter{#1}{\y}} + \xdef\Last@LoopValue{\n} + } + \ifnum\Last@LoopValue=1 + \xdef#1{\unexpanded\expandafter{#1}{0}} + \fi +} +\newcommand{\build@ZPK@plot}[4]{ + \foreach \feature/\values in {#4} { + \ifnum\pdf@strcmp{\feature}{z}=0 + \foreach \z in \values { + \ifnum\pdf@strcmp{#3}{linear}=0 + \add@feature{#2}{\PhZeroLin}{\z} + \add@feature{#1}{\MagZeroLin}{\z} + \else + \ifnum\pdf@strcmp{#3}{asymptotic}=0 + \add@feature{#2}{\PhZeroAsymp}{\z} + \add@feature{#1}{\MagZeroAsymp}{\z} + \else + \add@feature{#2}{\PhZero}{\z} + \add@feature{#1}{\MagZero}{\z} + \fi + \fi + } + \fi + \ifnum\pdf@strcmp{\feature}{p}=0 + \foreach \p in \values { + \ifnum\pdf@strcmp{#3}{linear}=0 + \add@feature{#2}{\PhPoleLin}{\p} + \add@feature{#1}{\MagPoleLin}{\p} + \else + \ifnum\pdf@strcmp{#3}{asymptotic}=0 + \add@feature{#2}{\PhPoleAsymp}{\p} + \add@feature{#1}{\MagPoleAsymp}{\p} + \else + \add@feature{#2}{\PhPole}{\p} + \add@feature{#1}{\MagPole}{\p} + \fi + \fi + } + \fi + \ifnum\pdf@strcmp{\feature}{k}=0 + \ifnum\pdf@strcmp{#3}{linear}=0 + \add@feature{#2}{\PhKLin}{\values} + \add@feature{#1}{\MagKLin}{\values} + \else + \ifnum\pdf@strcmp{#3}{asymptotic}=0 + \add@feature{#2}{\PhKAsymp}{\values} + \add@feature{#1}{\MagKAsymp}{\values} + \else + \add@feature{#2}{\PhK}{\values} + \add@feature{#1}{\MagK}{\values} + \fi + \fi + \fi + \ifnum\pdf@strcmp{\feature}{d}=0 + \ifnum\pdf@strcmp{#3}{linear}=0 + \PackageError {bodeplot} {Linear approximation for pure delays is not + supported.} {Plot the true Bode plot using `true' instead of `linear'.} + \else + \ifnum\pdf@strcmp{#3}{asymptotic}=0 + \PackageError {bodeplot} {Asymptotic approximation for pure delays is not + supported.} {Plot the true Bode plot using `true' instead of `asymptotic'.} + \else + \ifdim\values pt < 0pt + \PackageError {bodeplot} {Delay needs to be a positive number.} + \fi + \add@feature{#2}{\PhDel}{\values} + \add@feature{#1}{\MagDel}{\values} + \fi + \fi + \fi + } +} +\newcommand{\build@TF@plot}[3]{ + \gdef\num@real{0} + \gdef\num@im{0} + \gdef\den@real{0} + \gdef\den@im{0} + \gdef\loop@delay{0} + \foreach \feature/\values in {#3} { + \ifnum\pdf@strcmp{\feature}{num}=0 + \foreach \numcoeff [count=\numpow] in \values { + \xdef\num@degree{\numpow} + } + \foreach \numcoeff [count=\numpow] in \values { + \pgfmathtruncatemacro{\currentdegree}{\num@degree-\numpow} + \ifnum\currentdegree = 0 + \xdef\num@real{\num@real+\numcoeff} + \else + \ifodd\currentdegree + \xdef\num@im{\num@im+(\numcoeff*(\n@pow{-1}{(\currentdegree-1)/2})*% + (\n@pow{t}{\currentdegree}))} + \else + \xdef\num@real{\num@real+(\numcoeff*(\n@pow{-1}{(\currentdegree)/2})*% + (\n@pow{t}{\currentdegree}))} + \fi + \fi + } + \fi + \ifnum\pdf@strcmp{\feature}{den}=0 + \foreach \dencoeff [count=\denpow] in \values { + \xdef\den@degree{\denpow} + } + \foreach \dencoeff [count=\denpow] in \values { + \pgfmathtruncatemacro{\currentdegree}{\den@degree-\denpow} + \ifnum\currentdegree = 0 + \xdef\den@real{\den@real+\dencoeff} + \else + \ifodd\currentdegree + \xdef\den@im{\den@im+(\dencoeff*(\n@pow{-1}{(\currentdegree-1)/2})*% + (\n@pow{t}{\currentdegree}))} + \else + \xdef\den@real{\den@real+(\dencoeff*(\n@pow{-1}{(\currentdegree)/2})*% + (\n@pow{t}{\currentdegree}))} + \fi + \fi + } + \fi + \ifnum\pdf@strcmp{\feature}{d}=0 + \xdef\loop@delay{\values} + \fi + } + \xdef#2{((atan2((\num@im),(\num@real))-atan2((\den@im),% + (\den@real))-\loop@delay*t)*(\ph@scale))} + \xdef#1{(20*log10(sqrt((\n@pow{\num@real}{2})+(\n@pow{\num@im}{2})))-% + 20*log10(sqrt((\n@pow{\den@real}{2})+(\n@pow{\den@im}{2}))))} +} +\newcommand{\parse@opt}[1]{ + \gdef\optmag@axes{} + \gdef\optph@axes{} + \gdef\optph@plot{} + \gdef\optmag@plot{} + \gdef\opt@group{} + \gdef\opt@approx{} + \gdef\optph@commands{} + \gdef\optmag@commands{} + \gdef\opt@tikz{} + \foreach \obj/\typ/\opt in {#1} { + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{plot}=0 + \ifnum\pdf@strcmp{\unexpanded\expandafter{\typ}}{mag}=0 + \xdef\optmag@plot{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\typ}}{ph}=0 + \xdef\optph@plot{\unexpanded\expandafter{\opt}} + \else + \xdef\optmag@plot{\unexpanded\expandafter{\opt}} + \xdef\optph@plot{\unexpanded\expandafter{\opt}} + \fi + \fi + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{axes}=0 + \ifnum\pdf@strcmp{\unexpanded\expandafter{\typ}}{mag}=0 + \xdef\optmag@axes{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\typ}}{ph}=0 + \xdef\optph@axes{\unexpanded\expandafter{\opt}} + \else + \xdef\optmag@axes{\unexpanded\expandafter{\opt}} + \xdef\optph@axes{\unexpanded\expandafter{\opt}} + \fi + \fi + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{group}=0 + \xdef\opt@group{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{approx}=0 + \xdef\opt@approx{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{commands}=0 + \ifnum\pdf@strcmp{\unexpanded\expandafter{\typ}}{ph}=0 + \xdef\optph@commands{\unexpanded\expandafter{\opt}} + \else + \xdef\optmag@commands{\unexpanded\expandafter{\opt}} + \fi + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{tikz}=0 + \xdef\opt@tikz{\unexpanded\expandafter{\opt}} + \else + \xdef\optmag@plot{\unexpanded\expandafter{\optmag@plot}, + \unexpanded\expandafter{\obj}} + \xdef\optph@plot{\unexpanded\expandafter{\optph@plot}, + \unexpanded\expandafter{\obj}} + \fi + \fi + \fi + \fi + \fi + \fi + } +} +\newcommand{\parse@env@opt}[1]{ + \gdef\opt@axes{} + \gdef\opt@tikz{} + \foreach \obj/\opt in {#1} { + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{axes}=0 + \xdef\opt@axes{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{tikz}=0 + \xdef\opt@tikz{\unexpanded\expandafter{\opt}} + \else + \xdef\opt@axes{\unexpanded\expandafter{\opt@axes}, + \unexpanded\expandafter{\obj}} + \fi + \fi + } +} +\newcommand{\NyquistZPK}[4][]{ + \parse@N@opt{#1} + \gdef\func@mag{} + \gdef\func@ph{} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \build@ZPK@plot{\func@mag}{\func@ph}{}{#2} + \edef\temp@cmd{\noexpand\begin{axis}[ + bode@style, + domain=#3*\freq@scale:#4*\freq@scale, + height=5cm, + xlabel={$\Re$}, + ylabel={$\Im$}, + samples=500, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + \addplot [only marks,mark=+,thick,red] (-1 , 0); + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, \unexpanded\expandafter{\opt@plot}]} + \if@pgfarg + \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, + {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); + \opt@commands + \else + \stepcounter{gnuplot@id} + \temp@cmd gnuplot [parametric, gnuplot@prefix] { + \n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale)), + \n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale)) + }; + \opt@commands + \fi + \end{axis} + \end{tikzpicture} +} +\AtBeginDocument{% + \if@babel + \let\Orig@NyquistZPK\NyquistZPK + \renewcommand{\NyquistZPK}{% + \expandafter\shorthandoff\expandafter{\shorthand@list} + \NyquistZPK@Shorthandoff + } + \newcommand{\NyquistZPK@Shorthandoff}[4][]{% + \Orig@NyquistZPK[#1]{#2}{#3}{#4} + \expandafter\shorthandon\expandafter{\shorthand@list} + } + \fi +} +\newcommand{\NyquistTF}[4][]{ + \parse@N@opt{#1} + \gdef\func@mag{} + \gdef\func@ph{} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \build@TF@plot{\func@mag}{\func@ph}{#2} + \edef\temp@cmd{\noexpand\begin{axis}[ + bode@style, + domain=#3*\freq@scale:#4*\freq@scale, + height=5cm, + xlabel={$\Re$}, + ylabel={$\Im$}, + samples=500, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + \addplot [only marks, mark=+, thick, red] (-1 , 0); + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, \unexpanded\expandafter{\opt@plot}]} + \if@pgfarg + \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, + {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); + \opt@commands + \else + \stepcounter{gnuplot@id} + \temp@cmd gnuplot [parametric, gnuplot@prefix] { + \n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale)), + \n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale)) + }; + \opt@commands + \fi + \end{axis} + \end{tikzpicture} +} +\AtBeginDocument{% + \if@babel + \let\Orig@NyquistTF\NyquistTF + \renewcommand{\NyquistTF}{% + \expandafter\shorthandoff\expandafter{\shorthand@list} + \NyquistTF@Shorthandoff + } + \newcommand{\NyquistTF@Shorthandoff}[4][]{% + \Orig@NyquistTF[#1]{#2}{#3}{#4} + \expandafter\shorthandon\expandafter{\shorthand@list} + } + \fi +} +\newcommand{\addNyquistZPKPlot}[2][]{ + \gdef\func@mag{} + \gdef\func@ph{} + \build@ZPK@plot{\func@mag}{\func@ph}{}{#2} + \if@pgfarg + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, + {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); + \else + \stepcounter{gnuplot@id} + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd gnuplot [parametric, gnuplot@prefix] { + \n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale)), + \n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale)) + }; + \fi +} +\newcommand{\addNyquistTFPlot}[2][]{ + \gdef\func@mag{} + \gdef\func@ph{} + \build@TF@plot{\func@mag}{\func@ph}{#2} + \if@pgfarg + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, + {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); + \else + \stepcounter{gnuplot@id} + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd gnuplot [parametric, gnuplot@prefix]{ + \n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale)), + \n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale)) + }; + \fi +} +\AtBeginDocument{% + \if@babel + \AddToHook{env/NyquistPlot/begin}{\expandafter\shorthandoff\expandafter{\shorthand@list}} + \AddToHook{env/NyquistPlot/end}{\expandafter\shorthandon\expandafter{\shorthand@list}} + \fi +} +\NewDocumentEnvironment{NyquistPlot}{O{}mm+b}{ + \parse@env@opt{#1} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \edef\temp@cmd{\noexpand\begin{axis}[ + bode@style, + height=5cm, + domain=#2:#3, + xlabel={$\Re$}, + ylabel={$\Im$}, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + \addplot [only marks,mark=+,thick,red] (-1 , 0); + #4 + \end{axis} + \end{tikzpicture} +}{} +\newcommand{\parse@N@opt}[1]{ + \gdef\opt@axes{} + \gdef\opt@plot{} + \gdef\opt@commands{} + \gdef\opt@tikz{} + \foreach \obj/\opt in {#1} { + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{axes}=0 + \xdef\opt@axes{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{plot}=0 + \xdef\opt@plot{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{commands}=0 + \xdef\opt@commands{\unexpanded\expandafter{\opt}} + \else + \ifnum\pdf@strcmp{\unexpanded\expandafter{\obj}}{tikz}=0 + \xdef\opt@tikz{\unexpanded\expandafter{\opt}} + \else + \xdef\opt@plot{\unexpanded\expandafter{\opt@plot}, + \unexpanded\expandafter{\obj}} + \fi + \fi + \fi + \fi + } +} +\newcommand{\NicholsZPK}[4][]{ + \parse@N@opt{#1} + \gdef\func@mag{} + \gdef\func@ph{} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \build@ZPK@plot{\func@mag}{\func@ph}{}{#2} + \edef\temp@cmd{\noexpand\begin{axis}[ + ph@x@label, + bode@style, + domain=#3*\freq@scale:#4*\freq@scale, + height=5cm, + ylabel={Gain (dB)}, + samples=500, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + \edef\temp@cmd{\noexpand\addplot [variable=t,thick,\opt@plot]} + \if@pgfarg + \temp@cmd ( {\func@ph} , {\func@mag} ); + \opt@commands + \else + \stepcounter{gnuplot@id} + \temp@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set logscale x 10; + set dummy t; + set samples \pgfkeysvalueof{/pgfplots/samples}; + set trange [#3*\freq@scale:#4*\freq@scale]; + plot '+' using (\func@mag) : ((\func@ph)/(\ph@scale)); + unset logscale x; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($2*\ph@scale):($1); + }; + \opt@commands + \fi + \end{axis} + \end{tikzpicture} +} +\AtBeginDocument{% + \if@babel + \let\Orig@NicholsZPK\NicholsZPK + \renewcommand{\NicholsZPK}{% + \expandafter\shorthandoff\expandafter{\shorthand@list} + \NicholsZPK@Shorthandoff + } + \newcommand{\NicholsZPK@Shorthandoff}[4][]{% + \Orig@NicholsZPK[#1]{#2}{#3}{#4} + \expandafter\shorthandon\expandafter{\shorthand@list} + } + \fi +} +\newcommand{\NicholsTF}[4][]{ + \parse@N@opt{#1} + \gdef\func@mag{} + \gdef\func@ph{} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \build@TF@plot{\func@mag}{\func@ph}{#2} + \edef\temp@cmd{\noexpand\begin{axis}[ + ph@x@label, + bode@style, + domain=#3*\freq@scale:#4*\freq@scale, + height=5cm, + ylabel={Gain (dB)}, + samples=500, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + \edef\temp@cmd{\noexpand\addplot [variable=t,thick, \opt@plot]} + \if@pgfarg + \temp@cmd ( {\n@mod{\func@ph}{2*pi*\ph@scale}} , {\func@mag} ); + \opt@commands + \else + \stepcounter{gnuplot@id} + \temp@cmd gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta1; + set logscale x 10; + set dummy t; + set samples \pgfkeysvalueof{/pgfplots/samples}; + set trange [#3*\freq@scale:#4*\freq@scale]; + plot '+' using (\func@mag) : ((\func@ph)/(\ph@scale)); + unset logscale x; + set table $meta2; + plot "$meta1" using ($1):($2) smooth unwrap; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta2" using ($2*\ph@scale):($1); + }; + \opt@commands + \fi + \end{axis} + \end{tikzpicture} +} +\AtBeginDocument{ + \if@babel + \let\Orig@NicholsTF\NicholsTF + \renewcommand{\NicholsTF}{% + \expandafter\shorthandoff\expandafter{\shorthand@list} + \NicholsTF@Shorthandoff + } + \newcommand{\NicholsTF@Shorthandoff}[4][]{% + \Orig@NicholsTF[#1]{#2}{#3}{#4} + \expandafter\shorthandon\expandafter{\shorthand@list} + } + \AddToHook{env/NicholsChart/begin}{\expandafter\shorthandoff\expandafter{\shorthand@list}} + \AddToHook{env/NicholsChart/end}{\expandafter\shorthandon\expandafter{\shorthand@list}} + \fi +} +\NewDocumentEnvironment{NicholsChart}{O{}mm+b}{ + \parse@env@opt{#1} + \edef\temp@cmd{\noexpand\begin{tikzpicture} [\unexpanded\expandafter{\opt@tikz}]} + \temp@cmd + \edef\temp@cmd{\noexpand\begin{axis}[ + ph@x@label, + bode@style, + domain=#2:#3, + height=5cm, + ylabel={Gain (dB)}, + \unexpanded\expandafter{\opt@axes} + ]} + \temp@cmd + #4 + \end{axis} + \end{tikzpicture} +}{} +\newcommand{\addNicholsZPKChart}[2][]{ + \gdef\func@mag{} + \gdef\func@ph{} + \build@ZPK@plot{\func@mag}{\func@ph}{}{#2} + \if@pgfarg + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd ( {\func@ph} , {\func@mag} ); + \else + \stepcounter{gnuplot@id} + \addplot [#1] gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta; + set logscale x 10; + set dummy t; + set samples \pgfkeysvalueof{/pgfplots/samples}; + set trange [\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale]; + plot '+' using (\func@mag) : ((\func@ph)/(\ph@scale)); + unset logscale x; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta" using ($2*\ph@scale):($1); + }; + \fi +} +\newcommand{\addNicholsTFChart}[2][]{ + \gdef\func@mag{} + \gdef\func@ph{} + \build@TF@plot{\func@mag}{\func@ph}{#2} + \if@pgfarg + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \temp@cmd ( {\n@mod{\func@ph}{2*pi*\ph@scale}} , {\func@mag} ); + \else + \stepcounter{gnuplot@id} + \addplot [#1] gnuplot [raw gnuplot, gnuplot@prefix] + { set table $meta1; + set logscale x 10; + set dummy t; + set samples \pgfkeysvalueof{/pgfplots/samples}; + set trange [\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale]; + plot '+' using (\func@mag) : ((\func@ph)/(\ph@scale)); + unset logscale x; + set table $meta2; + plot "$meta1" using ($1):($2) smooth unwrap; + set table "\bodeplot@prefix\arabic{gnuplot@id}.table"; + plot "$meta2" using ($2*\ph@scale):($1); + }; + \fi +} +\endinput +%% +%% End of file `bodeplot.sty'. diff --git a/graphics/pgf/contrib/bodeplot/bodeplot.dtx b/graphics/pgf/contrib/bodeplot/bodeplot.dtx index d9964e4a1b..09031ab3f9 100644 --- a/graphics/pgf/contrib/bodeplot/bodeplot.dtx +++ b/graphics/pgf/contrib/bodeplot/bodeplot.dtx @@ -16,8 +16,10 @@ % \fi % % \iffalse -%<package>\NeedsTeXFormat{LaTeX2e}[2006/05/20] -%<package>\ProvidesPackage{bodeplot} +%<package>\NeedsTeXFormat{LaTeX2e}[2018/04/01] +%<package>\DeclareRelease{}{2024-02-06}{bodeplot-2024-02-06.sty} +%<package>\DeclareCurrentRelease{}{2024/04/18} +%<package>\ProvidesPackage{bodeplot}[2024/04/18 v1.2 Generate Bode, Nichols, and Nyquist plots] %<package>\RequirePackage{pdftexcmds} %<package>\RequirePackage{ifplatform} %<package>\RequirePackage{xparse} @@ -69,7 +71,7 @@ %</driver> % \fi % -% \CheckSum{1858} +% \CheckSum{1857} % % \changes{v1.0}{2021/10/25}{Initial release} % \changes{v1.0.4}{2021/11/05}{Fixed unintended optional argument macro expansion} @@ -80,11 +82,12 @@ % \changes{v1.1.0}{2022/07/06}{Fixed phase wrapping in gnuplot mode} % \changes{v1.1.1}{2022/07/31}{Enable Hz and rad units} % \changes{v1.1.7}{2024/02/06}{Detect and turn off shorthands to improve `babel' compatibility} +% \changes{v1.2}{2024/04/18}{Removed global option to process pgf commands in radians} % % \GetFileInfo{bodeplot.sty} % \DoNotIndex{\newcommand,\xdef,\gdef,\def,\edef,\addplot,\approx,\arabic,\opt,\typ,\obj,\else,\if@pgfarg,\if@Hzarg,\if@radarg,\if@declutterarg,\if@babel,\fi,\begin,\end,\feature,\footnotesize,\draw,\detokenize,\DeclareOption,\foreach,\ifdim,\ifodd,\Im,\Re,\let,\newif,\nextgroupplot,\noexpand,\expandafter,\unexpanded,\PackageError,\PackageWarning,\relax,\RequirePackage,\tikzset,\pgfmathsetmacro,\pgfmathtruncatemacro,\ProcessOptions} % -% \title{The \textsf{bodeplot} package\\version 1.1.7} +% \title{The \textsf{bodeplot} package\\version 1.2} % \author{Rushikesh Kamalapurkar \\ \texttt{rlkamalapurkar@gmail.com}} % % \maketitle @@ -105,6 +108,7 @@ % Since version 1.0.8, the |bodeplot| package places all |gnuplot| temporary files in the working directory. The package option |declutter| restores the original behavior where the temporary files are placed in a folder called |gnuplot|. % \subsection{Limitations} % \begin{itemize} +% \item Before version 1.2, in |pgf| mode, the package set |trig format plots| to |rad| globally. Version 1.2 onwards, this option is passed to each |addplot| command individually so that it does not affect other plots in the document. To roll back to the pre-1.2 behavior, load the package with |\usepackage[pgf]{bodeplot}[=2024-02-06]|. % \item In |pgf| mode, Bode phase plots and Nichols charts in TF form wrap angles so that they are always between -180 and 180$^\circ$ or $-\pi$ and $-\pi$ radian. As such, these plots will show phase wrapping discontinuities. Since v1.1.1, in |gnuplot| mode, the package uses the |smooth unwrap| filter to correct wrapping discontinuities. As of now, I have not found a way to do this in |pgf| mode, any merge requests or ideas you may have are welcome! Since v1.1.4, you can redefine the |n@mod| macro using the commands |\makeatletter\renewcommand{\n@mod}{\n@mod@p}\makeatother| to wrap the phase between 0 and 360$^\circ$ or $0$ and $2\pi$ radian. The commands |\makeatletter\renewcommand{\n@mod}{\n@mod@n}\makeatother| will wrap the phase between -360 and 0$^\circ$ or $-2\pi$ and $0$ radian. % \item Use of the |declutter| option with other directory management tools such as a |tikzexternalize| prefix is not recommended. % \end{itemize} @@ -602,7 +606,7 @@ Nyquist plots with additional commands, using two different macros % \DescribeMacro{\addBodeComponentPlot} % |\addBodeComponentPlot|\oarg{plot-options}\marg{plot-command} % -% \noindent Generates a single parametric function corresponding to the mandatory argument |plot-command| and passes it to the |\addplot| macro. The plot command can be any parametric function that uses |t| as the independent variable. The parametric function must be |gnuplot| compatible (or |pgfplots| compatible if the package is loaded using the |pgf| option). The intended use of this macro is to plot the parametric functions generated using the basic component macros described in Section \ref{sec:BasicComponents} below. +% \noindent Generates a single parametric function corresponding to the mandatory argument |plot-command| and passes it to the |\addplot| macro. The plot command can be any parametric function that uses |t| as the independent variable. The parametric function must be |gnuplot| compatible (or |pgfplots| compatible if the package is loaded using the |pgf| option, \textbf{with angles passed to trigonometric functions in radian}). The intended use of this macro is to plot the parametric functions generated using the basic component macros described in Section \ref{sec:BasicComponents} below. % % \subsubsection{Basic components up to first order\label{sec:BasicComponents}} % @@ -839,6 +843,7 @@ Nyquist plots with additional commands, using two different macros % \changes{v1.0.3}{2021/11/03}{Added jobname to gnuplot prefix} % \changes{v1.0.8}{2022/07/06}{Fixed issue \#6} % \changes{v1.1.4}{2023/10/12}{Changed phase wrapping in pgf mode} +% \changes{v1.2}{2024/04/18}{Removed global option to process pgf commands in radians} % We start by processing the class options. % \begin{macrocode} \newif\if@pgfarg\@pgfargfalse @@ -866,9 +871,6 @@ Nyquist plots with additional commands, using two different macros \newcommand{\n@mod@n}[2]{(#1)-((floor((#1)/(#2))+1)*(#2))} \if@pgfarg \newcommand{\n@pow}[2]{(#1)^(#2)} - \pgfplotsset{ - trig format plots=rad - } \else \newcommand{\n@pow}[2]{(#1)**(#2)} % \end{macrocode} @@ -1295,7 +1297,7 @@ Nyquist plots with additional commands, using two different macros \edef\temp@mag@cmd{\noexpand\nextgroupplot [ylabel={Gain (dB)}, xmajorticks=false, \optmag@axes] \noexpand\addplot [freq@filter, variable=t, thick, \optmag@plot]} \edef\temp@ph@cmd{\noexpand\nextgroupplot [ph@y@label, freq@label, \optph@axes] - \noexpand\addplot [freq@filter, variable=t, thick, \optph@plot]} + \noexpand\addplot [freq@filter, variable=t, thick, trig format plots=rad, \optph@plot]} \if@pgfarg \temp@mag@cmd {\func@mag}; \optmag@commands @@ -1375,7 +1377,7 @@ Nyquist plots with additional commands, using two different macros \edef\temp@mag@cmd{\noexpand\nextgroupplot [ylabel={Gain (dB)}, xmajorticks=false, \optmag@axes] \noexpand\addplot [freq@filter, variable=t, thick, \optmag@plot]} \edef\temp@ph@cmd{\noexpand\nextgroupplot [ph@y@label, freq@label, \optph@axes] - \noexpand\addplot [freq@filter, variable=t, thick, \optph@plot]} + \noexpand\addplot [freq@filter, variable=t, thick, trig format plots=rad, \optph@plot]} \if@pgfarg \temp@mag@cmd {\func@mag}; \optmag@commands @@ -1441,7 +1443,7 @@ Nyquist plots with additional commands, using two different macros \build@ZPK@plot{\plot@macro}{\temp@macro}{\approx}{#3} \fi \if@pgfarg - \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, thick, \opt]} + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, thick, trig format plots=rad, \opt]} \temp@cmd {\plot@macro}; \else \stepcounter{gnuplot@id} @@ -1474,7 +1476,7 @@ Nyquist plots with additional commands, using two different macros \fi \if@pgfarg \ifnum\pdf@strcmp{#2}{phase}=0 - \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, trig format plots=rad, #1]} \temp@cmd {\n@mod{\plot@macro}{2*pi}}; \else \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} @@ -1510,11 +1512,11 @@ Nyquist plots with additional commands, using two different macros % \end{macrocode} %\end{macro} % \begin{macro}{\addBodeComponentPlot} -% This macro is designed to issue a single |\addplot| macro capable of plotting linear combinations of the basic components described in Section \ref{sec:BasicComponents}. The only work to do here is to handle the |pgf| package option.\changes{v1.1.1}{2022/07/31}{Enabled `Hz' and `rad' units for frequency and phase, respectively}\changes{v1.1.3}{2022/11/02}{Changed implementation to respect user-supplied domain} +% This macro is designed to create a single |\addplot| macro capable of plotting linear combinations of the basic components described in Section \ref{sec:BasicComponents}. The only work to do here is to handle the |pgf| package option.\changes{v1.1.1}{2022/07/31}{Enabled `Hz' and `rad' units for frequency and phase, respectively}\changes{v1.1.3}{2022/11/02}{Changed implementation to respect user-supplied domain} % \begin{macrocode} \newcommand{\addBodeComponentPlot}[2][thick]{ \if@pgfarg - \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \edef\temp@cmd{\noexpand\addplot [freq@filter, domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, trig format plots=rad, #1]} \temp@cmd {#2}; \else \stepcounter{gnuplot@id} @@ -1884,7 +1886,7 @@ Nyquist plots with additional commands, using two different macros ]} \temp@cmd \addplot [only marks,mark=+,thick,red] (-1 , 0); - \edef\temp@cmd{\noexpand\addplot [variable=t, thick, \unexpanded\expandafter{\opt@plot}]} + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, trig format plots=rad, \unexpanded\expandafter{\opt@plot}]} \if@pgfarg \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); @@ -1939,7 +1941,7 @@ Nyquist plots with additional commands, using two different macros ]} \temp@cmd \addplot [only marks, mark=+, thick, red] (-1 , 0); - \edef\temp@cmd{\noexpand\addplot [variable=t, thick, \unexpanded\expandafter{\opt@plot}]} + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, trig format plots=rad, \unexpanded\expandafter{\opt@plot}]} \if@pgfarg \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); @@ -1981,7 +1983,7 @@ Nyquist plots with additional commands, using two different macros \gdef\func@ph{} \build@ZPK@plot{\func@mag}{\func@ph}{}{#2} \if@pgfarg - \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, trig format plots=rad, #1]} \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); \else @@ -2003,7 +2005,7 @@ Nyquist plots with additional commands, using two different macros \gdef\func@ph{} \build@TF@plot{\func@mag}{\func@ph}{#2} \if@pgfarg - \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, trig format plots=rad, #1]} \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos((\func@ph)/(\ph@scale))}, {\n@pow{10}{((\func@mag)/20)}*sin((\func@ph)/(\ph@scale))} ); \else @@ -2107,7 +2109,7 @@ Nyquist plots with additional commands, using two different macros \unexpanded\expandafter{\opt@axes} ]} \temp@cmd - \edef\temp@cmd{\noexpand\addplot [variable=t,thick,\opt@plot]} + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, trig format plots=rad, \opt@plot]} \if@pgfarg \temp@cmd ( {\func@ph} , {\func@mag} ); \opt@commands @@ -2159,7 +2161,7 @@ Nyquist plots with additional commands, using two different macros \unexpanded\expandafter{\opt@axes} ]} \temp@cmd - \edef\temp@cmd{\noexpand\addplot [variable=t,thick, \opt@plot]} + \edef\temp@cmd{\noexpand\addplot [variable=t, thick, trig format plots=rad, \opt@plot]} \if@pgfarg \temp@cmd ( {\n@mod{\func@ph}{2*pi*\ph@scale}} , {\func@mag} ); \opt@commands @@ -2220,7 +2222,7 @@ Nyquist plots with additional commands, using two different macros \gdef\func@ph{} \build@ZPK@plot{\func@mag}{\func@ph}{}{#2} \if@pgfarg - \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, trig format plots=rad, #1]} \temp@cmd ( {\func@ph} , {\func@mag} ); \else \stepcounter{gnuplot@id} @@ -2242,7 +2244,7 @@ Nyquist plots with additional commands, using two different macros \gdef\func@ph{} \build@TF@plot{\func@mag}{\func@ph}{#2} \if@pgfarg - \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, #1]} + \edef\temp@cmd{\noexpand\addplot [domain=\freq@scale*\pgfkeysvalueof{/pgfplots/domain}*\freq@scale, variable=t, trig format plots=rad, #1]} \temp@cmd ( {\n@mod{\func@ph}{2*pi*\ph@scale}} , {\func@mag} ); \else \stepcounter{gnuplot@id} diff --git a/graphics/pgf/contrib/bodeplot/bodeplot.ins b/graphics/pgf/contrib/bodeplot/bodeplot.ins index 1a80024f83..9ac5268913 100644 --- a/graphics/pgf/contrib/bodeplot/bodeplot.ins +++ b/graphics/pgf/contrib/bodeplot/bodeplot.ins @@ -1,5 +1,5 @@ %% -%% Copyright (C) 2021 by Rushikesh Kamalapurkar +%% Copyright (C) 2021-2024 by Rushikesh Kamalapurkar %% %% This file may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, either @@ -16,7 +16,7 @@ \usedir{tex/latex/bodeplot} \preamble This is a generated file. -Copyright (C) 2021 by Rushikesh Kamalapurkar +Copyright (C) 2021-2024 by Rushikesh Kamalapurkar This file may be distributed and/or modified under the conditions of the LaTeX Project Public License, either version 1.3c of this license or (at your option) any later diff --git a/graphics/pgf/contrib/bodeplot/bodeplot.pdf b/graphics/pgf/contrib/bodeplot/bodeplot.pdf Binary files differindex 34a34a224a..4e0bf79b67 100644 --- a/graphics/pgf/contrib/bodeplot/bodeplot.pdf +++ b/graphics/pgf/contrib/bodeplot/bodeplot.pdf |