PXI Shared Memory Data Transfer
The fastest way to transfer data out of a PXIe VNA is to use shared memory. Due to the nature of shared memory, these commands can ONLY be used to transfer data between your program and the VNA when your program runs locally on the same computer as the VNA.
Shared Memory Background: Usually, each process has its own memory, and anything saved in one process isn't visible to another process. With shared memory, both processes actually connect to the same memory, so what happens in one process is now visible to another process.
How to setup Shared Memory
Send SYST:DATA:MEM:INITialize. This tells the VNA to prepare for the creation of shared memory.
Decide which measurements to include in the shared memory. For each of those measurements, send SYST:DATA:MEM:ADD. This adds a request to copy the contents of those measurements into the shared memory. This does not start adding data.
Send SYST:DATA:MEM:COMMit. This command tells the VNA to create the Windows shared memory resource and to give that shared memory resource a name.
You can now create a handle to the shared memory and start reading the data from that shared memory. Once setup, the VNA remembers the setup, and the shared memory will be filled on each sweep. You do not send any more SCPI commands unless you need to change the list of requested measurements to include in the shared memory.
SYSTem: DATA:MEMory Commands
Controls Shared Memory.
The commands are listed in order of recommended use.
SYSTem:DATA:MEMory:
|
Click on a keyword to view the command details.
See Also
SYSTem:DATA:MEMory:INITialize
(Write only) Initializes the shared memory setup buffers. |
|
Parameters |
None |
Examples |
|
Query Syntax |
Not Applicable |
Not Applicable |
SYSTem:DATA:MEMory:ADD <string>
(Write only) Add a request to copy the contents of the specified measurement into shared memory. Call this command once for each measurement definition. Once the shared memory is setup, there is no need to send more SCPI commands. The shared memory is filled automatically on every sweep. |
|
Parameters |
|
<string> |
The following elements separated with colons (:). <Ch>:<MeasNum>:<dataFormat>:<numPoints> where:
|
Examples |
SYSTem:DATA:MEMory:ADD "2:3:SDATA:201" 'copies the data for channel #2, measurement #3, complex data, 201 points into the shared memory buffer. |
Query Syntax |
Not Applicable |
Not Applicable |
SYSTem:DATA:MEMory:OFFSet?
(Read only) The shared memory is a contiguous block of memory. Each measurement takes up a subset of this contiguous block. This command returns the offset (in bytes) into the shared memory for the most recently added parameter. The offset is a number that specifies the starting index (in bytes) of the data. This query can be sent after sending SYST:DATA:MEM:ADD. |
|
Parameters |
None |
Examples |
|
Return Type |
Numeric |
Not Applicable |
SYSTem:DATA:MEMory:NAME?
(Read only) Returns a unique, auto-generated name that can be used in the COMMIt command. By using this generated name, a client can be sure not to conflict with any other used shared memory regions. |
|
Parameters |
None |
Examples |
|
Return Type |
String |
Not Applicable |
SYSTem:DATA:MEMory:COMMit <memName>
(Write only) Allocates the memory mapped buffer. |
|
Parameters |
|
<memName> |
String. Name of the memory mapped buffer. This must be a unique name, and cannot conflict with other shared memory buffer names. Use this command in your program when connecting to the shared memory. |
Examples |
|
Query Syntax |
Not Applicable |
Not Applicable |
SYSTem:DATA:MEMory:SIZE?
(Read only) Returns the size of the memory mapped region. Send this immediately after SYST:DATA:MEM:COMMit. The result is the total size (in bytes) of all the measurements in the shared memory region. |
|
Parameters |
None |
Examples |
|
Return Type |
Numeric |
Not Applicable |
SYSTem:DATA:MEMory:CATalog?
(Read only) Returns a list of all the allocated shared memory buffers |
|
Parameters |
None |
Examples |
|
Return Type |
String |
Not Applicable |
SYSTem:DATA:MEMory:RESet
(Write only) Deletes all allocated shared memory buffers |
|
Parameters |
None |
Examples |
|
Query Syntax |
Not Applicable |
Not Applicable |
SYSTem:DATA:MEMory:DELete <memName>
(Write only) Allocates the specified memory mapped buffer. |
|
Parameters |
|
<memName> |
String. Name of the memory mapped buffer. This is the unique name that is used in the COMMit command. |
Examples |
|
Query Syntax |
Not Applicable |
Not Applicable |
Example Program
static void Main(string[] args) { // Connect to the hislip VISA address of localhost ResourceManager resourceManager = new ResourceManager(); FormattedIO488 formattedIO = new FormattedIO488(); formattedIO.IO = (IMessage)resourceManager.Open("TCPIP0::localhost::hislip0::INSTR");
// Destroy all measurements and add a window formattedIO.WriteString("SYST:FPR\n"); formattedIO.WriteString("DISP:WIND:STAT 1");
// initialize memory mapped structures in VNA formattedIO.WriteString("SYST:DATA:MEM:INIT\n");
// Create 4 SParameters string[] parameters = new string[] { "S11", "S21", "S12", "S22" }; int[] offsets_for_complex_data = new int[parameters.Length]; int[] offsets_for_formatted_data = new int[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { // Create a new parameter formattedIO.WriteString("CALC:PAR:DEF '" + parameters[i] + "'," + parameters[i]); formattedIO.WriteString("DISP:WIND:TRAC" + (i + 1).ToString() + ":FEED '" + parameters[i] + "'");
// Configure a new section of the memory map to monitor the complex data of this parameter formattedIO.WriteString("SYST:DATA:MEM:ADD '1:" + (i + 1).ToString() + ":SDATA:201'"); // add parameter to memory mapped formattedIO.WriteString("SYST:DATA:MEM:OFFSet?"); offsets_for_complex_data[i] = int.Parse(formattedIO.ReadString());
// Configure a new section of the memory map to monitor the formatted data of this parameter formattedIO.WriteString("SYST:DATA:MEM:ADD '1:" + (i + 1).ToString() + ":FDATA:201'"); // add parameter to memory mapped formattedIO.WriteString("SYST:DATA:MEM:OFFSet?"); offsets_for_formatted_data[i] = int.Parse(formattedIO.ReadString()); }
// Tell the VNA to allocate the memory map. Name it "VNA_MemoryMap" formattedIO.WriteString("SYST:DATA:MEM:COMM 'VNA_MemoryMap'");
// Query the size of the memory map formattedIO.WriteString("SYST:DATA:MEM:SIZE?"); int size = int.Parse(formattedIO.ReadString());
// Create the memory map in C#. This requires .NET 4.5 framework MemoryMappedFile mappedFile = MemoryMappedFile.CreateOrOpen("VNA_MemoryMap", size); MemoryMappedViewAccessor mappedFileView = mappedFile.CreateViewAccessor();
// Trigger a single sweep, and wait for it to complete formattedIO.WriteString("SENS:SWE:MODE SING"); formattedIO.WriteString("*OPC?"); formattedIO.ReadString();
// Allocate buffers to hold the output data float[][] complexData = new float[parameters.Length][]; for (int i = 0; i < complexData.Length; i++) { complexData[i] = new float[402]; }
float[][] formattedData = new float[parameters.Length][]; for (int i = 0; i < formattedData.Length; i++) { formattedData[i] = new float[201]; }
// Copy the data from the memory map into the output buffers // These copy the data from the in-process memory map. // This runs very fast - and is just a "memcpy" under the hood for (int i = 0; i < parameters.Length; i++) { ReadBytes(mappedFileView,offsets_for_complex_data[i],402,complexData[i]); ReadBytes(mappedFileView, offsets_for_formatted_data[i], 201, formattedData[i]); }
// Output some data to show that it worked System.Console.WriteLine(complexData[0][0].ToString()); // Output first point of S11 in complex System.Console.WriteLine(formattedData[3][200].ToString()); // Output last point of S22 as formatted }
static public unsafe void ReadBytes(MemoryMappedViewAccessor mappedFileView, int offset, int num, float[] arr) { // This is equivalent to: // //m_mappedFileView.ReadArray<float>(m_sharedMemoryOffsets[i-1], complexArray, 0, points*2); // But, using this "unsafe" code is 30 times faster. 100usec versus 3ms byte* ptr = (byte*)0; mappedFileView.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); System.Runtime.InteropServices.Marshal.Copy(IntPtr.Add(new IntPtr(ptr), offset), arr, 0, num); mappedFileView.SafeMemoryMappedViewHandle.ReleasePointer(); }
}
|