Jump to content
  • 0

Measuring frequency AD2 using Python


Adam1973

Question

Hi all. Hope you are all well.

Has anyone measured the frequency of a sine wave using the scope function of the AD2? Any help and guidance would be much appreciated. I have also looked at the impedance analyser option but I cannot see how to get a frequency measurement from the functions.

Many thanks Adam 

Link to comment
Share on other sites

22 answers to this question

Recommended Posts

  • 0

Well the obvious approach would be to acquire a bunch of data at a sampling rate reasonable w.r.t. the frequency you're expecting, apply a windowing function to the data captured, followed by a Fourier transform and a peak finder. For increased precision, use zero-padding (or, if you want to go all fancy, a chirp-z transform).

Would that work for your application (and if not, why not)?

As always, it largely depends on what precisely you're doing and what accuracy you're aiming for. An AD2 is not a frequency counter, and you will not get the precision from it that you can get from a more specialized instrument.
 

Edited by reddish
Link to comment
Share on other sites

  • 0

Hi @Adam1973

Here's an example of how to do frequency estimation from a captured analog signal.

The attached program (frequency_estimation.py) first sets up a sinusoidal test signal of 50.123456 Hz on CH1 of analog-out.

Presuming a loop-back from analog-out CH1 into analog-in CH1, it then samples a block of data. The example program currently takes 1000 samples at 1000 Hz, which takes one second.

Next, it applies the proper signal-processing voodoo to estimate signal power as a function of frequency. For that, I use a chirp-Z transform, which can be used as a generalization of the discrete Fourier transform, with a configurable spectral range and resolution.  It returns, for each of the investigated frequencies, a phase and signal strength estimation, the latter of which is shown below in the last graph. This graph will show the highest peak at the frequency where the input signal frequency is, assuming that the input is a single-frequency sinusoidal signal, with perhaps a bit of noise.

As you can see, it does very well at locating the frequency peak (emitted: 50.123456 Hz, estimated from analog-input data: 50.123460 Hz).

The advantage of this more mathematically advanced analysis (compared to, say, finding zero crossings) is that it is insensitive to nasty stuff like a voltage offset in your signal, and it will be a lot less sensitive to noise (so more accurate). The disadvantage is that to truly understand how this works you have to have a pretty good grasp of Fourier transforms, which may be quite daunting at first, depending on how strong your math skills are.

estimate_frequency.thumb.png.32cdcee0cbdcef1fd4d0a0541550dd30.png

 

czt.py frequency_estimation.py

Edited by reddish
Link to comment
Share on other sites

  • 0

Hi Sidney. The code starts to run but I am getting some errors. I have installed matplotlib but something is not working. Any ideas? 

Thanks 

Adam

Traceback (most recent call last):   File "C:\Users\arous\AppData\Local\Programs\Python\frequency_estimation.py", line 102, in <module>     main()   File "C:\Users\arous\AppData\Local\Programs\Python\frequency_estimation.py", line 77, in main     plt.subplots_adjust(hspace=0.5)   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 2253, in subplots_adjust     return gcf().subplots_adjust(   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 832, in gcf     return figure()   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\_api\deprecation.py", line 454, in wrapper     return func(*args, **kwargs)   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 773, in figure     manager = new_figure_manager(   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 348, in new_figure_manager     _warn_if_gui_out_of_main_thread()   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 338, in _warn_if_gui_out_of_main_thread     if (_get_required_interactive_framework(_get_backend_mod()) and   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 207, in _get_backend_mod     switch_backend(dict.__getitem__(rcParams, "backend"))   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 252, in switch_backend     switch_backend(candidate)   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\pyplot.py", line 265, in switch_backend     backend_mod = importlib.import_module(   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\importlib\__init__.py", line 126, in import_module     return _bootstrap._gcd_import(name[level:], package, level)   File "<frozen importlib._bootstrap>", line 1050, in _gcd_import   File "<frozen importlib._bootstrap>", line 1027, in _find_and_load   File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked   File "<frozen importlib._bootstrap>", line 688, in _load_unlocked   File "<frozen importlib._bootstrap_external>", line 883, in exec_module   File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_qtagg.py", line 12, in <module>     from .backend_qt import QtCore, QtGui, _BackendQT, FigureCanvasQT   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_qt.py", line 72, in <module>     _MODIFIER_KEYS = [   File "C:\Users\arous\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_qt.py", line 73, in <listcomp>     (_to_int(getattr(_enum("QtCore.Qt.KeyboardModifier"), mod)), TypeError: int() argument must be a string, a bytes-like object or a real number, not 'KeyboardModifier'

Link to comment
Share on other sites

  • 0

Hi @Adam1973

The frequency is determined in line 75 as "max_response_frequency". You can write that variable to file.

As to the error: it looks like your Python installation has a weird issue properly handling the matplotlib window's keyboard handler that is used to show the plot on the screen. Seems like some improper combination of installed Python package versions, which is always a headache to resolve. I haven't seen this particular error before (but then I rarely use Windows).

To circumvent this: replace the line

plt.show()

by

plt.savefig("frequency_estimation.png")

Now it will write out the plot to a PNG file, rather than trying to give you an interactive plot window.

Cheers Sidney

Edited by reddish
Link to comment
Share on other sites

  • 0

One does one's best :) It just so happens that you're solving a problem that I more-or-less solved myself in the last few years, or at least I have the building blocks already done. So it's not really a lot of work, and a nice way to refresh my signal processing knowledge.

Also, it makes me use pydwf  to do stuff I haven't used/tested in a long time, and the fresh look at especially the documentation makes me find small issues that can improved. So that's useful, too.

Link to comment
Share on other sites

  • 0

Hi Sidney

It's brilliant :) One question if I may. I want to power on the device at the start of my code because I want to set up other parts before I measure the frequency. I see in the code you have 

dwf = DwfLibrary()

with openDwfDevice(dwf) as device:

The code uses with statement with reference to device.analogIn. How do I get to power the device up at the start without messing up the code? 

Cheers 

Adam

Link to comment
Share on other sites

  • 0

Hi Adam, you're talking about the device under test rather than the AD2 itself I think? Am I understanding correcly that you want to power up the DUT before doing the frequency measurement?

In that case, inside the "with" statement, before doing the data acquisition, just use device.analogIO to access the analog I/O node that controls the power supply that you need.

Link to comment
Share on other sites

  • 0

Hi Attila. Thanks for the link to the thread about frequency measurements. I thought I would have a look but it won't run, I get this error:

Traceback (most recent call last):   File "C:\Users\450362\Documents\AnalogIn_FFT.py", line 99, in <module>     dwf.FDwfSpectrumWindow(byref(rgdWindow), c_int(nSamples), DwfWindowFlatTop, vBeta, byref(vNEBW))   File "C:\Users\450362\AppData\Local\Programs\Python\Python310\lib\ctypes\__init__.py", line 387, in __getattr__     func = self.__getitem__(name)   File "C:\Users\450362\AppData\Local\Programs\Python\Python310\lib\ctypes\__init__.py", line 392, in __getitem__     func = self._FuncPtr((name_or_ordinal, self)) AttributeError: function 'FDwfSpectrumWindow' not found

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...