v5.0 / 15 of 23 / 01 sep 99 / gvg
* This chapter discusses some interesting techniques in VEE programming.
* The following sample program demonstrates comparator operation. In this
example, we're comparing an array of integers versus a scalar value. If we
change the scalar value so that all the array values meet the condition, the
PASSED! Message Box pops up. If we change the scalar value so that one or
more array values don't meet the condition -- as the example shows (see
xcmpartr.vee for the source) -- then the FAILED! Message Box pops up,
and the failing array index and its value are displayed as an ordered pair.
Note that if you are attempting to compare two arrays with the Comparator,
they both have to be the same size and shape.
* A user wanted to know the fastest way to perform a bin sort with VEE, and I
went through considerable gyrations to figure it out. They turned out to be
futile, since you can do this with one object. All you have to use is
"AdvMath -> Freq Distribution -> magDist", as follows:
* A user was having trouble figuring out how to write a VEE program that
could obtain the maximum value of a sequence of inputs. This seemed
straightforward, however, through use of the "max()" function, and I wrote
the following program to demonstrate (see xgetmax.vee for the source):
* A user asked us how to perform limit testing under VEE -- that is, how to
determine if a waveform exceeds a particular limit and mark the points of it
that exceed that limit.
As it turns out, this is easy to do. The following program (see
xlimits.vee for the source) takes a sine wave from a Function Generator
and does a limit test on it against a value from a Real Slider. It then
displays the sine wave and limit line on an XY Trace object, with the values
of the sine wave above the limit line marked in bright red:
This real array is also input to a "totSize()" function, which returns the
number of points in the array. This and the value from the Real Slider are
fed to a "ramp()" function, which creates an array with that number of points
but all of whose elements are set to the same input value. This constant
array is driven into the "Trace2" input pin of the XY Trace object and
displayed as a flat blue line.
Finally, a Comparator compares the real array against the constant array. If
any elements of the real array are greater than the corresponding elements of
the constant, they are returned as an array of coordinate pairs through the
"Failures" pin. This array is fed to the "Trace3" input pin of the XY Trace
object and displayed as a set of red diamonds, which blur to appear as thick
red line segments.
* A user had a VEE application that displayed a bitmap in a popup panel.
the user was using it to view data from an external device. This worked
fine, except that the VEE program would continue to show the same bitmap
until it was rerun, even though new bitmaps were generated.
Obviously, VEE was loading the bitmap at prerun (or whatever) and did not
need to load it again during the execution of the program. I wrote the
following small program to test:
The answer was to bring out the "file" data input pin and the "clear" control
input pin on the Picture object:
I originally designed the UserFunction to ping the "clear" pin first and then
load the file name, but this generated a flicker when the object was first
displayed, so I rewired it so that it would clear the Picture object when the
UserFunction was exited, and then reload the bitmap the next time it was
invoked. Other requirements may demand a different usage, but in any case
the program must clear the Picture object and then load it again to allow the
bitmap image to be updated.
* A user called us up with a request: he wanted to use the VEE X vs Y
Plot object to display a sequence of traces -- either one at a time, or as a
cumulative set at once. That is, either he would view trace 1, then trace 2,
then trace 3 ... or trace 1, then trace 1 and 2, then trace 1 and 2 and 3.
This was an interesting puzzle and involved use of the optional "Next Curve"
pin on the X vs Y Plot object. A sample program that illustrates the
principles involved is shown below (see xmltrace.vee for source):
Depending on the state of the Toggle button, the If/Then/Else object will
either drive the "Next Trace" pin or not every time a trace is taken,
controlling whether single or multiple traces are displayed.
Note that the Sequence Out pin on the X vs Y Plot object is fed back to the
"Auto Scale" pin to ensure that the traces are displayed properly each time a
new trace is provided.
* Two of HP's Amsterdam support people got to tinkering and came up with a
way to convert the packed data values returned by the 3852's 44702A
high-speed DVM.
The packed data is returned as 16 bit values, with the bits assigned as:
Now on to obtaining the actual value ... getting the sign is fairly
straightforward:
* An Australian user contacted us with a problem: he was sampling a
sine-type signal with a data-acquisition box and wanted to know how to use
VEE to determine the signal amplitude, DC offset, phase, and frequency from
the data returned.
The first temptation was to use a few simple Formula boxes to do the job.
Given an array of data named "D", the amplitude could be given by:
This was clearly a job for VEE's Fast Fourier Transform (FFT) function,
but the details on exactly how to use the FFT to get the job done are tricky.
This section explains the issues involved.
* The first problem is understanding exactly what an FFT is and what it does.
Most engineers have a simple understanding of the Fourier Transform in
general: that it converts a signal represented as, say, a voltage over a
period of time (a "time domain" signal) into a "spectrum" of sine functions
of different amplitude and phase (a "frequency domain" signal).
This is perfectly accurate as far as it goes, but when you get into dealing
with "discrete" data samples on a computer (instead of simply manipulating
math equations on the chalkboard) then the details get a little murky.
In this case, you must use a Discrete (or "Digital", if you like) Fourier
Transform (DFT). The elementary DFT algorithm is slow, so in practice we
generally use a more complicated but much faster FFT, but as far as the user
is concerned (with one catch that will be discussed at the end of this
article) they are exactly the same thing, and so this discussion will refer
to the DFT.
A DFT has some interesting properties. Let's consider a DFT on a sampled
time domain data set of, say, 256 points, though the following comments apply
to any other number of points just as well:
All the lower values have periods one sample interval longer than this
last value. That is, the element with index 254 has a period of two
sample intervals, that with index 253 has a period of three sample
intervals, all the way down to the element with index 1, which has period
255 times the sample interval.
In reverse terms, the highest frequency is the highest "harmonic", or
integer multiple, of the sine curve represented by the value in the
spectrum with index 1. That means this "base harmonic" has a frequency
1/255th of the highest harmonic.
Note, for curiosity, that the only harmonic values in the spectrum are
those that have periods some multiple of the sample rate. This might seem
odd, but it is inherent in the fact that you are dealing with a sampled
signal: the sampling process only obtained values of the input signal at
the sample intervals, and so the reflection of the input signal into the
frequency domain is governed by the sample rate as well.
Note also that this discussion "talked around" the complex value with
index 0. This is the "zeroth harmonic", which, in plain language, is
just the "DC component" of the signal. It will always have an imaginary
part of 0.
For example, suppose we have our 256 points and they are sampling exactly
four cycles of a sine wave with a peak amplitude of 3. This means that
after we take the DFT of this data, we end up with a set of complex values
that are zero (or effectively zero) everywhere but in element 4, which has
a magnitude of 768! This seems crazy, but the input has 256 samples of a
harmonic with amplitude 3, so 3 * 256 = 768 is the right value.
This is result is actually the way the DFT is defined, and for
applications like image processing it's what you want, but people who
are used to dealing with spectrum analyzers and the like get rather
upset with it -- the sine signal has a value of 3, by Bob, and they expect
to get a value of 3 out of the DFT!
This can actually be obtained in practice by "scaling" the output of the
DFT. All you have to do is divide it by the number of original data
points, which is 256 in this case.
Attempting to sample a signal that is over half the sample rate can result
in bogus harmonic values known as "aliases", and you can't compensate for
them in software, you must have hardware filtering on your
data-acquisition system to ensure that the input signal doesn't
incorporate any frequencies higher than this.
This leads to an awkward question: if the highest harmonic in the complex
value set returned by the DFT has the same frequency as the sampling
frequency -- but you can't really detect any frequencies that are any
higher than half that frequency, then what sense does this highest
harmonic make? In fact, what sense do any of the harmonics that
correspond to sampling any faster than half the sampling rate make?
The answer is that, for purely real time-domain signals (which is all we
will be dealing with here, avoiding the nasty question of what a "complex
signal" is), the spectrum is split in two: the first half consists of the
DC term and all the harmonics to that just below the Nyquist frequency;
the upper half begins with the harmonic with a frequency corresponding to
the Nyquist frequency, while all the following complex values "mirror"
those of the lower half. They have the same magnitudes as the lower half,
though their phase is the negative of their counterparts -- that is, they
are "complex conjugates" of the lower half of the spectrum.
The last value in the set corresponds to the complex conjugate of the
first harmonic; the value before that corresponds to the complex conjugate
of the second harmonic; and so on.
The fact that the DFT algorithm generates two sets of values for each
harmonic component means that it splits the signal power in half; so if
you are scale the complex values, not only do you divide it all by the
number of samples, but you only take the first half of the harmonics, and
multiply them by two.
This means that if you have a DFT of a 256-point time-domain signal, then
what you actually get back is a DC component (element 0 of the complex
value set) plus 128 harmonic values (elements 1:128 of the complex value
set). You can throw away the other 127 values.
In fact, this is exactly what VEE does for its FFT -- it only returns 129
complex values for a 256-value time-domain signal -- a DC value and 128
harmonic values. To scale them, you would have to divide them by the
original number of time-domain samples -- 256 -- and then, because you
only have half the harmonics, multiply all the complex values (except the
DC value) by 2.
Having waded through all this headache, let me summarize briefly:
* So, to extract information from an input signal, you must first scale it.
You can easily do this with the following UserFunction:
* OK, given this, what sort of data can be extracted with it? For the
problem we're supposed to address, we're trying to sort out a sine wave from
sampled data and determine its amplitude, DC offset, phase, and frequency.
The following VEE objects can extract this data from the complex data output
by the ScaleFFT() function above:
Experiments with these tools show them to return highly accurate results --
as long as you have a set of samples that match an integer number of cycles.
If the input contains a fractional number of cycles, it creates an apparent
discontinuity in the signal that adds high-frequency components to the FFT
spectrum, reducing the validity of the conclusions.
The DC offset value tends to remain stable under such variations, as does the
frequency, but the signal amplitude varies considerable, and the phase varies
widely. Further investigation did not resolve these problems and I can only
suggest that the input waveform be trimmed to an integer number of cycles
(using displays with marker inputs) to get the best results.
In principle, use of VEE windowing functions to filter the input data can
help reduce the effects of such discontinuities, but my experiments with them
showed them to be ineffectual in this case. I also discovered that if you
want to get scaling to come out right, you have to multiply the output of the
windowing function by 2 ... this didn't seem right, but I didn't have the
background to investigate further.
* While I said earlier that an FFT was really just a version of the DFT that
was much faster, it does have the peculiarity that it wants to perform
computations on data sets whose number of elements is a integer power of two
(2, 4, 8 , 16, 32, 64 ... ). VEE will use an FFT algorithm if the input data
is this size; if it isn't, it really uses a slower DFT algorithm.
For N values, the time required to perform a DFT is proportional to N^2. For
an FFT, it is proportional to N*LOG(N). For large values of N, the
difference is one of days versus minutes, so you want to make sure your data
is a power of two. All you have to do is add zero elements to the end of
your array. It doesn't really affect the calculation.
The following function, PadZero(), does this for you:
The formula on the bottom then determines if the number of elements in the
input data is exactly the same as 2 raised to the integer power defined in
the first formula. If so, it just passes on the array; if not, it
concatenates the array with an array of zeroes long enough to make the number
of elements the value of 2 raised to the next highest integer power. (Note
how the ramp() function is used to generate this array.)
* A test function (xfftfunc.vee) showing off ScaleFFT() and PadZero() is
available.
* A user handed me an example program in which he was trying to extract a
record field with an equation (roughly) of the form:
As stated, this couldn't work because the formula above was specifying a
literal name as its first half, but expected to obtain a value in the second
(the Formula, of course, really just thinks it's a literal name all the way
through, which is reasonable). However, there is a relatively easy
workaround.
A "Formula" control pin (not a data pin, a control pin) can be added to a
Formula box (and related objects) that allows the programmer to express a
formula externally as a string and shove it in the pin. So, to get this to
work, two objects would be needed, as follows:
Notice that since "Formula" is a control pin, it's asynchronous, and so to
ensure synchronization the sequence-out pin of the first Formula box is wired
to the sequence-in pin of the second.
This technique has obvious potential for elaboration. A sample program is
available in the file xformpin.vee.
[15.1] A SIMPLE VEE COMPARATOR PROGRAM
[15.2] PERFORMING BIN SORTS
[15.3] SEARCHING FOR MAXIMUM VALUES
[15.4] LIMIT TESTING
[15.5] BITMAP DISPLAY UPDATES
[15.6] CONTROLLING PLOT DISPLAYS
[15.7] CONVERTING 3852 HSDVM VALUES
[15.8] SIGNAL PROCESSING, FFTS, & VEE
[15.9] DYNAMICALLY REPROGRAMMING VEE FORMULA OBJECTS
[15.1] A SIMPLE VEE COMPARATOR PROGRAM
+---------+
| Integer |
+---------+ +-------------------------------+
| 0000: 0 | | Comparator |
| 0001: 1 | +---+ +------+-------------+----------+
| 0002: 2 | | 2 +-->| Ref | | Passed +---------+
| 0003: 3 | +---+ | | Test != Ref | Failed +------+ |
| 0004: 4 +---------->| Test | | Failures +---+ | |
| 0005: 5 | +------+-------------+----------+ | | |
| 0006: 6 | | | |
| 0007: 7 | +--------------------------------------+ | |
| 0008: 8 | | | |
| 0009: 9 | | +--------------+ +-------------+ |
+---------+ | | AlphaNumeric | | |
| +--------------+ +------+------+ +------+------+
| | | | Message Box | | Message Box |
| | | | "FAILED!" | | "PASSED!" |
+->| 0: (2, 2) | +-------------+ +-------------+
| |
| |
| |
+--------------+
Source for this example is in the file xcmpartr.vee.
[15.2] PERFORMING BIN SORTS
+-------+ +----------------------------------------------+
|-100 +-----+ | magDist |
+-------+ | +------+------------------------------+--------+
data --------------|----->| X | | |
+-------+ | | | | |
| 100 +--+ +----->| From | | |
+-------+ | | | magDist(x, from, thru, step) | Result +-->
+-------->| Thru | | |
+-------+ | | | |
| 40 +----------->| Step | | |
+-------+ +------+------------------------------+--------+
See xmagdist.vee for a fully-developed example.
[15.3] SEARCHING FOR MAXIMUM VALUES
+-------------+ +------+
| Set Global | | Quit +--+
+---+ +-------------+ +------+ |
| 0 +-->| maxval | seed global variable |
+---+ +------+------+ with lowest possible value +---+---+
| | Stop |
+---+---+ +-------+
| Until |
| Break +--+
+-------+ |
| test for change in maxval
+-------------+ +-------------------+
| | If/Then/Else |
| +----------+ +---+--------+------+
+------+-----+ | Shift +-->| A |[ A!=B ]| True +--+
| Get Global | +-->| Register +-->| B | | | |
+------------+ | +----------+ +---+--------+------+ |
| maxval +--+ |
+------+-----+ | +-------+------+
| | | AlphaNumeric |
| | +--------------+
| +-------------------------------->| 75 |
| +--------------+
+-----+-----+
|[ 75 ]|
+-----------+ test for slider change
|[ 100] ^ | +-------------------+
| | | | If/Then/Else |
| | | +----------+ +---+--------+------+
| H | | Shift +-->| A |[ A!=B ]| True +--+
| | +--+-->| Register +-->| B | | | |
| | | | +----------+ +---+--------+------+ |
| | | | |
| | | | +---------------------+
| | | | |
|[ 0] v | | +----------+----------+ +------------+
+-----------+ | | Formula | | Set Global |
| +---+-----------------+ +------------+
+------->| A | max([maxval a]) +-->| maxval |
+---+-----------------+ +------+-----+
compare old value to |
new, store greater in +---+---+
maxval | next |
+-------+
Of course, to figure out the minimum value, all you have to do is use "min()"
instead (and initialize "maxval" to 100 instead of 0).
[15.4] LIMIT TESTING
+-------------+ |
| Real Slider | +-------------------------------------------->| Trace1
+-------------+ | |
| | | | +--------------------+ |
| | | | | Formula | |
| [ ] | | +---+----------------+ |
| | +----|----------------->| A |[ ramp(B,A,A) ] +--+ |
| | | | +->| B | | | |
| | | | +---------+ | +---+----------------+ | |
| | | +->| totSize +--+ | |
| | | | +---------+ | |
+------+------+ | | |
| | +---------------------------------------+->| Trace2
+------+------+ | | |
| Function | | | |
| Generator +-+ | | |
+-------------+ | | | |
| | | |
| | | +-----------------------------------+ |
+-----------------+ | | | Comparator | |
| | | +------+-----------------+----------+ |
| +----------+ | +->| Ref | Test [ <= ] Ref | | |
+->| UnBuild +------+---->| Test | | Failures +-->| Trace3
| Waveform | Real +------+-----------------+----------+ |
+----------+ Array |
The waveform data is converted to a real array by an UnBuild Waveform object.
This real array is driven into the "Trace1" input of the XY Trace object and
is displayed in yellow.
[15.5] BITMAP DISPLAY UPDATES
+-------+
| Until |
| Break +------+
+-------+ |
+--+--+ +-----+
| OK +-----+ | OK +---+
+-----+ | +-----+ |
| |
+-------+-------+ +---+---+
| Call Function | | Stop |
+---------------+ +-------+
| setpix() |
+---------------+
The "setpix()" UserFunction was quite simple:
+--------------------------------------+
| setpix() |
+--------------------------------------+
| +---------+ |
| | Picture | |
| +---------+ |
| | | |
| | | |
| | | |
| +----+----+ |
| | |
| +--+--+ |
| | OK | |
| +-----+ |
| |
+--------------------------------------+
I arranged it so that it would "Show Panel On Execute" and gave it the
pop-up appearance:
+-------------------+
| +---------+ |
| | Picture | |
| +---------+ |
| | | |
| | | |
| | | |
| +---------+ |
| |
| +-----+ |
| | OK | |
| +-----+ |
+-------------------+
I gave the Picture object the filename "test.bmp" for a file to display ... I
ran the program, displayed the bitmap, got rid of it, used PaintBrush to
change the bitmap, displayed the bitmap again, and it was the same, as I
expected.
+--------------------------------------+
| setpix() |
+--------------------------------------+
| +----------+ +-----------------+ |
| | Text | | Picture | |
| +----------+ +-------+---------+ |
| | test.bmp +-->| file | | |
| +----------+ | | | |
| | | | |
| +-->| clear | | |
| | +-------++--------+ |
| | | |
| | +--+--+ |
| | | OK +--+ |
| | +-----+ | |
| | | |
| +------------------+ |
| |
+--------------------------------------+
This worked fine. I displayed the bitmap, got rid of it, changed it in
PaintBrush, displayed it again, and got the new bitmap.
[15.6] CONTROLLING PLOT DISPLAYS
+-------+ +------+ OK
| Until | | Quit +-----+
| Break +--+ +------+ |
+-------+ | +---+---+
| +--------------+ | Stop |
| Toggle to get | If/Then/Else | +-------+
+--------+-------+ trace +-------+------+
+->| Get Next Trace +--+---->| A==1 | THEN +--+ +----------------------
| +--------+-------+ | +-------+------+ | | X vs Y Plot
| | | | +------------+---------
+-----------|----------+ +------+ | |
| | | |
| These two +----+----+ | |
| objects | ramp() +---->| XData |
| produce +----+----+ | |
| dummy | | |
| trace +------+------+ | |
| data. | randomize() +-->| YData1 |
| +-------------+ | |
| Toggle to | |
| select +--------------+ | |
| plot mode | If/Then/Else | | |
+--------+--------+ +-------+------+ | |
+->|[x] Single Trace +--+------>| A==1 | THEN +-->| Next Curve |
| +--------+--------+ | +-------+------+ | |
| | | | |
+-----------|-----------+ | |
| +--------------+ | |
| Toggle to | If/Then/Else | | |
+---+---+ clear plot +-------+------+ | |
+->| Clear +--+----------->| A==1 | THEN +-->| Clear |
| +-------+ | +-------+------+ | |
| | | |
+-------------+ +-->| Auto Scale |
| | |
| | |
| +------------+-----+---
| |
+----------------------+
|
+---+---+
| Next |
+-------+
Anyway, there's not much to this program. Note that the "ramp()" function
just generates an array with values from 0 to 5 to act as X-values, using the
invocation:
ramp(6, 0, 5)
-- and the "randomize()" function generates comparable Y-values as a set of
six random numbers in the range of 0 to 100, using the invocation:
randomize(ramp(6, 0, 5), 0, 100)
This is done simply to generate dummy data. In practice, you'd obtain the
data from I/O or files or whatever.
[15.7] CONVERTING 3852 HSDVM VALUES
00:11 count (0:4095)
12 sign (0 = positive, 1 = negative)
13:14 range (0 = 1M, 1 = 8M, 2 = 64M, 3 = 256M, where M = 9.765625E-6)
15 valid (0 = invalid, 1 = valid)
This gives the voltage as:
Voltage = Sign * Range * Count
The VEE Formula to convert the data to volts appears as follows (expanded to
multiple lines for clarity):
(100*(bit(A, 15)-1)) +
(1-2*bit(A, 12)) *
bitand(A,4095) *
(2^(clipupper(3*(bit(14,A)*2 + bit(13,A)), 8))) * 9.765625E-6
The first term in the Formula gives a strongly negative value to help
distinguish invalid data from valid data:
Valid = 100*(bit(A, 15)-1) // 0 if valid, -100 if Invalid
This works OK because the maximum absolute value the DVM can return is about
10.2 volts.
sign = (1 - 2*bit(A, 12))
Getting the count is even simpler:
count = bitand(A,4095)
However, getting the range multiplier is tricky. The values of the range
almost correspond to a power of 2 taken to an exponent with a value of
the range number times 3:
2 ^ (0 * 3) = 2^0 = 1
2 ^ (1 * 3) = 2^3 = 8
2 ^ (2 * 3) = 2^6 = 64
In principle, then, we could get the numeric value of that field and multiply
it by 3 to get the exponent we want:
3*(bit(14,A)*2 + bit(13,A))
-- except that the last range value by this scheme is:
2 ^ (3 * 3) = 2^9 = 512
The value is actually 256, not 512. But no worries, we can use the
clipupper() function to limit the maximum exponent to 8 to give the proper
value of 256:
clipupper(3*(bit(14,A)*2 + bit(13,A)),8)
This gives the range as:
(2^(clipupper(3*(bit(14,A)*2 + bit(13,A)), 8))) * 9.765625E-6
[15.8] SIGNAL PROCESSING, FFTS, & VEE
(max(D) - min(D))/2
-- and the DC offset (average value, really) could be given by:
(max(D) + min(D))/2
However, figuring out the frequency leads to a nightmare consideration of
zero-crossing detectors, and figuring out the phase is even worse. Such
techniques are also very sensitive to noise.
+----------------------------------------------------------------------+
| UserFunction: ScaleFFT |
+----+----------------------------------------------------------+------+
| | | |
| | +--------------------------+ | |
| | | Formula | | |
| | +----+---------------------+ | |
| | +-->| Td | fft(Td)/totsize(Td) +--+ | |
| | | +----+---------------------+ | | |
| | | | | |
| Td +--+ +---------------------------+ +-->| CpxD |
| | | | | |
| | | +--------------------------------------+ | | |
| | | | Formula | | | |
| | | +---+----------------------------------+ | | |
| | +-->| A | concat(A[0],2*A[1:totsize(A)-1]) +--+ | |
| | +---+----------------------------------+ | |
| | | |
+----+----------------------------------------------------------+------+
The first Formula box divides the FFT of the input data by the number of
points in the original signal. The second multiplies all the harmonic
components by 2 to generate the proper result.
+-------------+
---+-->| ScaleFFT(x) +--+ +-------------+
| +-------------+ | | Formula |
| | +---+---------+
| +----------------+------------->| C | re(C[0] +--> DC offset
| | +---+---------+
| |
| | +---------------------------------+
| | | Formula |
| | +--------+ +---------------------------------+
| +-->| mag(x) +--+-->| 1 + maxIndex(X[1:totsize(A)-1]) +--+
| | +--------+ | +---------------------------------+ |
| | | |
| | | +--------------------------------+
| | | |
| | | | +----------+
| | | | | Formula |
| | | | +---+------+
| | +-------|--------->| M | M[I] +--> Amplitude
| | +--------->| I | |
| | | +---+------+
| | |
| | | +-----------------+
| | | | Formula |
| | | +---+-------------+
| +-----------------------|-->| C | phase(C[I]) +--> Phase
| +-->| I | |
| | +---+-------------+
| |
| | +-----------------+
| | | Formula |
| +------------+ | +-----------------+
+-->| totsize(x) +----------|-->| S | R * (I/S) +--> Frequency
+------------+ +-->| I | |
+----->| R | |
| +---+-------------+
sample frequency ---+
Some notes to explain how this stuff works:
+--------------------------------------------------------------------+
| UserFunction: PadZero |
+-+----------------------------------------------------------------+-+
| | | |
| | +----------------------------------------+ | |
| | | Formula | | |
| | +---+------------------------------------+ | |
| | +-->| D | intPart(log(totsize(D)+0.1)/log(2) +--+ | |
| | | +---+------------------------------------+ | | |
| | | | | |
| | | +--------------------------------------------+ | |
| | | | | |
| | | | +--------------------------------------------------+ | |
| | | | | Formula | | |
| | | | +---+----------------------------------------------+ | |
| | | +-->| E |(S=2^E ? A : concat(D,ramp((2^(E+1)-S),0,0))) | | |
|D+--+----->| D | +-->|R|
| | | +-->| S | | | |
| | | | +---+----------------------------------------------+ | |
| | | | | |
| | | +---------------+ | |
| | | | | |
| | | +-----------+ | | |
| | +-->| totsize() +--+ | |
| | +-----------+ | |
| | | |
| | | |
+-+----------------------------------------------------------------+-+
The formula at top takes the number of elements of the input data, determines
the power of two that gives that number, and then truncates it to an integer
(note that I added a fudge-factor of 0.1 to ensure that roundoff error will
not cause problems).
[15.9] DYNAMICALLY REPROGRAMMING VEE FORMULA OBJECTS
+---------------------+
| Formula |
+-------+-------------+
-->| Rec | Rec.Field +----
-->| Field | |
+-------+-------------+
This would work OK if the input record actually had a field with the precise
name "Field" -- but what he really wanted to do was put an arbitrary field
name like "temp" or "pressure" in on the "Field" pin as a string and get the
"Rec.temp" or "Rec.pressure" field as programmed.
+-----------------+
---------------------------------+ | |
| | +----------+---------+
+------------------------+ | | | Formula |
| Formula | | | +---------+----------+
+-------+----------------+ +---|----->| Rec | +--->
--->| Field | "Rec." + Field +-------|--+-->| Formula | |
+-------+----+-----------+ | | +---------+----------+
| | |
+-------------------+ | +--------------+
| | AlphaNumeric |
| +--------------+
+-->| Rec.pass |
+--------------+
The first object performs simple string operations to create the formula that
actually to be executed. It shoves the formula into the second object,
which then executes it.