RSLogix5000. How to write Expression to find Greater Than other values

Please feel free to offer suggestions for streamlining and i will try them out. If for no other reason but to learn more useful coding tricks.


This is the one of the most complex data models that can be applied to this problem, which results in a lot of code. Asimpler data model e.g. vector averaging/summing, would simplify the code greatly, and avoid the result (SSE + SSW) / 2 = North instead of = South.


I am pretty sure the code is averaging over the past 9 minutes, not 10 minutes; reordering the FFU branch, conditional on [EQU control.POS control.LEN], to execute before the FFL branch would fix that. Also, if you do that, then the FFU and FFL feed rungs should not need the XIO/XIC tests of control.EN.

...i really went to town on the arrays...


We are only interested in one wind direction at any given time; put the directional booleans into an array, then many of the rungs-replicated-sixteen-times go away.
 
Yeah, I tend to write compact code that does a lot in a few instructions. For example, my two rungs 0003 and 0004 with seven instructions in LAD 6 (Ladder Subroutine 6) do pretty much the same task as your roughly two dozen rungs 29 through 53 with ~eighty instructions. Also some of the comments in my initial ten one-minute dominants approach were not rewritten in the latter ten-minute dominant approach.

But in this case there is nothing that is hard to understand or in any way obfuscated; the main difference between my code and yours is the underlying data model, which means my code much less to do.

RSLogix 500 was, I think, the precursor to Logix 5000. So if you can read the latter you can read the former. All of the instructions I used in 500 are either identical or nearly so to those for 5k. Also, you should ignore LAD 4, which emulates variable wind direction over time via a pseudo-random algorithm so I can test the rest of the code.

The main difference is the data ("variables") in 500 are "file-based," and the data in 5k are tag-based. In 500, if you look at the green symbols above each instruction, associated with the output of that instruction, you can usually think of those symbols as tag names.

So, where a 5000 program might have a Boolean variable (tag) with the name OSR_Trig_FFL_Wind_Dominant, 500 would need to use the boolean data file, B3, and bit B3:2/1 would be designated to represent the quantity OSR_Trig_FFL_Wind_Dominant (Boolean Data file 3, Word at offset 2, Bit-in-word at offset 1). The other bits in B3 would be available for other purposes, assigned by the programmer. For an INT, I use the 16-bit Integer files N7:

  • N7:0 (symbol COMPASS_POINT_SAMPLE) is the most-recently sampled wind direction
    • updated every 2.4s
      • = 2400ms, or
      • 25 times per minute, and
      • 250 times over ten minutes
    • value of 0 for North, of 1 for NNE, ..., of 8 for South, ..., of 15 for NNW
  • N7:1 (symbol 10MIN_DOMINANT) is the dominant wind direction (0-15) over the past 10 minutes
  • N7:2 (symbol LOOP_INDEX) is the step counter for the loop.
  • N254 is the Data File, with 251 elements, that is the FIFO of the past ten minutes of wind direction samples (250 values from N7:0)
    • N254:0 is the first value in the FIFO, so it is the oldest sample and the one that is being removed (First Out)
    • N254:250 is the last value in the FIFO, so it is the newest sample and the one that is being added (First In)
  • N253 is the Data File, with 16 elements, that is the array of the counts of each wind direction over the past ten minutes
    • N253:0 is the count of 0s (North samples) in FIFO N254
    • N253:1 is the count of 1s (NNE samples) in FIFO N254
    • ...
    • N253:8 is the count of 8s (South samples) in FIFO N254
    • ...
    • N253:15 is the count of 15s (NNW samples) in FIFO N254
    • N253:[N7:1] is the largest value in N253, and is at the index (offset from N253:0) of N7:1, i.e. the count of the dominant wind direction (in N253) over the past ten minutes.
There are no arrays by name in 500 per se, but a single Data File, or contiguous run of elements in a data file, can be used as a de facto array, and enclosing an INT in square brackets (cf. N253:[N7:1] above) in 500 yields more or less equivalent syntax to array_tag in 5k.

