Daqarta
Data AcQuisition And Real-Time Analysis
Scope - Spectrum - Spectrogram - Signal Generator
Software for Windows
Science with your Sound Card!
The following is from the Daqarta Help system:

Features:

Oscilloscope

Spectrum Analyzer

8-Channel
Signal Generator

(Absolutely FREE!)

Spectrogram

Pitch Tracker

Pitch-to-MIDI

DaqMusiq Generator
(Free Music... Forever!)

Engine Simulator

LCR Meter

Remote Operation

DC Measurements

True RMS Voltmeter

Sound Level Meter

Frequency Counter
    Period
    Event
    Spectral Event

    Temperature
    Pressure
    MHz Frequencies

Data Logger

Waveform Averager

Histogram

Post-Stimulus Time
Histogram (PSTH)

THD Meter

IMD Meter

Precision Phase Meter

Pulse Meter

Macro System

Multi-Trace Arrays

Trigger Controls

Auto-Calibration

Spectral Peak Track

Spectrum Limit Testing

Direct-to-Disk Recording

Accessibility

Applications:

Frequency response

Distortion measurement

Speech and music

Microphone calibration

Loudspeaker test

Auditory phenomena

Musical instrument tuning

Animal sound

Evoked potentials

Rotating machinery

Automotive

Product test

Contact us about
your application!

Macro String Arrays Str0-Str7, StrV

Introduction:

A string is simply text, such as "This is a string!". It is an array of bytes, each a displayable ASCII character, including numerals and the uppercase and lowercase letters of the English alphabet, plus all the symbols on a standard keyboard.

In Daqarta, strings are used in Notes and messages, as Labels and Fields, as titles for Meters and Custom Controls, and many other places. See String Variables and Expressions for more information.

A string is directly displayable because the sequence of character bytes can be sent to a display device without further conversion. In this respect strings are conceptually different from numerical values.

A Daqarta variable with a numerical value (such as a voltage or frequency) must first be converted to a string if it is to be displayed via Msg, for example. Usually that happens automatically, such as Msg=SmplRate + " Hz"; the sample rate is first converted to a string, such as "48000", then " Hz" is appended before the resultant "48000 Hz" message is displayed.

If you first set Str0=SmplRate + " Hz", then the sample rate is converted at that time; if you later use Msg=Str0 the string "48000 Hz" is simply copied to the message display.

Daqarta provides 8 string arrays, Str0 through Str7, which can accept strings just like the Msg example above. This allows the string to be manipulated before subsequent use, such as being displayed by Msg or a meter, added to Notes, output to a log file, or used to label a meter or control.

For example, Str0="Stimulus: " + L.0.ToneFreq + " Hz" could be followed by Mtr0=Str0 to display a Generator stimulus frequency on a large resizeable meter, which might be shown as "Stimulus: 1000.000 Hz". You might also want to use Notes=Notes +n +Str0 to add it to Notes (on a new line, via the 'n'), which will be saved with a .DQA file of the data. In addition, you could use LogTxt=n + Str0 + " at " +t to send it to the log file on a new line, with the current time appended via the 't'.

The above use allows Str0 to be written once but used for multiple purposes without repeating the entire "Stimulus: "... entry for each use. Note, however, that variables like ToneFreq will be converted to text at the time Str0 is defined, and simply copied when it is used. See the Expression Evaluation subtopic below for an alternative that evaluates variables when used.

Each of the 8 string arrays can hold up to 2048 characters. You may not often need to store a single string that long, although it can be used to hold a maximally-full Notes. But you aren't limited to a single string in each array: You can divide an array into as many smaller strings as will fit. See Multiple Substrings, below.

The Auto_Recorder macro mini-app provides examples of using string arrays to hold multiple Custom Controls labels for easy changes when a button state changes. It also uses string arrays to hold copies of file paths and display names for repeated later use, such as specifying where to write data file blocks, and for setting the Log File name and data headers.

Instead of explicit Str0 to Str7 forms you can use StrV to select the string via the Channel Select variable Ch. For example, if the current value of Ch is 3, then StrV will be equivalent to Str3 wherever it appears.

You can also use these "string" arrays to hold binary values. This allows compact and orderly storage of variables, used extensively by the DaquinOscope macro mini-app.

The _Get_String macro subroutine included with Daqarta manipulates a string array to first show a prompt such as "Pin Number: " via a Msg macro, then accept characters via WaitKey and add them to the displayed string one at a time, allow simple backspace editing, and finally, when the Enter key is hit, return to the calling macro with a string that holds the user entry. That string can then be used with numerical evaluation to set a variable.


