Posted Mon, 24 Oct 2022 20:45:08 GMT by Eglowstein, Howard
I have a shiny new AFG31022 that I selected because I need to generate waveforms greater than 128K pts in length. Try as I may, it seems to reject any commands that would download longer waveforms over the USB connection. I just installed the ArbExpress program to try out and it says that for waveforms longer than 128K you can't use th USB connection, but rather you must write them to a flash drive and transfer them that way.  Has anyone else noticed this, and is my plan to send it longer waveforms over USB completely hopeless?

Bonus question - the commands that used to clear user waveforms on the 3022 don't work. The manual isn't clear on a proper way to erase a user arbitrarry waveform. Is there a way?

Thanks!
Posted Mon, 24 Oct 2022 23:11:14 GMT by Teles, Afonso
Hi,

The feature for transferring waveforms over 131,072 points is currently under development, but should be coming out soon.
Until then, there is a workaround which involves breaking up your waveform into chunks smaller than 131k points, transferring them over and then stitching them together in the sequencer. This can be automated through a remote control script, if needed.
Loading a file over 131k points from a flash drive also works.

Bonus round:
There isn't a command to remove the waveform loaded in edit memory (EMemory), as far as I know. You could set it to an empty waveform (1000 points of all zeros is the default).
There's also MMEMory:DELete, which deletes a file in mass memory (MMemory).
Posted Tue, 25 Oct 2022 14:11:44 GMT by Eglowstein, Howard

Thank you for confirming my experiment, the quick reply and the update.  Can you also confirm that the older 3022 was like 11 or 12-bit data (two bytes per point) and the 31022 is 8-bit or one byte per point?  The documentation for the 31022 is quite sparse in comparison to that of the 3022.

Posted Tue, 25 Oct 2022 18:28:40 GMT by Teles, Afonso

Hi Howard,

Both the AFG3000 and AFG31000 have 14 bits of resolution.

Waveform data is often stored as 2 bytes per point, i.e. a 130k point .tfwx file will take up about 260k bytes.

Posted Tue, 25 Oct 2022 18:41:29 GMT by Eglowstein, Howard
Thanks. I was puzzled then by the example in the manual where a BASIC program computed a sine wave and sent it as one byte per point. I converted that to C and it generated a nice sine wave.

Dim num_points = 3000
Dim base = 9000
Dim offset = 6000
Dim SinePoint As Integer
Dim SineWaveData(num_points) As Byte
For i = 0 To num_points 'Build sine wave
   SinePoint = Int(base + offset * Math.Sin(i / 120))
   SineWaveData(i) = SinePoint / 256
Next i

Tvc1.SendEndEnabled = False
Tvc1.FormattedIO.WriteLine("TRACE:DATA EMEM1, #43000")
Tvc1.SendEndEnabled = True
Tvc1.RawIO.Write(SineWaveData)

It's been a while since I've written BASIC, but by my read this would send out 3000 bytes for 3000 points. I can believe the 31022 is the same as the 3022 was, but then this shouldn't work but it seems to?  

Anyway, thank you a ton for the help. I feel like I'm reverse engineering this puppy.
Posted Thu, 27 Oct 2022 18:41:02 GMT by Teles, Afonso
Hi Howard,

Indeed, that is an interesting implementation. I don't have a BASIC environment setup to try it out, but here are my guesses, in rough order of likelihood:

1) It's sending the array out as bytes but they are being read by the AFG as 16 bit words. This would result in a waveform that is half the number of points expected.
2) It's writing the array as bytes and they are being read as bytes (for a reason I do not fully understand). This would result in a correct number of points.
3) It's (for some other hidden reason) writing the array as 16 bit words (upscaled bytes) and so they are read in properly.

All of these depend on intricacies of the BASIC language, with which I am not familiar with.
Posted Wed, 04 Jan 2023 17:11:15 GMT by Eglowstein, Howard
Alfonso, we're well into our implementation now and I'd like to explore the sequencing to get in larger waveforms. Can you direct me towards a block diagram, an implementation of some sort or just a description of what commands I might send and in what order?

I assume it's using TRAC to send sections to EMEM, then I have to transfer those to files in drive M:?  or P:?  Then use the SEQ comnmand somehow?  The documentation isn't as clear as I'd like it to be.

