From c628b1aa9cf528de739c1de45acf3739561619ed Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Fri, 5 Nov 2021 03:01:04 +0000 Subject: CTAN sync 202111050301 --- graphics/pgf/contrib/bodeplot/bodeplot.dtx | 372 ++++++++++++++++++----------- graphics/pgf/contrib/bodeplot/bodeplot.pdf | Bin 492471 -> 498384 bytes 2 files changed, 226 insertions(+), 146 deletions(-) (limited to 'graphics/pgf/contrib') diff --git a/graphics/pgf/contrib/bodeplot/bodeplot.dtx b/graphics/pgf/contrib/bodeplot/bodeplot.dtx index eff87445fd..350e523ff6 100644 --- a/graphics/pgf/contrib/bodeplot/bodeplot.dtx +++ b/graphics/pgf/contrib/bodeplot/bodeplot.dtx @@ -27,7 +27,8 @@ % %<*driver> \documentclass{ltxdoc} -\usepackage{bodeplot,cprotect,hyperref} +\usepackage{bodeplot,cprotect} +\usepackage[colorlinks]{hyperref} \usetikzlibrary{decorations.markings,arrows.meta} \EnableCrossrefs \CodelineIndex @@ -40,7 +41,7 @@ % % \fi % -% \CheckSum{1162} +% \CheckSum{1287} % % \changes{v1.0}{2021/10/25}{Initial release} % @@ -49,15 +50,15 @@ % \DoNotIndex{\newcommand,\xdef,\gdef,\def,\edef,\addplot,\approx,\arabic,\opt,\typ,\obj,\else,\if@pgfarg,\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\thanks{This document -% corresponds to \textsf{bodeplot}~\fileversion, -% dated \filedate.}} +% corresponds to \textsf{bodeplot}~v1.0.3, +% dated November 3, 2021.}} % \author{Rushikesh Kamalapurkar \\ \texttt{rlkamalapurkar@gmail.com}} % % \maketitle % \tableofcontents % \section{Introduction} % -% Generate Bode, Nyquist, and Nichols plots for transfer functions in the canonical (TF) form \begin{equation}G(s) = e^{-Ts}\frac{b_ms^m+\cdots+b_1s+b_0}{a_ns^n+\cdots+a_1s+a_0}\label{eq:TF}\end{equation} and the zero-pole-gain (ZPK) form \begin{equation}G(s) = Ke^{-Ts}\frac{(s-z_1)(s-z_2)\cdots(s-z_m)}{(s-p_1)(s-p_2)\cdots(s-p_n)}.\label{eq:ZPK}\end{equation} In the equations above, $b_m,\cdots,b_0$ and $a_n,\cdots,a_0$ are real coefficients, $T\geq 0$ is the loop delay, $z_1,\cdots,z_m$ and $p_1,\cdots,p_n$ are complex zeros and poles of the transfer function, respectively, and $K\in \Re$ is the loop gain. For transfer functions in the ZPK format in (\ref{eq:ZPK}) with zero delay, this package also supports linear and asymptotic approximation of Bode plots. +% Generate Bode, Nyquist, and Nichols plots for transfer functions in the canonical (TF) form \begin{equation}G(s) = e^{-Ts}\frac{b_ms^m+\cdots+b_1s+b_0}{a_ns^n+\cdots+a_1s+a_0}\label{eq:TF}\end{equation} and the zero-pole-gain (ZPK) form \begin{equation}G(s) = Ke^{-Ts}\frac{(s-z_1)(s-z_2)\cdots(s-z_m)}{(s-p_1)(s-p_2)\cdots(s-p_n)}.\label{eq:ZPK}\end{equation} In the equations above, $b_m,\cdots,b_0$ and $a_n,\cdots,a_0$ are real coefficients, $T\geq 0$ is the loop delay, $z_1,\cdots,z_m$ and $p_1,\cdots,p_n$ are complex zeros and poles of the transfer function, respectively, and $K\in \Re$ is the loop gain. For transfer functions in the ZPK format in (\ref{eq:ZPK}) with zero delay, this package also supports linear and asymptotic approximation of Bode plots. \textbf{Limitation:} in TF form, the phase angles are always between 0 and 360$^\circ$, As such, the Bode phase plots and the Nyquist and Nichols plots will have phase wrapping discontinuities. I do not know how this can be rectified, pull requests are welcome! % % \section{Usage} % \subsection{Bode plots} @@ -81,6 +82,7 @@ % \item |plot/{opt}|: adds options |{opt}| to |\addplot| macros for both the magnitude and the phase plots. % \item |axes/{opt}|: adds options |{opt}| to |\nextgroupplot| macros for both the magnitude and the phase plots. % \item |group/{opt}|: adds options |{opt}| to the |groupplot| environment. +% \item |tikz/{opt}|: adds options |{opt}| to the |tikzpicture| environment. % \item |approx/linear|: plots linear approximation. % \item |approx/asymptotic|: plots asymptotic approximation. % \end{itemize} @@ -114,7 +116,7 @@ % % \begin{figure} % \begin{center} -% \BodeZPK[plot/mag/{red,thick},plot/ph/{blue,thick},axes/mag/{ytick distance=40,xmajorticks=true,xlabel={Frequency (rad/s)}},axes/ph/{ytick distance=90},approx/linear,group/{group style={group size = 2 by 1,horizontal sep=2cm},width=4cm,height=2cm},] {z/{0,{-0.1,-0.5},{-0.1,0.5}},p/{{-0.5,-10},{-0.5,10}},g/10} {0.01} {100} +% \BodeZPK[plot/mag/{red,thick},plot/ph/{blue,thick},axes/mag/{ytick distance=40,xmajorticks=true,xlabel={Frequency (rad/s)}},axes/ph/{ytick distance=90},approx/linear,group/{group style={group size = 2 by 1,horizontal sep=2cm},width=4cm,height=2cm}] {z/{0,{-0.1,-0.5},{-0.1,0.5}},p/{{-0.5,-10},{-0.5,10}},g/10} {0.01} {100} % \cprotect\caption{\label{customBode}Customization of the default |\BodeZPK| macro.} % \end{center} % \end{figure} @@ -166,7 +168,7 @@ %\end{BodePlot} % %\begin{BodePlot}[ylabel={Phase ($^{\circ}$)}, -% height=2cm, width=4cm, ytick distance=90,] {0.01} {100} +% height=2cm, width=4cm, ytick distance=90] {0.01} {100} % \addBodeZPKPlots[ % true/{black,thick}, % linear/{red,dashed,thick}, @@ -188,7 +190,7 @@ % {z/{0,{-0.1,-0.5},{-0.1,0.5}},p/{{-0.1,-10},{-0.1,10}},k/10} % \end{BodePlot} % \begin{BodePlot}[ylabel={Phase ($^{\circ}$)}, -% height=2cm, width=4cm, ytick distance=90,] {0.01} {100} +% height=2cm, width=4cm, ytick distance=90] {0.01} {100} % % \addBodeZPKPlots[ % true/{black,thick}, @@ -303,7 +305,7 @@ % \hspace*{2em}\marg{z/\marg{zeros},p/\marg{poles},k/\marg{gain},d/\marg{delay}}\\ % \hspace*{2em}\marg{min-freq}\marg{max-freq} % -% \noindent Plots the Nyquist plot of a transfer function given in ZPK format with a thick red $+$ marking the critical point (-1,0). The mandatory arguments are the same as |\BodeZPK|. Since there is only one plot in a Nyquist diagram, the |\typ| specifier in the optional argument tuples is not needed. As such, the supported optional argument tuples are |plot/{opt}|, which passes |{opt}| to |\addplot| and |axes/{opt}|, which passes |{\opt}| to the |axis| environment. Asymptotic/linear approximations are not supported in Nyquist plots. If just |{opt}| is provided as the optional argument, it is interpreted as |plot/{opt}|. Arrows to indicate the direction of increasing $\omega$ can be added by adding |\usetikzlibrary{decorations.markings}| and |\usetikzlibrary{arrows.meta}| to the preamble and then passing a tuple of the form\\ +% \noindent Plots the Nyquist plot of a transfer function given in ZPK format with a thick red $+$ marking the critical point (-1,0). The mandatory arguments are the same as |\BodeZPK|. Since there is only one plot in a Nyquist diagram, the |\typ| specifier in the optional argument tuples is not needed. As such, the supported optional argument tuples are |plot/{opt}|, which passes |{opt}| to |\addplot|, |axes/{opt}|, which passes |{\opt}| to the |axis| environment, and |tikz/{opt}|, which passes |{\opt}| to the |tikzpicture| environment. Asymptotic/linear approximations are not supported in Nyquist plots. If just |{opt}| is provided as the optional argument, it is interpreted as |plot/{opt}|. Arrows to indicate the direction of increasing $\omega$ can be added by adding |\usetikzlibrary{decorations.markings}| and |\usetikzlibrary{arrows.meta}| to the preamble and then passing a tuple of the form\\ %|plot/{postaction=decorate,decoration={markings,|\\ %| mark=between positions 0.1 and 0.9 step 5em with|\\ %| {\arrow{Stealth| |[length=2mm, blue]}}}}|\\ @@ -423,6 +425,7 @@ % \begin{macro}{\n@pow} % \begin{macro}{idGnuplot} % \begin{macro}{gnuplot def} +% \changes{v1.0.3}{2021/11/03}{Added jobname to gnuplot prefix} % \begin{macro}{gnuplot degrees} % This code is needed to support both |pgfplots| and |gnuplot| simultaneously. New macros are defined for the |pow| and |mod| functions to address differences between the two math engines. We start by processing the |pgf| class option. % \begin{macrocode} @@ -448,7 +451,7 @@ \tikzset{% gnuplot def/.style={% id=\arabic{idGnuplot}, - prefix=gnuplot/ + prefix=gnuplot/\jobname }% } % \end{macrocode} @@ -753,97 +756,105 @@ \gdef\func@ph{}% \build@ZPK@plot{\func@mag}{\func@ph}{\opt@approx}{#2}% % \end{macrocode} -% The |\noexpand| macros below are needed to so that only the macro |\opt@group| is expanded. +% The |\noexpand| macros below are needed to so that only the macro |\opt@group| is expanded. \changes{v1.0.3}{2021/11/03}{Added Tikz option} % \begin{macrocode} - \edef\temp@cmd{\noexpand\begin{tikzpicture}\noexpand\begin{groupplot}[% - bodeStyle, - xmin={#3}, - xmax={#4}, - domain=#3:#4, - height=2.5cm, - xmode=log, - group style = {group size = 1 by 2,vertical sep=0.25cm,}, - \opt@group,]} + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{groupplot}[% + bodeStyle, + xmin={#3}, + xmax={#4}, + domain=#3:#4, + height=2.5cm, + xmode=log, + group style = {group size = 1 by 2,vertical sep=0.25cm}, + \opt@group + ]% + }% \temp@cmd % \end{macrocode} % To ensure frequency tick marks on magnitude and the phase plots are always aligned, we use the |groupplot| library. The |\expandafter| chain below is used to expand macros in the plot and group optional arguments. % \begin{macrocode} - \if@pgfarg - \expandafter\nextgroupplot\expandafter[ytick distance=20, - ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% - \temp@cmd {\func@mag}; - \optmag@commands; - \expandafter\nextgroupplot\expandafter[ytick distance=45, - ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% - \temp@cmd {\func@ph}; - \optph@commands; - \else + \if@pgfarg + \expandafter\nextgroupplot\expandafter[ytick distance=20, + ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% + \temp@cmd {\func@mag}; + \optmag@commands; + \expandafter\nextgroupplot\expandafter[ytick distance=45, + ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% + \temp@cmd {\func@ph}; + \optph@commands; + \else % \end{macrocode} % In |gnuplot| mode, we increment the |idGnuplot| counter before every plot to make sure that new and reusable |.gnuplot| and |.table| files are generated for every plot. % \begin{macrocode} - \stepcounter{idGnuplot} - \expandafter\nextgroupplot\expandafter[ytick distance=20, - ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% - \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@mag}; - \optmag@commands; - \stepcounter{idGnuplot} - \expandafter\nextgroupplot\expandafter[ytick distance=45, - ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% - \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@ph}; - \optph@commands; - \fi - \end{groupplot}\end{tikzpicture} + \stepcounter{idGnuplot} + \expandafter\nextgroupplot\expandafter[ytick distance=20, + ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% + \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@mag}; + \optmag@commands; + \stepcounter{idGnuplot} + \expandafter\nextgroupplot\expandafter[ytick distance=45, + ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% + \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@ph}; + \optph@commands; + \fi + \end{groupplot} + \end{tikzpicture} } % \end{macrocode} % \end{macro} % \begin{macro}{\BodeTF} -% Implementation of this macro is very similar to the |\BodeZPK| macro above. The only difference is the lack of linear and asymptotic plots and slightly different parsing of the mandatory arguments. +% Implementation of this macro is very similar to the |\BodeZPK| macro above. The only difference is the lack of linear and asymptotic plots and slightly different parsing of the mandatory arguments. \changes{v1.0.3}{2021/11/03}{Added Tikz option} % \begin{macrocode} \newcommand{\BodeTF}[4][]{% \parse@opt{#1}% \gdef\func@mag{}% \gdef\func@ph{}% \build@TF@plot{\func@mag}{\func@ph}{#2}% - \edef\temp@cmd{\noexpand\begin{tikzpicture}\noexpand\begin{groupplot}[% - bodeStyle, - xmin={#3}, - xmax={#4}, - domain=#3:#4, - height=2.5cm, - xmode=log, - group style = {group size = 1 by 2,vertical sep=0.25cm,}, - \opt@group,]} + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{groupplot}[% + bodeStyle, + xmin={#3}, + xmax={#4}, + domain=#3:#4, + height=2.5cm, + xmode=log, + group style = {group size = 1 by 2,vertical sep=0.25cm}, + \opt@group + ]% + }% \temp@cmd - \if@pgfarg - \expandafter\nextgroupplot\expandafter[ytick distance=20, - ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% - \temp@cmd {\func@mag}; - \optmag@commands;% - \expandafter\nextgroupplot\expandafter[ytick distance=45, - ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% - \temp@cmd {\func@ph}; - \optph@commands;% - \else - \stepcounter{idGnuplot}% - \expandafter\nextgroupplot\expandafter[ytick distance=20, - ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% - \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@mag}; - \optmag@commands;% - \stepcounter{idGnuplot}% - \expandafter\nextgroupplot\expandafter[ytick distance=45, - ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] - \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% - \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@ph}; - \optph@commands;% - \fi - \end{groupplot}\end{tikzpicture} + \if@pgfarg + \expandafter\nextgroupplot\expandafter[ytick distance=20, + ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% + \temp@cmd {\func@mag}; + \optmag@commands; + \expandafter\nextgroupplot\expandafter[ytick distance=45, + ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% + \temp@cmd {\func@ph}; + \optph@commands; + \else + \stepcounter{idGnuplot}% + \expandafter\nextgroupplot\expandafter[ytick distance=20, + ylabel={Gain (dB)},xmajorticks=false,\optmag@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optmag@plot]}% + \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@mag}; + \optmag@commands; + \stepcounter{idGnuplot}% + \expandafter\nextgroupplot\expandafter[ytick distance=45, + ylabel={Phase ($^{\circ}$)},xlabel={Frequency (rad/s)},\optph@axes] + \edef\temp@cmd{\noexpand\addplot[thick,\optph@plot]}% + \temp@cmd gnuplot[gnuplot degrees,gnuplot def] {\func@ph}; + \optph@commands; + \fi + \end{groupplot} + \end{tikzpicture} } % \end{macrocode} % \end{macro} @@ -905,20 +916,24 @@ % \end{macrocode} %\end{macro} % \begin{environment}{BodePlot} -% An environment to host macros that pass parametric functions to |\addplot| macros. Uses the defaults specified in |bodeStyle| to create a shortcut that includes the |tikzpicture| and |semilogaxis| environments. +% An environment to host macros that pass parametric functions to |\addplot| macros. Uses the defaults specified in |bodeStyle| to create a shortcut that includes the |tikzpicture| and |semilogaxis| environments. \changes{v1.0.3}{2021/11/03}{Added tikz option to environments} % \begin{macrocode} \newenvironment{BodePlot}[3][]{% - \begin{tikzpicture} - \begin{semilogxaxis}[% + \parse@env@opt{#1}% + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz] + \noexpand\begin{semilogxaxis}[% bodeStyle, xmin={#2}, xmax={#3}, domain=#2:#3, height=2.5cm, xlabel={Frequency (rad/s)}, - #1] + \opt@axes + ]% + }% + \temp@cmd }{ - \end{semilogxaxis} + \end{semilogxaxis} \end{tikzpicture} } % \end{macrocode} @@ -1075,7 +1090,7 @@ % \end{macrocode} %\end{macro} % \begin{macro}{\parse@opt} -% Parses options supplied to the main Bode macros. A |for| loop over tuples of the form |\obj/\typ/\opt| with a long list of nested if-else statements does the job. The input |\obj| is either |plot|, |axes|, |group| or |approx|, and the corresponding |\opt| are passed to the |\addplot| macro, the |\nextgroupplot| macro, the |groupplot| environment, and the |\build@ZPK@plot| macros, respectively. The input tuples should not contain any macros that need to be passed to respective |pgf| macros unexpanded. If an input tuple needs to contain such a macro, the |\xdef| macros below need to be defined using |\unexpanded\expandafter{\opt}| instead of just |\opt|. For example, the |\parse@N@opt| macro in Section \ref{sec:NInternal} can pass macros in its arguments, unexpanded, to |pgf| plot macros and environments, which is useful, for example, when the user wishes to add direction arrows to Nyquist plots. I did not think such a use case would be encountered when plotting Bode plots. +% Parses options supplied to the main Bode macros. A |for| loop over tuples of the form |\obj/\typ/\opt| with a long list of nested if-else statements does the job. If the input |\obj| is |plot|, |axes|, |group|, |approx|, or |tikz| the corresponding |\opt| are passed, unexpanded, to the |\addplot| macro, the |\nextgroupplot| macro, the |groupplot| environment, the |\build@ZPK@plot| macro, and the |tikzpicture| environment, respectively. If |\obj| is |commands|, the corresponding |\opt| are stored, unexpanded, in the macros |\optph@commands| and |\optmag@commands|, to be executed in appropriate |axis| environments. \changes{v1.0.3}{2021/11/03}{Added Tikz option} % \begin{macrocode} \newcommand{\parse@opt}[1]{% \gdef\optmag@axes{}% @@ -1084,38 +1099,39 @@ \gdef\optmag@plot{}% \gdef\opt@group{}% \gdef\opt@approx{}% - \xdef\optph@commands{}% - \xdef\optmag@commands{}% + \gdef\optph@commands{}% + \gdef\optmag@commands{}% + \gdef\opt@tikz{}% \foreach \obj/\typ/\opt in {#1} {% \ifnum\pdfstrcmp{\obj}{plot}=0 \ifnum\pdfstrcmp{\typ}{mag}=0 - \xdef\optmag@plot{\optmag@plot,\opt}% + \xdef\optmag@plot{\unexpanded\expandafter{\opt}}% \else \ifnum\pdfstrcmp{\typ}{ph}=0 - \xdef\optph@plot{\optph@plot,\opt}% + \xdef\optph@plot{\unexpanded\expandafter{\opt}}% \else - \xdef\optmag@plot{\optmag@plot,\opt}% - \xdef\optph@plot{\optph@plot,\opt}% + \xdef\optmag@plot{\unexpanded\expandafter{\opt}}% + \xdef\optph@plot{\unexpanded\expandafter{\opt}}% \fi \fi \else \ifnum\pdfstrcmp{\obj}{axes}=0 \ifnum\pdfstrcmp{\typ}{mag}=0 - \xdef\optmag@axes{\optmag@axes,\opt}% + \xdef\optmag@axes{\unexpanded\expandafter{\opt}}% \else \ifnum\pdfstrcmp{\typ}{ph}=0 - \xdef\optph@axes{\optph@axes,\opt}% + \xdef\optph@axes{\unexpanded\expandafter{\opt}}% \else - \xdef\optmag@axes{\optmag@axes,\opt}% - \xdef\optph@axes{\optph@axes,\opt}% + \xdef\optmag@axes{\unexpanded\expandafter{\opt}}% + \xdef\optph@axes{\unexpanded\expandafter{\opt}}% \fi \fi \else \ifnum\pdfstrcmp{\obj}{group}=0 - \xdef\opt@group{\opt@group,\opt}% + \xdef\opt@group{\unexpanded\expandafter{\opt}}% \else \ifnum\pdfstrcmp{\obj}{approx}=0 - \xdef\opt@approx{\opt}% + \xdef\opt@approx{\unexpanded\expandafter{\opt}}% \else \ifnum\pdfstrcmp{\obj}{commands}=0 \ifnum\pdfstrcmp{\typ}{phase}=0 @@ -1124,8 +1140,12 @@ \xdef\optmag@commands{\unexpanded\expandafter{\opt}}% \fi \else - \xdef\optmag@plot{\optmag@plot,\obj}% - \xdef\optph@plot{\optph@plot,\obj}% + \ifnum\pdfstrcmp{\obj}{tikz}=0 + \xdef\opt@tikz{\unexpanded\expandafter{\opt}}% + \else + \xdef\optmag@plot{\optmag@plot,\unexpanded\expandafter{\obj}}% + \xdef\optph@plot{\optph@plot,\unexpanded\expandafter{\obj}}% + \fi \fi \fi \fi @@ -1135,35 +1155,60 @@ } % \end{macrocode} %\end{macro} +% \begin{macro}{\parse@env@opt} +% Parses options supplied to the Bode, Nyquist, and Nichols environments. A |for| loop over tuples of the form |\obj/\opt|, processed using nested if-else statements does the job. The input |\obj| should either be |axes| or |tikz|, and the corresponding |\opt| are passed, unexpanded, to the |axis| environment and the |tikzpicture| environment, respectively. \changes{v1.0.3}{2021/11/03}{Added tikz option to environments} +% \begin{macrocode} +\newcommand{\parse@env@opt}[1]{% + \gdef\opt@axes{}% + \gdef\opt@tikz{}% + \foreach \obj/\opt in {#1} {% + \ifnum\pdfstrcmp{\obj}{axes}=0 + \xdef\opt@axes{\unexpanded\expandafter{\opt}}% + \else + \ifnum\pdfstrcmp{\obj}{tikz}=0 + \xdef\opt@tikz{\unexpanded\expandafter{\opt}}% + \else + \xdef\opt@axes{\opt@axes,\unexpanded\expandafter{\obj}}% + \fi + \fi + }% +} +% \end{macrocode} +% \end{macro} % \subsection{Nyquist plots} % \subsubsection{User macros} % \begin{macro}{\NyquistZPK} -% Converts magnitude and phase parametric functions built using |\build@ZPK@plot| into real part and imaginary part parametric functions. A plot of these is the Nyquist plot. The parametric functions are then plotted in a |tikzpicture| environment using the |\addplot| macro. Unless the package is loaded with the option |pgf|, the parametric functions are evaluated using |gnuplot|. A large number of samples is typically needed to get a smooth plot because frequencies near 0 result in plot points that are very close to each other. Linear frequency sampling is unnecessarily fine near zero and very coarse for large $\omega$. Logarithmic sampling makes it worse, perhaps inverse logarithmic sampling will help, merge requests are welcome! +% Converts magnitude and phase parametric functions built using |\build@ZPK@plot| into real part and imaginary part parametric functions. A plot of these is the Nyquist plot. The parametric functions are then plotted in a |tikzpicture| environment using the |\addplot| macro. Unless the package is loaded with the option |pgf|, the parametric functions are evaluated using |gnuplot|. A large number of samples is typically needed to get a smooth plot because frequencies near 0 result in plot points that are very close to each other. Linear frequency sampling is unnecessarily fine near zero and very coarse for large $\omega$. Logarithmic sampling makes it worse, perhaps inverse logarithmic sampling will help, pull requests to fix that are welcome! \changes{v1.0.3}{2021/11/03}{Added commands and tikz options} % \begin{macrocode} \newcommand{\NyquistZPK}[4][]{% \parse@N@opt{#1}% \gdef\func@mag{}% \gdef\func@ph{}% \build@ZPK@plot{\func@mag}{\func@ph}{}{#2}% - \edef\temp@cmd{\noexpand\begin{tikzpicture}\noexpand\begin{axis}[% - bodeStyle, - domain=#3:#4, - height=5cm, - xlabel={$\Re$}, - ylabel={$\Im$}, - samples=500, - \opt@axes,]}% + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{axis}[% + bodeStyle, + domain=#3:#4, + height=5cm, + xlabel={$\Re$}, + ylabel={$\Im$}, + samples=500, + \opt@axes + ]% + }% \temp@cmd \addplot [only marks,mark=+,thick,red] (-1 , 0); \edef\temp@cmd{\noexpand\addplot[thick,\unexpanded\expandafter{\opt@plot}]}% \if@pgfarg \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos(\func@ph)}, {\n@pow{10}{((\func@mag)/20)}*sin(\func@ph)} ); + \opt@commands; \else \stepcounter{idGnuplot}% \temp@cmd gnuplot[parametric,gnuplot degrees,gnuplot def] {% \n@pow{10}{((\func@mag)/20)}*cos(\func@ph), \n@pow{10}{((\func@mag)/20)}*sin(\func@ph)}; + \opt@commands; \fi \end{axis} \end{tikzpicture} @@ -1171,32 +1216,37 @@ % \end{macrocode} % \end{macro} % \begin{macro}{\NyquistTF} -% Implementation of this macro is very similar to the |\NyquistZPK| macro above. The only difference is a slightly different parsing of the mandatory arguments via |\build@TF@plot|. +% Implementation of this macro is very similar to the |\NyquistZPK| macro above. The only difference is a slightly different parsing of the mandatory arguments via |\build@TF@plot|. \changes{v1.0.3}{2021/11/03}{Added commands and tikz options} % \begin{macrocode} \newcommand{\NyquistTF}[4][]{% \parse@N@opt{#1}% \gdef\func@mag{}% \gdef\func@ph{}% \build@TF@plot{\func@mag}{\func@ph}{#2}% - \edef\temp@cmd{\noexpand\begin{tikzpicture}\noexpand\begin{axis}[% - bodeStyle, - domain=#3:#4, - height=5cm, - xlabel={$\Re$}, - ylabel={$\Im$}, - samples=500, - \opt@axes,]} + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{axis}[% + bodeStyle, + domain=#3:#4, + height=5cm, + xlabel={$\Re$}, + ylabel={$\Im$}, + samples=500, + \opt@axes + ]% + }% \temp@cmd \addplot [only marks,mark=+,thick,red] (-1 , 0); \edef\temp@cmd{\noexpand\addplot[thick,\unexpanded\expandafter{\opt@plot}]}% \if@pgfarg \temp@cmd ( {\n@pow{10}{((\func@mag)/20)}*cos(\func@ph)}, {\n@pow{10}{((\func@mag)/20)}*sin(\func@ph)} ); + \opt@commands; \else \stepcounter{idGnuplot}% \temp@cmd gnuplot[parametric,gnuplot degrees,gnuplot def]{% \n@pow{10}{((\func@mag)/20)}*cos(\func@ph), \n@pow{10}{((\func@mag)/20)}*sin(\func@ph)}; + \opt@commands; \fi \end{axis} \end{tikzpicture} @@ -1242,17 +1292,21 @@ % \end{macrocode} %\end{macro} %\begin{macro}{NyquistPlot} -% An environment to host |\addNyquist...| macros that pass parametric functions to |\addplot|. Uses the defaults specified in |bodeStyle| to create a shortcut that includes the |tikzpicture| and |axis| environments. +% An environment to host |\addNyquist...| macros that pass parametric functions to |\addplot|. Uses the defaults specified in |bodeStyle| to create a shortcut that includes the |tikzpicture| and |axis| environments. \changes{v1.0.3}{2021/11/03}{Added tikz option to environments} % \begin{macrocode} \newenvironment{NyquistPlot}[3][]{% - \begin{tikzpicture} - \begin{axis}[% +\parse@env@opt{#1}% + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{axis}[% bodeStyle, height=5cm, domain=#2:#3, xlabel={$\Re$}, ylabel={$\Im$}, - #1] + \opt@axes + ]% + }% + \temp@cmd \addplot [only marks,mark=+,thick,red] (-1 , 0); }{% \end{axis} @@ -1261,12 +1315,14 @@ % \end{macrocode} %\end{macro} % \subsubsection{Internal commands\label{sec:NInternal}} -% \begin{macro}{\parse@opt} -% Parses options supplied to the main Nyquist and Nichols macros. A |for| loop over tuples of the form |\obj/\opt|, processed using nested if-else statements does the job. The input |\obj| is either |plot| or |axes|, and the corresponding |\opt| are passed to the |\addplot| macro and the |axis| environment, respectively. If the input tuples contain macros, they are to be passed to respective |pgf| macros unexpanded. +% \begin{macro}{\parse@N@opt} +% Parses options supplied to the main Nyquist and Nichols macros. A |for| loop over tuples of the form |\obj/\opt|, processed using nested if-else statements does the job. If the input |\obj| is |plot|, |axes|, or |tikz| then the corresponding |\opt| are passed, unexpanded, to the |\addplot| macro, the |axis| environment, and the |tikzpicture| environment, respectively. \changes{v1.0.3}{2021/11/03}{Added commands and tikz options} % \begin{macrocode} \newcommand{\parse@N@opt}[1]{% \gdef\opt@axes{}% \gdef\opt@plot{}% + \gdef\opt@commands{}% + \gdef\opt@tikz{} \foreach \obj/\opt in {#1} {% \ifnum\pdfstrcmp{\obj}{axes}=0 \xdef\opt@axes{\unexpanded\expandafter{\opt}}% @@ -1274,7 +1330,15 @@ \ifnum\pdfstrcmp{\obj}{plot}=0 \xdef\opt@plot{\unexpanded\expandafter{\opt}}% \else - \xdef\opt@plot{\unexpanded\expandafter{\obj}}% + \ifnum\pdfstrcmp{\obj}{commands}=0 + \xdef\opt@commands{\unexpanded\expandafter{\opt}}% + \else + \ifnum\pdfstrcmp{\obj}{tikz}=0 + \xdef\opt@tikz{\unexpanded\expandafter{\opt}}% + \else + \xdef\opt@plot{\opt@plot,\unexpanded\expandafter{\obj}}% + \fi + \fi \fi \fi }% @@ -1283,8 +1347,11 @@ % \end{macro} % \subsection{Nichols charts} % \begin{macro}{\NicholsZPK} +% \changes{v1.0.3}{2021/11/03}{Added commands and tikz options} % \begin{macro}{\NicholsTF} +% \changes{v1.0.3}{2021/11/03}{Added commands and tikz options} % \begin{macro}{NicholsChart} +% \changes{v1.0.3}{2021/11/03}{Added tikz option to environments} % \begin{macro}{\addNicholsZPKChart} % \begin{macro}{\addNicholsTFChart} % These macros and the |NicholsChart| environment generate Nichols charts, and they are implemented similar to their Nyquist counterparts. @@ -1294,22 +1361,27 @@ \gdef\func@mag{}% \gdef\func@ph{}% \build@ZPK@plot{\func@mag}{\func@ph}{}{#2}% - \edef\temp@cmd{\noexpand\begin{tikzpicture}\noexpand\begin{axis}[% - bodeStyle, - domain=#3:#4, - height=5cm, - xlabel={Phase (degrees)}, - ylabel={Gain (dB)}, - samples=500, - \opt@axes]} + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{axis}[% + bodeStyle, + domain=#3:#4, + height=5cm, + xlabel={Phase (degrees)}, + ylabel={Gain (dB)}, + samples=500, + \opt@axes + ]% + }% \temp@cmd \edef\temp@cmd{\noexpand\addplot[thick,\opt@plot]}% \if@pgfarg \temp@cmd ( {\func@ph} , {\func@mag} ); + \opt@commands; \else \stepcounter{idGnuplot}% \temp@cmd gnuplot[parametric, gnuplot degrees, gnuplot def] { \func@ph , \func@mag }; + \opt@commands; \fi \end{axis} \end{tikzpicture} @@ -1319,29 +1391,34 @@ \gdef\func@mag{}% \gdef\func@ph{}% \build@TF@plot{\func@mag}{\func@ph}{#2}% - \edef\temp@cmd{\noexpand\begin{tikzpicture}\noexpand\begin{axis}[% - bodeStyle, - domain=#3:#4, - height=5cm, - xlabel={Phase (degrees)}, - ylabel={Gain (dB)}, - samples=500, - \opt@axes]} + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{axis}[% + bodeStyle, + domain=#3:#4, + height=5cm, + xlabel={Phase (degrees)}, + ylabel={Gain (dB)}, + samples=500, + \opt@axes + ]% + }% \temp@cmd \edef\temp@cmd{\noexpand\addplot[thick,\opt@plot]}% \if@pgfarg \temp@cmd ( {\func@ph} , {\func@mag} ); + \opt@commands; \else \stepcounter{idGnuplot}% \temp@cmd gnuplot[parametric, gnuplot degrees, gnuplot def] { \func@ph , \func@mag }; + \opt@commands; \fi \end{axis} \end{tikzpicture} } \newenvironment{NicholsChart}[3][]{% - \begin{tikzpicture} - \begin{axis}[% + \edef\temp@cmd{\noexpand\begin{tikzpicture}[\opt@tikz]% + \noexpand\begin{axis}[% bodeStyle, domain=#2:#3, height=5cm, @@ -1349,7 +1426,10 @@ xtick distance=15, xlabel={Phase (degrees)}, ylabel={Gain (dB)}, - #1] + \opt@axes + ]% + }% + \temp@cmd }{ \end{axis} \end{tikzpicture} diff --git a/graphics/pgf/contrib/bodeplot/bodeplot.pdf b/graphics/pgf/contrib/bodeplot/bodeplot.pdf index f2dd0879b8..64592fd6ec 100644 Binary files a/graphics/pgf/contrib/bodeplot/bodeplot.pdf and b/graphics/pgf/contrib/bodeplot/bodeplot.pdf differ -- cgit v1.2.3