Thank you for details. Will make interpreting the code much less impossible for me.

Will revisit.
 
I am pretty sure the code is averaging over the past 9 minutes, not 10 minutes; reordering the FFU branch, conditional on [EQU control.POS control.LEN], to execute before the FFL branch would fix that. Also, if you do that, then the FFU and FFL feed rungs should not need the XIO/XIC tests of control.EN.

Thank you DB.

Averaging over 9 min not 10 min. I agree. I have written the code based on a youtube video but i was always suspicious that it is averaging over 1 less value because the last value is always 0. I just never understood how to fix it. I would like to try your suggestion, but I do not really understand how i should reorder the branches. Is it possible to get one of your sketches showing how it would look?
 
We are only interested in one wind direction at any given time; put the directional booleans into an array, then many of the rungs-replicated-sixteen-times go away.

The booleans i actually need to keep. I created them then created the array replicas because in the array, i am not able to use different tag names that identify the wind directions. It makes it easier for me to keep up with what i am doing if i have a descriptor, (eg. wind direction) associated with the tag name.
 
This is the one of the most complex data models that can be applied to this problem, which results in a lot of code. Asimpler data model e.g. vector averaging/summing, would simplify the code greatly, and avoid the result (SSE + SSW) / 2 = North instead of = South.

Thanks DB. I would like to try out the vector averaging/summing method you described. Can you please elaborate a little more? (i do not know what vector averaging/summing is). Maybe a sketch of how the code would look?
 
Thanks DB. I would like to try out the vector averaging/summing method you described. Can you please elaborate a little more? (i do not know what vector averaging/summing is). Maybe a sketch of how the code would look?

A vector is a quantity comprising two parts: a magnitude and a direction. For now let's assume all magnitudes are the same non-zero value (e.g. 1 or 1000 or 32767).

In the Dreaded ASCII Graphics below, imagine the blue and red vectors (on the right) point to the direction the wind is coming from, SSE and SSW, over two successive samples (not the wind velocities themselves, which have the arrows pointing in the opposite directions).

The black vector (on the left) is the sum of the red and blue vectors, which is the result of putting them nose to tail i.e. the nose of the first SSE vector is at the tail of second SSW vector, and drawing a vector from the tail of the first (SSE) to the head of the second (SSW). One way to think about it is to consider how a massless balloon would move with the wind. When the first SSE sample occurs, the balloon would move NNW; when the second SSW sample occurs, the balloon would move NNE; the net movement of the balloon over the two samples would be North, because the mean wind was from the South (assume constant wind speed for all samples).

If we expand that concept to sum 300 sample vectors over ten minutes, the direction of the summed result will be a representation of the overall* wind direction during that period.

* "overall wind direction" is probably a better phrase than "mean wind direction" here.

Code:
[COLOR=Red][B]          [COLOR=Black]|[/COLOR] [COLOR=Blue]\[/COLOR][/B][/COLOR]
[COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]|[/COLOR][/B][/COLOR]  [COLOR=blue]\[/COLOR][/B][/COLOR]
[COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]|[/COLOR][/B][/COLOR]  [COLOR=blue] \[/COLOR]
[/B][/COLOR][COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]|[/COLOR][/B][/COLOR]  [COLOR=blue]  \ [/COLOR] [/B]   [COLOR=blue]<- SSE sample vector[/COLOR][/COLOR]
[COLOR=Red][B][COLOR=Red][B][COLOR=black]vector   [/COLOR] [COLOR=Black]|[/COLOR][/B][/COLOR]    [COLOR=Red][COLOR=Blue]_\|[/COLOR][/COLOR][/B][/COLOR]
[COLOR=Red][B][COLOR=Red][B][COLOR=Black]   sum ->[/COLOR] [COLOR=Black]|[/COLOR][/B][/COLOR]     /[/B][/COLOR]
[COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]|[/COLOR][/B][/COLOR]    /[/B][/COLOR]
[COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]|[/COLOR][/B][/COLOR]   /[/B][/COLOR]
[COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]|[/COLOR][/B][/COLOR]  /       [/B]<- SSW sample vector[/COLOR]
[COLOR=Red][B][COLOR=Red][B]          [COLOR=Black]v[/COLOR][/B][/COLOR]|/_[/B][/COLOR]
The individual vectors can be further broken down into a sum of orthogonal components; see DAG below. The SSE sample vector (blue) has a North-South (black, positive to the North, negative to the South) component and an East-West component (red, positive to the East, negative to the West).