Thanks in advance!

Howard
Posted Mon, 09 Jan 2023 23:14:07 GMT by Teles, Afonso

Hi Howard,

I don't have a specific resource that describes this process, but I will try to give a short summary of it:

  1.  Send the waveform data into edit memory using trace:data
  2.  Save that data into mass memory using mmemory:store:trace
  3.  Piece the waveform back together in the sequencer using wlist:waveform:import and sequence:elem[n]:waveform[m]

You will also need a couple other miscellaneous commands to configure the amplitude, sample rate, sequence, etc.

Additionally, please note there will be a delay between waveforms in the sequencer. I have empirically found this delay to be around several hundred nanoseconds under certain conditions. This may make this whole idea of little use to you.

Please do let me know if you need any help in this matter!

Posted Tue, 10 Jan 2023 13:48:08 GMT by Eglowstein, Howard

Thanks!  That's exactly the leg up I was looking for. There other things to consider as well, like what kind of sync signal we'll get out of the unit but I'd like to experiment and see what we can do.   Yes, the delay as the waveform chunks switch from one to another might be a problem.  We'll see.

Thanks again!

Howard

Posted Fri, 13 Jan 2023 22:43:39 GMT by Eglowstein, Howard
Yesterday I started exploring this sequencing thing, and now it appears that I'm unable to send ARB data at all.&nbsp; When I asked about the programming example a few months ago we puzzled over why the program in the manual only sent out one byte per data point. That didn't make any sense.&nbsp; I though the manual I had might have been buggy (early printing) and I found two other versions on the web that appear to be newer. In the latest one the BASIC example does indeed show two bytes per point, the high byte of a word before the low byte.&nbsp; So that made more sense.<br> <br> I'm working in LabWindows/CVI and I had a working test program that did a great job downloading waveforms to a 3022.&nbsp; I'm trying to update it for the 31022 and it looks something like this:&nbsp; (the functions are essentially wrappers around the VISA functions provided by NI)<br> <br> &nbsp;&nbsp; &nbsp;error = AFG_Init();<br> &nbsp;&nbsp; &nbsp;if (error &lt; 0)<br> &nbsp;&nbsp; &nbsp;{<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;sprintf(strtemp, "I can't init the AFG. Error = %d. Should I try again?\n", error);<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;userchoice = ConfirmPopup("Can't init the AFG!", strtemp); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;// 1 = yes, 0 = no &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (0 == userchoice)<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;goto Error;<br> &nbsp;&nbsp; &nbsp;}<br> &nbsp;&nbsp; &nbsp;error = AFG_Command("*RST\r");<br> &nbsp;&nbsp; &nbsp;// squirt out a waveform<br> &nbsp;&nbsp; &nbsp;unsigned short wave1[128*1024]; &nbsp;// 128K points<br> &nbsp;&nbsp; &nbsp;<br> &nbsp;&nbsp; &nbsp;// call a function that opens a data file of numbers between 0 and 16382, writes<br> &nbsp;&nbsp; &nbsp;// each value into the array 'wave1' and returns the number of lines it read<br> &nbsp;&nbsp; &nbsp;// (the number of values) into 'linesread'<br> &nbsp;&nbsp; &nbsp;error = ReadWFMfile("mytestfile.dat", &amp;linesread, &amp;wave1);<br> <br> &nbsp;&nbsp; &nbsp;// Format a string so it looks something like "DATA:DATA EMEM1,#6128000" without a terminator<br> &nbsp;&nbsp; &nbsp;sprintf(str1,"%d", linesread);<br> &nbsp;&nbsp; &nbsp;sprintf(ourcommand, "TRACE:DATA EMEMORY1,#%d%s", strlen(str1), str1);<br> &nbsp;&nbsp; &nbsp;// Send that part of a command<br> &nbsp;&nbsp; &nbsp;error = AFG_Command(ourcommand);<br> <br> &nbsp;&nbsp; &nbsp;for (i=0; i&lt;linesread; i++)<br> &nbsp;&nbsp; &nbsp;{<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;// Send each of the integer values as two bytes, high endian<br> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;error = AFG_Word(wave1[i]);<br> &nbsp;&nbsp; &nbsp;}<br> &nbsp;&nbsp; &nbsp;// Send a terminator for the DATA:DATA command<br> &nbsp;&nbsp; &nbsp;error = AFG_Command("\r");<br> <br> &nbsp;&nbsp; &nbsp;error = AFG_Command("FUNCTION EMEM1\r");<br> &nbsp; &nbsp; error = AFG_Command("FREQUENCY 8K\r");<br> &nbsp;&nbsp; &nbsp;error = AFG_Command("OUTPUT ON\r");<br> <br> ------------------------------------------------<br> <br> The AFG resets and changes states just fine but no variation of the TRACE command seems to make a difference. I wondered if there could have been firmware updates so that maybe my device doesn't properly interpret these commands?&nbsp; The manual shows words like EMEMory in upper and lower case where the lower case characters are for clarity and can be eliminated - yet the example shows the commands spelled out fully.&nbsp; I have firmware 1.6.1 installed if that matters.<br> <br> Any suggestions?&nbsp; Is that code above (which worked well on the 3022 with slightly different commands) fundamentally wrong somehow?<br> <br> Thanks!
Posted Tue, 17 Jan 2023 15:03:35 GMT by Eglowstein, Howard
I can't seem to find a way of pasting a screen of text without it converting to HTML.  :(  

Since I can't easily share my C program for downloading waveforms, does anyone have a sample, tested program that works?  (any language is fine).  I tried replicating the BASIC program in the manual but so far I've found 3 different versions of the manual that have different programs and the command sequences don't seem to work.

Thanks!
Posted Tue, 17 Jan 2023 17:31:52 GMT by Teles, Afonso

Hi Howard,

You might have better luck posting your code as an attached file or using the example code block (go into Source mode and you can see and edit the HTML).

Additionally, the following link has several AFG programming examples: https://forum.tek.com/viewtopic.php?f=580&t=133570

I will note that it doesn't appear to have any AFG31000 examples and the programming interface between it and the AFG3000 do differ.

Posted Tue, 17 Jan 2023 19:07:41 GMT by Eglowstein, Howard
No joy. I can't figure out a way to do this without it expanding to HTML and showing the HTML formatting...  Browser dependent maybe?  I'm using Chrome.....
Posted Tue, 17 Jan 2023 21:08:31 GMT by C, Andrea
Here is a Python sample for loading a custom waveform to the standard ARB mode.
It was targeting generation of double pulse test waveform type.
It is easy matter to save the EMEM memory to a tfwx file on the M: or U: drive for use in the SEQ.
 
'''
Create waveform and send to AFG31102

Double Pulse Test Waveform

              --- A1----                ---A2--- 
             |          |              |        |
            |            |            |          |
-----------|              |----------|            |------------

<--  t1 -->|<--- t2 --->|<-----t3---->|<---t4---->|<---t5---->|


Pulse Heights, A1 and A2 , correspond to the Vgs applied to the FET
and therefore control the Ids current level.

rise/fall times = don't care yet

duration of other time segements = important

'''

import pyvisa
import numpy as np # for test wave
import matplotlib.pyplot as plt # for plotting, not required

#time durations for our waveform
t1 = 1e-6
t2 = 1e-6    #duration of A1
t3 = 1e-6  # off time in between two pulses
t4 = 1e-6   #duration of A2
t5 = 1e-6
#voltage amplitudes of the pulses
A1 = 1.25    #volts
A2 = 3    #volts
AFG_SampleRate = 250e6



total_time = t1 + t2 + t3 + t4 + t5
t_axis = np.arange(0, total_time, 1/AFG_SampleRate)
wave = np.multiply(0, t_axis)      # populate with zeros

ARB_SRC_FREQ = int (AFG_SampleRate / len(t_axis))


print('********************')
print('ARB_SRC_FREQ: ' + str(ARB_SRC_FREQ))
print('without option, limit size of ARB to 131K entries')
print('Num Pts per ARB Waveform: ' + str(len(t_axis)))
print(len(wave))

#overwrite some of the zeros with our pulse height values

#first pulse
start_index=int(t1 * AFG_SampleRate)
stop_index = start_index + int(t2 * AFG_SampleRate)
print('start index pulse1: ' + str(start_index))
print('stop index pulse1: ' + str(stop_index))
for i in range(start_index, stop_index):    #pulse top
    wave[i] = A1

# second pulse
start_index=int((t1+ t2+ t3) * AFG_SampleRate)
stop_index = start_index + int(t4* AFG_SampleRate)
print('start index pulse2: ' + str(start_index))
print('stop index pulse2: ' + str(stop_index))
for i in range(start_index, stop_index):    #pulse top
    wave[i] = A2
    
#build normalized array
# normalize to dac values
m = 16382 / (wave.max() - wave.min())
b = -m * wave.min()
dac_values = (m * wave + b)
np.around(dac_values, out=dac_values)
dac_values = dac_values.astype(np.uint16)


# plot (optional)
plt.plot(dac_values)
plt.show()

#Get the this from NI-MAX
instrumentdescriptor = 'USB0::0x0699::0x035A::C013392::INSTR'
#instrumentdescriptor = 'GPIB0::11::INSTR'

    
    
print('****************************************')
debug = 0
if debug:
    for item in wave:
        print(item)
    

#Write the waveform to a text file 
debug = 0
if debug:
    fid = open('waveform.txt', 'w')
    for j in wave:
        fid.write(str(j) + '\n')
    fid.close()


print('Sending waveform to AFG')


#Next sending to the AFG
resource_mgr = pyvisa.ResourceManager()
AFG = resource_mgr.open_resource(instrumentdescriptor)
AFG.read_termination = '\n'
AFG.encoding = 'latin_1'

def halt_on_msg():
    e = int(AFG.query('*esr?'))
    if e != 0:
        raise Exception('non-zero esr')


print(AFG.query('*IDN?'))
#reset and clear the AFG status
AFG.write('*rst')
AFG.write('*cls')

#configure the channel to play arbitrary waveform, burst mode, .2ms timer
#on trigger, 0 - 5V output, 1 cycle of waveform per occurance.
AFG.write('source1:function ememory')
AFG.write('OUTP1:IMP INF')    #MAX=10Kohms, INF is >10K, MIN=1ohm
AFG.write('source1:frequency ' + str(ARB_SRC_FREQ)  )
AFG.write('source1:burst:mode trig')
AFG.write('source1:burst:ncycles 1')
AFG.write('source1:burst:state on')
AFG.write('trigger:sequence:timer 0.005')
AFG.write('source1:voltage:high ' + str(wave.max()))
AFG.write('source1:voltage:low ' + str(wave.min()))
#AFG.write('source1:voltage:offset 2.5')


# ****************  send the ARB waveform ******************

halt_on_msg()

# datatype = "H" means unsigned short format
AFG.write_binary_values(':TRAC:DATA EMEM1,',
                        dac_values,
                        datatype='h',                 
                        is_big_endian=True) 

halt_on_msg()



#finally, turn the output on
AFG.write('output1 on')
Posted Tue, 17 Jan 2023 22:41:24 GMT by Eglowstein, Howard
Thanks Andrea!  I see a few things that are different than the examples in the manual, and what I captured with the USB analyzer from ARBExpress.  In general, do extra letters and upper/lower case matter as much as the manual suggests?  Line 128 in your code would be "SOUR1:FUNC EMEM1" if it were treated strictly. If the device doesn't care...

The money line that transfers the data is line 145.  ARBExpress sends a command that looks like "TRACE:DATA EMEM1,#3200", where there's no leading colon, the hash tag is present, then the 3200 means there are 200 bytes that follow and there are 3 characters in the number 200.  Your example doesn't send a length. Does AFG.write_binary_values format that line for you? The PyVisa doesn't mention it and I'd think it's critical, no?

It's frustrating since each of these sources seems to document something different than ARBExpress does, yet if I have my program emulate ARBExpress I can't get that to work either. Then if I switch back to 3022 commands and send them to the 3022, that works fine.  This is really weird.  I need to get arbitrary waveforms working first and then I can try out the really useful looking sequence code you shared.  I installed the trial option and our purchasing people are working on buying up a whole boatload of licences for all of our new 31022s.

Howard
Posted Mon, 23 Jan 2023 22:51:28 GMT by Teles, Afonso

Hi Howard,

SCPI commands are case insensitive, as mentioned on page 15 of the programmer's manual.

the write_binary_values method does indeed automatically do the binary formatting, as mentioned in the pyvisa docs

I have included a python example of loading a waveform into ememory of an AFG31000. I hope it's of use to you.

Posted Tue, 24 Jan 2023 17:58:36 GMT by Eglowstein, Howard
*sigh*  It works as I expected it would, yet the same calls from C just don't seem to do the job.  I'm going to try and install Python on this machine (it's off-network so it's hard to install stuff) and see if I can just get this example to run verbatim. Then I can watch it on the USB analyzer and see if it sneaks any extra SCPI commands that aren't clearly spelled out here.

I appreciate the help and your patience.  I do offer beer/pizza money for useful suggestions so we can arrange some if you'd like. Again, I really appreciate your and Andrea's help with this.
Posted Thu, 02 Feb 2023 14:53:14 GMT by Eglowstein, Howard
Part of the issue, I think, is that the AFG3100 seems to be finicky about the waveform itself. I'd still like to see a clear set of documentation that shows the SCPI commands that work, but the examples above and elsewhere on the forum (thanks to Andrea and Afonso for that!!) got my code sending data the AFG likes. But here's the weird thing:
This program segment produces a buffer of chars that when sent to the AFG properly produces one sine wave cycle in 360 points, or 720 bytes:
float angrad, outsine, outcos;
outcount = 0;
for (i=0; i<360; i++)
{
angrad = (float)i / 180.0 * 3.14159;
outsine = 8192.0 + 8191.0 * sin(angrad);
outcos = 8192.0 + 8191.0 * cos(angrad);
val = (int)outsine;
val = (int)outcos;
outbuff[outcount++] = (char) (val & 0xff) // low byte
outbuff[outcount++] = (char) (val >> 8) // high byte
}

The buffer is surrounded by appropriate commands and sent via NI_VISA viWrite commands from CVL/LabWindows.&amp;nbsp; The AFG properly displays a sine wave cycle and it comes out the output jack as expected.
I then change the commented line so the value written is the cosine wave (starts high, goes to 0, then negative, then back to 0 and ends high) the treats it as all zeroes:
float angrad, outsine, outcos;
outcount = 0;
for (i=0; i<360; i++)
{
angrad = (float)i / 180.0 * 3.14159;
outsine = 8192.0 + 8191.0 * sin(angrad);
outcos = 8192.0 + 8191.0 * cos(angrad);
val = (int)outsine;
val = (int)outcos;
outbuff[outcount++] = (char) (val & 0xff) // low byte
outbuff[outcount++] = (char) (val >> 8) // high byte
}

if I change the code again so that it smashes the beginning and end of the waveform to be 0, the AFG displays it properly (the beginning and end is 0 though)
float angrad, outsine, outcos;
outcount = 0;
for (i=0; i<360; i++)
{
angrad = (float)i / 180.0 * 3.14159;
outsine = 8192.0 + 8191.0 * sin(angrad);
outcos = 8192.0 + 8191.0 * cos(angrad);
if ((0 == i) || (359 == i)) outcos = 0.0;// smash both ends to 0.0 instead of 1.0 if the angle is 0 or 359 degrees
val = (int)outsine;
val = (int)outcos;
outbuff[outcount++] = (char) (val & 0xff) // low byte
outbuff[outcount++] = (char) (val >> 8); // high byte
}

Finally, if I only smash the beginning to 0 it fails to show anything but if I only smash i=359 with “if (359 == i) outcos = 0.0” to 0 but leave the first point set to 1.0 I get the waveform but the beginning has the dip, not the end.
It looks like there some undocumented and (dare I say) buggy requirement that the waveform ends with a voltage near 0. My original test data set was a sample of audio which is constantly changing data. I think this 'feature' was preventing my old test code from generating anything. I tried a variety of SCPI command variations and many of them seem to work, but the data itself seems to matter. The 3022 didn't have these limitations.
 
Posted Wed, 08 Feb 2023 23:39:12 GMT by Teles, Afonso
Hi Howard,

I took the liberty to fix some of the formatting in your previous post.
For future reference, using the <code> tag is a good way to avoid formatting issues when posting code.

I wonder if your issue may be related to how you have set the write termination for your VISA connection as I have not faced the same issue, I am able to, using the Python script I previously shared here, send a cosine waveform (not ending or starting with 0) and have it received without issue.

You must be signed in to post in this forum.