DataPlugins can be used to read almost any file format into DIAdem or LabVIEW. Here are some tips and tricks for creating one in VBScript

April 10, 2007

When channel values aren't interleaved

There are two ways in which binary channel values are commonly written: ABCABCABC... or AAA...BBB...CCC...

In the first case, the writing application alternates between each of the channels. This is a common manner of writing data which comes from a streaming application where all channels have the same sample frequency.

In the second case the writing application wrote all the values of one channel before continuing on to the second channel. Applications which perform one measurement after the other, or applications which do some caching are more likely to write this second kind of channels. As of this writing, the DataPlugins help doesn't address this second use case with an example, so I'm posting an example here.

In the DataPlugin API each binary block represents a single channel or multiple channels interleaved in the first use case above. In order to cover the second use case, create a binary block for each channel, and set its length as the number of channel values.

I devised a simple file format to illustrate this case. This format looks like this:
[file header]
[1st channel header]
[1st channel values]
[2nd channel header]
[2nd channel values]
...
[nth channel header]
[nth channel values]

The file header looks like this:
22 1-byte characters: file format marker
1 4-byte integer: number of channels in the file.

And the channel header looks like this:
10 1-byte characters: channel name
1 4-byte integer: number of values in a channel

The code to read this file starts by reading the file header, and then reads the individual channels:

Sub ReadStore(File)
Dim ChannelCount : ChannelCount = ReadFileHeader(File)
Dim ChannelGroup : Set ChannelGroup = Root.ChannelGroups.Add("ChannelGroup")

Dim i : For i = 1 to ChannelCount
Call ReadChannel(File, ChannelGroup)
Next
End Sub

Reading the file header is fairly straightforward. First we use the file marker to reduce our chances of trying to read a file that the DataPlugin isn't made for (more detailed checks would be possible). Then we read out the number of channels in the file.

Function ReadFileHeader(File)
If Not CheckValidity(File) Then RaiseError "Not a valid Ducks file."
ReadFileHeader = File.GetNextBinaryValue(eI32)
End Function

Function CheckValidity(File)
CheckValidity = False
Dim Identity : Identity = File.GetCharacters(22)
If Identity = "NI example binary file" Then CheckValidity = True
End Function

Once we know how many channels there are we can loop over that number to create those channels. In the channel reader we read out the name of each channel and the number of values it contains. Then we create a block containing one channel to read out the values of each channel. We finish off by moving the file pointer to the beginning of the next channel.

Sub ReadChannel(File, ChannelGroup)
Dim ChannelName : ChannelName = File.GetCharacters(10)
Dim ChannelSize : ChannelSize = File.GetNextBinaryValue(eI32)

Dim Block : Set Block = File.GetBinaryBlock()
Block.BlockLength = ChannelSize

Dim DAChannel : Set DAChannel = Block.Channels.Add(ChannelName, eI32)
Call ChannelGroup.Channels.AddDirectAccessChannel(DAChannel)

File.Position = File.Position + ChannelSize*4
End Sub
One mistake I've made and seen others make is mixing up the units for BlockLength and File.Position. BlockLength contains the number of values in each channel in the block. If there is one channel in the block with 30 values then BlockLength is 30. If there are two such channels BlockLength is still 30. File.Position is in bytes. A block with one 30 value channel of type eI32 would cover 120 bytes out of the file. A block with two such channels would cover 240 bytes.

Acquiring the channel length will be different for each file format. Sometimes it is explicit as in the above example. Sometimes it is implicit, for example, there are n equal-length channels in the file, so divide the file size by n and you have the channel length in bytes. Use the size of the type to convert this to the channel length in number of values.

No comments: