LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Variables in block diagrams only

Why can I not create a Local Variable of some kind that is not attatched to a control on some front panel?

I am trying to oversample a signal I'm aquiring from hardware average my oversampled data into what I want to sample.  So if I want to record data at 1 samples/sec, I am actually aquiring the data at say 10 samples/sec and have those 10 samples averaged into 1 sample.  This is something I can do if I'm using DAQmx hardware through the Measurement and Automation, I know... What I'm needing this for is non DAQmx hardware, where I have to do all my aquiring logic within LabVIEW.

So... I have an outer loop running at my 10 samples/sec.  There is a running average on those 10 samples/sec within this outer loop.  Every 10th sample I want to record the output of my running average with a conditional block within the 10 sample/sec block.  At the end of the event I want to save my recorded samples to a file.  The only way I can get this to work is to have an array of recorded values in my front panel and create a local variable passing those values to the save function at the end of my program.  I don't like this as it adds more clutter to the front panel of my application.  I think that the block diagram should be used to support the controls of the front panel, not the other way around....

-nickerbocker
0 Kudos
Message 1 of 10
(4,276 Views)
Why not just save the values to a shift register? You shouldn't have to use a local variable at all.
0 Kudos
Message 2 of 10
(4,271 Views)
Yes, shift registers are typically the best solution.
 
I prefer a simple demo VI of what you are doing instead of a confusing mutiparagraph description. Please attach a simplified demo version of what you are doing and we show you a better way. 🙂 You definitely don't need any locals or globals!
 
(If you have LabVIEW 8, there is also the shared variable, which has not FP item, but it still would inappropriate for your problem here).
0 Kudos
Message 3 of 10
(4,267 Views)
Sorry guys Smiley Indifferent

I'm still learning about how to encapsulate things properly in LabVIEW.  Part of the difficulties that I'm having is that my hardware is so closely tied to my LabVIEW program that it is difficult for me to build an application on my development machine because my test runs have to be with the hardware present.  I need to make dummy Hardware VI's that simulate my hardware responce and I can replace with my hardware driver VI's when I build my application and run it on the actual production computer.

Ok... so here is what I have come up with:


This is my data collector sub vi.  I don't like putting shift registers in my main program, even though my main program is usually a mess anyway, shift registers just add to the chaos.  I would really prefer to have something like local variables as I believe they look a LOT cleaner then shift registers and are a lot clearer in functionality.  But working with what I have available and your suggestions this what I've come up with.

This solves a problem in my main program because now the "indexing" is taken care of in this data collector sub vi.  Before, the indexing was happening on the exit of my main program loop which didn't make sense becuase it would record 10 samples of the same values and my data file would have 10x more samples then needed.

Here is my main program loop, which I'm still having some problems with:



I've had to use Paint.NET to annotate some parts.  So my outter loop is running at some speed.  The DAQmx read outputs into my running average vi which is then input into my conditional.  Now I need to get the control of my conditional block worked out.  The most obvious solution to me is to have the timer output go into a modulus function that checks for multiples of my sample rate.  In the case of 1 sample per second it would fire my conditional at 1000ms, 2000ms, 3000ms...etc.  What worries me about this is I'm afraid of missing a value if my ms counter misses a beat that I'm absolutely looking for.  Say it lands on 1001ms at one part of the loop and so my conditional doesn't fire or something like that?  Do I need to worry about this if it is some evenly divided number from my outter loop that I'm looking for?

Sorry...this is all very difficult to explain and if you've made this far I thank you very much for your time.

-Nickerbocker
0 Kudos
Message 4 of 10
(4,263 Views)
For the first comment about the clutter of shift registers: Use the feedback node. It essentially works the same as a shift register, if you do not need the data outside the loop. It only occupies a small amount of diagram space.

Conditional timing. You are right to be concerned about exact equality testing in such situations. I use a shift register and a case structure. The shift register contains the "end time" for the current conditional. Then compare the current tick count to the "end time." If it is greater or equal then run the conditional case and create a new "end time" = current tick count + interval (1000 ms). The Greater than test assures that the conditional occurs as soon as possible after the specified interval. The shift register starts the next interval from the current time so the duration of the next interval does not depend on the duration of the previous one.

If the conditional code may take more time than the desired minimum loop time of the DAQ loop, consider moving the conditional code to a parallel loop, with the data passed via queues, functional globals, or shared variables.