Code:
[COLOR=Red][B][COLOR=Black]+[/COLOR][/B][/COLOR]
   [COLOR=Red][B][COLOR=Black]|[/COLOR][COLOR=Blue]\[/COLOR][/B][/COLOR]
   [COLOR=Red][B][COLOR=Red][B][COLOR=Black]|[/COLOR][/B][/COLOR] [COLOR=blue]\[/COLOR][/B][/COLOR]
   [COLOR=Red][B][COLOR=Red][B][COLOR=Black]|[/COLOR][/B][/COLOR] [COLOR=blue] \[/COLOR]
   [/B][/COLOR][COLOR=Red][B][COLOR=Red][B][COLOR=black]o[/COLOR][/B][/COLOR] [COLOR=blue]   \ [/COLOR] [/B]   [COLOR=blue]<- SSE sample vector; + are tail[/COLOR][/COLOR]
   [COLOR=Red][B][B]+[/B]-[COLOR=Red]---o[/COLOR][/B][/COLOR]
So we can say a SSE vector of magnitude 1 comprises two components: -0.924 North; +0.382 East; in shorthand [-0.924,+0.382]. And a SSW vector of magnitude 1 comprises similar components: -0.824 North; -0.382 East; [-0.924,-0.382]. If we sum those Northerly components separately from the Easterly components,
[-0.924,+0.382] + [-0.924,-0.382]
= [(-0.924 + -0.924), (+0.382 + -0.382)]
= [-1.848, 0.000]
we get a pair of components that represents the sum of the SSE and SSW vectors: -1.848 North; 0.000 East; [-1.848, -0.000].

Since the sum has no net Easterly component, and the Northerly component is negative, the summed vector is pointing South.


It should be intuitive to extend this to three or 300 vector samples; keeping the 300 NS sampled components in one FIFO and the 300 EW sampled components in another FIFO, then adding the newest sample components and subtracting the oldest sample from two running sums of the components as each new sample comes in.

The only thing left is to determine the wind direction of the current 300-vector sum e.g. [-273.123, +65.456]. We could use arc tangent to get a numeric angle and select the compass point based on which range that angle falls into e.g. in degrees, 168.75 to 146.25 is SSE. Or, since we have pre-calculated the components for each compass point, we can take the dot (a.k.a. scalar a.k.a. inner) product of each compass point's vector with the summed vector, and the compass point's product that is the most positive is the closest compass point to that wind direction.

The dot product of two vectors, [A,B] . [c,d] is the quantity (A*c + B*d); note that that result is a scalar i.e. not a vector. E.g. if our summed vector is [-273.123, +65.456], then it should be obvious that the closest compass point will be either S or SSE:

  • South: [-273.123, +65.456] . [-1.0, 0.0] = [-273.123*-1.0, +65.456*0.0] = +273.123 + 0.0 = +273.123
  • SSE: [-273.123, +65.456] . [-0.924,+0.382] = [-273.123*-0.924, +65.456*+0.382] = +252.365 + 25.004 = +277.370
277.370 is greater than 273.123, the overall direction would be SSE.

For the purposes of this exercise it would be adequate to perform all of these calculations using DINTs instead of REALs, since the scaling is constant.
 
A vector is a quantity comprising two parts: a magnitude and a direction. For now let's assume all magnitudes are the same non-zero value (e.g. 1 or 1000 or 32767).

.......

For the purposes of this exercise it would be adequate to perform all of these calculations using DINTs instead of REALs, since the scaling is constant.