Command Summary:

Write text to string:
    Str0="Text"                    Immediate text string
    Str0[123]="Text"               Indexed string
    Str0[123]=UA                   Integer variable
    Str0[123]="Hex=" +UA(h) +"h"   String expression
    Str0=Str0 + " Added text"      Appended text
    Str0="Preface " + Str0         Prepended text
    Str0="Text1" +z +"Text2" +z    Null-terminated strings
    Str1=Str0                      String copy
    Str1=Str0[0,100]               Copy between indexes
    Str1=Str0[100,0]               Reverse copy
    Str1=Str0[100,-23]             Copy 23 chars from 100 on
    Str1#A=Str0                    Copy All of Str0 to Str1, nulls included
    Str0#A=Buf0[0](aE)             Copy 2048 bytes from Buf0, with nulls

Clear string:
    Str0=                          Clear entire string
    Str0=z(2048)                   Same as above
    Str0[123]=                     Clear 123 through 2047
    Str0=z(1925)                   Same as above (1925 = 2048 - 123)
    Str0[100,120]=z                Clear 100 through 120
    Str0[100,-11]=z                Same as above (-11 = size to clear)

Read from string:
    Field1=Str0[100 + 16 * UA]     Read selected string
    UN=Str0?N                      String size
    UN=Str0?N[100,123]             String size between indexes
    UA=Str0?R[100]                 Raw integer at index, nulls included
    X=Str0?V                       Value of numerical text, decimal
    X=Str0?H                       Value of numerical text, hexadecimal
    X=Str0?B                       Value of numerical text, binary
    UT=Str0?T                      Terminator after numerical text
    UP=Str0?P                      String pointer after numerical text
    A=Str0?E[100,123]              Evaluate between indexes
    Msg=Str0?U[100,123]            Uppercase
    Msg=Str0?L[100,123]            Lowercase

Write binary to string:
    Str0[123]#b=UA                 Copy low binary byte from UA
    Str0[123]#1=UA                 Same as above
    Str0[123]#w=UA                 Copy low binary word from UA
    Str0[123]#2=UA                 Same as above
    Str0[123]#d=UA                 Copy 4-byte dword from UA
    Str0[123]#4=UA                 Same as above
    Str0[123]#q=A                  Copy 8-byte qword from A
    Str0[123]#8=A                  Same as above
    Str0[123]#f=A                  Copy 10-byte float from A

Read binary from string:
    UA=Str0?b[123]                 Read binary byte into UA
    UA=Str0?1[123]                 Same as above
    UA=Str0?w[123]                 Read binary word into UA
    UA=Str0?2[123]                 Same as above
    UA=Str0?d[123]                 Read 4-byte dword into UA
    UA=Str0?4[123]                 Same as above
    A=Str0?q[123]                  Read 8-byte qword into A
    A=Str0?8[123]                  Same as above
    A=Str0?f[123]                  Read 10-byte float into A

Special commands:
    Str0#B=n                  Link Str0 to Bufn
    Str0#B=-1                      Unlink Str0 from any Bufn
    Str0[UN]#F="FindString"        Search Str0 for string, from UN
    UF=Str0?F                      Result status after search
    UP=Str0?P                      String pointer after search

String Indexes:

A single 2048-character string variable such as Str0 can be accessed at arbitrary locations via indexes. Index values start at 0 and run to 2047. The index is the starting byte of the substring to write or read. If you use two indexes, the second is the ending byte.

For example, if Str0="1234ABCD" then Msg=Str0[4] would display ABCD, while Msg=Str0[0,3] would display 1234.

If the second index is negative, its absolute value is used as a count instead of an actual index. In the above example, Msg=Str0[2,-4] would display 34AB.

The above rules apply to StrN strings on either the left or right side of the command. However, when used on the left side the lowest index must come first; on the right side the order may be reversed to copy that string in reverse. See Reading And Copying Strings, below.

You are not limited to immediate values for indexes; you can use variables and numeric expressions as needed. For example, you can use Str0[16*UA]="New Text" to space entries 16 characters apart based on variable UA.

Note that since indexes are always integers, you can use integer variables U0-UZ or Q0-QZ and save the floating-point A-Z and 64-bit fixed-point Var0-VarZ for calculations that really need fractions.


Writing to Strings:

