PXI Shared Memory Data Transfer

M9370A / M9371A / M9372A / M9374A / M9375A

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

  1. Send SYST:DATA:MEM:INITialize. This tells the VNA to prepare for the creation of shared memory.

  2. 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.

  3. 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.

  4. 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.

See C# example


SYSTem: DATA:MEMory Commands


Controls Shared Memory.

The commands are listed in order of recommended use.

SYSTem:DATA:MEMory:

INITialize

ADD

OFFSet?

NAME?

COMMit

SIZE?

DELete

RESet

 

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

See an example program

Query Syntax

Not Applicable

Default

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:

  • <Ch> - Channel number of the measurement to share memory. Use SYST:ACTive:CHAN to return the active channel number. Use SYST:CHAN:CAT? to return the channel numbers in use.
  • <MeasNum> - Measurement (Tr) number to share memory. Use CALC<ch>:PAR:MNUM? just after the trace is created to read the measurement number. See also: Referring to Traces, Measurements, Channels, and Windows Using SCPI.
  • <dataFormat> - Choose from:
    • SDATA - Complex measurement data.

      • Reads data from Apply Error Terms (access point 1). Returns TWO numbers per data point. Corrected data is returned when correction is ON. Uncorrected data is returned when correction is OFF.

    • FDATA - Formatted measurement data to or from Data Access Map location Display (access point 2).

      • Corrected data is returned when correction is ON.

      • Uncorrected data is returned when correction is OFF.

      • Returns one number per data point for all other formats.

      • Format of the read data is same as the displayed format.

  • <numPoints> - Number of data points in the measurement trace.

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.

See an example program

Query Syntax

Not Applicable

Default

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

See an example program

Return Type

Numeric

Default

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

See an example program

Return Type

String

Default

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.

See SYSTem:DATA:MEMory:NAME?

Examples

See an example program

Query Syntax

Not Applicable

Default

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

See an example program

Return Type

Numeric

Default

Not Applicable


SYSTem:DATA:MEMory:CATalog?

(Read only) Returns a list of all the allocated shared memory buffers

Parameters

None

Examples

See an example program

Return Type

String

Default

Not Applicable


SYSTem:DATA:MEMory:RESet

(Write only) Deletes all allocated shared memory buffers

Parameters

None

Examples

See an example program

Query Syntax

Not Applicable

Default

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

See an example program

Query Syntax

Not Applicable

Default

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();

    }

 

}