The Next Step in TCP-IP

Several conversations at once

A question came up on the LabVIEW forum the other day about multiple connections, and how hard it was to have two connections transmitting at two different rates.  This surprised me a bit, because I have been doing just that for quite a few years.  The allegation was made also that TCP requires a minimum packet size of 32 bytes. That is also a surprise since I have been doing things contrary to that “rule” for quite a few years.

So, in an attempt to clarify things above and beyond the Beginner’s Guide to TCP/IP, I present this example with a CLIENT and a SERVER.

The SERVER should be run first and listens for four connections on four consecutive ports. It has four data generators, running at different rates.  When data is available, it transmits it over the connection  if there is one, or listens for one if there’s not.

The SERVER uses a re-entrant VI so that the same code can be executing in four instances at one time.  The four instances are given four different intervals. The WAITs do not conflict because of this reentrancy.

The CLIENT initiates four connections to these same ports, and waits for data in four loops.  I did not use reentrant VIs here, because of the connection to the charts.  Although you could pass a reference to the chart to four reentrant subVIs, I chose not to, thinking that the speed would be better with the direct approach.

Given that this is based on the mSec Timer in LabVIEW, I wouldn’t vouch for it’s accuracy as you approach 1000 Hz. But the basic techniques are sound and will work beyond that frequency.

Click here for a downloadable example of four-channel client server communications.

Speed of En Masse Operations

Zip-zap-zowee and swoosh!

Just in case you thought I was kidding in the article on en masse operations, I decided to offer some proof of the speed advantages they can give you.

I used the Timing Template vi to measure the time it takes to multiply an array of DBLs by two, both with a loop, and without.  I set up the timing VI to create an array of 1000 random numbers, and then time the multiply operation.

First, the loop method, where you auto-index every value out of the array, multiply it, and auto-index it back in:

viaLoop

As you can see, this took 7.47 uSec per loop.  Not all that shabby.  But just removing the loop lets the en masse operation do it:

EnMasse

Holy Speed Demon, Batman!  That’s 0.77 uSec or about ONE TENTH the time!

Now, I’m not guaranteeing that all such operations will save you that much time, but if you have a chance to use them, then you should!

It’s easier on you and easier on the hardware!

Keeping your charts up to date

Use your chart to indicate time of day.

LabVIEW charts, out of the box, don’t lend themselves to displaying the actual time of day.  By default they give you 1024 history points and a visible scale of 0-100 so what you see is in terms of sample numbers, having no relation to actual time. This is reasonable, given that the scale is adjustable in so many ways; there’s no way to guess what you want, so it’s up to you to tailor it to fit what you want.

Sometimes it’s useful to display the time of day on your charts, and keep that axis correctly updated as data flows in. For example:

ChartClock

As you can see, the leading edge of the plots coincides with the current time on the clock  (see here for how to do the clock indicator).   As data keeps coming in to this chart, the leading edge will reach the right side and the chart (and the X-axis scale) will scroll to the left, thus always matching the time of day.

Out of the box, a LabVIEW chart has a history of 1024 samples, and an X scale of 0-100 samples.  This scale has nothing to do with time, so if you want it to display the time, then you have to do several things to make it work.  If you have a CLEAR CHART function, triggered by a button or something, then attach this code to that action, otherwise just do it once when you start the data flowing  (Ideally, you should do this as you feed the first sample to the chart, but the few mSec difference that makes probably won’t matter).

ChartProperties

First, you clear the history.  That makes sure the OFFSET number is operating from an empty buffer (Time 0).  If the history buffer contains data, then the OFFSET applies to the end of it, and that’s not what you want.  The constant has to be an empty array of the same type of data as you feed to the chart (cluster of two DBLs in this case).

Next you set the XSCALE.OFFSET to set the OFFSET number to the current time.

Next you set the XSCALE.MULTIPLIER to the time (in sec) between samples that you display.

Next you set the XSCALE.MAXIMUM to now + the duration (in seconds) that you want the chart to show.

Do them in that order, or it could get confusing.  You also can set the CHART HISTORY LENGTH to match the duration / the display period.  For one minute at 2 Hz, that would be 120 samples.  You only need to do that step once – it’s a property that is not programmable.