You can write immediate text to a string by surrounding it with quotes, as in Str0="Some Text".

Numerical values can be entered the same way, such as Str0="123.456", but they will be stored as strings. That's fine if they are only intended for later display, but not for direct use in calculations... see Expression Evaluation for that.

You can also set a string with any Daqarta variable, as in Str0=UA or Str0=SmplRate. The current value of the variable will be converted to a string, and stored as such. As noted above, fine for display but not calculation.

You can send a string expression to a string, such as Str0="THD: " + A(0.4) + " at " + L.0.ToneFreq + " Hz". Note that string expressions can use all the usual formatting options here, such as n for 'newline' and b(UA) to insert UA blank spaces, as well as d to insert the current date and t to insert the current time.

However, in this usage the number given with the p (position) and f (fill) commands is now treated as a simple character count, not a column number. So f_(10) (or f_10) will simply insert 10 underscores, and p(UA) will insert UA spaces just like b(UA).

One option that is only available with StrN arrays is z to insert one or more nulls, which are binary 0 (not the ASCII character for "0" which is 30 hex). z10 or z(10) will insert 10 nulls, but the first format only allows a count up to 999, while the second allows up to 2048 (as does the z(UA) format) to fill the entire string with nulls.

You can uses indexes to write to an arbitrary portion of the 2048-character string array. For example, if you have previously used Str0="1234ABCD", followed by Str0[4]="56", then Msg=Str0 will display 123456CD.

If instead of Str0[4]="56" you use Str0[8]="EFGH", then Msg=Str0 will show 1234ABCDEFGH.

However, if instead you write to the next-higher index via Str0[9]="EFGH", then (assuming that the string was empty before beginning) there would be a null between the D at index 7 and the E at index 9. So Msg=Str0 would show the original 1234ABCD, while Msg=Str0[9] would show EFGH.

You can use two indexes to write to a limited region of the string array, as in Str0[100,115]="1234" will write the given 1234 text to bytes 100-103 and clear everything from 104-115. Conversely, Str0[100,103]="12345678" will write the same 1234 to bytes 100-103, ignoring the rest of the given string and likewise not affecting 104 or above.

Instead of a second index you can give the negative of the desired size, including both ends of the target region. Str0[100,-16]="1234" is equivalent to Str0[100,115]="1234" above, and Str0[100,-4]="12345678" is equivalent to Str0[100,103]="12345678".


Clearing Strings:

Clearing a string is the same as filling it with nulls, which are binary zeros and not the ASCII character for "0" (which is 30 hex).

You can clear an entire string via Str0=, with no value given.

Str0=z(2048) will accomplish the same thing.

You can clear everything starting from a given point by using an index, as in Str0[1024]=. This will clear the top half of the string, positions 1024 through 2047.

Str0[1024]=z(1024) is equivalent. (You could just as well use a too-large z value such as Str0[1024]=z(9999), since the clearing will automatically stop at the end of the string.)

When you write to a string using only a single index, or no index to write to the start, then everything past the end of the string is automatically cleared. So Str0="0123" will write to bytes 0-3 and clear bytes 4-2047.

If you use two indexes to write to a specific region, the region is cleared from the end of the text to the end of the region. Str0[100,120]="ABCD" will write to bytes 100-103 and clear 104-120. Everything above 120 will be left as-is. The same is true if the second index is negative to specify the size of the region, as in Str0[100,-11]="ABCD".

If you want to clear the whole region, you can use Str0[100,120]=z or Str0[100,-11]=z. Please note that you can not use Str0[100,120]=. If you do, the second index (or negative size) is ignored; everything above the first index will be cleared.


Reading and Copying Strings:

Reading and copying string variables are really equivalent, since "reading" involves copying to some other string such as a Label or Field, a message buffer, or another StrN array.

Field1=Str0[100 + 16 * UA] copies a string from the computed position in Str0 to Field1. Fields (and Labels) can only hold a maximum of 15 characters; if the string at the computed position is longer than that, it will be truncated. Otherwise, the copy will stop at the first null encountered.

If you want to pack strings together without terminal nulls to separate them, you can use 2 indexes; the second one indicates the final position to be copied, or (more usefully) the negative of the maximum number of characters. In the above example, the strings could be 15 characters apart and accessed via Field1=Str0[100 + 15 * UA,-15].

Either or both index / count values can be given as numeric variables or expressions.