Lynn
0 Kudos
Message 5 of 10
(4,255 Views)
Ok.. I'm confussed.  On the other right side of my conditional block I now how a terminal that isn't filled during the false condition.  What do I route on the false condition to that terminal?  Really, the output of my Data Collection vi does not need to have a wire through the oversampling portion of my code, just to the save routine.

Any ideas?  Does anything that I'm doing make any sense at all?  Honestly, I'm an experienced code writter and I feel like my LabVIEW drawn code is getting out of hand here.  Do I just need to keep practicing... is there a book on advanced G programming that I should pick up and read that covers these kinds of topics?  Can someone maybe explain how they would approach the same problem?

Thanks again for your time and input.

-Nickerbocker
0 Kudos
Message 6 of 10
(4,237 Views)

Here again, a shift register is the answer. A case statement must must have an output from each case. In can be data or you can righ click on the output tunnel and select "Use Default". In you case, the default (an empty array) won't work because you want to keep old values. If you use a shift register and in one case, append new results and in the other case just pass the shift register wire straight through.

By the way, about your earlier comment about shift registers versus local variables. It is always preferable to use wires (i.e. like in a shift register) over locals. It is never considered good programming practice to use a local variable just to make the diagram look "prettier". Every time you use a local, you are creating a copy of the data. Locals can also be the cause of race conditions when one part of the program is writing and another part is reading and you have no control of what is happening when. The LabVIEW Style Guide that is part of the shipping documentation has a lot of good recomendations on on your terminals and wires should be laid out in order to avoid 'spaghetti' code.

Message Edited by Dennis Knutson on 06-08-2006 02:45 PM

0 Kudos
Message 7 of 10
(4,231 Views)
Woohoo!  Got something to work mostly the way I was wanting.  I built this "Hz Trigger" VI:

On the left condition block, the false condition outputs a false to the output, and the right terminal is passed the Last recorded ms line.  The right conditional initializes the shift register.

Here is what I had to do with my main program loop:



Following your direction I added a shift register in order to keep the output of my sampling block current on the false condition.  So everything is working the way I was wanting it (minus a few bugs I still need to work out).

Now, my beaf with the above image (and really LabVIEW) is the following:

1. That isn't a shift register, it is a buffer!  Correct me if I'm wrong.  Calling it something that it isn't is very confussing.  I understand that if you add elements to that buffer, it then becomes a shift register.  But 1 element is a buffer.
2. I have "loops" in my subvi's and on my main block program that are not doing any real looping!  I have to use a run once loop in order to take advantage of these buffering functions. 

So do you understand why this is making me cringe?

I need to work towards encapsulating this idea further now.  I am really big on building lots of small functions to support a larger program.  Maybe it is just my lack of experience, but LabVIEW doesn't seem to be built in a way that promotes this idea.

I am having to work on a lot of earlier work from a previous programmer, and there are a lot of huge chaotic programs that will take me hours to trace.

Ok...my rant is over.  Thank you all for your help!  It has really saved me a lot of banging my head against the wall time.

-nickerbocker

0 Kudos
Message 8 of 10
(4,222 Views)

Here is a sample vi that shows how I deal with having local variables on a block diagram without having to make controls or indicators on the front panel.

Again, it involves a shift register.  Use a cluster to hold all local variables needed, and use unbundle and bundle functions to retreive and store the variables.

You can add any type of variable to the cluster at any time.

- tbob

Inventor of the WORM Global
0 Kudos
Message 9 of 10
(4,209 Views)

You did not have to add a while loop around your case statement. You already had a while loop that you should have added the shift registers to. Without seeing your entire program it's hard to say but what you did might present some problems. For one thing, if you stop your program and re-start it without closing it, the inner most while loop's shift register will retain its last value. If you had put the shift register on the existing while loop, you could have initialized it. I think initializing the new one will defeat the purpose. If you have other loops that only execute once, then there may be a basic design flaw in the program but again, without seeing the entire thing, it's hard to say.

I know that moving from traditional, text based languages can be hard. I had to do it myself but once you're over the initial hump and start thinking about how LabVIEW does it and not how would I do that in c++, I think you'll enjoy it.

0 Kudos
Message 10 of 10
(4,190 Views)