- Notifications
You must be signed in to change notification settings - Fork441
New plotting paradigm#645
-
@billtubbs posted inbilltubbs/python-control-examples and mentioned in#65 (comment): Plotting ParadigmThis is a proposal to introduce a different paradigm for the way specialised control analyses and plots are created in Python-Control. It is based on the way plotting is done inPandas. For example: s=pandas.Series(my_data,index=my_index,name="my series")# instantiate objectprint(s.dtype)# get an attributes.plot(style="o-")# make a plotplt.show()avg=s.mean()# do something else Functions this proposal could affect
RationaleThe current paradigm for these plot functions is based on the way they are done in MATLAB using the functions Problem:
Solution:
The benefits of this approach are:
To illustrate how this might work, I have outlined some examples below. Example 1 – Root locus plotCurrent method: fromcontrolimportrlocusrlocus(my_sys,**kwargs)plt.show() Proposed method: fromcontrolimportRootLocusrl=RootLocus(my_sys,**kwargs)# calculation arguments go hererl.plot(**kwargs)# plotting arguments hereplt.show() or: fromcontrolimportRootLocusRootLocus(my_sys).plot(**kwargs)plt.show() Calculate the root locus without making the plotCurrent method: rlist,klist=rlocus(my_sys,Plot=False) Proposed method: rl=RootLocus(my_sys)# does not make a plot by defaultrl.rlist,rl.klist# access the data Calculate and plot the root locusCurrent method: rlist,klist=rlocus(my_sys)plt.show() Proposed method: rl=RootLocus(my_sys)rl.rlist,rl.klist# access the datarl.plot()plt.show() Customizing the root locus plotCurrent method: rlocus(my_sys)ax=plt.gca()#ax.lines[??].set_linewidth(2) # not easy to doax.annotate("label", (-1,0))plt.show() Proposed method: rl=RootLocus(my_sys)ax=rl.plot(linewidth=2)# plotting arguments hereax.annotate("label", (-1,0))plt.show() Adding root locus to a custom plotCurrent method: fig,axes=plt.subplots(2,1)ax=axes[0]rlocus(sys1,ax=axes[0])ax.set_title("System 1")ax=axes[1]rlocus(sys2,ax=axes[1])ax.set_title("System 2")plt.tight_layout()plt.show() Proposed method: rl1=RootLocus(sys1)rl2=RootLocus(sys2)fig,axes=plt.subplots(2,1)ax=axes[0]rl1.plot(ax=ax)ax.set_title("System 1")ax=axes[1]rl2.plot(ax=ax)ax.set_title("System 2")plt.show() Other commentsIntroducing this would not require the replacement of the MATLAB-like functions Functions such as The solution is not going to be as simple as the rlocus example above in the case of all plot functions. Some generate their own axes objects (e.g. radial plots) and some generate more than one axis (i.e. a complete figure), so each function will have to be considered case-by-case. |
BetaWas this translation helpful?Give feedback.
All reactions
Replies: 22 comments 58 replies
-
I like it very much. We can keep the The MATLAB wrappers could to the same without deprecation. |
BetaWas this translation helpful?Give feedback.
All reactions
-
For reference, I found the old discussion we were having about this at the back end of#291. I think the main problems encountered back then are now fixed (e.g. functions |
BetaWas this translation helpful?Give feedback.
All reactions
-
I also like this proposal quite a lot. We might think through what we want to do from time response and how this fits it. For example, the various time response functions ( Another thing to think about: what happens if you want to combine several plots, either in an array (a la |
BetaWas this translation helpful?Give feedback.
All reactions
-
The question about time responses and time series data is a good one. I've been thinking about it too. We don't want to make Python-Control dependent on Pandas but I would argue that the PandasSeries andDataFrame objects are perfectly-designed tools for time series data storage, plotting and analysis (I use them all the time when working with industrial time series data). If we can find a way to seamlessly use Python-Control and Pandas 'hand-in-glove' so-to-speak, that would be very powerful I think. (This could also save us writing a raft of tools to do all that). |
BetaWas this translation helpful?Give feedback.
All reactions
-
Found another example of this paradigm from scipy.spatialhere: importmatplotlib.pyplotaspltimportnumpyasnpfromscipy.spatialimportConvexHull,convex_hull_plot_2dpoints=np.random.random((30,2))hull=ConvexHull(points)_=convex_hull_plot_2d(hull)plt.show() Here, they use a separate function |
BetaWas this translation helpful?Give feedback.
All reactions
-
I am definitely in favor of separating plots and computation. Just curious about the advantage to@billtubbs suggestion of creating new types of objects, perhaps built on or inspired by pandas, that include a plot method. I think that would require creation of a series of new classes to hold root locus info, frequency response info, and time response info, to name the new classes I can think of that would be required. Is there a reason it wouldn't be enough to use the simpler alternative of finishing separating out dual-purpose functions like importcontrolasctfig,axes=plt.subplots(2,1)ax=axes[0]ct.root_locus_plot(ax=ax)ax.set_title("System 1")ax=axes[1]ct.step_plot(ax=ax)ax.set_title("System 2")```*outsideofthematlablayer,frequencyresponseplotslargelyexist:``nyquist_plot``,``bode_plot``,``nichols_plot``,buttimeresponseplotsdon'tseemto,e.g.``step_plot``,``impulse_plot`` |
BetaWas this translation helpful?Give feedback.
All reactions
-
Thanks, I agree, we need to have a good justification to create any new data objects. One thing with two functions, you need to transfer the data from the calculation function to the plotting function. If it's just a couple of things, like 2 arrays, then I think a tuple or dict is fine and it doesn't merit creating an object. A benefit of an object would be if there is additional 'meta data' that could be automatically passed to the plotting method, such as axis and legend labels. As an example, The case for a new data object gets stronger if it also has additional uses in its own right (i.e. for things other than simply making a plot). I was also wondering, if we make one So, in summary I think we need to really challenge the need for any new object and only do it if it has enough benefits over a simpler solution (like two separate functions). |
BetaWas this translation helpful?Give feedback.
All reactions
-
I think there already is an object that can store I think there is a case to be made that a Then this proposal could include simply adding a plot method to these, and maybe adding one more object for |
BetaWas this translation helpful?Give feedback.
All reactions
-
@murrayrm , in response to your other point:
I made a quick demo of how this can be done in Pandas: |
BetaWas this translation helpful?Give feedback.
All reactions
-
FYI, there is a similar concept that has been implemented inJuliaControl PR #529, where they return a result structure for time responses. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
It also says this at the bottom:
|
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Had a quick look at theJulia proposal. Looks like functions like sim_result=lsim(my_sys, u, t)plot(sim_result) or simply: plot(lsim(my_sys, u, t)) For comparison, here is what this could look like in Python Control: sim_result=input_output_response(my_sys,u,t)sim_result.plot() or input_output_response(my_sys,u,t).plot() |
BetaWas this translation helpful?Give feedback.
All reactions
-
Perhaps, we could preserve the old usage of returning a tuple for the matlab-like functions yout,t,xout=lsim(sys,u,t,x0) |
BetaWas this translation helpful?Give feedback.
All reactions
-
I've been playing around with some ideas for time responses and I have an implementation that allows the following to work:
or you can say
This is all done by defining a new class So far, everything is backward compatible so that all unit tests run without change. But we get the advantage of having an object returned to which we can add additional methods (like I'll do a bit more work on it and post a draft PR so that people can take a look. I think it fits the intent of the paradigm here. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
For the input-output response, it would be nice (IMO) if |
BetaWas this translation helpful?Give feedback.
All reactions
-
Interesting proposal. During the pandemic, I've developed a simple plotting lib on the top of python control for my students. The lib uses the Plotly backend (which is also supported now by Panda) since my students mainly use the web-based interface Jupyter for running python scripts |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Here is a summary of the relevant analysis and plotting functions currently inJulia ControlSystems (v0.8.0).
Looks like the pattern is to make separate functions for calculations and plotting where applicable. Is there any reason why we didn't name the Python-Control |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Here is a table showing the correspondence between current Julia ControlSystems and Python-Control plot functions and the changes I am proposing to
Can some of you please go through these and provide comments and feedback?
* - these changes are part of a different pull request by Richard. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Hi@billtubbs,
|
BetaWas this translation helpful?Give feedback.
All reactions
-
Thanks for the feedback@sawyerbfuller.
I agree that providing the most commonly-used functions as methods of
Or we could have some kind of catch-all plot method (similar to Pandas):
or
I don't plan to do anything with the |
BetaWas this translation helpful?Give feedback.
All reactions
-
I see that now. I'll add this to the table above. Thanks. |
BetaWas this translation helpful?Give feedback.
All reactions
-
A few comments:
|
BetaWas this translation helpful?Give feedback.
All reactions
-
@billtubbs i am also in favor of everything suggested by@murrayrm above. In regards to the @billtubbs in regards to In regards to bode vs. nyquist plots, it turns out they both kind of want similar frequency points( eg both need lots of frequency points around resonant peaks) so it might be possible to do as you suggest and use the same data for each. This is how it works now. |
BetaWas this translation helpful?Give feedback.
All reactions
-
For the return value for
Doing things this way will make everything backward compatible with the current code but also alow us to be consistent with the new plotting paradigm. (This is also compatible with the way that Regarding Bode vs Nyquist: there are two differences that I can think of that will need some thought:
Finally, the current code for |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Thanks for the feedback. This looks like a good path forward. Quick question:
Why not
|
BetaWas this translation helpful?Give feedback.
All reactions
-
Oh, I see that |
BetaWas this translation helpful?Give feedback.
All reactions
-
I agree that those variants should also work. The main thing is that the |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I see now that MATLAB has afrequency response data model object which can be legitimately used to store FR data simply for analysis: >>loadAnalyzerData>>sys= frd(resp,freq);>>syssys= Frequency(rad/s)Response ---------------- --------0.10002.384e-01-1.951e-03i0.10272.540e-01-3.511e-03i0.10562.430e-01+1.807e-03i0.10852.573e-01-4.672e-03i0.11142.525e-01-7.747e-03i...
|
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
They allow bode, nyquist and nichols plots on FRD sys objects. I tested them and they look okay with the example data above. For a system with imaginary poles I'm not sure. I tried this: >>sys= tf(1, [12*0.21]);>> [re,im,wout]= nyquist(sys);>>fr_data= squeeze(re)+ squeeze(im)*i;>>fr_sys= frd(fr_data,wout);>> subplot(1,2,1)>> nyquist(sys)>> subplot(1,2,2)>> nyquist(fr_sys) and the two plots are identical. But when I generated some random FRD data from this system and tried to make the nyquist plot it had to think for a very long time and the resulting plot looked odd. Is this what you meant? >>fr_data2= squeeze(re)+0.01*randn(size(re))+ (squeeze(im)+0.01*randn(size(im)))*i;>>fr_sys2= frd(fr_data2,wout); |
BetaWas this translation helpful?Give feedback.
All reactions
-
Oh wait, you said pure imaginary. Sorry. >>sys= tf(1, conv([-0.5i1],[0.5i1]));>> figure(1)>> subplot(1,2,1)>> nyquist(sys)>> [re,im,wout]= nyquist(sys);>>fr_data= squeeze(re)+ squeeze(im)*i;>>fr_sys= frd(fr_data,wout);>> subplot(1,2,2)>> nyquist(fr_sys) |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Ah, note the scaling on the axes above (10^-8, 10^7). When you re-scale the MATLAB nyquist plot it looks like this: Here is Python control Nyquist plot with same axis scaling: What is going on here? |
BetaWas this translation helpful?Give feedback.
All reactions
-
BetaWas this translation helpful?Give feedback.
All reactions
-
Ah right. Neat. To represent "to infinity and back" is it? |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
I just noticed this comment in python-control/control/freqplot.py Lines 311 to 312 ina111b03
This hadn't occurred to me actually—that you could take an existing subplot axis and divide it into two to make the mag and phase plots. As discussed, my plan was to return a complete new figure and axes: fig,axes=bode_plot(sys) Are there scenarios where we would want to embed a complete (mag + phase) Bode plot inside an existing figure? I think it might be simpler to provide the ability to make individual |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
How about this solution: bode_mag_plot(syslist)# quick plotbode_mag_plot(syslist,ax=ax)# adding to existing figureax=bode_mag_plot(syslist)# returning axis handle and same for And then in the new paradigm: fr=frequency_response(syslist)# quick plotfr.plot(kind='bode')fr.plot(kind='bode_mag')fr.plot(kind='bode_phase')# orfig,axes=fr.plot(kind='bode')fr.plot(kind='bode_mag',ax=ax)ax=fr.plot(kind='bode_phase')...etc |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Can anyone think of a good name for this function to provide the calculations we are proposing to remove from counts,contours=________(syslist,omega=None,omega_limits=None,omega_num=None,warn_nyquist=True,*args,**kwargs) Docstring:
I'm not entirely sure we will need to keep this function when the new plotting paradigm is complete. Then we only need to return Other thoughts: Or we could return a dictionary called (I checked my French control course notes and there it is "le nombre de tours" which is much more concise!) |
BetaWas this translation helpful?Give feedback.
All reactions
-
Some ideas:
Also, we should keep in mind that for a MIMO system, the Nyquist criterion is different (one looks at net encirclements of the origin for |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Progress update on my work on this so far. I'm about 75% through step 1 right now.@murrayrm would you prefer me to submit a pull request now or at the end (it passes all the tests including new ones I added). Or I can wait until State 1 is complete: Stage 1 - separate plotting and frequency analysis calculations into separate functions
Stage 2 - Introduce new plotting paradigm for frequency response
|
BetaWas this translation helpful?Give feedback.
All reactions
-
If you want comments along the way, post as a draft PR and we can take a look at the code and post thoughts. Otherwise, fine to wait until you have something working. Don't forget unit tests (coverage should not go down) and doc/ updates (adding functions, perhaps a new section describing the basic framework). |
BetaWas this translation helpful?Give feedback.
All reactions
-
How do I post as a draft PR? Just give you a link to my fork? All the unit tests have been updated so it's a working branch. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Yes I need to write the documentation. And I am working on a Jupyter notebook as a tutorial to all the plotting commands. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Like a normal PR, but there will be a point in the process where you can mark it as draft. Once it is there, everyone can see and comment, but can't merge. As you update your branch on GitHub, the PR gets updated. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Sounds good. I should probably merge my branch with my master first I guess, and then submit my master for the draft pull request. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
It's usually better to work in a branch and then submit the PR from the branch (in your fork). Just make sure to build (or rebase) on top of the latest (upstream) master. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Just to confirm. I'm finding code like this in some of the current plotting functions, E.g. from
As I understand it, what this is doing is getting the current figure from pyplot and looking for an axes with the right label and plotting the output to that. Under the changes here, there is the option to pass the axes you want to plot into as an argument and so none of this axes-labelling and searching is needed. Or am I missing something? (I know sisotool and gangof4 plots also use this but I think these functions will be able to use the new approach using the axes argument). |
BetaWas this translation helpful?Give feedback.
All reactions
-
For this case ( More generally, think the question is what we want the behavior to be if you do something like this:
versus something like this:
I think the behavior in the first case should be to plot the second system on the same axes as the first. But for the second case it seems like we should get a new set of axes (probably on the same figure, clearing the old one?). |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
BetaWas this translation helpful?Give feedback.
All reactions
-
B.t.w. I can't see any reason why you would want to clear any existing content in an axis or figure and replace it. In matplotlib, plots aren't shown until the end (unlike Matlab), so the content that was wiped would never be seen. After a plot is rendered, I don't think you can change it can you? |
BetaWas this translation helpful?Give feedback.
All reactions
-
Seems fine to take the matplotlib/pandas approach. But I do think that most people would expect that calling
rather than
(though the latter should also work, of course). Happy to be overridden and you are writing the code, so you get to choose -:). |
BetaWas this translation helpful?Give feedback.
All reactions
-
Yes, no problem with that. That is what it does (if no The only debate was what to do if someone tries to add a nyquist plot to a bode plot, or some other strange combination. |
BetaWas this translation helpful?Give feedback.
All reactions
-
matplotlib's recommended function signature returns a list of |
BetaWas this translation helpful?Give feedback.
All reactions
-
The link points to the MPL 2.0.2 documentation which is a bit outdated. The closest I could find in the current doc isUsage Patterns. I totally agree with the conclusion: We should do it the same way as most of the |
BetaWas this translation helpful?Give feedback.
All reactions
-
Hi@roryyorke, I'm currently working on this. If you can go through thetable above which is a list of the proposed changes we're making (also compares to the latest Julia ControlSystems equivalents) and see if you think these make sense. As well as having every plot function return a fig and/or axes, we have to come up with alternative ways to calculate the data that these functions previously returned. This is perhaps the trickier part of this proposal. (Although we will always have the original MATLAB equivalents which do plotting and return data in one function). |
BetaWas this translation helpful?Give feedback.
All reactions
-
HI@billtubbs, thanks for working on this. The changes on the whole make sense; splitting computation from presentation is a good idea. I'm not sure about using custom classes to represent results; it does look tidy, and as you mentioned it could be handy if one needs metadata. I love using pandas, but it's a fairly heavy dependency. A pandas-compatibility module, separate from plotting, could be useful, to generate DataFrames from time series and frequency response data. FWIW, one of my favourite Matlab plotting functions,bodemag, is missing; however, we could add it later (it's hardly complicated if one has To expand on my suggestion of returning a list of h1=nyquist_plot(sys1)ax=h1[0].axesh2=nyquist_plot(sys2,ax=ax)h1[0].set_label('System 1')h2[0].set(label='System 2',linewidth=5,alpha=0.5,linestyle='--')ax.legend() The equivalent using a plt.close('all')fig,ax=nyquist_plot2(sys1)nyquist_plot2(sys2,ax=ax)h1,h2=ax.get_children()[:2]h1.set_label('System 1')h2.set(label='System 2',linewidth=5,alpha=0.5,linestyle='--')ax.legend() |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
@roryyorke I agree with your point about fr=freqresp(sys,w)plt.figure(1)fr.plot()# make a Bode plot (by default, kind='bode'), returns figplt.figure(2)fr.plot(kind='mag')# make the mag plot, returns axplt.figure(3)fr.plot(kind='phase')# make the phase plot, returns ax However, we could retain: plt.figure(1)ct.bode_plot(sys)# returns (fig, axes) not (mag, phase, w) as currentlyplt.figure(2)ct.mag_plot(sys)# returns axplt.figure(3)ct.phase_plot(sys)# returns ax |
BetaWas this translation helpful?Give feedback.
All reactions
-
@billtubbs I have put this feature on the list for possible release in 0.10.0 (see#917). Are you still working on this/interested in working on this/have the time to work on this? I have some ideas of how to do all of this for time series data (using the |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Actually, I found more work I did dating to Apr 30, 2022 titled 'work in progress' which I have now merged into the master of this fork: |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I added some temporary files that I was using to guide the code development that might be useful to understand what I was doing: |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
B.t.w. I tried to contact someone on the Julia ControlSystems project (@murrayrm you gave me the name) but they never replied. I was hoping to collaborate and co-ordinate the changes a bit—see table above. I think this would still be worthwhile as some consistency between the projects would be good (but not essential). I think it's only the last 6 rows of that table (inplotting_readme.md) that I didn't complete. Although there will be more work to do to integrate the stale fork now. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I recall one of the biggest challenges I encountered but I can't remember if I overcame it or not:
In matplotlib, you're supposed to specify the projection type when you create the figure. E.g.: fig,ax=plt.subplots(subplot_kw=dict(projection='polar')) But if you look in the notebook linked above, I have some code as follows, which suggests I did get it working (almost, look at the plots, there are still some bugs) using a # Create figure with subplotsfig=plt.figure(figsize=(9,8))sgrid(position=(2,2,1))ax2=fig.add_subplot(2,2,2)zgrid(ax=ax2)# alternative for zgrids#zgrid(position=(2, 2, 2))sgrid(position=(2,2,3))ax4=fig.add_subplot(2,2,4)zgrid(ax=ax4)# alternative for zgridsplt.tight_layout()savefig('grids_subplots') |
BetaWas this translation helpful?Give feedback.
All reactions
-
This is probably where I got bogged down and should have called for help! |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I've taken a first crack at implementing the new plotting paradigm (#645) for the time response functions. The following code
You can still do old style plotting using this code
(note that Or, if you prefer, you can call the plot function explicitly:
There are lots of options for changing around the way things plot, allowing multiple calls (which plot on the same axes), and more. Some preliminary documentation ishere. I'll post this as a PR as soon as#916 gets merged into the main branch (since this set of changes builds on that one). |
BetaWas this translation helpful?Give feedback.
All reactions
-
Another question: If you define a system with named input and output signals, do the names appear on the plot axes? |
BetaWas this translation helpful?Give feedback.
All reactions
-
Yup.
|
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Final thing for me, would be to pass unrecognized keyword arguments on to the plot function. sys=ct.rss(4,2,2)ct.step_response(sys).plot(linewidth=2,linestyle='--') |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
BetaWas this translation helpful?Give feedback.
All reactions
-
See PR#920 for the changes that implement the time response functions above. Moving on to frequency response functions (Bode, Nyquist) next. |
BetaWas this translation helpful?Give feedback.
All reactions
-
See PR#924 for frequency response implementation. Moving on to pole-zero maps (pzmap, rlocus). |
BetaWas this translation helpful?Give feedback.