that actually makes good sense. Thank you DB for going to the trouble of explaining it with diagrams.

Will keep this method in mind next time.

I would usually give it a try. it may be difficult to make work with my anemometer. I mentioned in an earlier post that my unit is 30 years old. It is soooo out of calibration. For example, wind between S and SW is measured between 26650 and 26900. While the wind between E and SE is between 0 and 13000.

I have compensated by simply defining the range the wind directions are determined. I might be able to use the vector method if i used a multiplier for each wind range to force the measurements to a linear scale.
 
Thanks DB. This makes sense. This is another code i copied from youtube. I was happy enough to get it to work when i did, (it took hours), so i did not poke at the code too much afterwards.

But now that you explained that the chunk in the middle was used as a "trigger" to load and unload. But i am using the GRT instruction as the trigger for my case, then i do not need a second trigger.

i will clean up my code to reflect your suggestion. Will post an update when i am done.
 
I would usually give it a try. it may be difficult to make work with my anemometer. I mentioned in an earlier post that my unit is 30 years old. It is soooo out of calibration. For example, wind between S and SW is measured between 26650 and 26900. While the wind between E and SE is between 0 and 13000.

I have compensated by simply defining the range the wind directions are determined. I might be able to use the vector method if i used a multiplier for each wind range to force the measurements to a linear scale.

What you want to do is have an array of easterly components (call it X) and an array of northerly components (call it Y), with a value for each compass point.

Compass points are 0 to 15, e.g. North=0 then clockwise to NNW=15. The quickest way to get the compass point is with a binary search using an array of the breakpoints (cf. here), but this is a small problem so hardcoding the range like you did is perfectly adequate.