If you use two index values, rather than an index and a negative count, the string will be copied in reverse if the first index is greater than the second. For example, if the raw string is Str0="1234ABCD" and you use Str1=Str0[7,3] followed by Msg=Str1, the displayed message will be DCBA4. (You could get the same display by skipping the Str1 copy and just using Msg=Str0[7,3].)

Note that, as mentioned in Writing to Strings above, any numerical values that have been written will be stored as strings of ASCII text characters. They can be read or copied for display uses, but not directly used for calculations without first converting back to raw numbers; see Expression Evaluation, below.

For example, if Str0="1234", then Field1=Str0 will show 1234. But UA=Str0 will set it with the hex value 31323334 since "1" = 31h, "2" = 32h, etc. You can see that if you display it with Msg=UA(h), while if you use Msg=UA you'll see the decimal equivalent of 825373492. If you want to see the original string you can use Msg=UA(A), but note that this is limited to 4 characters for integers like U0-UZ and Q0-QZ. For floating-point variables A-Z or 64-bit fixed-point values like Var0-VarZ you can get up to 8 characters, including a decimal point, if any.

Note that in the above example UA=Str0 is equivalent to UA="1234"; the string is just copied to UA from Str0 instead of from immediate data.

There are special cases where you want to copy everything from the right side of the command to the string on the left. Those cases are where you are copying another Str0-7 string or a Buf0-7 macro array buffer, including any nulls they may contain. If you just use Str1=Str0, the copy process will stop at the first null encountered in Str0. To make sure they are included, use Str1#A=Str0.

Similarly, a macro array buffer Buf0-7 may hold many null-terminated strings. (See String Storage for more details, including size specifiers.) You could use Str0#A=Buf0[0](aE) to copy the first 2048 bytes of Buf0 to Str0, nulls included. Since Buf0 can hold up to 8192 bytes (8 bytes per index), it could be used to fill 4 different StrN strings. You could use Str1#A=Buf0[256](aE), plus Str2#A=Buf0[512](aE) and Str3#A=Buf0[768](aE) to copy them all.

One reason you might want to do this is to copy a set of strings from a file, since BufN commands support file operations and StrN commands don't.

To copy a full StrN to a BufN, such as for a subsequent file save, use Buf0[0]#aE=Str0[0,2047] or equivalent. Note that in this case you need to use two indexes on the right side to copy everything; if you use Buf0[0]#aE=Str0 it will stop at the first null.

You can use smaller ranges as desired. For example, use Buf0[0]#aD=Str0[0,1023] to copy the first 1024 bytes from Str0, or Buf0[0]#aC=Str0[512,1023] to copy the second 512 bytes. Likewise, you can send them to different portions of the BufN by changing its starting index. (Note that BufN arrays hold 8 bytes per index, while StrN arrays are one byte per index.)


Testing Strings with IF Statements:

You can use IF tests with string arrays, 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].


Multiple Substrings:

Suppose you need a bunch of labels, such as for Custom Controls whose labels change according to current state, with the longest label being (say) 15 characters. You could store the strings spaced 16 characters apart (allowing a terminal null after each), and refer to them via an index variable like UA. To get the UAth string (starting from 0), you would use Str0[16 * UA] where UA ranges from 0 to 15.

These "substrings" can be of arbitrary mixed lengths, as long as you keep track of them. For instance, you might have 10 of the labels discussed in the above example at 16 bytes each, followed by 12 strings of up to 40 bytes each, including terminal nulls. If these strings are numbered 0-11 you could find the location of the UBth string via Str0[16 * 10 + 40 * UB].

You can use two-index addressing to eliminate the need for terminal nulls, or to select only a portion of a substring. For example, the Auto_Recorder macro mini-app sets the following substrings near the start of the macro:

    Str0[0]="RMS Event Threshold, %FS" +z      ;Ctrl0 w. Btn2 = 0
    Str0[32]="Peak Event Threshold, %FS" +z    ;Ctrl0 w. Btn2 = 1
    Str0[2*32]="RMS Quiet Threshold, %FS" +z   ;Ctrl1 w. Btn2 = 0
    Str0[3*32]="Peak Quiet Threshold, %FS" +z  ;Ctrl1 w. Btn2 = 1

    Str0[130]="RMS Mode" +z                    ;Btn2 = 0
    Str0[130 + 16]="Peak Mode" +z              ;Btn2 = 1

    Str0[170]="Event = Left " +z               ;Btn4 = 0
    Str0[170 + 16]="Event = Right" +z          ;Btn4 = 1
    Str0[170 + 2*16]="Event = Both " +z        ;Btn4 = 2

    Str0[220]="Setup TC = 10  " +z             ;Btn5 = 0
    Str0[220 + 16]="Setup TC = 100 " +z        ;Btn5 = 1
    Str0[220 + 2*16]="Setup TC = 1000" +z      ;Btn5 = 2

