## 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?

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 = new Array(cdatapoints); // max
datapoints = new Array(cdatapoints); // avg
datapoints = new Array(cdatapoints); // min
datapoints = new Array(cdatapoints * csamples); // raw data

// clear the raw data array
for(var j = 0; j < cdatapoints * csamples; j++)
{
datapoints[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[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[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[i] = rgmax;
datapoints[i] = rgavg;
datapoints[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; // max
plot1.Y2.data = datapoints; // avg
plot1.Y3.data = datapoints; // min
}
}```

## 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. 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 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:

```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 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 on other sites
• 0

Awesome, thanks Attila!

##### 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!