So North is {X[0]:0,Y[0]:1000}, East is {X[4]:1000,Y[4]:0}, NE is {X[2]:707,Y[2]:707}, ESE is {X[10]:924,Y[10]:-382}. The sum of the squares of the X and Y components (Pythagorean Formula) should be close to the same value for all compass points (e.g. one million if North=[0,1000]. The scaling is largely irrelevant, but bigger numbers will have better accuracy.

So each time you get a new reading, you add the X and Y components for that reading to the Xsum and Ysum components, respectively, and subtract the oldest readings X and Y components from the Xsum and Ysum components. Then do the sixteen dot products, one with each compass point from X and Y, and take the index (compass point, 0-15) of the dot product with the greatest value as the dominant wind direction.
 
Last edited:
wind between S and SW is measured between 26650 and 26900. While the wind between E and SE is between 0 and 13000.

Here is a simple binary search routine to convert values over the range ±32650 to the compass points 0-15 (0=West; 1=WNW; 2=NW, ... 15=WSW). It assumes you preload the lowest (breakpoint) value for each compass point into the array wdbk[0..15], and the INT wind16bit is the raw value (±32650) from the sensor that reads the wind vane position. The winddir tag is initialized to 0, which initializes the invariant condition
wdbk[winddir] <= wind16bit
as true (wdbk[winddir] = windbk[0] = -32768, and then optionally increases the value of winddir in each of four sequentially halved steps (8, 4, 2, 1) to maintain the invariant condition. At the end of those four steps, winddir will be the compass point closest to wind16bit, i.e. such that
wdbk[winddir] <= wind16bit < windbk[winddir+1]
That is the essence of a binary search. This could be done in a loop, but for a search space of 16* elements, this is good enough.

* actually 17 i.e. [0..15,0], but the first rung deals with the wraparound.
PXL_20220730_205040487.png
To implement this, I put random, monotonically increasing values in the breakpoints array (windbk tag), as sketched in the compass rose in the next image; you would need to load the actual values, of course.
Untitled.png
The last image shows my test harness, generating random values for wind16bit between -32768 and +32767, at around 4Hz. The four most recent wind16bit samples (±32650) are stored in smps[0..3,0]; the corresponding compass points are in smps[0..3,1].
PXL_20220730_204929789.png
 
Here is the whole meghilla: the tag dominantdir approximates the direction (compass point) to the release point of a zero-mass balloon released ~ten minutes ago, which balloon is arriving just now at the measuring wind vane, using an ideal model of an infinite, uniform, and instantaneous wind flow field with a constant wind speed; it would not be difficult nor fundamentally different to incorporate varying wind speeds into the model.

This code uses an synthetic model (Routine _01_emulate_rawwind) for raw wind direction input values that heavily oversamples easterly winds, using a random number generator. That routine should be removed and replaced with the raw inputs from an actual wind vane.

The array tags wdbk[16] and XYdirs[16,2] need to be manually filled in per the calibration of the actual wind vane.

It's 2-3 dozen rungs/branches, and four dozen instructions. I find it simple, but then I am very comfortable with vectors and dot products; I hope it's not too opaque to others.


The Main program and the six subroutines each fit on one page. The _05dominant_10_winddir subroutine that use the vector dot product determine the dominant wind direction from the past 10-minute vector sum of samples is but four branches and ten instructions, although it does use a CPT (compute) instruction which would require another half-dozen or so atomic instructions to replace.
 
Last edited:
Did anybody check to see how other people are doing this. You know it has been done before.
https://control.com/forums/threads/calculating-an-average-value-of-the-wind-direction.20840/page-2
https://www.ndbc.noaa.gov/wndav.sht...wind direction is derived from "arctan(u/v)."
https://support.industry.siemens.com/forum/ww/en/posts/calculate-average-wind-direction/254198
https://www.scadacore.com/2014/12/19/average-wind-direction-and-wind-speed/

I think you guys are getting close to what has been done before when you start talking about adding or averaging vectors. Note that some examples break down the direction into x and y components to do the math. This way they don't need to worry about averaging 355 and 5 degrees and getting 180 degrees.
 
getting close to what has been done before


"Close" to what has been done before? That is exactly what I have been describing and doing the whole time once we streamlined OP's original frequency-based algorithm (which is good enough, btw; the vector approach is just for fun). OP's original approach dispensed with the (355+5)/2=180 problem several dozen posts ago. Try to keep up.

Oh, and by the way, I have done it before: https://mars.nasa.gov/insight/weather/#InSight-Weather-Graphic
 
Here is another vector-based solution that, instead of binning each ~0.5Hz raw value sample into one of sixteen compass points, converts each raw sample into its actual angular direction (0-360°; 0-2πradian) and its orthogonal vector components. From there the logic is basically the same as the last one, except this one does all its work in REALs, so there will be some accumulating error in the last few decimal places.

winddirection_real_tags.pdf shows the two most interesting arrays used, which arrays are also static:

  • r2a[17,2] contains the piecewise-linear correlation between the raw wind direction (INTs range=±32650*) to angular wind direction (REALs range=0-360).
    • * Because I am using a random number generator, the range here is ±32768/7, and the synthetic correlation oversamples easterly winds again
  • XYdirs[16,2] contains the vector components for the sixteen compass points; it should really be initialized in subroutine _00initialize, but that is left as an exercise for the user.
 

Similar Topics

I am replacing a PLC5 with a 1756-L73 and i am trying to reuse a 1771-DB Basic Module. I am leaving the PLC5 in the chassis setup as RIO adapter...
Replies
7
Views
5,118
Need good example/sample of aoi RSLogix5000 code to read&write through RS232 port on Need good example/sample of aoi RSLogix5000 code to...
Replies
0
Views
1,843
Hey All, As the title states, how do I write a MinorFault generated by an instruction, for example a negative .PRE value was entered for a timer...
Replies
12
Views
2,999
Is it possible to partially restrict the types of communications allowed through a ControlLogix communications module? Ideally I'd like something...
Replies
7
Views
3,325
Hi! So my problem is a little funky, I had Studio 5000 v 24 and 30 installed, but forgot to install RSLogix (which I cannot go without). Is there...
Replies
2
Views
136
Back
Top Bottom