Data AcQuisition And Real-Time AnalysisScope - Spectrum - Spectrogram - Signal Generator
Software for Windows
Science with your Sound Card!
Contact us about
Macro IF Statements
An optional ELSE block holds commands to be performed if the IF is false.
An ENDIF must be used to close each IF structure.
Note that IF, ELSE, and ENDIF are macro prefixes, so each must be followed by a period:
IF.L.0.StreamOn=1 Msg="Left Stream 0 is ON" ELSE. Msg="Left Stream 0 is OFF" ENDIF.
With values that could include decimal places, it may be better to test if a value is above or below specified limits, rather than testing for exact equality. That's because there may be hidden digits that are nonetheless used in the IF test. For example, if variable A is the result of a calculation and you display it with Msg=A(0.5), you might see a value like 6.54321. But that may have been rounded from an internal value like 6.543212345, so IF.A=6.54321 would fail.
This is a particular issue for Generator frequencies, since the value you see on the Tone Frequency control, for example, is the actual value generated, not ncessarily the value you entered. For example, if you enter 1000, the control may show 1000.00003. That's because Generator frequencies have a finite resolution, determined by the sample rate, which is considered in the display. But an IF test would compare this internal value to the value in the statement, which in this case would be 1000.00000.
The preceding StreamOn example shows the "true" and "false" branches indented for clarity. Indenting is not required, but it is strongly recommended. (You can use CTRL+Tab in the Macro Definition editor to move to the next tab stop.)
The example shows a single IF structure. IF statements can contain nests up to 16 levels deeper (more indented) than the main IF. You can have an arbitrary number of IF statements at each level.
IF.L.0.StreamOn=1 IF.R.0.StreamOn=1 Msg="Left and Right Streams 0 both ON" ELSE. Msg="Only Left Stream 0 is ON" ENDIF. ELSE. IF.R.0.StreamOn=1 Msg="Only Right Stream 0 is ON" ELSE. Msg="Left and Right Streams 0 both OFF" ENDIF. ENDIF.
You can test for various conditions besides equality, but the equal sign is always required... even for "greater than". A redundant equal sign is needed for "greater than or equal to", as shown below:
= Equal =! Not equal => Greater than =< Less than =>= Greater than or equal to =<= Less than or equal to =& Bit field test (see below)
It may be helpful to think of the first equal sign as "is", such that IF.A=>=B becomes "If A is greater than or equal to B".
Please note that unlike some languages, you can not have a one-sided IF like IF.UA; you must always have an expression with an '=', such as IF.UA=>0.
You can have expressions on both sides of the IF statement, such as:
IF.(A^2 + B^2)=>(C^2 + D^2)
Expressions can include logical (True/False, or "Boolean") operators for AND (&&), OR (||), NOT (!), and XOR (##). Since True = 1 and False = 0, you can test if an overall logical expression is true by comparing it to 1:
IF.(A && B) || (C && D)=1
One thing you can not do is have multiple tests (multiple equal signs) in the same IF statement. For example, to test if A=4 OR B=5, this won't work:
WRONG: IF.(A=4) || (B=5)
Instead, you must append a separate OR statement like
IF.A=4 OR.B=5 ;Commands here will run if A=4 OR B=5 ELSE. ;Commands here will run if neither is true ENDIF.
Note that the OR. line must immediately follow the IF. line. Besides OR. you can use AND. or XOR. (exclusive OR) lines. You can cascade as many of these extra logic lines as needed to effectively make one large IF statement, such as:
IF.A=4 OR.B=5 AND.C=6 XOR.D=7 ;...
When cascading these logic lines, each operates on the result of everything that has gone before. So the above example would be the Daqarta equivalent of the forbidden:
WRONG: IF.(((A=4) || (B=5)) && (C=6)) ## (D=7)
If you want to test a NOT condition, you can express the statement as its inverse, such as IF.A=!4 instead of IF.A=4, or IF.A=<=B instead of IF.A=>B, etc. You can apply that strategy to any of the added AND/OR/XOR logic lines.
Instead of value or logical comparisons, as shown in the preceding examples, you can use bit field tests by putting an & after the equal sign. Then the IF will pass if any bits that are set in test value are also set in the tested variable.
To make it easier to work with bit fields, the test value can be given in hexadecimal by preceding it with an h. For example, IF.VarA=&h80000001 will be true if VarA has either its most-significant or least-significant bit set. This type of test is particularly useful for Limits tests, where 4 different Pass/Fail test results are present in a single variable.
Many Daqarta controls are "radio buttons", consisting of two or more buttons where only one can be active (depressed) at a time. These buttons can be set or read via macros, either by name or by number. For example, the SpectWind macro is used for Spectrum window function select, which is a set of 6 buttons for Hann, Hamming, Blackman, Blackman Exact, Blackman-Harris, or Flat Top window type. You can use values 0-5 to refer to these, or you can use mnemonic names: Hann, Hamm, Bkmn, BkEx, BkHr, or Flat.
For example, either SpectWind=2 or SpectWind=Bkmn will set the Blackman window type. You can use any variable or expression in place of the immediate value, but if you use the name it must appear alone.
However, if you use the name (instead of a value or expression) in an IF statement you must put quotes around it, as in IF.SpectWind="Bkmn".
Buttons that toggle between two states must use 0 for off and 1 for on in IF statements, even though some buttons may allow alternate mnemonics when a macro sets a value directly. For example SmplSec=sec sets the units used in many dialogs (like Burst, Frequency Sweep, and Trigger) to seconds instead of samples. Alternatively, you could use SmplSec=1 to do the same thing, or use any variable or expression in place of the immediate value.
But in an IF statement, you must use numbers. Unlike the radio buttons discussed above, you can't use the mnemonic whether quoted or not.
See Macro Variables for an example that uses IF statements to provide a system of prompts for novice users, which can be toggled off by experienced users.
Use E.IF instead of IF to test if a control is enabled. You don't need a value after the equal sign. For example, to test if the Trigger Level control is enabled, use:
E.IF.TrigLevel= Msg="Enabled" ELSE. Msg="Disabled" ENDIF.
Trigger Level is disabled during Gen Sync trigger mode. (In this particular case you could have simply tested for Gen Sync mode with a normal IF, using IF.TrigMode=GenSync.)
If a macro tries to set a disabled control, the macro aborts with the message "Control disabled. Aborting macro". By testing first, you can take other action.
IF statements are intended to work with numerical values, but you can perform limited string tests since the first 4 or 8 characters of a string can be regarded as an integer made of their ASCII equivalents. For example, if you set UA="Test" then it can be displayed as Msg=UA(A) to see Test, but it can also be shown as its hexadecimal equivalent 54657374 via Msg=UA(h).
Thus, if you use IF.UA="Test", the IF will pass. Likewise, you can use IF.UA=UB and it will pass if UB holds the same string or equivalent integer.
If you use floating-point variables A-Z or 64-bit fixed-point variables Var0-VarZ, each can hold up to 8 ASCII characters by utilizing the fractional portion. For example, A="12345678" can be displayed in hex notation via Msg=A(H) to see that it is stored as 31323334.35363738 hex.
Although IF tests for equality are usually a bad idea for math results that involve computations and possible rounding, here they are no problem. It's perfectly reasonable to use these "fractional" values if they have only been obtained from text strings.
You can use IF tests with string arrays like Str0, but the test only applies to the first 8 characters specified. For example, after Str0="Test1234ABCD", you can use IF.Str0="Test1234" and it will pass, as will IF.Str0="Test1234ABCD" or in fact IF.Str0="Test1234EFGH" since the difference is after the first eight characters. But IF.Str0="Test123" will fail.
The 8-character limit only applies to the length being tested; you can use string indexes to test any section of the string array that is 8 characters or less, so with the above string IF.Str0[4,10]="1234ABC" will pass. You can also test between different portions of the same or different strings, such as IF.Str0[4,7]=Str2[8,11].
Buf0-7 arrays can be used for string storage, such as Buf0#a="Test1234", but these can't be tested with IF.Buf0(a)="Test1234" since the IF command won't accept output formatting like (a). Instead, you can copy to a string array using Str0=Buf0(a) and then use IF.Str0="Test1234". (Alternatively, you can set an intermediate floating point variable as in A=Str0 and then use IF.A="Test1234", but you can't directly set A=Buf0(a) due to the output formatting.)
You can also use string tests with Labels and Fields. When these are set with immediate data (not variables) they must use strings in quotes, as in Field1="ABCDEFG" or Field1="123.456", but can be read, and hence tested, as values. However, please note that while IF.Field1="ABCDEFG" (with quotes) is used to test the first case, the second case requires IF.Field1=123.456 without quotes. That's because in both cases the Field is read as a value, and the quotes are needed to tell Daqarta to regard "ABCDEFG" as a value and not some strange variable, which it can't find and so would report an error.
CAUTION: When reading a Label or Field, Daqarta defaults to attempting to interpret it as a numeric value, and only if that fails does it interpret it as an alphanumeric string "value". For example, if the original had been set with Field1="Mouse234", that would always be read as an alphanumeric string. But Field1="M234" would be read as a value of 123400, since the "M" would be interpreted as a Mega scientific prefix replacing the decimal point in a value using "European" notation.
To force the Field or Label to be read as an alphanumeric, use the ?A suffix as in IF.Field1?A="M234" or A=Field1?A. (You can't use A=Field1(A) here.)
You can also set Labels and Field with numeric variables, such as A=123.456 followed by Field1=A, which could be tested by IF.Field1=123.456 or by IF.Field1=A.
You can use IF.Field1=Field2, but only the first 8 characters are considered.
To determine if a Field is empty, use IF.Field1= with nothing on the right side. (You can not use IF.Field1="".)
To invert the logic so that the IF passes if Field1 is not empty, use IF.Field1=!
See the _Phase_Mtr_Ctrls macro listing in the Phase Meter mini-app for an example that tests to see if Field1 is empty, and if so prompts the user to enter a file name there.
Sometimes when a macro is called as a subroutine by another macro, it may need to return to the caller early, before the end of the code. This occasion might arise, for example, if a condition is detected that makes it impossible to complete the main task of the subroutine. The best approach might be to return to the caller with an error code in a variable, perhaps after displaying an error message for the user.
Or the subroutine might be such that there are multiple paths to completion, which don't neatly converge at the end of the subroutine.
In such cases, constructing the subroutine to force all paths to converge at the end can require a lot of cascading IF statements that contribute to cluttered code, and to "creeping indents" if you indent for neatness.
One simple solution is to use LoopBreak=0 to force an early return from any place needed. Consider a case where the code begins by examining its input parameters, and determines that one or more are out of bounds; perhaps there would be an obvious divide-by-zero problem later in the computation, or an imaginary result of a square root, or an overflow. Here is the "conventional" approach:
IF.A=0 Msg="Error, A = zero" ELSE. IF.B=<0 Msg="Error, B negative" ELSE. IF.C=>A * B Msg="Error, C overflow" ELSE. ;Main computation here ENDIF. ENDIF. ENDIF.
Here is the LoopBreak=0 approach:
IF.A=0 Msg="Error, A = zero" LoopBreak=0 ENDIF. IF.B=<0 Msg="Error, B negative" LoopBreak=0 ENDIF. IF.C=>A * B Msg="Error, C overflow" LoopBreak=0 ENDIF. ;Main computation here
Alternatively, you can use LoopBreak=-1 to force the subroutine and its caller (and the caller of the caller, etc) to exit immediately. This is a more user-friendly way to abort than the Cancel= option, as long as you leave the user with a clear explanatory Msg or Mtr0-3 message, or perhaps a WaitMsg that requires confirmation.
See also Macro Overview
Questions? Comments? Contact us!We respond to ALL inquiries, typically within 24 hrs.
Over 35 Years of Innovative Instrumentation
© Copyright 2007 - 2021 by Interstellar Research
All rights reserved