These are all null-terminated (the +z after each inserts a zero), and they are used that way for normal labels. But these same strings are used to send shortened versions of current settings to a log file. The Mode may be either "RMS" or "Peak", depending on the Btn2 setting, but the whole "RMS Mode" or "Peak Mode" strings are not sent. Instead, only the first 4 characters are used by setting the second index to -4. (See String Indexes, above.)

The mode thus could have been sent as Str0[130 + 16*Btn2,-4], but note that the Ctrl0 labels at Str0[0] also include the current mode at the start, so Str0[32*Btn2,-4] does the same job in less space. See, for example, the Btn3 Event Logging toggle code in _Auto_Rec_Ctrls custom controls handler, where the relevant substring is italicized here for clarity:

    ...
    IF.Ctrls=7             ;Btn3 = Event Logging toggle
        ...
        LogTxt=n +"Chan" +p7 +"Mode" +p13 +"Thresh" +p21 _  ;Settings
            +"QuTh" +p27 +"PreSt" +p34 +"MinQu" +p42 +"TC" _
            +n + Str0[178 + 16*Btn4,-5] +p8 +Str0[32*Btn2,-4] _
            +p16 +Ctrl0(0) +p23 +Ctrl1(0) +p29 +Ctrl2(0) _
            +p36 +Ctrl3(0) +p43 +Str0[231 + 16*Btn5]

Likewise, the Btn4 Event Channel strings start at Str0[170] but the leading "Event = " portion is not sent to the log. Instead, the start index is advanced to 178 to skip over that portion, and only the following 5 characters are sent via Str0[178 + 16*Btn4,-5]. (Note that an extra space was included after "Left " and "Both " to pad them up to 5 characters to match "Right".)

The same strategy is used to show the Btn5 Setup TC. Those strings start at index 220, but by starting instead at 231 we skip over the "Setup TC = " portion and only send the values of "10 ", "100 ", or "1000" via Str0[231 + 16*Btn5]. In this case the second index can be omitted since we are using the tail end of these strings.


String Size:

UN=Str0?N sets variable UN with the size of the string in Str0. The count includes all displayable characters before the first null.

If you have multiple substrings, say at 16 characters apart, you can compute the size of the UAth string via UN=Str0?N[16*UA]. Note, however, that this assumes that each substring has a terminal null; if not, the count will continue until it finds a null or the end of the entire array.

To deal with such packed strings, you can supply a (negative) count as the second index: UN=Str0?N[16*UA,-16]. This will set UN to 16 maximum, or to the actual size if a null is encountered before that.

You can also find the size of the first string between two arbitrary indexes, as in UN=Str0?N[100,123].

Either or both index / count values can be given as numeric variables or expressions.


Uppercase and Lowercase:

These are special forms of reading or copying that force the case of the text to upper or lower. For example, if there is a substring starting at index 100 you can use Msg=Str0?U[100] to display it in Uppercase, or Msg=Str0?L[100] to display it in Lower.

The Uppercase option is useful to make certain text stand out, such as for use as a header when used in a log file that includes lots of other information.

As with other read and copy operations, you can specify a second index to limit the text copied, as in LogTxt=n + Str0?U[100,123]. Alternatively, the second value can be a negative count of the maximum number of characters to copy, as in LogTxt=n + Str0?U[100,-24].

Either or both index / count values can be given as numeric variables or expressions.

If you use two index values, rather than an index and a negative count, the string will be copied in reverse if the first index is greater than the second.

The uppercase and lowercase options are useful in IF statements to compare two strings while ignoring case, by forcing both to be read as upper or lower. Examples: IF.Str0?U[UA,UB]="TEST STRING" or IF.Str0?U[UA,UB]=Str1?U[UC]. Please note that you must use two indexes on the left side for this to work.


Raw Values:

UA=Str0?R[100] reads the Raw value, which consists of the 4 bytes starting at the given index. The UA value is "raw" because it is exactly as-is, including nulls. For example, if Str0[100]="12" +z +"4", then the raw value (in hexadecimal, viewed via Msg=UA(h)) would be 31320034, where hex 31 is the ASCII value corresponding to "1", etc. Conversely, with the ?R omitted, UA=Str0[100] would give hex 00003132 because reading would stop at the null.

