Spectrum Lab's Interpreter

Contents of this chapter:

There is a simple command interpreter in the program, used to define periodic, scheduled, and conditional actions; macro buttons and other purposes. Multiple commands can be entered in a single line using the ':'-character as separator (like in the old BASIC programming language). An example:

capture:export.start("NewFile01.txt"):export.write(";-- new file segment --")

For testing purposes, you can enter and run commands in the periodic actions dialog, or you can enter them in the command window

Functions: A function is a subroutine which usually requires some kind of input (passed in an argument list), and it always returns a value to the caller. Function names are written in lower case to tell them from variable names (do not mix functions with procedures. A procedure does not return a value). Functions and numeric expressions can also be entered in the watch window where they will be periodically evaluated and displayed.

Note: Most (but not all) interpreter commands and -functions can be invoked from a remote PC through SpecLab's integrated HTTP server, using a piece of Java script which you can find in the examples in the "server_pages" directory.

See also: Spectrum Lab's main index , programmable buttons .

Overview of interpreter procedures and functions

A procedure does not return a value (in contrast to a funcion. Sometimes (for historic reasons), the terms 'procedure' and 'command' are used for the same type of keywords. In future versions of the interpreter language, there will also be program flow commands like if-then-else, for-to-next, etc.

A function returns a value to the caller, in contrast to a procedure. In the syntax of Spectrum Lab's interpreter language, the result returned by a function must be 'used' in some way (because otherwise calling a function makes no sense), for example assign it to a variable, use it as part of an expression, or pass it in the argument list to another function or procedure.
With a few exceptions, the argument list begins with an opening parenthesis after the name of the procedure or function being called.

Most procedures and functions can also be invoked by other programs(!) using a message-based protocol, or through the DOS command line (even if the program is already running), or remotely from Javascript through SL's web server.

The following list contains an alphabetically ordered overview of procedures and functions which are implemented in the interpreter itself.

Browse interpreter commands and functions by 1st character: a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s   t   u   v   w

  • asin(arg)
    returns the arc sine of the input value (argument). The input must be in the range -1 to 1. The output (returned value) is in the range -pi/2 to +pi/2, i.e. radians. To convert from radians to degrees, multiply the result with (180/pi) .
  • angle(x,y)
    calculates the angle for a given rectangular coordinate. The result is in the range -pi/2 to +pi/2, i.e. radians. Examples:
    angle(1,0)=0 ; angle(1,1)=pi/4 ; angle(0,1)=pi/2 ; angle(0,-1)=-pi/2 ; angle(-1,0)=pi
  • avrg(freq1, freq2), avrg_n(freq1, frequ2)
    returns the (power-)average value in the specified frequency range
  • azim(freq1, freq2)
    returns the angle-of-arrival (or "bearing") of a signal received in the specified frequency range. Only works in RDF mode.
  • blackbox (abbrev'd as "bb")
    When invoked as function, reads parameters and current values of the DSP blackbox components in the test circuit.
    When used as the destination of an assignment, these tokens can be used to modify parameters of the DSP blackbox components in the test circuit.
  • button[N].xyz
    Reads or modifies a few properties of a programmable button.
    N is the button number, currently between 1 and 16. xyz is the name of the property, for example color.
  • capture
    Saves the current waterfall and/or spectrum plot on the main window as a bitmap file. Filename and JPEG quality can be specified in the command too, like this: capture("LowQual.jpg",30). If the quality (2nd parameter) is omitted, it will be as configured . Note that backslashes in strings can have a special meaning (as in the "C" programming language). More info on the capture command, and how to upload captured images to an FTP site, is described here.
  • ca.xyz     (conditional actions)
    Commands to start, stop, or re-initialise SL's Conditional Actions. Mainly used for debugging.
  • click
    Generates a short clicking sound with the PC speaker. Used for debugging. This used to work under Win95, Win98, NT and Win2000. Not sure about any later windows version. (Under Windows XP, the "click" sound is, by default, a dull-sounding "bong!", which can be modified somewhere in the swamps of the windows system control).
  • cfg.xyz
    Command to read or modify a component (xyz) in Spectrum Lab's configuration. To modify parameters, use a formal assignment (cfg.xyz := new_value).
  • circuit.xxx
    A group of procedures and functions to control other components of the test circuit, for example the oscillator (NCO) for the frequency converter.
  • continuation
    For the 'conditional actions':
    Returns TRUE if the condition in the previous line was TRUE; and FALSE if the condition in the previous line was FALSE. This feature can be used to split long command sequences into subsequent lines in the CA definition table.
  • counter.xyz
    Retrieve results from the Counter/Timer module (pulse- or event counter, frequency counter, etc).
  • cursor.xyz
    readout cursor functions
  • dB(voltage [,ref_value] )
    Function to convert a linear ratio (or "voltage") into decibels. If the reference level (2nd argument) is not specified, the reference value is assumed to be 1.0 (unity). Note that unlike dividing by ref_value in a formula, the dB-function will handle reference values of zero without throwing an error (and return +200 as a "very large" value). Examples:
    dB( 2 ) = 6.021 ;  dB(25,100) = -12.041
  • dec( <variable>)
    Decrements the specified variable by one. The instruction 'dec( Counter )' has the same effect as 'Counter=Counter-1' but is a bit faster. Only works for floating point and integer variables (not for strings).
  • digimode (abbrev'd as "di")
    Accesses settings and parameters of the digimode module, for example the decoder's AFC'ed frequency.
  • edit
    Edits a string-variable (single line) or a numeric variable.
  • else
    For the 'conditional actions':
    Returns TRUE if the condition in the previous line was FALSE; and FALSE if the condition in the previous line was TRUE.
  • exec
    Used to run an external file, with optional passing of parameters in the (DOS-) command line. Be careful with this command, because running other programs may steal a lot of CPU power which may cause the loss of audio samples !
  • export.xxx
    A group of commands to start and stop the user-defined text file export.
  • export.value[column]
    returns the last calculated value in a particular column of the export file. There are also some functions to read the contents of the export file definition table with the interpreter.
  • fo_cal.xxxx
    Used to control the frequency offset calibrator.
  • fopen, fread, fwrite, fprint, fclose, FileExists
    open, create and close text files, write strings or lines of text into a file for special applications, etc.
  • fft.export.xxx
    A group of commands to control the export of FFTs into simple text files. Only required if the normal (periodic) export of FFTs isn't sufficient.
  • filter.xxx
    A group of procedures and functions to control the audio filters.
  • fmarker[n].xxx
    Read or modify a property of a frequency marker (one of the diamond-shaped objects visible on the frequency scale).
  • fo_cal.xxxx
    Used to query values from the frequency offset calibrator. Can be (ab-)used for a precise measurement of a single frequency.
  • generator.xxx
    A group of functions and procedures to control the test signal generator. Use a formal assignment (":=") to set a parameter.
  • GetSystemTime
    Retrieves the PC's current system time in UTC, without any of corrections and 'deliberate offsets' (unlike 'now'). For very special applications, SL can also set the current system time via SetSystemTime.
  • gps_dec.xxx
    A group of functions and procedures to control the GPS decoder (mostly an NMEA parser).
  • inc( <variable>)
    Increments the specified variable by one. The instruction 'inc( Counter )' has the same effect as 'Counter=Counter+1' but is a bit faster. Only works for floating point and integer variables (not for strings).
  • int(X)
    returns the integer part of X. For example, int(123.456) is 123 . A similar function, round(), returns a rounded floating point value instead.
  • initialising, terminating
    Used in the "IF"-column of the conditional actions. The 'initialising'-flag is set once after starting Spectrum Lab, or after loading a new configuration. The 'terminating' flag is set shortly before SL terminates. It was used for automation, for example to copy certain files to a website showing "spectrogram offline" or similar.
  • log(x)
    This function returns the decade logarithm of x .
  • min(value1, value2, ..)
    This function compares two or more numeric values (or expressions), and returns the miminum value all arguments.
    For example, min( 1,2,5,-2 ) returns -2 .
    Of course this function makes more sense when operating on variables, not constants.
  • max(value1, value2, .. )
    This function compares two or more numeric values (or expressions) and returns the largest (most positive) value.
  • NameWithoutPath( filepath )
    This function returns a filename (without path) for the specified full path (drive letter, path, filename, file extension).
  • new_spectrum
    Used in the "IF"-column of the conditional actions. Whenever a new spectrum (FFT) has been calculated, the entire table of 'conditional actions' is executed with this flag set. Follow the above link for details.
  • new_strip
    Similar like 'new_spectrum', but this flag for the conditional actions signals the begin of a new "strip" in the multi-strip spectrogram. Follow the links for details.
  • never
    Dummy for the conditional actions. This condition 'never' gets true (thus the name). Actually, 'never' is implemented as an interpreter function which simply returns zero (=FALSE).
  • noise(freq1, freq2)
    returns the noise level in a specified frequency range. Uses an algorithm suggested by G4JNT (which is also used in SpecLab's ALERT module).
  • now
    This function returns the current time (including the date) in the internal format of the program, which is a combination of date plus time as a floating point number.
  • pam(center_frequency, mode).phase, pam().ampl, ...
    Phase- and amplitude monitor. To be used in the watch / plot window. Replaces the phase meter in the time domain scope.
  • peak_a(freq1, freq2)
    returns the maximum amplitude in the specified frequency range.
    Usually in decibels, if the FFT output is logarithmic.
  • peak_f(freq1, freq2), peak_rf(freq1,freq2)
    returns the frequency of the highest peak in the specified frequency range (by default, audio frequency range; optionally RADIO frequencies)
  • plot.xxx
    A group of procedures to control the real-time plotter, including a screen capture of the current diagram.
  • print
    Prints some numeric or string values into one line of the interpreter's message window. Used for debugging.
  • ptt_port.xxx
    Procedures and functions to access the COM-port used for PTT control (PTT="push-to-talk", to control a radio transmitter). Can be used to polling some signals on the serial port in the conditional actions.
  • rct.xxx
    Remote Control for certain HF- and VHF radios. Allows changing the frequency which the radio is tuned to, etc.
  • REM
    Remark. The rest of the command line (after the REM) is ignored.
    Can be used to embedd comments in the conditional action table, etc.
  • rec.xxx
    Commands for the "triggered audio recorder"
  • round(x)
    This function returns the rounded value (still as a floating point number!).
    round(0.4) returns 0; round(0.5) returns 1.
    See also: int(x) (returns the truncated integer part).
  • sdr.freq, etc
    Procedures and functions to control an external software defined radio (like Perseus and SDR-IQ) .
  • send(<recipient>,<message>)
    This procedure sends a text message to another application.
  • SetSystemTime( unix_date_and_time )
    Sets the PC's current system date- and time, measured in seconds elapsed since 1970-01-01 00:00:00.0 UTC ("Unix seconds"). Only works when the program runs 'as admin'. Don't use when any other time-synchronizing service is available (GPS, DCF, NTP, ..). See also: GetSystemTime, rsNTP, time sync per GPS.
  • sin(x), cos(x), sqrt(x), pi
    some of the usual math functions and constants
  • sinad(#channel, f_center, notch_width, filter)
    Calculates the SINAD value (SIgnal Noise And Distortion).
  • sigma( #channel, freq1, freq2 )
    Calculates the standard deviation (greek sigma) in a certain spectrum (#channel) within a certain frequency range.
  • sound(<freq>)
    Can produce a tone ("beep") of any frequency in the PC speaker.
  • spa.xxx (spectrum analyser)
    Access to a few parameters of the spectrum analysers, mainly the pre-processing of data before the FFT.
  • spectrum.xxx (abbreviatable as "sp.xxx")
    Procedures to save the current spectrum (="FFT result") as a text file, start/stop the spectrum display, print into the spectrogram, etc;
    and functions to query some properties of the current spectrum, "pause"-status, etc.
  • spa.xxx
    Parameters and additional values of the spectrum analyser, or the readout-cursor in the spectrum analyser.
  • sr_cal.xxxx
    Procedures and functions for the sampling rate calibrator.
  • sref.xxx (spectrum reference)
    Turn the spectrum reference on and off, pick it from the current spectrum, load and save as disk file.
  • str(<format_string>, <value>)
    turns a numeric value into a string. The format string defines how the number shall be formatted.
  • system.info
    Returns some "system info" as a string (including the number of free variables and free string memories, etc). Used for debugging purposes.
    returns the directory path where the executable (Spectrum Lab) has been installed.
  • system.browse(filename, anchor)
    opens the default HTML browser to load a certain file, and jump to a certain anchor within that file.
  • terminate
    Terminates Spectrum Lab. Can be sent as a command to other instances too, to terminate them all.
  • time
    returns the time when the last line of the waterfall (FFT) has been calculated. Depending on the scrolling speed, this can be different from "now". The unit is the same as for "now" (seconds elapsed since 1970-01-01 00:00:00.0 UTC).
  • timerX.start, timerX.restart, timerX.periodic (X = 0..9)
    Commands for the user-programmable timers, mostly used in the "Conditional Actions" (to produce events, etc).
  • timerX.expired (X = 0..9)
    These functions check if one of the user-programmable timers is expired. Can be used to generate events for the "conditional actions".
  • time_dec.xxx
    A group of functions and procedures for the (longwave-) time signal decoder (mostly limited to DCF 77).
  • tds.xxx
    access the time-domain scope (current amplitude and phase value, depending on the scope mode)
  • trigger.xxx
    Procedures and functions to control the universal trigger module, for example from the "Conditional Actions".
  • tzo     (time zone offset)
    Returns the difference between local time and UTC (i.e. "local time MINUS UTC"), measured in seconds. Useful to display date and time as local time instead of UTC.
  • val(<string> [, <format string> ] )
    turns a string into a numeric value. The <format string> is optional, only really required if <string> represents a time+date.
  • veff (freq1, freq2)
    returns the effective voltage in a given frequency range (normalised to 1 Volt for 100% SINE WAVE at the ADC's input).
  • water.xxx
    Procedures and functions to access to waterfall settings and -variables. Some affect the spectrum graph too, thus the prefix "water." may be a bit confusing.
  • wave.xxx
    A group of commands (and functions) for recording and playing audio streams as wave files. 
  • window.xxx
    Allow setting the window size and -position of the main window. Components (xxx):  left, top, with, height . When called as a function, window.xxx returns the current window settings.

See also:

  • special functions for the conditional actions ("events")
  • numeric operators (in the next chapter)

back to top

Numeric expressions

The numeric interpreter described here is used to evaluate 'formulas' wherever needed in the program. Since 2013-13, the interpreter supports floating point and integer values (besides strings). Examples for numeric expressions:

  100.0 * sin( 2 * pi * X)     [the result will be a floating point value]
  1 + 3*4         [here the result is an integer because all 'inputs' are integer]

The basic function is similar to a programmable ("basic") pocket calculator, expressions are evaluated from left to right (with different operator precedence levels: multiply before add etc, similar to the "C" programming language).

Any expression is internally converted from the normal infix notation into RPN (Reverse Polish Notation) before being evaluated ("calculated"). Details about RPN, and how the intermediate RPN can be examined (for debugging purposes) follow at the end of this chapter.


  • + - * / % ^  (arithmetic: add, subtract, multiply, divide, modulo, X power Y )
  • < <=  == <>  >=  > (comparations: less than, less or equal, equal, not equal, .... greater than; same priority as 'add')
  • |, || (pipe character, ALT-"<", boolean and bitwise OR, same priority as 'add')
  • &, && (ampersand character, boolean and bitwise AND, same priority as 'multiply')
  • ! (boolean inversion, aka "NOT", like in the C programming language)
  • XOR bitwise exclusive OR, frequently used to "toggle" bits, for example:
    filter[0].fft.options = filter[0].fft.options XOR 16
  • <condition> ? <value1> : <value 2> (the "arithmetic if-then-else" as known from the "C" programming language)
    returns <value 1> if the condition if TRUE (non-zero), or <value 2> otherwise .

The operands in a numeric expression can be simple numbers, variables, or function calls.


  • 1234.5    (german users beware (*): there is a decimal point, not a "decimal comma")
  • 14.060e6  (scientific exponential form; 14.060 * 10 power 6 = 14060000 )
  • In most numeric expressions, there is also this 'technical' notation instead of the 'scientific' exponential form:
    (same result as above, using a "technical" exponent; the rest -here "Hz"- is skipped and ignored). Technical exponents must be single characters; and one of the following:
    k=kilo(10^3), M=Mega(10^6), G=Giga(10^9), T=Tera(10^12), m=milli(10^-3), u=micro(! .. 10^-6), n=nano(10^-9), p=pico(10^12).

Numeric expressions can be used as argument for some other commands (they can be "nested"). Example:

Some more examples can be found in the description of the print command.

Note for German users / Hinweis für deutsche Anwender: Spectrum Lab verwendet - wie die meisten Programmiersprachen - das Komma als Aufzählungszeichen. Als Dezimaltrenner dient immer der international übliche Dezimalpunkt, nicht das verfluchte Dezimalkomma ! Es ist daher nicht nötig, in irgendwelchen Windoze-Systemeinstellungen herumzuwühlen um die "deutschen Besonderheiten" wie das Dezimalkomma abzuschalten.

Evaluation of expressions via Reverse Polish Notation

(only for advanced and curious readers)

As already mentioned, any expression is first converted into RPN (Reverse Polish Notation) before being evaluated. For debugging purposes (to find bugs with operator precedence, etc), the intermediate RPN can be displayed in SL's command window by the function 'infix2rpn()', which accepts any infix expression in its argument list, and returns a string with a symbolic representation of the RPN (kind of "disassembly"). The value returned by 'infix2rpn()' can be printed to the output in the command window.

  print( infix2rpn( 1 + 2 * 3 / 4 ^ 5 ) );

Output (expression in RPN "printed" into SL's command window):
  1 2 3 * 4 5 POW / +

Basically, RPN is evaluated as follows:

  • Evaluation begins at the left side
  • Operands (like 1,2,3,4,5 in the example shown above) are pushed to the stack
  • Binary operators (like +, -, *, /, and ^ aka POW) pop two operands off the stack,
    operate on them, and push the result back to the stack
  • At the end of the expression, only the result remains on the stack

See also: overwiev , variables, functions, procedures , string expressions .


An interpreter variable can accept a single numeric value, or a single string. It begins to exist by assigning a value to it, there is no need to declare a variable (in contrast to the "C" programming language). Variables were initially used to define conditional actions, etc, but they may be used in a script-like language one day too.

The name of a variable must begin with an upper- or lower case letter, the next characters may include decimals (0..9) and the underscore. Nothing else !
Furthermore, be careful not to use built-in keywords as variable names. This will usually result in a syntax error or similar.

Some examples for variables:

CurrentPeak = peak_a(300, 3000)

InfoString = "Oh, well"
InfoString = InfoString + " .. so what"

TimeString = str("hh:mm:ss",now)

Variables can help to reduce the CPU load. For example, if you need the result of a complicated function (or subroutine) in many different places, it is more efficient to calculate the function only once and store the result in a variable, instead of computing the same result over and over again. A good place to do this is in the conditional action table. Since all variables in SpecLab are "global" (up to now), you can read the value from the variable wherever you need it, for example in the watch window, on a programmable button, etc.

Note: You can even access those variables from a Java script running in your web browser ! The integrated HTTP server allows any application, even on a remote PC, to communicate with SpecLab - which includes read- and write access to the interpreter variables.

Time- and Date Functions

now, time (functions)

The following interpreter functions return date- and time as a floating point value (which can be converted into a string if required)

  • now
    returns the current time (including the date) in the internal format of the program (see below). The result is always in UTC (Universal Time Coordinated), but it can be converted into local time by adding 'tzo' (time zone offset).
  • time
    returns the time when the last line of the waterfall (FFT) has been calculated. Depending on the scrolling speed, this can be different from "now".
    The format is the same as used for "now".

 The format of the values returned by these functions is defined as:

"seconds elapsed since Jan 1 1970, 00:00:00 UTC".
(Note: because it is a floating point value, the resolution is much better than a second).

To display time and/or date, use the str-function which can turn numeric values into strings . Example:

print("Date="+str("DD-MM-YYYY",now), "Time="+str("hh:mm:ss",now) )

tzo (time zone offset)

This function returns the difference between local time and ('minus') UTC (Universal Time Coordinated).
For good reasons, Spectrum Lab uses only UTC internally. Thus, functions like 'now' and 'time' return UTC, too. If you want to display the time (and date) as 'local time', possibly depending on daylight saving, you can add the UTC plus the value returned by 'tzo' (time zone offset, measured in seconds, because that's the SI unit for time).

Example (for the 'string expression' of a programmable button, displaying the current local time:
       "Local Time: "+str("hh:mm:ss.s", now + tzo )      

See also:   Overview of interpreter functions, spectrum.time, timezone selection in Spectrum Lab.

Spectrum Analyser / Channel Numbers for some numeric functions

Most of the calculation functions explained in the following chapters accept an options "spectrum analyser channel number".

Valid spectrum analyser/channel numbers are:
#1 = first channel of the first spectrum analyser ("main spectrum analyser / spectrogram")
#2 = second channel of the first spectrum analyser ("main spectrum analyser / spectrogram")
#3 = first channel of the second spectrum analyser ("second spectrogram window")
#4 = reserved for the second channel of the second spectrum analyser (future plan !)
#LTA1 = long-term average of the first channel in the first analyser (if enabled)

If specified at all, the spectrum analyser/channel number must be the first parameter in a function's argument list. If it is not specified (which will often be the case), the first channel of the first analyser will be used by default. This applies to the following interpreter functions:

See also: peak detection functions,  noise calculation functions, spectrum average functions,  interpreter function overwiev .

Peak detection functions

  • peak_a(freq1, freq2)
  • peak_a(#<channel>, freq1, freq2)
    returns the maximum amplitude in the specified frequency range.
    Usually in decibels, if the FFT output is logarithmic. The peak_a function checks the contents of all FFT bins(*) in the specified frequency range, and returns the amplitude (magnitude) of the strongest peak using an interpolation method which is described here.
    If the optional "channel" (number or name) is not specicied as the first parameter, the function operates on the first channel of the current spectrum of the first frequency analyser (a special channel name accesses the long-term average).
  • peak_f(freq1, freq2)
    returns the audio frequency of the highest peak in the specified frequency range.
  • peak_rf(freq1, freq2)
    returns the radio frequency of the highest peak in the specified frequency range.
  • azim(freq1, freq2)
    returns the angle-of-arrival (or "bearing") of the strongest signal in the specified frequency range. More information in the documentation of the radio direction finder.

If no channel number is specified, the peak detecting functions operate on the 'latest' result from Spectrum Lab's main spectrum analyser, first channel (= "#1"). Optionally,  a spectrum analyser / channel number can be specified. Examples:

  • peak_a( 45, 1000) or peak_a(#1, 45, 1000)  calculates the peak amplitude for the first channel of the first spectrum analyser (=main spectrogram) in the frequency range 45... 1000 Hz
  • peak_a(#2, 45, 1000)  calculates the peak amplitude for the second channel of the first spectrum analyser in the audio frequency range 45... 1000 Hz
  • peak_f(#3, 45, 1000)  calculates the peak frequency for the FIRST channel of the SECOND spectrogram analyser in the audio frequency range 45... 1000 Hz

In most cases, the frequency range of interest is specified as an audio (or 'baseband') frequency range. Alternatively, the frequency range of interest can be specified as 'radio' frequency range (i.e. including the VFO frequency of a software defined radio, or similar). To use radio frequency ranges, put the token "rf:" before the start frequency as in the following examples:

  • peak_a( rf:14.058e6 ,  14.062e6 ) determines the peak amplitude between 14.058 MHz and 14.062 MHz.
  • peak_rf( rf:14.058e6 ,  14.062e6 ) detects the peak radio frequency (thus _rf) between 14.058 MHz and 14.062 MHz.

Instead of specifiying start- and end frequency, a frequency range can also be defined as center frequency (either audio or radio frequency), and bandwidth in Hz. Use the token "bw:" (bandwidth) before the 2nd frequency. Example:

  • peak_a( rf:14.060e6 ,  bw:4000 ) returns the peak amplitude around 14.060 MHz, in a 4 kHz wide range (-> 14.058 to 14.062 MHz)
  • peak_f( af: 50,  bw: 5 ) measures the exact peak frequency near 50 Hz (audio frequency), in a 5 Hz wide span (bandwidth 5 Hz).

Note: Since most signal analysis functions operate on the current FFT from the main spectrum analyser, their resolution or accuracy depends on the FFT parameters configured here. Don't expect a frequency accurary in the milli-Hertz range when the FFT frequency bins are 100 Hz wide.

See also:

Frequency interpolation (for peak-detecting functions and cursor display)

To increase the frequency-resolution of the peak detecting functions to a fraction of an FFT bin width, an interpolation algorithm is used as explained below. It is based on a recipe suggested by DF6NM.

First, the FFT bin with the strongest magnitude (dB) is searched in the frequency range of interest. Next, from the magnitude of the strongest FFT bin and it's strongest neighbour, the exact frequency and and amplitude of the bin can be interpolated. This is possible, because the curvature of the fourier-transform near the peak is known - it only depends on the windowing function which was applied to the samples in the time domain, before the FFT. This only works if the carrier is stable and quite strong, but under such circumstances the result (the frequency accuracy) can be stunning:

It is often possible to measure the frequency of a carrier with a resolution of a few milli-Hertz, though the FFT bin width is in the Hz-region ! This way, you can measure frequencies quickly and accurately, which would otherwise take an endless gate-time (consider this: a classic "frequency counter" needs a tate time of 1000 seconds to measure a signal with a 1-mHz-resolution, which doesn't mean accuracy).

The frequency-interpolation subroutine is internally used by the peak-detection functions and for the readout-cursor on the main spectrum display.

Average Functions


  • avrg(freq1, freq2)
  • avrg_n(freq1, freq2)
  • avrg( #<channel>, freq1, freq2)

The avrg functions work like this (in detail):

  1. Add the values (converted into POWERs) from all FFT bins which belong to the specified frequency range.
  2. Divide the sum by the number of FFT bins.
  3. Convert the result back into the spectrum analyser's "display"-unit, which is often (but not always) decibels.
  4. (only for avrg_n)
    Add or subtract a few decibels, depending on the current FFT settings. The normalization procedure is explained here in detail.

The avrg function can be used to determine the relative strength of (relatively) broad-band signals with known modulation patterns. For example, there may be a digital emission on 137.0kHz with a bandwidth of 100Hz. Assuming you have an LF receiver tuned in USB to 135.0kHz, use an AF frequency range of 1950.0 to 2050.0 Hz for the avrg function. This will give an estimate for the signal strength of that particular station.

To measure the signal-to-noise ratio of broadband signals, use the avrg-function in combination with the noise-function. (Use a LARGER frequency range for the noise function, at least 5 times the bandwidth used for "avrg").

The relation between input voltage into the A/D-converter and the FFT output value in decibel (dB) is explained here.

Optionally,  a spectrum analyser / channel number can be specified if you don't want to use the 1st channel of the 1st spectrum analyser. Example: avrg(#2,200,1000)for the 2nd channel of the 1st spectrum analyser.

See also:  effective voltage, SINAD,   function overwiev

Calculation of an "effective" voltage

veff(freq1, freq2)

This function returns the effective voltage in a given frequency range, normalised to 1 Volt for 100% SINE WAVE at the ADC's input, based on the latest spectrum calculated in the main spectrum analyser.


  • The result is always proportional to a voltage, regardless of the amplitude display unit of the spectrum analyser. If the analyser returns "dB"-values, the veff-function converts these dB-values back to voltages internally.
  • Unlike a "true RMS" voltmeter, this function is frequency sensitive. It does not operate on data in the time domain, but operates on the data in the frequency domain (in fact, it uses the result from the main spectrum analyser. If the main frequency anayser is "off", this function does not work).

See also:  "average" functions,  SINAD,   function overwiev

Calculation of the standard deviation ("sigma") in a part of the spectrum

For some 'special' applications (or for debugging..), a function was implemented to calculate the standard deviation (greek lower case sigma; german: Standardabweichung).

sigma(#channel, frequ1, frequ2 )

The returned value is the standard deviation (sigma) in the spectrum of the specified channel, between freq1 and freq2.

The frequency range must be specified because a part of the spectrum will often be unusable, maybe due to filtering, 1/f-noise, or other reasons which only you (and not SL) will know.

See also: function overwiev 

SINAD calculation

The sinad function calculates the ratio betwen SIgnal, Noise And Distortion to noise and distortion as a logarithmic value. It provides a quantitative measurement for the quality of an audio signal.

sinad(#channel, center_freq, notch_width, filter_model)

where all these values are optional:

  • channel: defines the source of the data used for SINAD measurement.
    In Spectrum Lab, this must be one of the spectrum analyzer channels.
    #1 is the first channel, #2 is the second channel.
  • center_freq: the frequency of the audio test tone.
    Usually 1000 Hz which is the recommended -and also the default- value.
  • notch_width: Width of the notch filter in Hertz.
    The default value (if this parameter is not specified) is 30 Hz.
  • filter_model: Specifies the characteristic of the audio filter inside the SINAD meter.
    Possible values are:
    0 = no filter (flat response), no bandwidth limit
    1 = C-MESSAGE filter used in North America
    2 = CCITT filter specified in ITU-T ("p53 filter")


Calculates the SINAD value in dB using the C-MESSAGE filter.
Notch width = 30 Hz is OK for FM receivers, but may be a bit too narrow for SSB receivers with coarse tuning steps or drifting VFO's.
Calculates the SINAD value in dB using the CCITT ("p53") filter.
Notch width = 200 Hz should be ok for VHF SSB receivers even if there is some VFO drift.


  • To get the most accurate results, use the lowest possible notch width. If the notch is too wide, a lot of noise power will not be taken into account. ITU-T Recommendation 0.132 describes a notch filter used with 1.02 kHz tones with a maximum notch width of 400 Hz, and a minimum notch width of 25 Hz with a depth of 50 dB. The notch in SpecLab is realized by simply leaving away all FFT power bins within the notch width, the effecive notch is much deeper than the required 50 dB.
  • If you make the notch too narrow, the test signal may be outside the notch so it would erroneously be considered as "distortion". For more info, read the extra document about SINAD measurements and how the SINAD meter in SpecLab works internally. That documents also describes some pitfalls which must be avoided when using the a soundcard simultaneously as a sine generator and a SINAD meter.
  • There is a preconfigured settings file named SINAD_1.USR in the installation archive. It displays three SINAD values for the three different filter models simultaneously on the programmable macro buttons on SpecLab's main screen. Be sure to watch also the spectrum when making SINAD measurements. This helps to avoid false results when the signal from a VHF SSB receiver 'wanders' outside the notch range.
  • To display the SINAD value in one of the programmable macro buttons, use a string expression like this (taken from sinad_1.usr):
    "SINAD1="+str("00.00",sinad(#1,1000,100,1))+" dB"

See also:  more about SINAD measurements,  average functions,  effective voltage,   function overwiev

Noise calculation functions

The noise functions calculate the noise level in a specified frequency range.

  • noise(freq1, freq2)
    returns the noise floor in the specified frequency range. Uses an algorithm suggested by G4JNT (which is also used in SpecLab's ALERT module).
  • noise_n(freq1, freq2)
    Same as noise(), but noise_n additionally normalizes the result for a receiver with exactly 1 Hertz bandwidth, to make the reading independent of the current FFT settings. The normalization procedure is explained here in detail.

 The definition of noise levels is not easy. Here is the basic algorithm of the 'noise' function:

  1. An array of amplitudes (usually dB values) from the last FFT calculation is sorted into order of increasing amplitude.
  2. The amplitude of the lower quartile value (for example bin number 256 in a sorted set of 1024 points) PLUS 3dB is then returned as an estimate of the mean noise level.

This technique automatically throws away very high values (strong signals) that would otherwise affect the result.

So the value returned by the noise()-function is very different from the average value for the same frequency range (there is one special case where both should be equal, see the notes below).

Optionally,  a spectrum analyser / channel number can be specified if you don't want to use the 1st channel of the 1st spectrum analyser. Example: noise_n(#2,200,1000) to retrieve the noise level from the 2nd channel of the 1st analyser, between 200 and 1000 Hz.


  • The values returned by the noise() function depend on the current FFT settings. Because of the "FFT gain" and the additional gain from optional FFT averaging, the noise()-result will become lower when the FFT size is increased.
    When you double the FFT size, the noise will drop by 3dB. Examples:
    FFT size = 1024 -> noise result = 59.5 [dB]
    FFT size = 2048 -> noise result = 56.5 [dB]
    FFT size = 65536 -> noise result = 31.5 [dB] (for the same WGN signal)
  • The value returned by the noise_n() function does NOT depend on the current FFT settings (because the FFT- and decimation 'gain' are internally subtracted). Therefore, the result of noise_n() may be very different from the noise 'floor' which you see in the spectrum graph.
  • If the observed frequency contains 'white gaussian noise' (WGN), the values returned by noise(f1,f2) and avrg(f1,f2) should be equal. You can verify this with SpecLab's noise generator.

The relation between input voltage into the A/D-converter and the FFT output value in decibel (dB) is explained here.

See also: average functions, sigma( ),  SINAD,  function overwiev

Noise normalised to 1Hz bandwidth

The 'noise' level (and average value of noisy signals) calculated from a spectrum with the "noise()"-function is influenced by some FFT settings (FFT size, window function) and the soundcards sampling rate. But for some applications it is desirable to have 'noise level' readings which do not depend on the FFT settings and the sample rate.

Spectrum Lab offers the possibility to 'normalize' the result of the noise() and avrg()-functions (then called noise_n and avrg_n) to make them independent from the current FFT settings. To be precise: make them independent from the effective FFT bin width, measured in Hz.

The noise (and average) value can be normalised for a receiver bandwidth of 1 Hz, no matter if the currently used FFT bin width is 6 Hz or 0.0001 Hz or whatever. You can consider an FFT bin as a single receiver for a very small frequency range, and all power in that frequency range is dumped into the bin. An FFT frequency bin actually contains an ENERGY because it collects the power over a certain time, but don't care about that now. The FFT bin with is called "frequency resolution" somewhere else in this manual. It is related with the "equivalent receiver bandwidth", but the latter also depends on the FFT windowing function as explained here.

Due to the nature of noise, if the bin width is halved (for example by doubling the number of bins), the noise(-power) collected in every single bin is reduced by 3 dB. One may call this phenomenon "FFT gain".

The formula used by Spectrum Lab to "normalize" a noise value (in decibels) is this:

db_norm = db_calculated - 10 * log10( fft_bin_width_hz )

FFT bin width (~"RX bandwidth") = 0.5 Hz,
measured noise (db_calculated) = -80dB.

The noise measured with an RX-bandwidth of 0.5 Hz must be lower than it would be with 1.0 Hz bandwith. The normalised "noise" value is:

db_norm = -80[dB] - 10*log10( 0.5Hz / 1.0Hz)

= -76.99 dB, which -as expected- is 3dB MORE than the non-normalized value noise.


  • It does not make sense to normalize "Peak"-values, because the FFT gain only lowers the 'noise floor' but it does not affect the measured amplitude of a single-frequency (monochromatic) signal.
  • The normalised noise calculation was implemented primarily for Spectrum Lab's export function, to get comparable results for propagation experiments on longwave, not depending on the FFT settings.
  • In radio amateur software, signal-to-noise ratios are often specified for an 'assumed' receiver bandwidth of 2.7 kHz. Spectrum Lab doesn't follow that path, but if required, you can convert any SNR-reading as explained above:
    An SNR of 0 dB in 1 Hz bandwidth would be indicated as -10 * log10( 2700 Hz / 1 Hz ) = -34.31 dB by programs like WSJT, WSPR, and many others.

See also:   average-function ,   noise-function , veff-function , dB-function ,  function overwiev

Decibel conversions and the '0-dB-point'

Usually, values from the ADC (analog/digital converter) are fed into an FFT (fast fourier transform) which generates an amplitude spectrum at first. The next (optional) stage of processing turns the amplitudes into decibels.

This page contains some background information how the dB conversion works, and how it can be modified.

Logarithmic scales and the 'reference level'

If a pure sine wave A*sin(omega*t) is fed into the ADC's input, one of the FFT bins will contain the peak value which is proportional to the input voltage.

The values from all FFT bins are then converted into decibel value. One of them contains the peak value Pdb:

Pdb = 20 * log(A / R). (simplified, if the peak if in the "center" of an FFT bin)

The value R is an "internal Reference parameter". It depends on the soundcard and on the FFT size, but don't worry about it (the program cares for the FFT size). The maximum value of PdB is 0 dB. Above this point, the ADC gets overloaded by a single sine wave.

  • Note: Always stay below the clipping point. In case of doubt check the input monitor to avoid overloading the ADC, especially while looking only at a small part of the spectrum. The A/D converter may be overloaded from a signal which you don't see in the displayed spectrum range !

For this reason, all internally stored dB values (and those returned by some interpreter functions) are negative (they can go down to -90dB .. -140dB, depending on the FFT size and decimation).

For display, you can add an offset to the internally stored dB value. If you prefer to see positive dB values on the screen, or want to 'calibrate' the dB values to have absolute values on the screen, read the next chapter.

User-defineable 'dB-display-offset'

This 'offset' will be added to the result of the 'PdB' formula explained above. By default, the offset is 0.0 dB and you will only see negative dB values on the SpecLab screen. Set the offset to +90 if you like to see mostly positive values on the screen (and to be compatible with old versions of Spectrum Lab before V1.65). If you have a calibrated AF source, you can set the offset so that 1 Microvolt will be displayed as '0.0 dB'.

You may even enter a numeric expression ("formula") as the 'dB-offset'. This formula will be evaluated once per second (or less frequent), but only after the calculation of an FFT.

You can use this for a programmed AGC (etc..), using the noise- or avrg-function. Note that the functions "noise", "avrg", "peak" etc are not affected by the 'dB-display-offset', but they can be used to affect the 'dB-display-offset' !

An example: You want the noise level in the AF range from 500Hz to 2.5kHz to appear at the '0dB' line in the spectrum plot. Enter the following expression as "Offset" (Menu Options..Display Settings):

  • -noise(500,2500)

Set the range for the 'amplitude scale' to -10..+80dB for this example, because most of the displayed dB values will now be positive.

See also:  "dB"-function ,  function overwiev

String expressions

Some interpreter commands (like export.start or wave.record) require strings in the argument list. These strings can be simple constants, like


Note that a string constant should be embedded in double-quotes as shown, otherwise white spaces in a string would be impossible. You can also 'add' strings like this:

  • "Test"+str("hh_mm",time)+".txt"

The second example is a bit tricky. The 'str'-function formats a numeric value (here: the value of 'time') into a string, using the specified format string (here: 'hh_mm'). The result of the str-function will be a string, for example 16:45 if the last spectrum was calculated at 16:45 o'clock. Three strings are added, the result is a filename containing the time, like:


More possibilities of the format-string are explained in the next chapter. Without the format string, the 'str'-function generates the shortest possible decimal representation for the numeric argument, truncated (to a few decimal places after the decimal point). Example: If N were 123.456, str(N) would return "123.5".

Third example: String expression for one of the programmable buttons, showing the average amplitude in a specified frequency range:

  • "Avrg = "+str("0.0",avrg(500,2000))+" "+spa.ampl_unit

This expression consits of a string literal ("Avrg = "), followed by a string function which formats the result of the average-function into a string, a separator, and finally another interpreter function which returns a string (here: spa.ampl_unit which returns the amplitude display unit, usually "dB").

If you want to use an evaluated string expression (=the 'result' of the evaluation of a string expression) where the program expects a simple text string, use the '@' character before the string expression. Example: In the "watch window", you want to use the same format string as in the "export definition table". The program expects a string literal (a "simple text" there), so instead of typing:
export.format[1]  (in the "watch list", "format" column)
you must use
which forces the interpreter to evaluate this string expression. Complicated, isn't it ? Without this, the interpreter would think "export.format[1]" is the format string itself, instead of the evaluated result which may be "####0.0##" (as defined in the "export" table). However, these special cases are quite rare. Using the leading '@' character is -at the moment- only required in
  • the "format string" column in the watch list or export definition table,
  • the "title" column in the watch list or export definition table, 
if a cell does not contain the string itself but a reference to a string defined in another table or cell.

Backslash sequences (in string expressions)

The backslash character has a special function (like in the "C" programming language). The reason for this is manifold, so -for a start- here only some basic facts which you should observe when using strings with backslash sequences.

One of the reasons why we need backslash sequences in string expressions is this: Because the double quote is used as delimiter for string literals (in many programming languages), we must replace the double quote character with the backslash sequence \" (backslash-doublequote). So here are the basic rules for backslash-characters in string expressions. More info can be found in textbooks about the "C" programming language !

  • A backslash followed by a double-quote character in the sourcecode (\") will be translated into a double-quote character (without the backslash)
  • A double-quote character which is NOT preceeded by a backslash marks the begin and the end of a string constant, like in "C" and Java .
  • Depending on the context where the string is used (for example in the "print"-commands), the backslash sequences \n, \r, \t will be translated into the control characters NL, CR, and TAB .
  • Two backslashes together (\\) will be converted into a single backslash. Remember this when you specify path names.
  • If a single backslash is not followed by any of the special characters mentioned above, it will be copied 1:1 from the string expression into the resulting string. This feature was implemented to keep SpecLab compatible with "old" applications, which used pathnames with single backslashes. See the "fopen"-command for more info.

Example: String with backslash characters in the screen-capture-command :

capture( "C:\\Users\\Myself\\Desktop\\VLF Spectrograms\\recent.jpg" )

Since each pair of backslashes is converted into single backslash characters (when the string is parsed by the interpreter), the resulting file (screen capture) will actually be saved as:

C:\Users\Myself\Desktop\VLF Spectrograms\recent.jpg

Without the double backslash before the filename in the above example ("\\recent.jpg"). backslash-r would be converted into a carriage return character, which is certainly not what you intended. The 'capture' command may be an exception to this rule: The interpreter 'knows' that the argument is actually a filename, in which control characters like 'carriage return' (#13) and 'new line' (#10) don't make sense in a file. But don't rely on this feature - it may be subject to change in future versions.

See also:   formatting options , numeric expressions , interpreter commands, str (turns numbers into strings), print (procedure).

Format String for numeric values

Format strings are used in several functions like str and val . The following formatting options are available:

Numerical Values
# = placeholder for an optional digit in a number
0 = placeholder for a non-optional digit in a number
. = placeholder for the decimal point in a number
(German users: please forget about the decimal comma if you want to share ASCII data files with others !)
Time+Date Values
YY = Year, two digits only
YYYY = Year, four digits
MM = Month, two digits
MMM = Month-NAME (Jan, Feb, Mar, Apr, ...)
DD = Day of month, two digits
hh = hour of day, two digits
mm = minute, two digits
ss = second, two digits
ss.s = seconds, three digits (two places before and one place after the decimal point)


  • The internal representation of time+date are the Unix-style "seconds elapsed since January 1st, 00:00 UTC, 1970" (leap-seconds are ignored).
  • To convert a number into a string, use the str function
  • To convert a string into a number, use the val function
  • The tokens for date and time can also be used in the file name 'template' for the audio recorder, and to log outbound web audio streams

Examples for valid format strings:


  • Format a fixed-point number with up to 4 places before the decimal point and up to two fractional places (IF NOT ZERO).


  • Format a fixed-point number with exactly 4 places before and 2 places after the decimal point.
  • ACHTUNG, deutsche User: Spectrum Lab verwendet intern IMMER den Dezimalpunkt, niemals das verfluchte Dezimalkomma ! Irgendwo unter "Systemsteuerung"/"Ländereinstellungen" kann man auch andere Programme wie z.B. Excel dazu bringen, den PUNKT zu verwenden. Wenn Sie Textfiles mit Dezimalkomma über den großen Teich mailen, wird sich der Empfänger bestimmt darüber freuen !


  • Format a date-and-time value, so that only the hour and minute are displayed

DD.MM.YYYY hh:mm:ss

  • Date and time used in Germany. There are so many 'flavours' especially for formatting a DATE worldwide, so it's up to you... apart from ALWAYS to use UTC, the author suggests the following date format...

YYYY-MM-DD hh:mm:ss

  • Date and time as suggested (required) by ISO 8601. This is the most "logical" format, with the 'most significant value' (the year) first. Preferred by most scientists, and becoming more and more widespread amongst programmers.

back to the overwiev

print (procedure)

Prints some numeric or string values into one line of the interpreter's message window, or (one fine day) into a communication channel. Used for debugging.

print( Argument1, Argument2, ...)

The arguments can be either numeric or string expressions. All arguments must be separated by comma or semicolon (not colon.. the colon character is used to separate commands - remember the old BASIC programming language) . A comma between two arguments inserts an additional space, a semicolon does not.
Like in the "C"-programming language, the print function replaces certain backslash sequences into their equivalent ASCII control characters as explained here. These sequences are: /n = new line,  /r = carriage return, /t = tab, // = single backslash, /" = double quote character.


  1. print("date="+str("DD-MM-YYYY",now), "time="+str("hh:mm:ss",now) )
  2. print(1/3):print("OK?")
  3. print(str("0.#################",1/3),".. 17 decimal places !")
  4. print("fo=",1/(2*pi*sqrt(1.22e-3*1114e-12)),"Hz")
  5. print(val("2001-11-05 22:00:00","YYYY-MM-DD hh:mm:ss"))

Resulting text for the above examples:

  1. date=17-09-2001 time=19:01:01.
  2. 0.333
  3. 0.33333333333333332 .. 17 decimal places !
  4. fo= 136520.422 Hz
  5. 1004997600 (which is the number of seconds passed since Jan 1, 1970, 00:00:00)

Note: Very similar like print are the commands fprint and fwrite, which write lines of text into a file. See file commands.
Other functions (for example, rct.send) use the same format for the string expression. So the examples given above are also valid for those commands.

See also: Numeric expressions, string expressions, command overwiev, backslash sequences.

edit (procedure or function)

This command opens a small window, in which the operator is prompted to enter of modify a string or a number (depeding on the type of the variable).

edit(<variable>[,<caption>][,<info1>][,<info2>][,<options>] )
<variable> is the name of an interpreter variable;
<caption> is the text to appear in the edit window's title bar;
<info 1> is the optional first line with a fixed 'info' text inside the window;
<info 2> is an optional 2nd line with a fixed 'info' text (see screenshot further below);
<options> is a future plan (it may, one fine day, restrict the input to decimal or hexadecimal digits, etc).
Please note that strings literal ("text constants") must be embedded in double quotes so the interpreter can tell them from names of functions and variables.


  1. Text="":edit(Text,"Enter a string","max. 80 characters")
    In this example, the variable is set to an empty string before editing it. The edit field will be initially empty, there will be no "old" text to be edited.
  2. MyName="Mr X":edit(MyName,"What's your name ?")
    In this example, the variable is set to a default value before editing. That text (here: "Mr X") will appear in the edit field. The operator may edit or overwrite it, or immediately press ENTER (which is the same as clicking "OK" in this case) to leave the default value unchanged.
  3. Text="":edit(Text,"Enter Capture Info","The text will be shown","in the captured image."):capture("capt"+str("YYMMDDhhmm",now)+".jpg")
     ( remember copy & paste ? )
    This command sequence can be used in a programmable button to do a screen capture with a custom text line.
    The content of the variable 'Text' is dumped into the screen capture's info-area by adding a line with a reference to the variable 'Text' in the screen capture options (more details there). For the above example to work, 'Text' (as variable name without the quotes) must be entered in one of the screen capture info definition lines.

(sample edit window, with caption and two "info" lines)


  • The script (from which the edit command was invoked) will be paused until input is finished; i.e. until the operator presses the ENTER key or clicks the "OK"- or "CANCEL"-button in the edit window. Clicking CANCEL will discard the edits; the variable will not be modified in that case, regardless of what was typed into the edit field.
  • If your script needs to know if the operator clicked "OK" or "CANCEL", call edit as a function. The returned value is the button code, which can be one of the following values:
    <ToDo: describe the button codes here>
    Example:  Text = "" : Answer = edit( Text, "What happened ?", "Select 'CANCEL' to abort" )

See also: variablescommand overwiev

str( < format >, < value >) [string function]

Turns a numeric value into a string.

str( <format-string>, <numeric expression> )


  1. print("date="+str("DD-MM-YYYY",now), "time="+str("hh:mm:ss",now) )
  2. export.start(str("YYMMDDhhmm",now)+".txt")

See also: Numeric expressions, string expressions, backslash sequences, command overwiev, val-function, format string .

NameWithoutPath( < filepath > ) [string function]

Returns a filename (without path) for the specified full filename with path (drive letter, path, filename, file extension). Example:
   NameWithoutPath( "C:\\Spectrum\\logfiles\\test.txt" ) will return the string test.txt .
In a real application, the argument will be a variable, or a function call like
   NameWithoutPath( wave.fname_in )

fopen, fread, fwrite, fprint, fclose, FileExists [file access commands]

Note: The file access commands have not been tested thoroughly. This may change if -one day- the interpreter in SL is extended with program flow commands like loops and branches, turning the interpreter into a more powerful scripting language than it is now.
From SL's integrated HTTP server, these commands can only be called if the option 'enable remote control' is set.

fopen( <filename> [,<open-mode>] )

Opens or creates a disk file. The optional open-mode is  a single-letter-parameter with one of the following values:

Open for reading only. Does not make sense at the moment because there are no file-read-routines yet !
Create for writing. If a file by that name already exists, it will be overwritten.
Append; open for writing at end-of-file or create for writing if the file does not exist.

The default open-mode is "w". If only the filename is specified, an old (existing) file will be overwritten (truncated). Also the line printer can be opened for plain text output with this routine, use fopen("LPT1") for this purpose.

Since 2006-03, a third argument can be specified for shared files. The syntax is

fopen( <filename>, <open-mode>, <share-mode> ) then. The share mode is a combination of flags which are explained in the Win32 programmer's reference for the CreateFile function, parameter dwShareMode. According to that source, the share modes can be combined if necessary:

Subsequent open operations on the file will succeed only if delete access is requested (FILE_SHARE_DELETE).
Subsequent open operations on the file will succeed only if read access is requested (FILE_SHARE_READ).
Subsequent open operations on the file will succeed only if write access is requested (FILE_SHARE_WRITE).

Example for a file created for writing, granting read-access for other applications:

fopen("c:\\share_test.txt",w,r) : REM Write new file, share for Read-access
fprint("Try to open the file with a text editor now,")
fprint("BEFORE closing it here, to see the effect of the read-share flag")

Note: Like in the "C" programming language, special care must be taken with the backslash ! For example, a double backslash in the sourcecode will be replaced with a single backslash in the filename. More details in the chapter about backslash sequences .


Closes a file (a disk file, a printer, or something similar). Never forget !

fprint( Argument1, Argument2, ...) (abbreviated "fp")
fwrite( Argument1, Argument2, ...)
   (abbreviated "fw")

These commands do almost the same as "print", exept that they write into disk files instead of the interpreter's output window.
The fprint command appends a carriage return and a new line character after the text, fwrite does not. Use fwrite if many values must be written into a single line of the output file. Control codes can be placed in the output as in the "C" programming language, like \r for carriage return and \n for new line (may be nice to have if you want to send a formfeed to the printer, or to write unix-style line separators .. see the notes on line breaks below).

If you need more than one file opened at the same time, append digits (0..9) directly after the function names, like this:
fp0("Hi, I am the first file.")
fp1("This is the second file!")
fp2("and this is the printer")

An example for the file commands can be found under "screen capture extension macros" (which was the first application using file commands, but they can be used for many other purposes).

Note on line breaks in text files:
Windows and DOS use a pair of Carriage Return and Line Feed (aka "newline") characters to terminate lines ("\r\n" as a C-backslash sequence). UNIX and the like use an LF character only. Most Apple computers use a CR character only.
In other words: a complete mess.
If you don't like the CR + LF line terminator used by the fprint command, use fwrite instead, and place the terminator in the argument list.
The file read function (fread) will accept any of the these - hopefully ;-)

freadln( ["format1",]Argument1, ["format2",]Argument2, ...)

Reads one line of text from the file, and parses a number of values (i.e. variables) from that line. The command "freadln" means "file read line".
Optionally, the argument list may contain information about the formatting of the data in the file (which is sometimes necessary to "skip" certain characters).  

The following example (taken from command_files/testcmd.txt) first writes a short, two-line text file, and then reads it again to parse eight numbers from it.
It can be examied by single-stepping through it in the command window. A few details about the fread commands follows below the example.

; Create (or OVERWRITE) a file which will be used to test fread() further below.
fopen("rwtest.txt",w) : REM open for writing, create, or overwrite existing
fprint(1234;",";2345;",";3456;",";4567) : REM write some comma-delimited numbers
fprint(5678;",";6789;",";7890;",";8901) : REM write some comma-delimited numbers
fclose ; close the file again (never forget!)

; Open the test file (which was written above) FOR READING, and read from it:
fopen("rwtest.txt",r) : REM open for reading, error if the file doesn't exist
; Don't cheat.. forget A..H so we know they were really read from the file:
A=0 : B=0 : C=0 : D=0 : E=0 : F=0 : G=0 : H=0
freadln( A, B, C, D ) : REM read variables A..D from the 1st line
freadln( E, F, G, H ) : REM read variables E..H from the 2nd line
fclose ; close the file again (never forget!)

; Show what's in the variables:

Details (about fread / freadln)

fread was not tested yet, so forget about it for the moment. It doesn't care for lines (carriage return, new line characters), and is reserved for future versions.

freadln is easier to use and understand. It is designed to read (and parse) data from text files. The basic function is:

  1. Read the next line of text from the input file, up to (and including) the carriage return / new line character at the end of the line.
  2. Begin analysing the argument list, beginning with the leftmost argument.
  3. If the argument is the name of a variable, parse the value from the string, and assign it to the variable.
  4. Look for the separator character in the command's argument list (not in the file). Up to now, there are:
     ,  (comma) is a placeholder for a comma, or a semicolon, or a single TAB character, or any number of spaces in the file.
    ; (semicolon) is the separator between two arguments (variables) in the command, but the interpreter doesn't skip anything in the file on this separator.
  5. Look at the next character in the argument list. If it's a closing parenthesis, it's the end of the command. Otherwise, repeat the process at step 3.

The separator characters ',' and ';' in the argument list have a similar syntactic meaning as in the print- and fwrite command: the comma puts a separator character (space by default) in the file, while the semicolon is just a separator in the argument list, but doesn't write anything into the file.

Note for german users: As in the print command, SL always uses the decimal point - not a "decimal comma"- to separate the integer and fractional part in a number. The same applies to numbers read from a file. The decimal separator (between the integer and fractional part) is a dot. The comma is used as a separator between different numbers, as in a "CSV" file (comma separated values). With the freadln.

When called as a function (rather than as a procedure), fread returns the number of variables successfully read. The return code is negative if there was a file error, and zero if there is nothing more to read.

Future plan: Double-quoted format specifiers like the following allow skipping other characters (besides the separator characters mentioned above). For example,

will skip the characters Freq= , read the following number, and assign it to the variable Frequency1 .

FileExists( <Filename> )

Returns TRUE (1) if the specified file exists, otherwise FALSE .
Unlike most other file access functions, FileExists doesn't require a handle but a name.

See also: command overwiev, str-function, print command  format string .

send (command)

Sends a text message (string) to another window. Internally, a WM_COPYDATA message is used for this purpose (see details further below).


<recipient> = window class name (string), or window handle (integer number) identifying the receiver
<message> = Any string of characters, actually generated by a string expression

send("RdfCalc","set brg1x="+str(azim(23350,23450)) )
send("rsNTP","set offset=1.5" ); // let the NTP client add 1.5 seconds when synchronising

The first example sends the measured azimuth angle of a signal near 23.4 kHz to a recipient called "RdfCalc" (which is a utility for radio-direction calculation).
The recipient is the so-called class name of the main window of another program running on the same computer. Sending a message to a program running on a remote computer is not possible with this command.
The second example sends a command to DL4YHF's rsNTP (ridiculously simple NTP client) to add an offset of 1.5 seconds to the NTP-based time, before setting/synchronising the PC'ssystem time. This feature can be helpful in combination with programs like WSPR, which require an accurate timing (using the PC's system time) and cannot tolerate timing errors caused by the preprocessing delay, e.g. when Spectrum Lab is used as a pre-processor for the audio fed into WSPR (using a software 'audio cable', etc).

For programmers: This command sends a string to the module "CL" (=command line interpreter inside the receiving application), the command-ID is "EC" (execute command, don't send a response). Under windows (any version), the 'transport vehicle' is the WM_COPYDATA message.


  • Under Windows 8, and quite likely any later windows version, the WM_COPYDATA-based 'send'-command fails if the receiving application runs 'as admin' but the sending application (here: Spectrum Lab) does not. For example, if rsNTP runs 'as admin' (which it must, to synchronize the PC's system time), and you want to modify the 'deliberate offset' for the system time via command from Spectrum Lab, also run SL 'as admin'.
  • Since version 2.7, it is possible to send messages between two instances of SpecLab running on the same PC. Use recipient "SpecLab1" for the first instance, and "SpecLab2" for the second instance, etc. The old class name "TSpectrumLab" remains for backward compatibility, but it only works for the first instance.
  • From SL's integrated HTTP server, the 'send' command can only be called if the option 'enable remote control' is set. By default, this is not allowed because it's a security risk (you won't be amused if a visitor runs a "format c:/" command remotely on your PC).
  • The same window class name may be used by multiple instances of the same window (or program), but a window handle seems to be unique. To avoid problems with identical window 'class' names when multiple instances of Spectrum Lab are running, the 2nd instance creates an invisible 'communication'-window named 'SpecLab2', etc. For others programs (which don't use similar tricks), the recipient must be identified by its handle instead of its 'class' name.
    Thus, if the 1st function argument looks like a 'number', the send command will directly treat it like a handle, instead of calling the 'FindWindow' API function to retrieve the window handle for a given window 'class' name.
  • Downside of using window handles: Windows usually assigns a new (different) handle for a window each time a window is created. So when using a numeric window handle as the first parameter, you need to implement some clever mechanism to retrieve the handle of the receiving window...
  • Identifying a window by its 'window name' (2nd argument of the 'FindWindow' function) is mostly useless, because what Microsoft calls 'window name' is actually the 'window title', i.e. "the text you see in the blue title bar at the top of the window".
    Since many programs change the text in the window title during runtime (including Spectrum Lab, showing version info, or the name of a "loaded file"), the 'window name' is often not suitable to identify it in another program.
  • If necessary, the built-in interpreter can retrieve SL's own (main-) window handle via window.handle (interpreter function). Example:
    print( "My own window handle is ",window.handle);

See also: Communication with external programs, command overwiev, SL's integrated HTTP server.

val (function)

Converts a string into a numeric value.

val( <string> )
val( <string>, <format-string> )


  1. val("1.2345")
  2. val("2001-11-05 22:00:00","YYYY-MM-DD hh:mm:ss")

The first example returns the value 1.2345 as a floating point value. Instead of the string constant string variables and -expressions can also be used (which makes more sense).
The second example returns the value 1004997600 (which is the number of seconds passed between Jan 1, 1970, 00:00:00 and Nov 5, 2001, 22:00:00).

See also: Numeric expressions, string expressions, command overwiev, str-function,  format string , fread .

min, max  (functions)

These functions return the minimum or maximum value from a number of arguments. Examples:

returns 1
returns 4
max( peak_a(200,500),  peak_a(900,1000) )
returns the "highest peak amplitude" in the audio frequency ranges from 200..500 Hz and 900..1000 Hz.

See also: functions, numeric expressions .

click, sound (procedures)

Can both produce some kind of acoustic signal with the PC's internal speaker (not the soundcard!).

with no parameters produces the standard "Message Beep" using a windows API routine (so chances are good this works on EVERY machine).
You can use this for debugging: Insert it whereever you like to check if the program does what you expect. For example, with this command you can *hear* if data is being written to a file from the export function, without having the PC's monitor on.
sound( <frequency> [,<duration>] ) REMOVED, becaue direct register access doesn't work under windows anymore.
Expects the frequency in Hertz as parameter. The tone will be generated until it is turned off by another sound-command or by "click". To turn the sound off, use "sound(0)". Because this procedure uses direct port accesses, it may cause problems under future Windows versions, but at the moment it runs properly under Win95 / Win98, Win NT, Win ME and Win 2000.
The optional <duration> parameter may be used to turn the tone off after a certain time, <duration> is a value in seconds.
This command was intended for debugging, with the ability to distinguish between different "messages" (unlike the click-command).
sound(1000)  -> 1 kHz tone from the PC speaker, lasting endless (until the next "sound" command)
sound(0)     -> turns the sound from the PC speaker off
sound(2000,3)-> 2kHz tone from the PC speaker for 3 seconds

back to the command verwiev

wave (commands and functions)

A group of commands for recording and playing audio streams as wave files.

  • wave.record
  • wave.record("filename.wav")   or   wave.record("filename.ogg")
  • wave.record("filename.wav", <option> )
    Starts saving audio samples as a wave file. If this is already active, the old file will be closed automatically. If the new file already exists, it will be OVERWRITTEN (the old data in an existing file will be lost).
    The filename can be a flexible string expression.
    The file type (uncompressed wave audio or compressed Ogg/Vorbis audio) is detected from the extension (.wav or .ogg).
    The <option> value defines what type of samples shall be recorded, and the source of the samples. Possible values are (at least):
    1 = input samples, using the 'internal' processing rate
    2 = output samples; possibly from generator,digimode,mixer, or filter.
    See also: 'template'-option in the filename for the triggered audio recorder.

  • wave.recorded_kByte
    This function returns the current size, in kByte, of the recorded file (if any).
    If there is no file currently being recorded, the result is zero.

  • wave.play("filename.wav" [, <analysis_type>] )
    Starts playing or analysing a wave file. The second parameter (analysis_type) is optional:
    p = play with output to the D/A-converter, through the DSP-chain (like in real-time mode)
    q = quick file analysis, no DSP, no output to the D/A-converter

  • wave.pause
    This flag (formally, a variable) can be used to temporarily pause playback. It can be read or written. Assigning any nonzero value actually pauses the file:
    wave.pause := 1; // wave file paused
    wave.pause := 0; // wave file not paused

  • wave.stop
    Stops recording a wave file (or playing a wave file, if already implemented).
    Note: Do not use this command to stop the Triggered Audio Recorder, because it has its own set of commands !

  • wave.wr_info
    A pseudo variable with an 'info string' which may be written as 'info' into the (audio) logfile, or in an auxiliary text file for GPS data.
    Details about how to record GPS positions along with audio logfile are here .

  • wave.rd_info
    A pseudo variable with an 'info string' read as 'info' from the (audio) file, or in an auxiliary text file for GPS data, during playback (or file analysis).
    Usage: see wave.wr_info, and look here .

  • wave.fname_in
    Retrieves the filename of the audio file (usually a wave file) currently opened for input (analysis). This parameter is read-only; to open a different file for input use wave.play .
    To retrieve only the name (without path), use the function NameWithoutPath(wave.fname_in) .

See also : Wave file analysis (interactive),   commands for the triggered audio recorder.

back to the overwiev

cfg.xxx (configuration parameters, accessable like variables)

Offer some access to spectrum Lab's configuration data. Most of them are only implemented for "internal" use (fellow programmers will find them in a different file). You read the values from your own program using a simple message-based communication protocol. Trying to modify values through these functions will (usually) not have an immediate effect, so do not use them to change the normal behaviour of the program !

back to the overwiev

water.xxx (waterfall display parameters, accessable like variables)

Control the waterfall display.
A few of the commands below can be 'selectively' applied to one of the analyser channels, for examle:
  water.brt[0]=100; // set waterfall brightness slider (0..255), first channel
  water.ctr[0]=100; // set waterfall contrast slider (0..255), first channel
  water.brt[1]=140; // set waterfall brightness slider (0..255), second channel
  water.ctr[1]=130; // set waterfall contrast slider (0..255), second channel

Currently implemented functions and parameters for the waterfall display are:

water.avrg_cnt, water.avrg_max
Returns the current / the maximum number of FFTs which were added to the average of a single waterfall line. Details about spectrum averaging can be found here.
water.f_min, water.f_max
Sets or retrieves the visible frequency range for the waterfall (and possibly the spectrum graph which uses the same range).
To modify these values (in Hertz), use a formal assignment like these:
water.f_min=600 : water.f_max=900
Waterfall brightness value.
Waterfall contrast value.
Command to clears the waterfall screen and the spectrogram buffer.
Access the "frequency offset" for the spectrum display and the waterfall. Unlike the following function, this one does not affect f_min and f_max (the frequency range displayed in the waterfall).
Almost the same as above, but write-access to this value changes the min- and max- frequencies too. In effect, the frequency scale will be redrawn, but a 1-kHz-AUDIO frequency will remain at the same position in the waterfall screen (and the spectrum graph, if visible).
On read, water.f_offset2 returns the same result as water.f_offset .
Returns the number of waterfall lines which have been calculated since the program was started. Can be used in the Conditional Actions to do something "special" as soon as a certain amount of waterfall lines (alias FFT's) have been calculated. Note: If the waterfall is turned off, its counter won't count ! (joking aside, FFTs may be calculated for the spectrum graph even if the waterfall display is turned off). See also: water.line_nr .
Returns the number of completed waterfall sweeps (scans across the waterfall screen) since the program was started. Can be used in conditional actions, similar like the function 'water.count' . By comparing the value returned by this function with the previous value, you can -for example- upload a capture of the waterfall to a website whenever a new screen is finished.
Note: If the waterfall runs in multi-strip mode, the value of water.sweeps will be incremented whenever a new strip is finished. Doing a capture precisely at that time would contain an empty strip, because in the moment your application recognizes that 'water.sweeps' was incremented, the display routine has already scrolled the waterfall screen by one strip. To circumvent this, use the function 'water.line_nr' - see below.
Returns the waterfall's current line number (within the current sweep or strip). This number counts DOWN from the maximum to zero. When water.line_nr reaches zero, a new sweep begins (and the event 'new_strip' occurs for the Conditional Actions), and the line number wraps from zero to water.line_max (see below). You can use this function to test if a sweep of the waterfall is "almost" complete (in that case, water.line_nr will be very low. If the waterfall scrolls slowly, you may be able to catch the time when water.line_nr is exactly zero. Otherwise, don't expect to see every step in water.line_nr ... in fact, if the waterfall scroll interval is less than 50 ms, two or even more pixel lines are painted in a single over to speed things up.
Returns the number of lines in one complete sweep of the waterfall. Depending on the orientation of the waterfall, this is the height (if the frequency scale is horizontal) , or the width (if the frequency scale is vertical) of the waterfall bitmap in pixels.
Forces the multi-strip waterfall display to begin a new strip, even if the current strip has not reached the maximum width on the display screen. Together with the 'periodic' or 'scheduled' actions, this feature can be used to synchronize the multi-strip display to the nearest UTC hour, or (for very slow displays) to one 24-hour period per line.
Example (using the scheduled actions):
      Time of day     :    00:00
      Action (macros) :    water.new_strip : REM begin new strip in spectrogram
After being synchronized (triggered) by the command 'water.new_strip', the multi-strip display will pause at the end of each strip. It will not begin a new line automatically. Instead, it will always wait for another call of 'water.new_strip'. This is done on purpose. If, for example, only one 'strip' per 24 hours shall be displayed but the screen is not wide enough (say only wide enough for 23 hours per strip), there would be two strips without the automatic pause: One line with 23 hours, followed by a short line with only 1 hour (which ends at the time when the water.new_strip command was issued).
Note: There is also an event ("new_strip") for the Contitional Actions, which is set whenever a "strip" is complete. Don't confuse both... "water.new_strip" is a command to begin a new strip, "new_strip" (used as an event in the Conditional Actions table) is a function which tells your application if a new waterfall-strip has begun or not.

Almost the same as above, but expects a timestamp specifying the precise start-time for the next strip of the waterfall ("planned in advance").
An example is in the test application 'MultiStripSyncTest.usr' .

Related subject: You can also let the multi-strip spectrogram 'run free', i.e. let the output position wrap from the end of one line to the begin of the next, and catch this event ("new_spectrum_strip") in the table of Conditional Actions. This gives your application the chance to do whatever you need when the multi-strip spectrogram display wraps from one line (strip) to the next. The author used this feature for an automatic bat-monitoring application, which changed the frequency of a VHF receiver to the transmit frequency of another bat after each strip of the spectrogram, resulting in a four-strip cycle for four bats (btw, named "Betti", "Kalli", "Lotti", "Netti") transmitting with low-power transmitters on four different frequencies (see www.fledermaus-aksa.de).
The other alternative would have been to monitor each bat for a fixed time (say 60 seconds) and then begin the next strip (after switching the frequency), but in that case the width of the spectrogram window would have to be carefully adjusted to fit the length of each 'bat monitoring cycle'.

<there may be other interpreter commands to control the waterfall which are not mentioned here>

See also: spectrum.xxx (access the current spectrum), spa.xxx (controls the spectrum analyser), fft.xxx (FFT control commands), overview of interpreter-commands and -functions .

back to the overwiev

spa.xxx (commands and functions for "special" control of the spectrum analysers)

For some 'special' applications, some parameters of the spectrum analysers can be controlled "directly" through the interpreter, without affecting the configuration data. There are at least the following functions... some of them may be used as commands to assign new values to these parameters, but most are "read-only" (so their value cannot be changed by a formal assignment):

  • spa.ampl_max, spa.ampl_min
    returns the displayed amplitude range in the spectrum graph (and the waterfall, if visible).
  • spa.ampl_unit
    returns the spectrum analyser's amplitude unit (for display) as a string. In many cases, this is "dB". The amplitude unit (for display) can be configured here. Example (expression for a programmable button, shows the average amplitude in a certain frequency range, and the display unit):
    "Avrg = "+str("0.0",avrg(500,2000))+" "+spa.ampl_unit
  • spa.c_width, spa.c_height
    returns the width or height of the spectrum display's "client area"; i.e. the size of the window area usable for the spectrum display(s) including the frequency axis, but excluding the window frame, scrollbars, etc. This parameter is read-only (trying to change it doesn't "programmatically" resize the window - use the 'window' function to change the size of the main window).
  • spa.lta_cnt
    returns the current average counter of the "long-term average spectrum" (which was implemented for the Earth-Venus-Earth experiment, to dig extremely weak and uncoherent signals out of the noise). An overview of the spectrum averaging functions is here.
  • spa.clear_avrg
    clears the long-term average spectrum (to begin a new integration).
  • spa.cursor.freq ,  spa.cursor[N].freq , .. .ampl, .. .time
    spa.cursor.freq returns the frequency of the 'readout cursor' in Hertz, including the optional offset (VFO frequency or similar). cursor[0] is the first cursor (shown as "red ball" in the spectrum graph, which usually follows the mouse pointer there),  cursor[1] is the second cursor (the "green ball", which is usually fixed somewhere via mouse-click).
    spa.cursor.ampl returns the amplitude or magnitude (depending on the display settings);
    spa.cursor.time returns the time of acquisition in UTC(number of seconds passed since the birthdate of UNIX, as a floating point number).
    Details about the readout-cursor are here.
  • spa.decimator
    returns the current decimation ratio between "input" to the spectrum analyser, and the FFT.  This parameter is READ-ONLY. To change the decimation ratio, use the configuration (dialog or control commands).
  • spa.inp_rate
    returns the current input sample rate into the spectrum analyser. READ-ONLY (to change this parameter, change the audio sample rate). But, in contrast to the soundcard sampling rate, this value takes the optional decimation ratio into account.
  • spa.lo_freq
    returns the current L.O. (local oscillator)- frequency of the spectrum analyser. Only has an effect if decimation and a complex FFT is used (like some other, L.O.-related parameters too)
  • spa.sweep_rate
    is the sweep rate for the local oscillator in "Hz per second" (or s^-2). This value can be modified by a formal assignment, like:
    spa.sweep_rate=-17m : REM set doppler shift rate to -17 mHz / second
  • spa.sweep_offs
    Momentary frequency offset for the local oscillator in Hz. This value is *not* included in spa.lo_freq. In contrast to spa.lo_freq, which is usually constant, this value changes permanently as soon as spa.sweep_rate is non-zero. The momentary oscillator frequency may be calculated as spa.lo_freq + spa.sweep_offs. To "reset" a sweep (for example, shortly before the moon enters the NavSpaSur radar fence ;-) , use a command like this:
    spa.sweep_offs=0 : REM reset doppler shift for new moon pass

If no index in square brackets after the keyword "spa" is given, the above commands apply to the first channel of the first spectrum analyser. Otherwise, use...

  • spa[0].xxx : 1st spectrum analyser, 1st input
  • spa[1].xxx : 1st spectrum analyser, 2nd input
  • spa[2].xxx : 2nd spectrum analyser (only has one input)
  • spa[3].xxx : time domain scope, 1st input (ok, not really a "spectrum analyser"... )
  • spa[4].xxx : time domain scope, 2nd input


  • Try to avoid spa[3] and spa[4]because they may change in future versions.
  • To stop, start, or restart the L.O. sweep, especially when analysing audio files in loop mode, the 'replay_started'-event in the conditional action table may be helpful. Alternatively use the spa.sweep_offs function to reset the sweep if the offset gets too large.
  • Many other "configuration data" values, some of them related to the spectrum analyser / waterfall display, can be accessed through the cfg-functions (config).

See also: spectrum.xxx, fft.xxx (FFT control commands), water.xxx ('waterfall' control commands), overview of interpreter-commands and -functions .

spectrum.xxx (commands to process the current spectrum)

Procedures and Functions to control or export the latest calculated spectrum (= "Result of the latest FFT calculation for the spectrum analyser"). To control the spectrum analyser (which produces these spectra), use the spa-commands/functions. Some additional, FFT-related commands are described further below.

Implemented up to now:

returns the time (UTC) of the calculation of the lastest spectrum, using the UNIX time format (number of seconds passed since 1970-01-01 00:00:00). The result may be slightly different from "now". See notes further below.
returns the local time of the calculation of the lastest spectrum, same format as for spectrum.time but in 'local time' instead of UTC.
reads or sets the "pause"-flag of the spectrum analyzer. You can use this to pause/restart the analyzer on a programmable event, for example a button to toggle the pause flag. pause=0 means "not paused", pause=1 means "paused". Example for toggling the pause flag:
spectrum.pause = !spectrum.pause
spectrum.save(#<channel_nr>,  "filename.txt" )
saves the current spectrum ("FFT results") as a textfile. Includes a header and -after the header- one line of text for every FFT bin.
spectrum.save(#1, "my_spectrum.txt")
saves the latest spectrum for the first channel of the first spectrum analyser as a textfile.
Note: The file format is the same as for the spectrum reference curve. But there may be some command-options in future to leave some parts of the spectrum away to save disk space. In addition to this command, SpecLab allows to export the FFTs for the main spectrum analyser periodically, using the FFT export function described here.
spectrum.print( [#<channel_nr>,]  [f=<frequency of interest>,]  <string> )
Prints a label (or a small text message) into the spectrogram, which is scrolled together with the lines of the waterfall. Can be used to mark special "points of interest" in a waterfall, either manually or automated. If <channel_nr> is not specified, the command prints into both channels of the first spectrum analyzer, #1 prints into the first (left) channel, #2 prints into the second (right) channel. If f=<frequency_of_interest> is  not specified, the label will appear on the left (or lower) edge of the spectrogram, as close to "0 Hz" as possible.
sp.print("Meteor Nr "+str(MsBurst)):MsBurst:=MsBurst+1
The example could be used in one of the programmable buttons, or in the automated spectrum alert function.

sp.print(f=peak_f(500,2000),"* Peak "+str("hh:mm:ss",sp.time))
This example prints a label into the spectrogram close to a certain frequency, here: near the "peak" between 500 and 2000 Hz.

Unless explicitly specified, sp[ectrum].print uses the font-name), -size, -colour, and -attributes from the 'Display Colours and Fonts' tab on the configuration screen.
Tokens of those optional argument: fn=Font-Name, fs=Font-Size, fc=Font-Color (RGB, usually 3*8 bit hex with prefix 0x),
fa=Font-Attributes (0=normal, 1=bold, 2=italic, 4=underlined, bitwise combineable).
   sp.print(fn="Arial",fs=20,fc=0x0000FF,fa=1,  "Red Text in Arial Size 20, bold");
   sp.print(fn="Calibri",fs=16,fc=0x00FF00,fa=2,  "Green Text in Calibri Size 16, italic");
   sp.print(fn="Courier New",fs=30,fc=0xFF0000,fa=4, "Blue Courier Size 30, underlined");


  • The 'spectrum' token can be abbreviated as sp to save space.
  • The spectrum.print commands are temporarily stored in a buffer, before SL can print them to the screen for technical reasons. The size of this 'command stack' is limited (four entries or so). So wait a few hundred milliseconds before printing into the spectrogram again, otherwise some entries may get lost.
  • Use the function sp.time, not now, to retrieve the timestamp of the current spectrogram line.
    When analyzing files, sp.time may be very different from now ! When running in real-time, the difference is usually small.
  • To retrieve the timestamp of the readout cursor in the spectrum analyser, use the function spa.cursor.time instead .

See also: command verwiev,   fft.xxx,   export functions, spa.xxx (spectrum analyser),  channel numbers for the spectrum analyser

trigger.xxx : Programmed control for the 'universal' trigger module

The following functions (and commands) beginning with "trigger" can be used to fire events into the 'universal trigger module' :

fires a trigger event (into the universal trigger module) "now", which means in the moment this command is executed.
fires a trigger event at the specified absolute time (UNIX format, "seconds since 1970-01-01 00:00:00 UTC").
Using a command like trigger.time( now + 10 )

The main purpose for these commands is to generate trigger events from the programmable 'Conditional Actions' .

Note: Usually the trigger-module uses an internal timer to generate trigger events, or monitors the signal level tapped from the test circuit.
If you want to produce trigger events exclusively through the trigger commands listed above, set the trigger level to a very high level (which cannot be reached by the input signal).

cursor.xxx (functions for the readout-cursor, with the mouse over the main spectrum display)

Functions to retrieve information about the point under the mouse cursor (or, sometimes, in the vincinity of the mouse) :

  • cursor.spectrum.xxx :
    Accesses the spectrum (data structure) under the mouse cursor. The result changes when the mouse is moved over the spectrogram area, or in the spectrum graph.
  • cursor.freq
    Returns the cursor frequency (same as displayed in the main window)
  • cursor.ampl
    Returns the amplitude under frequency (which may be a 'peak', depending on the cursor configuration)
  • cursor.time
    Returns the timestamp of the spectrogram line under the cursor.

These functions can be used, for example, to turn the 'programmable buttons' on the left side of the main window into additional info fields (if necessary).

See also: command / function overview , spa.xxx (spectrum analyser functions) .

fft.xxx (commands to export FFTs from the spectrum analyser, etc)

Functions to control the export of FFTs as text files (one FFT per line in the file), in addition to the options on the FFT export panel.

  • fft.export.xyz  :
    All commands and functions beginning with "fft.export", or (abbreviated) "fft.expt", are explained here (in an extra document).

See also: spa.xxx (configuration of spectrum analyser), command- and function overview .

exec (procedure)


  • exec(<progname>)
  • exec(<options>,<waitflags>,<progname>, <argument1>, <argument2>, ...)

This interpreter command is used to launch or run an other application. In its simplest form, you just have to supply the name of the program which shall be run. Example:

  • exec("WakeMeUp.exe")

Usually, the interpreter will continue to operate while the operating system is still initializing the other program. Most windows applications will never terminate themselves, while many 'console' applications will be ready sooner or later.

(future plans: The numeric parameters <options> and <waitflags> can let the interpreter wait until the called program is ready.)

Many console applications (and DOS commands) need a list of arguments passed in a command line. These arguments follow as <argument1>, <argument2> after the name of the executable program. You may also pass all arguments in a single string. Example:

  • exec("cw.exe alarm alarm")

will do quite the same as

exec("cw.exe", "alarm", "alarm")

Notes on exec:

  • Like other commands (print, fopen, etc), the exec function translates certain backslash sequences ! Because of this, single backslash characters (as often found in directory paths) must be duplicated - see example below.
  • OS commands like "copy" and a few other internal functions of the DOS-style command line interpreter do not work under Windows XP etc. To copy files, you may use "xcopy", but beware of the stupid question if the target is a file or a directory. If the target is a file, there is no annoying question if the target file already exists, but you must specify the /y switch to suppress the question of the target file may be overwritten. Example:
    exec("xcopy last_min_"+str("mm",now-60)+".wav c:\audio.wav /y")
  • A quite ugly workaround to run the "internal" DOS commands under WinXP is to invoke the to invoke command processor (Cmd.exe) and its internal commands. So instead of writing something like
    exec("copy c:\\test\\source.txt c:\\test\\copied.txt") < deliberately wrong !

    (which does not work under XP because CreateProcess does not recognize it), enter the command
    exec("cmd /C copy c:\\test\\source.txt c:\\test\\copied.txt")
    which did work when I tested it under Win XP. Don't ask me what the /C switch is for; I found it somewhere on the web. It may mean "execute a single command and terminate after that". Under Win 98, "command.com" does a similar job like "Cmd" - but don't forget the extension.
  • Another workaround for the problem described above is to use batchfiles like this one (call it "copyjob.bat", located in c:\test):
    cd c:\test
    copy source.txt copied.txt

    To launch this batchfile, use:
    Beware, the current directory is not automatically changed, for this reason use the "cd" (change directory) command in your batchfile if the batch is not located in the Spectrum Lab directory.
  • From SL's integrated HTTP server, the 'exec' command can only be called if the option 'enable remote control' is set.

About space characters in file- or directory names:

Avoid spaces in directory- and filenames wherever possible ! They only leads to problems, because you need additional quotes to get the syntax for the command line arguments right. Because someone (!) once decided to allow spaces in filename, it gets terribly complicated now. If you don't use spaces in any of your data directories, skip this paragraphs. If you do, the punishment follows because you have to read on !
Because the double quote is used as delimiter for string literals (in many programming languages), we must replace the double quote character with the backslash sequence \" in into a double quote character. So here are the rules for embedding spaces in filenames (pathnames) without breaking up a single argument:

  • A backslash followed by a double-quote character in the sourcecode (\") will be translated into a double-quote character (without the backslash) in the argument list of the exec command.
  • A backslash followed by anything else than a double-quote character will not be translated, but be copied 1:1 into the resulting string. (Note: This is different than in the "C" programming language, where a backslash followed by certain letters will be translated into special control characters. Not here in SpecLab's exec()-command !).
  • A double-quote character which is NOT preceeded by a backslash is still used as a delimiter of a string constant, like in most programming languages including "C".
  • Unlike the "C" programming language, the backslash sequences \n, \r, \t are not translated into the control characters NL, CR, and TAB when used in the argument list of the exec command. These sequences will only be translated when it makes sense, for example in the print command (more details in the chapter String Expressions,  "Backslash sequences" ).

Phew ! Time to present an example to make this a bit clearer. Lets assume you have an executable file named "cw.exe" in the directory c:\test\ugly space\, which you want to launch via SpecLab's exec command, and passing the string "hello" to it in the command line. You may be tempted to write something like..

exec("c:\\test\\ugly space\\cw.exe hello") <<<< deliberately wrong

Thanks to Mr B.G. this is totally legal under windoze (it was not under pure DOS). But after parsing the string expression, we'll find THREE arguments (separated by space characters): c:\test\ugly, separation, space\cw.exe, separation, and hello . To concatenate the path+name of the executable into a single parameter for the command line, double quotes must be used. But double quotes are already used here as delimiters for the string constant in Spectrum Lab's primitive interpreter language. We cannot simply add more double-quote characters like this:

exec(""c:\\test\\ugly space\\cw.exe" hello") <<<< also deliberately wrong

because this doesn't make sense to the interpreter in Spectrum Lab. The interpreter would see an empty string enclosed with double-quotes, and something strange which is not a string constant after that. The correct way to achieve what we want is:

exec("\"c:\\test\\ugly space\\cw.exe\" hello")

Note that the very first and the very last double-quote character in the above example is the string limiter, which indicates a "string constant". The string expression in this interpreter-command-line will be translated to:

"c:\test\ugly space\cw.exe" hello

before passing it a windows API function named CreateProcess (all we need to know here is CreateProcess can be used to launch a program from another program, and that's what SpecLab's exec command is used for).

Note: Your browser may be irritated by all those backslashes and double-quotes above. You will find a few examples for the exec-function in the file testcmd.txt which was used for testing purposes when developing Spectrum Lab.

back to the overwiev

export (procedures and functions)

A group of commands for exporting data as text files. Intended to be used for monitoring applications, but can also be used elsewhere.
Note: The export file may contain whatever you like. It is very flexible but not easy to understand how it works for occasional use. For many applications, the settings "export control window" has all you need. 

Interpreter commands to control the user-defined text export function (not for the FFT-export) are:

  • export.start
    export.start(#<file_nr>, "filename.txt")
    Starts writing to an export file. May also be used to change the NAME of the export file. If exporting to a file is already active, the old file will be closed automatically. If the new file already exists, data will be appended (an existing file will not be truncated, already existing data will not be destroyed).
    In combination with the scheduled action definitions, it is possible for example to export into a "new" file every hour, or triggered by other events. The filename can be a flexible string expression.
    The optional parameter #<file_nr> (#1 or #2) defines, which of the two possible export files shall be started with a new name. If #<file_nr> is not specified, the command applies only to the 1st export file. Examples:
         starts the 1st export file without changing the name
         (re)starts the 2nd export file with a new name which includes the current date and time.
  • export.stop
    Stops writing to an export file. An existing export file will be closed (so it can be analyzed by an other program after this). Without parameters, both export files are stopped and closed (#1 and #2).
    export.stop(#1) stops only the first export file,
    export.stop(#2) stops only the second export file.
  • export.print( <text-line> )
    Appends a "special" line of text to the export file if opened. This is only required if strangely formatted output shall be written. You don't need this to write a "normal" export file.
    Note that the <text-line> in the argument can also be a flexible string expression like in the standard print-command.

The following export- functions return numeric or string values:

  • export.value[<column index>]
    returns the last value which has been calculated by the export function (to be precise, by an expression in the export file definition table). The column index runs from 1 to the number of columns defined in the export definition dialog.
    Example 1: export.value[2] returns the latest value which has been calculated during the evaluation of the expression for the second column of the export file... phew !
    For very special applications, you can use this to modify the value as a command:
    Syntax:  export.value[<column_index>] = <new_value>
    Example:  export.value[2]=0  clears the value. Only makes sense, if export.value is evaluated in the expression for export column 2 itself.
    Another example can be found in the application "Measuring the signal strength of low-duty-cycle beacons". The export.value function can also be used to plot the exported values by SpecLab itself, in "almost real-time", using the watch list / plot window.
  • export.title[<column index>]
    returns or sets the title of a column in the exported file.
  • export.format[<column index>]
    returns or sets the format string for a column in the exported file.
  • export.expression[<column index>]
    returns or sets the expression ("formula") which defines the contents in a column in the exported file. Usually, you define the contents for the export file in the export control dialog.

See also: saving a single spectrum in a textfile,  command overwiev , function overview, string expressions .

Accessing Configuration Parameters with the interpreter

Many components of the configuration data can be accessed from the interpreter. From a programmer's point of view, all configuration data are organized in a large structure (here: "cfg"), some of its components can be read (and, in rare cases, modified) by these functions. Examples:

print( cfg.Wat1ClContrast )
shows the current "color contrast" setting for the first waterfall display
cfg.Wat1ClContrast = 127
sets the "color contrast" setting for the first waterfall display. Note: This is a formal assignment.

An overview of all accessable components of the configuration can be found here ("subject to change").

PTT Control Functions/Commands

When Spectrum Lab controls the PTT function of a transmitter (usually from the "digimode terminal"), there is no need to use the following interpreter commands. But in some cases, someone else (a human operator, or another program) controls the transmitter and SpecLab shall behave differently depending on the current transmitter status. The following functions can be used to sense the current transmitter status. They will often be used for event definitions in the conditional action table.

The following functions poll inputs on the serial port :

  • ptt_port.cts
    Senses the current state of the CTS line. This is one of the inputs(!) of the COM port configured for "PTT Control" (!) in SpecLab's system configuration .
    0 Volts (or negative voltages) on the CTS line will be signalled as ptt_port.cts = 0 (zero);  high levels (+ 3 V or more) are returned as 1 (one). Note that these are not the "logic" states of the RS232 standard, because RS232 defines the signals as Active LOW (no problem since we don't use this port in a standard manner anyway..).
    On a 9-pin serial port connector, CTS is on pin 8, and ground on pin 5.
  • ptt_port.dsr
    Similar as "ptt_port.cts" (see above), but this function returns the current state of the DSR line.
    On a 9-pin serial port connector, DSR is on pin 7.
  • ptt_port.dcd
    Similar as above... this function returns the current state of the DCD line (Data Carrier Detect, geeks call it "RLSD" = receive-line-signal-detect ).
    On a 9-pin serial port connector, DCD is on pin 1. Caution, some cheap USB<->RS-232 adapters don't support this signal (or their drivers are faulty).
  • ptt_port.ri
    Similar as above... this function returns the current state of the RI line (RI=Ring Indicator).
    On a 9-pin serial port connector, RI is on pin 9.
    On some cheap "USB<->RS-232 adapters", it's not.

With the interpreter, you can also switch some of the output signals on the serial port. Be careful not to interfere with the signals controlled automatically from the digimode terminal. The following commands can be used to set output signals on the serial port. If <new_state> is zero (= "FALSE"), an output will be cleared (negative voltage on the serial port); if <new_value> is non-zero (="TRUE"), the output will be set (typically +10 to 12 V on the serial port).

  • ptt_port.rts = <new_state>
    Sets the RTS output (Request To Send, often used as the PTT line). On a 9-pin serial port connector, RTS is on pin 7.
  • ptt_port.dtr = <new_state>
    Sets the DTR output (Data Terminal Ready, pin 4 on a 9-pin serial port connector).
  • ptt_port.txd = <new_state>
    Sets the TxD output (Transmit Data) to a permanent negative or positive level, as long as no data are sent through the serial port. In fact, this is the "Break"-flag in the modem control register. On a 9-pin serial port connector, TxD is on pin 3.

The functions ptt_port.rts, ptt_port.dtr, and ptt_port.txd can also be used to retrieve the last state written to the port. For example, the following command line toggles the state of the RTS output ( "!" = negation ) :

ptt_port.rts = ! ptt_port.rts // toggle RTS

See also: command overwiev , function overview , numeric expressions ,  configuration of the PTT control port  .

SDR Control Functions/Commands

In addition to the commands to control an external receiver through the serial port, there are the following commands (and functions) to control a software defined radio. As of 2009-07, only Perseus and SDR-IQ / SDR-14 were directly supported.

  • sdr.freq
    Reads or modifies the VFO frequency of an external software-defined radio. To set a new VFO frequency, use a formal assignment like
    sdr.freq = 14.060MHz
    (Note that there must not be a space between the number and the 'technical' exponent. If there's no exponent, the unit is Hertz)

See also: command overwiev , function overview  .

Remote Control Functions/Commands

For some special application (shortwave propagation studies), it was necessary to control an HF receiver through the serial port. First, Icom's CI-V protocol was implemented (by the time you read this, other manufacturer's protocols may be supported too).

Please note that before using these interpreter functions and -commands, the serial port must be configured to match the settings of your receiver (like baudrate, serial data format, "address" of the radio, etc). This can be done in Spectrum Lab's main menu, select "Options".."System Settings".."Tx/Rx Interface settings".

The prefix "rct" means "remote control". The following functions / commands are implemented (possibly more) :

  • rct.freq
    Retrieve or change the frequency which the radio is tuned to. Use a formal assignment to set a new frequency like in the examples below.
    rct.freq = 144.300MHz : REM tune to 2m SSB calling frequency
    Note that the "MHz" must not not be separated from the number, because it's an alternative form of the scientific notation "144.300E6" (the 'M' is 10 power 6, 'k' is 10 power 3, and so on).
  • rct.send(<string expression>)
    Sends a string of characters to the radio, regardless of the protocol. The string expression syntax is the same as for the print command (the characters are only sent to the serial 'radio control' port, while print actually sends the characters to the command interpreter's output window).

Hint: ICOM radios autonomously report the new frequency through the serial port when the VFO knob is turned. Spectrum Lab receives and parses such messages. So the frequency can be displayed without much overhead on one of the programmable buttons of the main window, using the following interpreter code ("string expression" for the  button text):

str("###0.000 kHz",1e-3*rct.freq)

A simple example for this can be found in the settings file "CI_V_tst.usr", and a more sophisticated example in the Conditional Action file "ncdxf_4u1un_tracker.txt" (which periodically switches the frequency of a receiver, like shortwave beacons operated by the Northern California DX Foundation).

The Command Window

The command window can be used to enter and execute any interpreter command.

Enter a few lines with interpreter commands in the upper part of the window. To execute a single command line, press the F2 key. The command in that line wil be executed, and after that the cursor jumps into the next line to be executed (kind of "single-step" operation).

Alternatively, press F9 to run the whole "batch" of commands.

The lower part of the window shows the output from the print command. Can be used for testing purposes, as a simple calculator, or as a logging display (if you use the print command in event-driven subroutines like periodic actions, scheduled actions, etc.

See also: command overwiev, function overview, main index.

Last changes (YYYY-MM-DD):

  2015-01-19: Replaced the formula evaluator with a new, RPN-based implementation.
  2013-11-08: Added support for integer calculations, besides the data types
              floating point and string. Cleaned up the documentation.
  2006-09-16: Added a few new commands, and the option to run multiple commands
              like a "batch" (in the command window).
  2005-10-26: Documented the new "spa"-functions (spectrum analyser control functions).

Benötigen Sie eine deutsche Übersetzung ? Vielleicht hilft dieser Übersetzer - auch wenn das Resultat z.T. recht "drollig" ausfällt !