Enjoy.

An Improved Analog Clock

Sometimes all that digital stuff is just too bland.

A bug undocumented feature of the original analog clock was that the markers on the scale were at intervals of 1.25 seconds, a consequence of LabVIEW preferring to use 4 intervals per major tick, when we silly humans use 5.  As a result, it looked a bit odd.  As I said then, if we’re going for an analog, then let’s go for the analog.

Attempts to coerce LabVIEW into making the scale the way we wanted were not successful; it has a long habit of thinking that four is a nice number and five is just an odd number, so I could not make it work.

So how do we improve it?  Simply disregard the built-in scale and substitute a picture.  After all, we all know where the numbers are on a clock face, if we’ve lined up the 0 and the 12 on our LabVIEW scales at the top of the circle, then all else has to fall into place.

Clock2Pic

Click here to download the example LLB file, in LV 8.0 format. It has the same math VI as before (read the previous post for details), just the clock face is different.

Enjoy.

Operations en Masse

The things that I used to do…

En masse is a French term meaning “as a whole” or “all together”; treating a group of something as a single unit.   LabVIEW has the ability to treat arrays this way, which can greatly reduce your workload. If you come to LabVIEW from a text-based language, it’s easy to miss the capabilities that are right at your fingertips.

For example, if you need to scale a series of readings into percent of the total (a procedure called normalizing),  then you tend to think:

I need to find the total:

  • I need to start with a zero sum     sum = 0.0;
  • I need to loop over every element  for (int i = 0; i < nElements; i++)
  • I need to add this element to the sum   sum += array[i]

Now I need to divide each entry by the total, to get the fraction of the total:

  • for (int i = 0; i < nElements; i++)
  • array[i] /= sum;

Now I need to multiply by 100 to get percentages:

  • for (int i = 0; i < nElements; i++)
  • array[i] *= 100.0;

That’s all well and good, and you could translate that literally into LabVIEW and it will get you the answer you want to see.  But that’s not the LabVIEW way of thinking.

What newcomers often fail to realize is that most primitive numeric functions (the ones with yellowish icons) will accept an array of numbers directly. This goes for basic arithmetic (add,subtract, multiply, divide), comparisons (greater than, less than, MAX/MIN), and many other operations.  It will happily multiply an array of numbers by a single scaler number, to produce an array of numbers.

This has great power to reduce the work that you do as the programmer. Consider the literal translation of the above code:

EnMasse-11

If that’s as good as it gets then why should I go with LabVIEW?

Well, it does get better.  There is a function in the numeric palette called ADD ARRAY ELEMENTS.  If we replace the entire first loop with this function, then we get to this:

EnMasse-21

Now for the en masse parts: You can replace the entire second loop with a single operation as well:

EnMasse-31

Any guesses what we can do with the third loop?    Yes, that’s right:

EnMasse-41

Now you have SO much more room to add comments about what you’re doing!

Now THIS is what makes you more productive in LabVIEW than in C; your chances for error are far less when you let the en masse operators handle the details, and you don’t even have to think about the details.

But be aware of what’s going on, however; there is no magic here.  Under the hood there is still a loop somewhere.  It’s now hidden somewhat; it’s not as obvious, but the work is still being done.  Don’t let the simplicity obscure the real processing that’s going on.

Here is an example of the normalizing function in use, from the real LabVIEW example examples\general\graphs\charts.llb\Draw Stacked Graph.vi (in LabVIEW 8.6. anyway).

EnMasse-5

This amounts to the same as our last part above.  In the example, the array given contains five elements and this is executed only once, so efficiency is not a concern.

But consider if the array was 10,000 elements. Don’t forget that the first operation is doing 10,000 divide operations, and the second is doing 10,000 multiplications.  Can you improve things?

Well, certainly! What you have to realize is that, by the associative property of numbers, (X / sum) * 100 is equal to (100 / sum) * X.  You also have to realize that 100 / sum, in this context, is a constant, and therefore needs to be calculated only once.  In effect, you are dividing by sum and multiplying by 100, but you are doing it 10,000 times!

With any luck at all, you get the same answer every time, so you only need to do it once:

