• 0
Sign in to follow this  
Andras

Frequency profile generation with script

Question

Hi,

I'm working on a WaveForms script which would be able to generate chart with the frequency profile of a liquid.
For this, I need to use a Wavegen and a Scope: first I need to set the generator to a certain frequency and then I'd like to check the power of that signal on the scope. Between the probes of the generator and the scope there will be the liquid I would like to test.

I created a basic script to step from 1 Hz to 150 Hz, check that signal with the scope, repeat this loop 5 times, and calculate the min, max and average values at each frequency.

Although the script works, I have a few questions:

* I couldn't find any documentation about the Scope.Channelx.fftmagnitude and fftfrequency arrays. Where should I look for them? Are there any other properties related to the FFT configuration?

* Is there a way to set the number of BINs and samples of the scope's FFT?

* Can I somehow use the Goertzel algorithm instead of FFT? (The Görtzel algorithm is designed to tell us the power of a signal at one predefined frequency. https://en.wikipedia.org/wiki/Goertzel_algorithm) I would need this to get the power for the exact Hz value I define, and not the closest one I have from the FFT like I do it now.

* Is there a way to change the color/style of the charts?

image.thumb.png.ea959eecbe1a4b9380b23434d1be2aa7.png

 

Here is the script itself, but bear in mind that it is just the very first version :)

const hzmax = 150; // the maximum frequency to measure
const csamples = 5; // the number of samples to measure for each frequency
const cdatapoints = 150; // chart X-axis resolution (currently: 1 datapoint / 1 Hz)
var datapoints = new Array(4); // datapoint array

datapoints[0] = new Array(cdatapoints); // max
datapoints[1] = new Array(cdatapoints); // avg
datapoints[2] = new Array(cdatapoints); // min
datapoints[3] = new Array(cdatapoints * csamples); // raw data

// clear the raw data array
for(var j = 0; j < cdatapoints * csamples; j++) 
{
    datapoints[3][j] = 0;
}

// generate the freqeuncies to measure
var frequencies = new Array(cdatapoints);
for(var j = 0; j < cdatapoints; j++) {
    frequencies[j] = j+1;
}

{ // configure plot
    plot1.X.Units.text = "Hz";
    plot1.X.Offset.value = -hzmax/2
    plot1.X.Range.value = hzmax;
    plot1.Y1.AutoScale.checked = false;
    plot1.Y2.AutoScale.checked = false;
    plot1.Y3.AutoScale.checked = false;
    plot1.Y4.AutoScale.checked = false;

    const vmax = 1.00;
    const vmin = 0.98;
    var offset = -(vmin + (vmax - vmin)/2);
    var range = (vmax - vmin);

    plot1.Y1.Offset.value = offset;
    plot1.Y2.Offset.value = offset;
    plot1.Y3.Offset.value = offset;
    plot1.Y4.Offset.value = offset;
    plot1.Y1.Range.value = range;
    plot1.Y2.Range.value = range;
    plot1.Y3.Range.value = range;
    plot1.Y4.Range.value = range;
}
{ // configure wavegen
    Wavegen1.Channel1.Mode.text = "Simple";
    Wavegen1.Channel1.Simple.Type.text = "Sine";
}
{ // configure scope
    Scope1.BufferSize.value = 1;
    Scope1.Buffer.value = 1;
    Scope1.Time.Mode.text = "Shift";
    Scope1.Channel1.checked = true;
    Scope1.Channel2.checked = false;
}

Wavegen1.run();

for (var n=0; n<csamples; n++)
{
    Scope1.Time.Samples.value = 1024;

    for (var i=0; i<hzmax; i++)
    { 
        // change the frequency to the next one
        var frequency = frequencies[i];
        Wavegen1.Channel1.Simple.Frequency.text = frequency;
        Scope1.Time.Rate.value = frequency * 256;
        
        print("Frequency: ",frequency," Hz | Running @ ",Scope1.Time.Rate.value," Hz | ", Scope1.Time.Samples.value," samples");
    
        // take a single sample   
        Scope1.single();
        Scope1.wait();
    
        // check out the FFT values
        var rgmag = Scope1.Channel1.fftmagnitude;
        var rghz = Scope1.Channel1.fftfrequency;
        var c = rgmag.length;
        var minHzDistance = frequency;
        
        // check all the FFT values, and get the one closest to our inspected frequency
        for(var j = 0; j < c; j++)
        {
            var hz = rghz[j]
            var hzDistance = frequency - hz;
            if (hzDistance < 0)
            {
                hzDistance = -hzDistance;
            }

            if (hzDistance <= minHzDistance)
            {
                minHzDistance = hzDistance;
                datapoints[3][i*csamples + n] = rgmag[j];
            }
        }
    
        // calculate the min, max, avg values
        var rgmin = 1000.0;
        var rgavg = 0.0;
        var rgmax = 0.0;
        var validsamples = 0;
        for(var j = 0; j < csamples; j++)
        {
            currentsample = datapoints[3][i*csamples + j];

            if (currentsample != 0)
            {
                if (currentsample < rgmin)
                {
                    rgmin = currentsample;
                }
        
                if (currentsample > rgmax)
                {
                    rgmax = currentsample;
                }
        
                rgavg += currentsample;
                validsamples++;
            }
        }
        rgavg /= validsamples;
    
        print("Min:",rgmin," V | Avg:",rgavg," V | Max:",rgmax," V | error: +/-",minHzDistance," Hz");
        print();
            
        // save the calculated data into their arrays
        datapoints[0][i] = rgmax;
        datapoints[1][i] = rgavg;
        datapoints[2][i] = rgmin;


        // check if the error if too high
        if (minHzDistance > 0.5)
        {
            Scope1.Time.Samples.value *= 2;
            i--;
        }

    
        // plot the most recent values on the chart
        plot1.Y1.data = datapoints[0]; // max
        plot1.Y2.data = datapoints[1]; // avg
        plot1.Y3.data = datapoints[2]; // min
    }
}

 

Share this post


Link to post
Share on other sites

5 answers to this question

Recommended Posts

  • 1

Szia @Andras

I would suggest you to use the Network Analyzer which is intended for such purposes, but this doesn't have average, min, max. So you would have to use Script here too.

The fftmangintude/frequency simply returns the magnitude and corresponding frequency arrays.
The FFT options what you can also adjust in the interface are here: Scope1.FFT

The number of BINs can't be adjusted directly in Scope/FFT, this is half of the number of Scope samples.
image.png.6f927c12b52da30fe52bcb6267032b22.png

Earlier Goertzel was used in Network Analyzer, but with the addition of external signal option it was replaced with FFT.

The Gertzel function in Script would look like this:

function Goertzel(data, hzData, hzSig){
    var n = data.length;
    var o = 2.0* PI * hzSig / hzData;
    var rew = 2.0 *cos(o);
    var imw = sin(o);

    var d1 = 0;
    var d2 = 0;
    for(var i = 0; i < n; i++) {
        var y  = data[i];
        y += rew*d1 - d2;
        d2 = d1;
        d1 = y;
    }
    var re = 0.5*rew*d1 - d2;
    var im = imw*d1;
    var ma = sqrt(re*re+im*im)*2/n;
    var ph = atan2(re, im); 
    if(ph>PI) ph -= 2.0*PI;
    if(ph<-PI) ph += 2.0*PI;
    return ma;
    //ma = 20.0*qLog10(ma); // to dB
    //ph = ph*180/M_PI; // to degrees
    //return [ma,ph]
}
var hzSig = 1e3
var mag = Goertzel(Scope1.Channel1.data, Scope1.Time.Rate.real, hzSig)

The Script/plot color can't be changed...

Share this post


Link to post
Share on other sites
  • 1

Hi @Andras

You have in private message WF beta 3.8.14
This adds magnitude set option from script.
You could do averaging, min, max with it like this:

image.thumb.png.f487a544ddffd3b5a5521c213f3a0095.png

Network1.run() // start Network Analyzer
var source = Network1.Channel1 // source channel
var n =  10 // iterations
var rgAvg = [] // average array
var rgMin = [] // minimum array
var rgMax = [] // maximum array
for(var k = 0; k < n; k++){
    print(k+1)
    Network1.wait()
    if(k==0){ // initialize Refs with the first capture
        Network1.Reference1.Clone(source)
        Network1.Reference2.Clone(source)
        Network1.Reference3.Clone(source)
        rgAvg = source.magnitude // initialize script arrays
        rgMin = source.magnitude
        rgMax = source.magnitude
    }else{
        var rg = source.magnitude // source channel magnitude array
        var c = rg.length // array length
        for(var i = 0; i < c; i++){
            var v = rg[i] // magnitude at step 'i'
            rgAvg[i] += v // average
            rgMin[i] = min(rgMin[i],v) // min
            rgMax[i] = max(rgMax[i],v) // max
        } 
    }
}
{ // normalize average
    var c = rgAvg.length
    for(var i = 0; i < c; i++){
        rgAvg[i] = rgAvg[i]/n
    }
}
Network1.Reference1.magnitude = rgAvg
Network1.Reference2.magnitude = rgMin
Network1.Reference3.magnitude = rgMax
Network1.stop()

 

Edited by attila

Share this post


Link to post
Share on other sites
  • 1

Szia @Andras

The Network Analyzer by default takes controls over the Wavegen channel 1 and configures the required frequency for each step. You could select NA/Wavegen/Channel/External but to be able the control the Wavegen manually, but in this case the previous Script solution won't work. 

The Insert/Local lists specific variables and is available in other scriptable places, like scope custom math, measurements, logging, network analyzer custom plots
In each script editor including the Script tool you can use the Ctrl+Space to list available objects, variables... or child objects, properties, functions..

 

Share this post


Link to post
Share on other sites
  • 0

Szia @Attila!

I'm trying to make the Network Analyzer and the Waveform Generator work at the same time, because I would need to generate a sine wave at a frequency and then check if that frequency is detectable by the Network Analyzer.

When I start my test by first running the Waveform Generator and then I start the Network Analyzer, the Generator stops automatically. Is it intentional, is there a reason why they can't run at the same time? I use WaveForms version 3.8.15 beta.

Btw, thank you for showing me the min and max functions, I didn't know they exists. I have just found them listed in the 5.6. section of the help (Help/Browse menu) and you also mention the following: "The specific objects and variables available in each of these can be found under the Insert menu Locals group." Could you help me get to the Insert/Locals menu item, I can't seem to find it anywhere :)

Thanks!

Share this post


Link to post
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
Sign in to follow this