This function can be useful in debugging a string macro that doesn't seem to be giving the expected results.

Note that this function only returns integer values. Since it reads 4 bytes, it is a perfect match for 32-bit integer variables like U0-Z or Q0-Z. If you use it with floating-point variables like A-Z or 64-bit fixed-point variables like Var0-VarZ, the fractional part will be null. If you use 32-bit fixed-point variables like Ua-Uz or Qa-Qz, the 16-bit integer part can only hold 2 ASCII characters, so unless the first 2 string bytes happen to be nulls the raw value will overflow the integer portion and be limited to hex FFFF.


Numerical Text Values:

As noted under Writing to Strings and Reading and Copying Strings above, a value written to a string such as Str0="1.234" will be stored as its directly-displayable ASCII text equivalent... not a numerical value that can be used directly in calculations. The same is true for numbers that appear in text files.

To extract such a number to a binary value for calculations, you can use X=Str0?V. If you know the starting position of the number, you can use X=Str0?V[123].

If a text number starts with 'h', such as "h1234", it will be treated as hexadecimal and returned as 4660. If the text number starts with 'b', as in "b10110110" it will be treated as binary and returned as 182. If the text does not supply the 'h' or 'b' prefixes, but you know that a certain number is hexadecimal or binary, you can use X=Str0?H or X=Str0?B, respectively.

Entries are limited to 32 bits. The ordinary X=Str0?V format will accept values from -2147483648 to +2147483647.999999999.

X=Str0?V will accept values using European notation like "1m234" for 0.001234, where the "m" can be any of the standard scientific prefixes as long as the result does not exceed the above 32-bit limits. It will also accept a decimal and a trailing prefix, like "1.234M" for 1234000. However, it will not accept exponential or scientific notation like "1.234e6" or "1.234*10^6".

These operations assume that text numbers are separated by commas, spaces, tabs, or carriage returns, or are followed by a null. After the operation you can use UT=Str0?T to get the ASCII value of the terminating character, such as 32 for space, 44 for comma, 9 for tab, or 13 for carriage return.

You can also use UP=Str0?P to get a pointer to the character after the terminator. You can use this as an index to acquire the next value, as in X=Str0?V[UP].

Typically, you won't even need to read the pointer explicitly, since after one value is read the pointer is set automatically to the character after the terminator. Any non-value characters after the terminator, such as extra spaces, tabs, or line feeds, will be ignored by the next Str0?V until it finds a value.

If the text is a table or array of numbers you can just keep reading them as needed, such as with Buf0[UI]=Str0?V. If you don't know how many values are in the table, you may be able to tell when the table ends by a change in the terminator. For example, if the values are separated by commas and the last value is terminated with a carriage return or space.

Another way to tell when to stop reading is by prior knowledge of where the next table or section of the text begins, which you may be able to determine ahead of time by a string search for the next section header, or for a special character that starts each section header, such as "[", even if you don't know the full header string. (See the String Search subtopic, below.) In such cases you would read the pointer after each value is acquired, and stop when it points to the next section.

You can also set the pointer manually, for example to skip over a fixed-size header section, using Str0#P=UP.

The pointer is reset to zero if the string array is cleared via Str0=, or any operation with no left index, such as Str0=Str1[100,200].


Expression Evaluation:

As noted under Writing to Strings and Reading and Copying Strings above, a value written to a string such as Str0="1.234" or Str0=A will be stored as its directly-displayable ASCII text equivalent... not a numerical value that can be used directly in calculations.

Individual values like "1.234" or "h1234" can be extracted via the X=Str0?V and similar operations discussed under Numerical Text Values.

You can also extract a value by using the Evaluate function ?E, as in A=Str0?E. You can then use the extracted value in calculations. (Note, however, that you can not use the Str0?E in calculations directly, as in B=C * Str0?E + D; you have to extract it as a separate step, and then use the extracted value in the calculation.)

In fact, you can do much more than simply extract a previously stored value; you can evaluate any expression that Daqarta's macro system can handle. For example, if Str0="pi * R^2" then if R is 10, A=Str0?E will set A to 314.159265359. It will use the value of R that is current at evaluation time, not at the time the string was stored.