EnMasse-6

THIS is why we use LabVIEW!

NOTE:  En masse is my term for this feature, it is not an official LabVIEW term.

Delays, delays, delays

Can’t you signals just work together?

Usually, in a data acquisition program,  all the signals you measure are “live”, meaning they represent the current conditions at the time they are sampled. However, in some cases you might have some signals which are not live, but delayed. For example, suppose you’re measuring engine operation, and you have gas analyzers sampling the exhaust airstream. These analyzers conduct the gas from the measurement point in the airflow system around the engine to the analyzer mechanism itself. This gas flows through the sampling tubing at a specific rate, and therefore arrives at the sample point at a specific time AFTER it left the main airstream. These delays might be multiple seconds in duration.  There could be other reasons for this delay, such as an echo-measuring device, or the mechanical response time of some piece of hardware.

For analysis purposes, you want to look at the engine holistically and see causes and effects. If the speed changed at a particular point in time, you want to see the CO concentration change as a consequence. But unless you do something to compensate, the change in gas concentration will lag far behind the change in speed that caused it in graphs and tables, making it difficult to judge consequences.

To arrange the signals back into time-synchronous alignment, the solution is to think of ALL signals has having TWO delay lines: one physical, and one logical.  In our example case, the physical part is the gas piping conducting the gas. The logical part is completely within the  DAS software. If EVERY channel has a physical delay time (even if it’s zero), and if EVERY channel also has a logical delay time (even if it’s zero), and if EVERY channel has the sum of the physical delay time and the logical delay time equal to a constant, then the signals coming out of the logical delay lines will be time-aligned.

Example

For example, consider three channels: Channel A records data “live”, i.e. no delay. Channel B has a delay of 3 seconds. Channel C has a delay of 5 seconds.

We want to make the entire chain (physical + logical) the same length for all channels, so we pick the longest delay (5 sec) and make that our total delay time. For channel A, which has no physical delay, we have to have a logical delay of 5 sec. For channel B, which has a physical delay of 3 sec, we add a logical delay of 2 sec for a total of 5. For channel C, which has a physical delay of 5 seconds, we have a logical delay of zero. 5+0 = 3+2 = 0+5, so every channel has the same delay, when we count both physical and logical.

The logical delay is implemented in queues with every channel having its own queue. Every sample received goes into a queue belonging to that channel; and we pull out one sample from the end of the queue to be recorded. Since one goes in and one comes out, the length of the queue never changes. The initial length of the queue corresponds to the delay time we need for a particular channel. If a queue is empty (of zero length), then the sample we put in and the sample we take out are the same sample. If the queue is of length 10, then the sample we get out was taken 10 sample times ago.

At configuration time, the queues are set up, and populated with zero values to set their length according to how much delay is required. After that, the sample process means putting a sample into a queue and taking one out for use.  Every channel operates the same when actually sampling, it’s only the configuration where they differ.

Note that if you are recording a TIMESTAMP value, then the TIMESTAMP signal requires its own queue as well, so that it comes out in alignment with the signals. Of course this means that the data does not become usable until all the zeroes have been flushed out of the queues. That will happen when the longest delay time has expired. We can judge this by putting a RECORD signal into its own queue. We don’t actually record data coming out of the queues until the RECORD signal coming out is true.

You can keep the data flowing into (and out of) these queues all the time.  When you want to start recording, set the RECORD flag TRUE. When you want to stop recording, set the RECORD flag FALSE.  Pay attention to the value out of the RECORD flag’s queue, and act appropriately. (Don’t act on the RECORD flag itself, act on the delayed RECORD flag).

This also means that when the test is over, there is still data remaining in the queues. We have to keep recording until the valuable data has worked it’s way through the queues, meaning 5 seconds after the last sample (in the example case).

    Affiliations

    • TI Alliance

    Calendar

    February 2012
    M T W T F S S
    « Mar    
     12345
    6789101112
    13141516171819
    20212223242526
    272829  

Testimonial  Contact Info

207-790-0949

1-877-676-8175 (toll-free)

Fax: 1-815-572-8269

General questions:

Sales@Culverson.com

Accounts/billing questions:

Accounts@Culverson.com


Logo