Note: The string expression within StrN must not be a complete equation itself; it must not have an '=' sign. Effectively, the string expression becomes the right-hand portion of the equation, assigned to the left-hand variable (A in the above example).

Important: Unlike Str0?V, the value or expression must be terminated by a null, not a space or other separator, or the evaluator will try to include subsequent text as additional terms in the evaluation. (But see below.)

If you have multiple expressions stored in the string, you can select the desired expression via indexes. If the expressions all have the same maximum size and are terminated by nulls, you only need to compute and use the starting index, as in A=Str0?E[UI * US] where each string is up to US-1 characters, plus the null. Without terminal nulls, you'd use A=Str0?E[UI * US,-US] to specify the (negative) size in the second term.

Note that the evaluation can't be used as an index, except under limited conditions: You can only have a single evaluation on the right side of the command, and if it is used to form an index you can only use the single-index form of the evaluation. For example, Msg=Str0[Str1?E[UB]] is OK, but A=Str0[Str1?E[UB,UC]] is not.

Expressions for evaluation can use all standard Daqarta variables, like SmplRate, L.0.ToneFreq, and TrigLevel, just like normal macro expressions. This could be used to create a "select case" macro that performs different computations based on the value of selector UI, as opposed to a whole bunch of IF.UI=0..., IF.UI=1... statements.

Since StrV can be used to select the particular string, the set of expressions can be responsive to changing situations. A Buf0-7 macro array can store up to 4 complete StrN strings using the #aE 2048-byte string size format, and two adjacent BufNs can be saved as a 2-channel file, so you can replace all 8 StrNs with a single file read.


Binary Write and Read:

Although Str0-Str7 are called string arrays, they also allow pure binary values of 5 different sizes from single bytes to 10-byte floats. A single 2048-byte string array can thus provide storage for:

    2048  byte values
    1024  words (2 bytes each)
     512  dwords (4 bytes each)
     256  qwords (8 bytes each)
     204  floats (10 bytes each)

These can be combined in any desired arrangement, along with ordinary text storage. This approach is not only more compact than schemes using the numerical evaluation of strings, but is much easier to use.

For example, the DaquinOscope macro mini-app uses single bytes to store Arduino pin numbers selected for oscillator outputs, 4-byte values to store their step sizes and accumulator start positions, and 8-byte values to store frequencies and phases. It also uses single bytes to store bitmaps of digital input configurations.

By being in an array of fixed sizes, these values can be accessed by simple indexing schemes. For example, to save the frequency Z of oscillator Q0 it uses Str7[1500 + 8 * Q0]#q=Z. Since Q0 can run from 0 to 3, and each frequency takes 8 bytes (indicated by the #q), these values run from index [1500] through [1531]. No separators are needed. To read the stored value later it uses Z=Str7?q[1500 + 8 * Q0].

When storing a value to the string, the size code follows the index with a '#', as in the above Str7[1500 + 8 * Q0]#q=Z.

When reading a value from the string, the size code precedes the index with a '?', as in Z=Str7?q[1500 + 8 * Q0].

You may optionally use a digit instead of a letter code for size. Only the following sizes and codes are accepted:

Write binary to string:
    Str0[123]#b=UA         Copy low binary byte from UA
    Str0[123]#1=UA         Same as above
    Str0[123]#w=UA         Copy low binary word from UA
    Str0[123]#2=UA         Same as above
    Str0[123]#d=UA         Copy 4-byte dword from UA
    Str0[123]#4=UA         Same as above
    Str0[123]#q=A          Copy 8-byte qword from A
    Str0[123]#8=A          Same as above
    Str0[123]#f=A          Copy 10-byte float from A

Read binary from string:
    UA=Str0?b[123]         Read binary byte into UA
    UA=Str0?1[123]         Same as above
    UA=Str0?w[123]         Read binary word into UA
    UA=Str0?2[123]         Same as above
    UA=Str0?d[123]         Read 4-byte dword into UA
    UA=Str0?4[123]         Same as above
    A=Str0?q[123]          Read 8-byte qword into A
    A=Str0?8[123]          Same as above
    A=Str0?f[123]          Read 10-byte float into A

Note that although it's possible to use the Binary-to-String Format typically used in Port Access commands (as discussed in String Variables and Expressions) to store binary values, this is not recommended.

For example, you could use Str0[123]=$b(UA) to store a byte, Str0[123]=$w(UA) to store a word, or Str0[123]=$d(UA) to store a dword. However, the numerical equivalents are not the same for 2 or 4 bytes, since $2 and $4 store the bytes in reverse order. Plus, this system doesn't include a complementary way to read values from the string, and does not support 8-byte or 10-byte values.


Indirect Macro Strings:

Daqarta macros allow calling other macros as named subroutines by preceding the name with @, as in @_ComDev_Scan to invoke the _ComDev_Scan subroutine.

You may want to use different subroutines depending upon certain conditions, such as the type of serial device found by _ComDev_Scan. For example, the DC_Chart_Recorder macro mini-app supports 5 different devices (Arduino plus 4 Numato models), and the code needed to support each is substantially different. Instead of a single named subroutine macro that would need many separate device tests (which would slow operation), each device has its own subroutine that is called on each acquisition time point.

Suppose variable UN has been set to some identifying value that is different for each device (0, 8, 16, 32, or 64) If simple named subroutine calls were used, that would still require multiple IF tests to determine which subroutine to call at each time point:


    IF.UN=0
        @_DC_Chart_Arduino
    ENDIF.
    IF.UN=8
        @_DC_Chart_Numato08
    ENDIF.
    IF.UN=16
        @_DC_Chart_Numato16
    ENDIF.
    IF.UN=32
        @_DC_Chart_Numato32
    ENDIF.
    IF.UN=64
        @_DC_Chart_Numato64
    ENDIF.

Instead, the DC_Chart_Recorder uses a single indirect macro subroutine call through string variable Str7:


    @@Str7[400]

Note the double @@ symbols. This uses a null-terminated string that starts at index 400. This string is created when DC_Chart_Recorder starts up, after setting UN as above based on the results of _ComDev_Scan. It then uses:


    IF.UN=0
        Str7[400]="_DC_Chart_Arduino"
    ELSE.
        Str7[400]="_DC_Chart_Numato"+UN(A)
    ENDIF.

The UN(A) at the end of the ELSE branch converts the numeric value (8-64) to ASCII string text characters.


Linking a String Array to Buf0-Buf7:

A string array is limited to 2048 characters, but can be linked to a macro array buffer Buf0-Buf7 that can be packed with up to 8192 characters. When so linked, you can use string commands for text processing, including string searches.

The link command is StrN#B=n, where N is 0-7 to specify the string array and n is 0-7 to specify the buffer to be linked.

Please note that the string commands may not write to the string, only read it.

To unlink the string and buffer, use StrN#B=-1.


String Search:

To determine if a string array (including an array linked to Buf0-Buf7 as above) contains a particular substring, use Str0[UN]#F="FindString". This will scan Str0 starting from index UN, or from 0 if no index is given, looking for "FindString" (without the quotes).

The target string on the right does not need to be an immediate quoted string as shown above, but can be array string such as Str1, or Str1[UA], or Str1[UA,UB].

Subsequently, UF=Str0?F returns the status of the find operation. This is a 32-bit integer whose 4 bytes have individual meanings. You can isolate these via:

U0=UF & hFF        ;Low byte = last char found
U1=UF>>8 & hFF     ;Length of search string
U2=UF>>16 & hFF    ;Count of chars matched
U3=UF>>24          ;h80 if complete match, else 0

Note that if you just want to determine whether the search succeeded, the 80h in the high byte will make UF appear negative; you can test UF directly with IF.UF=<0.

UP=Str0?P returns the index of the next character after the found string, which can be used as the start of a new scan to find the next instance of the string, or to process data that follows the string. If the search failed (U3 = 0) then UP will point to the character after the failing character.

The string search operation is designed to work with Str0?V and related functions discussed in the Numerical Text Values subtopic above, to efficiently read blocks of data from text files that use specific headers like "[DATA]" to mark various sections.

Once you locate the section to be read, you may want to locate the next section header in order to know when the current section is done (assuming there is no specific terminator). In some cases you can just search for the '[' (or whatever special character starts each section header) and save the pointer, to be compared with the pointer obtained after reading each value in the current block.

By default, the string search is case insensitive. You can specify a case-sensitive search via 'Fc' as in Str0[UN]#Fc="FindString".


See also Macro Overview, Macro Arrays Buf0-Buf7

GO:

Questions? Comments? Contact us!

We respond to ALL inquiries, typically within 24 hrs.
INTERSTELLAR RESEARCH:
Over 35 Years of Innovative Instrumentation
© Copyright 2007 - 2023 by Interstellar Research
All rights reserved