Making Your Own Software With PureBasic
While searching for a not too cryptic programming
language I found PureBasic. The list of
features was mainly the same as my wish list, so I didn't try the demo version
but bought the full version at once. And now after more than one year of
experience with it, I can say: it was worth it.
The main target was to make a log program that not
only saves and evaluates QSO data, but
also controls the station transceiver. This target was reached, and this is a
report about how this was done. Since I am not a software profi, I had to
master a lot of difficulties, but finally I got a working program which
fulfills my needs.
Does it pay out these days to build an own log
program, where so many exist?
The answer is Yes, because the self written program is
unique, and all the features built in are those you wanted, not somebody else.
This article should encourage you to try something on
your own.
Owners of the TS-2000 can take the source code with
only changing name and call. Who has
another brand, has to translate the control commands into a language his rig
understands.
PureBasic
With this programming environment you can do
everything which can be done with a high level language. And there is not only
a version for Windows, but for Linux and MacOS. I only have experience with the
Windows version, but as I learned from the forum, the developers try to keep
all versions on the same level. And who has paid once, can download all versions
and all updates.
Well, where is light, there is shadow as well. At the
first moment the fact, that Windows functions can be used like native PureBasic
commands, seems to be a bonus, but you will learn very fast, that only with
this feature you can reach the desired functionality. But, now a real bonus,
the needed constants for these functions are built in.
A great plus for the beginner is the forum. Here one can find via the
search function a lot of informations for own problems, and I could solve
nearly all this way. When the search has no result, you can of course ask
questions and will always get an answer.
Furthermore there is a code
archive in which ready source code about many themes is waiting for
download. And last, but not least there are user libraries with compiled
code.Especially should be mentioned here PBOSL,
Pure Basic Open Source Library, which covers many topics.
We don't use user libs in the log program, but source
code from PBOSL. Code
archive and PBOSL is found here.
The programs
All needed programs are contained in Log.zip. This file can be downloaded from here.
To make introductions in PureBasic (following PB)
easier, there are some training programs in Log.zip.
Demo or full
version
To execute the programs there is necessary of course
PB itself.
For the training programs a demo version is
sufficient. The most important limitations for us in the demo version are the
missing support for API functions and the number of code lines of 800.
There is a workaround for the API functionality by DLL
access. We make use of this in the training programs. But for the complete log
program the full version is needed, since the number of lines is about 4000.
Installation
The installation follows the usual rules. It's best to
follow the recommendations. Then in the program directory there is established
a directory PureBasic.
For the training and log files we establish in this
directory a folder Log and extract all files
from Log.zip in this folder. Then we establish
in the PB directory a folder Help into which
the file PBOSL.chm is moved from Log.
Windows API
For programs which make in many cases use of API
functions it is a great help to have a documentation about this topic. There is
such a help, even at no cost. In
Platform SDK
all functions, structures, etc. are explained.
Transceiver
control
All control commands are related to the TS-2000. Since
all manufacturers have their own syntax, the commands can not be used for other
equipment. To make the translation somewhat easier, in Table
1 all control commands with their meanings are listed. This table is
contained in Log.zip. By this means it should
be possible to find equivalent commands for your own radio.
The training programs
Buttons
The user surfaces in programs are windows, on which
controls are placed. Controls are called gadgets in PB. First we want to a have
look at buttons. A click on a button releases an event, which is evaluated.
Since in Windows the change of colours with normal
buttons is not supported, we use instead
TextGadgets. TextGadgets are static controls, normally used as labels.
They don’t react on user actions, but we will see
later how we can change this behaviour.
To show the source code of the training program TestButtons.pb, this file is selected by menu
<File><Open> in the PB directory, folder Log.
Who works with the demo version must delete the semicolon at the beginning of
line 5 to change this line from a comment to executable code. This includes the
file TestAPI.pbi in our little program to ensure that API functions can be used
with the demo version.
Then the window is opened by OpenWindow().
Parameters are window number, X position on the screen, Y position on the
screen, width, height and title. These are default parameters, the title can be
empty (""""). The following parameters are optional. Now
the gadget list is created and the gadgets are build. The gadget parameters are
the same as with the window, except that the position is related to the window.
The gadgets are, as mentioned earlier, TestGadgets as
button replacement.
With the optional parameter #SS_NOTIFY
Windows is prompted to send a message when a left click occurs, #WS_BORDER makes a single line border, #SS_CENTER centers the text horizontally and #SS_CENTERIMAGE centers it vertically.
After the definition of the gadgets they get assigned
colours for text and background.
The following Repeat-Until
loop is a central part of each PB program with user surface. Here the events
are evaluated. Possible events are clicks on button 1 and button 2, and closing
of the window. As with buttons, the evaluable clicks with TextGadgets are only
left clicks. When we want to use right clicks as well we must make use of an
API function. In line 22 the Windows message #WM_RBUTTONUP
is trapped and evaluated. With ChildWindowFromPoin_()
we use an API function for the first time. As we can see, an underscore is
appended to the function name and can now be used like PB's own commands. This
command retrieves the handle of the gadget from the click position. With GetDlgCtrlID_() the handle is converted to the gadget
number, which allows access to the gadget within PB. Now with a SELECT-CASE-ENDSELECT statement a message is displayed
dependent on the case. On click on the close button of the window the program
is finished. Until this moment the Repeat-Until
loop is run permanently.
The program is executed by click on the cog symbol .
Be sure to have enabled the debugger in compiler options to display error
messages.
String- and
TextGadgets
The next training file is TestStrTxtGadg.pb
. It starts with the command EnableExplicit.
With this it is forced that every variable has to be declared explicitly with Define.
This helps to prevent wrong results by faulty
variablenames.
It follows an enumeration of all gadgets. Each gadget
is assigned a numerical constant, which starts with ' # '. If not defined
otherwise, the enumeration begins with 0 and has a stepwidth of 1.
After that the window and the gadgets are created. We
have a TextGadget as button, two TextGadgets as labels and two StringGadgets
for keyboard input. When the program is started, none of the StringGadgets has
the the keyboard input cursor. We have to click on one of them to accept
keyboard input. This behaviour can be changed, when we remove the semicolon in
line 25. Now after start the gadget #Str_Upper is the active gadget and gets
the input cursor.
The Repeat event loop
here doesn't have as end statement Until, but ForEver. This is to point out that the loop really is
run through until the window is closed.
We have two kinds of events, gadget events and the
CloseWindow event. In ladder case the program is finished by End.
With gadget events we must find out, which gadget caused
the event. Was it a StringGadget, we test for a change event. In this case the
text in the gadget had been changed. When we click on the button, a message is
displayed.
When we input "asdfg" in the gadget named Eingabefeld 1, "ASDFG" is displayed. That
is correct, because we have declared the gadget with the parameter #PB_String_UpperCase. If we input the same text in Eingabefeld 2, nothing happens. This field accepts
only numerical characters, because it was declared with #PB_String_Numeric. We can proof that with "12345".
Now we know gadget and window events, but one is
missing: menu events.
Menus
The test file for this topic is TestMenus.pb. There are six TextGadgets created as
display elements. After the gadget list the menu list is created with appropriate
menu entries. In the event loop there are besides the close window event only
menu events. By click on a menu the related menu is displayed in one of the
TextGadgets.
After we can create and use a user surface we now have
a closer look to the central part of a log program: the database.
The Database
Because the QSO data should be stored permanently we
need a database. There are many of them, which one should it be? When searching
the PB forum, I saw that SQLite seemed to be popular among PB users. So I took
that and didn't regret the decision yet.
SQLite isn't a
program monster like Access, it is a slim DLL for creation and administration
of databases.
The interface between PB and SQLite is SQLite3. This is part of the mentioned PBOSL, we use the source code of SQLit3, not the userlib.
Databases consist of tables in which data of different
kind are stored. The number of tables is dependent of the purpose of the
database. But the storage of data is not a purpose on its own. Who doesn't
recall data, doesn't need to store them at all. Only the evaluation fills a
database with life. The means for evaluating data are queries. These are SQL
statements (Structured Query Language). With queries it is possible to make a
sensefull selection from the variety of stored data. Queries consist at least
of one SELECT statement.
To access our database outside of the log program,
there exists a very good tool: SQLite Expert. This allows for easy testing of
SQL statements. In the Personal Edition it is freeware and can be downloaded
from here.
In the following section, where we get familiar with
queries, the existence of SQLite Expert is assumed.
The database for our experiments is Log DK1IO.db3 and should be in the folder Log. After start of SQLite Expert we open the
database in menu <File><Open Database>. There are several tables in
the database, one of them is Tab_Log, my
complete log with more than 16.000 entries.
We want to display the log. When the checkbox Limit is checked, uncheck it. Below the dark gray bar
with the database name click on tab Data and choose from the tables Tab_Log. Now the table content is displayed. With the
arrow buttons one can navigate.
Now we will construct our first query. Click next to Data on SQL and input
the following text in the upper window:
(1)
SELECT *
FROM Tab_Log
Now in the upper menu bar select <SQL> and
<Execute SQL>. We can see now the same display as before. The character '
* ' is placeholder for all columns of the
table. When we want to display only certain columns, they must be placed
between SELECT and FROM.
With
(2)
SELECT Date, UTC, Call FROM Tab_Log
only the selected columns are displayed, but from all
entries. To limit the number of lines, we must append a WHERE clause:
(3)
SELECT Call FROM Tab_Log WHERE Call BETWEEN 'DA%' AND 'DS%'
This statement only shows German calls. No, DS-calls
are not displayed, the selection is inclusive DA, but exclusive DS. ' % ' is a
placeholder for none, one ore more characters.
By the way, the SELECT statements are strings, and
when there are other strings within that they must be enclosed in single
quotation marks as can be seen with 'DA%' and 'DS%'.
Now we can select data. But we want more, we want to
sort the data, too. This is done with ORDER BY.
The statement
(4)
SELECT * FROM Tab_Log ORDER BY Call
shows the complete log again, but sorted by calls.
Multiple QSOs with one station are shown multiple. By use of DISTINCT, the multiple appearance can be prevented:
(5)
SELECT DISTINCT Call FROM Tab_Log ORDER BY Call
shows every call only once. But with more than one
column it's different.
(6)
SELECT DISTINCT Call, Date, UTC FROM Tab_Log ORDER BY Call
will only prevent multiple appearances when call, date,
and UTC are the same. That is unlikely. Here we have to work with GROUP BY.
(7)
SELECT Call, Date, UTC FROM Tab_Log GROUP BY Call
shows all calls only once and sorts as well.
Up to now we have taken our data from only one table. But
that is not at all obligatory. The general syntax of a query is
SELECT Result FROM Source [WHERE] [GROUP BY] [ORDER BY]
where the expressions in brackets are optional.
Source can be one
or more table, one or more queries, or a combination of both.
Now we will construct a query , where Source is another query. With
(8)
SELECT * FROM (SELECT Date, UTC, Call FROM Tab_Log)
we get the same result as with query (2). But there
are only shown 3 columns, though we have used the character ' * ' as a placeholder for all columns. But the result
is correct this way, because Result cannot have
more columns than Source.
The next query uses as Source
two queries, which bas on two different tables. To Tab_Log
is now added Tab_EPC_Members. Since February
2007 I am member in EPC (European PSK Club), which has an attractive award
program, and for award evaluation a homemade log program is really predestined.
The task is to acquire the number of different
European areas (EU-Areas), which have been worked since
Here is the query:
(9) SELECT COUNT(DISTINCT B.Area) FROM
(SELECT DISTINCT Call FROM Tab_Log WHERE Mode = 'BPSK31' AND Date > '2006-06-09')
AS A,
(SELECT Call, Area FROM Tab_EPC_Members) AS B ON B.Call = A.Call
As we can see, the query is somewhat lengthy. And
something else we can observe: with the complexity of the query the time needed
for execution rises. In SQLite Expert you can see the time at lower left
margin.
The result of the query is incidentally 229. But more
important than the result is the way to get it.
Both subqueries have aliases (A and B) to address
them.
Now we will top our last query and bring a third table
into the game. The task: for an award the number of worked asian EPC-members is
to find out. Thus we need a table with contains the continent. This is the case
with Tab_Prefix. The link with Tab_Log is the column LandID,
which exists in both tables. LandID is a number which I took from ADIF2. It
clearly identifies all DXCC-countries.
The query:
(10) SELECT COUNT(DISTINCT A.Call) FROM
(SELECT Call, LandID
FROM Tab_Log WHERE Mode = 'BPSK31' AND Date >
'2006-06-09') AS A,
(SELECT Call FROM Tab_EPC_Members) AS B ON B.Call = A.Call,
(SELECT LandID FROM Tab_Prefix WHERE Kont = 'AS') AS C ON A.LandID
= C.LandID
Because of the complexity, the time need is anymore
risen. By the way, the result is 13, not enough for the award.
After we were busy with all kinds of Source, we will have a closer look at Result. Here too SELECT
statements can take the place of result columns.
A new task: we have to find out the number of
different DOKs on 80m and 40m in CW. In Tab_Log
we have the column AwdID, in which are entered
the award IDs of national awards: DOK for
When an award ID is new, this is stored in column New. Thus New has to
be True as well as QSLi, which means the QSL is
received. With these preconditions we can build our query:
(11) SELECT DISTINCT
(SELECT COUNT(*) FROM Tab_Log WHERE Call BETWEEN 'DA%' AND 'DS%'
AND Band = 80 AND Mode = 'CW' AND AwdID <> '' AND AwdID <> 'NM' AND
New = 1 AND QSLi = 1) AS '80',
(SELECT COUNT(*) FROM Tab_Log WHERE Call BETWEEN 'DA%' AND 'DS%'
AND Band = 40 AND Mode = 'CW' AND AwdID <> '' AND AwdID <>
'NM' AND New = 1
AND QSLi = 1) AS '40'
FROM Tab_Log
Both subqueries beginning with the second and third SELECT are identical except for the band
specification.
Though the query isn't really short, the execution
time is rather short compared with the previous query. This is why we have only
one table involved.
So far we were busy only with read accesses to the
database. Bu at the latest when we are at the end of a QSO, we must write our
data into the database.
Writing to
the database
When QSO data are written into the database, a
complete recordset is stored. The syntax for this write access is:
INSERT INTO
Tablename [(Column List)] VALUES(Value List)
When there is a column list, the number of entries
must be equal to the number of entries in the value list. The new recordset is
written to the end of the table.
But we have to write into the table, too, when we want
to alter QSO data later. This is done with an UPDATE
statement.
UPDATE Tablename SET Columnname = Value [WHERE]
When the value shouldn't be written in all lines of
the table, there must be a limitation in the WHERE
clause, e.g with
WHERE Call = 'XX0YY'.
A special kind of write access is executed with CREATE TABLE. With this command a new table is
installed.
It is a good to advice to make a copy of a table
before we are experimenting with it for write accesses. With
CREATE TABLE Tab_Log1 AS SELECT * FROM Tab_Log
we establish a
copy of Tab_Log named Tab_Log1.
Sometimes it happens that we don't want to write data
in a table, but to remove data from a table.
Deleting
from a database
For deleting data we use the DELETE
statement. With
DELETE FROM Tablename
all entries are deleted. To delete only certain
entries, a WHERE clause can be appended, e.g. WHERE Call = ' XX0YY'.
To remove a table from a database, the following
statement is used:
DROP TABLE Tablename
Now where we have become database experts we will be
busy with the display of the data we have extracted by queries. We don't want
to leave each time the log program and construct an elaborate query in SQLite
Expert. No, this should be done program controlled by click on a menu.
The
ListIconGadget
To display the contents of a database we have in PB
the ListIconGadget. The display is like with an Excel sheet. But there is an
important difference between them. While in Excel I can set the keyboard input
cursor into one cell and edit this cell, this can't be done with the
ListIconGadget. How we can bypass this limitation we will see later.
To get familiar with this gadget we have test file
TestListIconGadget.pb. This program fills the gadget with the complete log and
shows it. Let's have a closer look at the source code of this file.
Users of the PB demo version have to remove again the
semicolon in line 7. The statement after includes the file SQLite3.pbi in the program, because we want to access
the database. In lines 14 to 16 global variables are declared, so that we can
use them in procedures. RS is a variable, which
is assigned a structure of SQLite3. *RS is the
pointer to this variable. Beginning in line 53 the window and the two gadgets (#LI_Log and #Str_Edit)
are established. In line 66 is referred to the callback function in line 23.
The callback function is in close connection with the
API functions. Here the needed event messages are evaluated, which are not
provided by PB.
After that the SQLite-DLL is initialized and the
database is opened. Then the column assembly is organized. In our database
there exists a table Tab_AddGadgetColumn,
in which the column assembly for all occurring
purposes is stored, and of course for the log, too. In a For-Next loop the column informations (title and
width) are transferred to the gadget. When the columns contain Boolean values,
they are centered. In line 94 we have the SELECT
statement for the data selection. This string is assigned as a parameter to the
SQLite3 function SQLite3_GetRecordset(). Further
parameters are the database handle hDB and the
pointer *RS. All SQLite3 database commands
begin with SQLite3_.
In two nested For-Next loops
the gadget is now filled with data. In line 106 we have the feature, that data
are exchanged, when the data is "1" or "0". They are
replace by the characters $53 or $A3. "$" marks hexadecimal numbers. $53 and $A3 are
characters from the font set Wingdings 2, which
we have defined in line 21. They are a checked and unchecked checkboxes. To
really show these characters, we
evaluate in the callback function from line 32 the Windows message #NM_CUSTOMDRAW. For the Boolean columns the font set Wingdings 2 is selected, for all others Microsoft Sans Serif.
When we start the program, nothing happens at first,
at least nothing visible. That is the needed time to fill the gadget. The
nearly 16.500 entries can't be filled in zero seconds. But finally we have our
displayed data.
Well, a display is nice, but anytime we have the need
to change data. As mentioned earlier, this is not provided by Windows. But with
a trick we can afford it although.
When we click on a cell, whose content we want to
change, in the call back function starting at line 28 the column and line
informations are taken over. In the event loop we evaluate the #PB_EventType_LeftClick from line 126 on. First the
cell content and column title are read and stored. Then the QSO Nr. (ID) in column 1 is read and stored. Dependent on the
column title we carry on.
If it is a Boolean column, the empty checkbox is
changed with a filled one or vice versa. Into the database is written
"1" or "0". We use the UPDATE
statement for that, which is assigned as a parameter to the function SQLite3_Execute().
If it is a cell with text content, this content is
written to the still invisible StringGadget. Then the measurments and position
of the cell are transferred to the gadget and this made visible. What we see
now isn't any more the cell, but the StringGadget with the same text. That gets
the keyboard cursor and can be edited.
When we in fact change the text, the change event is
released for the gadget (line 151). But there we only store the fact of change.
When we leave the gadget by clicking on the column
header, the LostFocus event will occur (line 153). If the text in the
StringGadget was changed, the text is read and written to the database table
with the UPDATE statement. And of course the
text is written to the cell in the ListIconGadget. Then the StringGadget is
hidden again, and we see the original cell, but with changed text. By the
described manipulation it seems as if the ListIconGadget was directly edited.
After our intensive activity with database themes we
will now allocate to a theme which is not directly associated with the logbook,
but well with the transceiver control.
The Serial Interface
This hardware component is found in always less
computer, bit is needed for communication with the transceiver. Who owns a
computer without serial interface must now noway do without PC control. There
are USB/Serial converters as a replacement. Here two of them are in use, and
there were noticed no negative variations from the original.
The file for accessing the serial interface is LogCommCtrl.pbi. It is universally applicable, since
almost all settings are assigned from outside. Heart is the procedure OpenCommPort(), which are assigned 8 parameters.
Parameter
1: Denomination of the
commport, e.g. "COM1:", COM2:", etc.
Parameter
2: Baudrate. Here is to choose
the baudrate, which is set in the transceiver as well, e.g. 9600.
Parameter
3: Parity. Possible values are #NOPARITY
#ODDPARITY
#EVENPARITY
#MARKPARITY
#SPACEPARITY
Parameter
4: Stopbits. Possible values
are #ONESTOPBIT
#ONE5STOPBITS
#TWOSTOPBITS
Parameter
5: Databits; mostly 7 or 8.
Parameter
6: Flagbits. Flag Possible
values
fBinary #True; #False
fParity #True; #False
fOutxCtsFlow #True; #False
fOutxDsrFlow #True; #False
fDtrControl #DTR_CONTROL_DISABLE
#DTR_CONTROL_ENABLE
#DTR_CONTROL_HANDSHAKE
fDsrSensitivity #True; #False
fTXContinueOnXoff #True; #False
fOutX #True; #False
fInX #True; #False
fErrorChar #True; #False
fNull #True; #False
fRtsControl #RTS_CONTROL_DISABLE
#RTS_CONTROL_ENABLE
#RTS_CONTROL_HANDSHAKE
#RTS_CONTROL_TOGGLE
fAbortOnError #True; #False
Parameter
7: Size of input buffer
Parameter
8: Size of output buffer
At
the beginning of LogMain.pb we can see, which
values are used when calling OpenCommPort().
With
the API function CreateFile_() the comport is
opened. First parameter is the pointer to the variable which holds the
denomination of the comport. The other parameters contain values which are
needed for correct opening. Return value of the function is the comport handle,
which is needed for every access. With the next function SetupComm_() input and
output buffers are set to the parameter values.
Then
the variable dcb is loaded with the structure
DCB (Device Control Block). The members of this structure contain all for the
control needed values, among other things the flagbits. The actual values are
loaded to dcb with GetCommState_().
Then the members of dcb are overwritten
with the parameter values.
The
member dcb\fBits is a 32 bit long value. The 17
high-order bits belong to the system and shouldn't be altered. The 15 low-order
bits contain the flags as previously described. dcb\fBits is ANDed with a 32
bit value, which leaves the 17 high-order bits untouched, and sets the 15
low-order bits to 0. The result is ORed with the assigned parameter lFlags,
which sets each flag to the required value. With SetCommState_() these settings
are written back and now control the data flow between PC and transceiver. The
following delay the PC needs for execution of the action.
After
that some timeout values are assigned to Windows.
Return
value of the procedure is the commport handle hComm.
The next procedure CommRead()
reads the received data. Required parameters are the comport handle and the
pointer to the receive buffer. With ReadFile_()
the data are taken over into the receive buffer. From there they are read with PeekS(). Then the buffer is cleared.
Return value of the procedure are the received data.
Writing to
CommPort
The appropriate procedure for writing is CommWrite(). Required parameters are the comport
handle and the transmit data. First the RX-buffer is cleared to prevent overtaking
old data when a read access follows. Then the transmit data are written to the
commport and the transmit buffer is cleared. The following delay shall prevent,
that possibly following read access occurs too fast for the transceiver. With
the value may be experimented in direction to a shorter time.
The procedure has no return value.
CW/Tune
For keying of the transmitter the procedure CommSetDTR() is used. Parameters are the commport
handle and the desired state of the DTR-line. Inside the function EscapeCommFunction_() sets or clears DTR.
A successful action retrieves a value <> 0.
The first application program
Often the little helpers are those which make life
easier. Here a program does nothing else than to prepare the transceiver and PC
for starting another program.
Now and then I look into APRS, and for that are
several settings necessary at the transceiver. This little auxiliary program
takes over the activities, from switching on the rig via starting UIView (my
APRS program) to switching off after finishing.
The program doesn't need a user surface. Its source
code is in TS2K
UIV-Init VHF.pb.
For the users of the demo version the usual game for
including TestAPI.pbi. The next line includes LogCommCtrl.pbi into the program. In lines 13 to 25 the
flags are set to the required values. Afterwards the flags are multiplied with
their significance and added. The multiplication is done by leftshifting. Of
course one could do the computation externally and only use the result, which
is incidentally decimal 4097.
Then follows the call of OpenCommPort()
with all needed parameters. The return value, the comport handle, is stored in
hCommTS2K and is <> 0 with a successful action.
Now we send the first command to the commport, whereas
the first command "U" isn't a command at all but a dummy, which
generates polarity changes at the serial interface of the transceiver and wakes
it to life. But the following command "PS1;" is a genuine one, it
switches on the transceiver. The following delay of 1 s the transceiver needs
for setting up. The following statements establish the rig for packet
operation. In RunProgram() the complete path for
the executed program must be specified.
Afterwards the serial interface is closed, and the
APRS program is called. This takes over the control for the interface.
When the APRS program is finished, our program takes
over again and switches off the TS-2000.
As you can see, this is nothing awesome, but something
useful.
The great project – a log program
After we have build a base we now can dare to start
our great project, the log program. But "great" is only related to
the extent of the task, since actually it is an accumulation of small and
uncomplicated programparts.
Where it would be difficult really, namely with PSK,
there is fortunately help in form of a DLL, which takes over all computation
and the administration of the soundcard.
We will see that later.
Concerning the control of the transceiver, everybody
will have well his own philosophy on which rig functions should be controlled
by PC. For me existed no doubt, that the tuning should be done at the rig. But
rather everything else, what must/can be changed during operation, is handled
by the PC via mouseclick.
Here an overview over the program controlled
functions:
CW
bandwidth
CW
speed
Transmitting
of stored texts in CW and PSK
Setting
of the noise reduction
Output
power
Mode
AGC
on/off
Split
on/off
Switching
on the transmitter for tuning purposes
But who wants, can go on to include more rig functions.
I didn't detect any transceiver function, which could not be executed by PC.
And who is an award hunter has with an self written
log program his eldorado. As mentioned before I am EPC member and at once after
joining I built in new features for award evaluation. When there appears a
callsign in the receive window during PSK and is transferred to the appropriate
field, the call will be displayed in red when it represents a new EU-Area. And
it gets green, when it is a new prefix. Then you know, that you have to get
him.
While our little auxiliary program didn't need a user
surface, it is essential for the log program. Hereon are arranged the input
fields for the QSO data, the buttons for certain activities or simply the
labels for the input fields. Additionally to the gadgets we know yet there are
two new gadget we need: the ImageGadget and the EditorGadget. The ImageGadget
allows for displaying images. In our case it is used to show the waterfall for
PSK. The EditorGadget is als used in PSK mode and serves as the window for
displaying receive and transmit texts.
The program execution starts at the beginning of LogMain.pb. First there are six include files to
embed:
LogCommCtrl.pbi and SQLite3.pbi we already know.
In LogConstGlob.pbi
the constants and global variables are defined. Constants begin with
"#" and can contain numeric or text values.
LogWndOp.pbi contains
the program parts for establishing the window, gadgets, and menus for the modes
CW, FM, and SSB. There the colour settings for the single elements are carried
out, too, as well as the assignment of events to certain keys with AddKeyboardShortcut().
LogGenProc.pbi contains
many procedures for various purposes.
In LogPSKProc.pbi the
functions for the mode PSK are called.
After including the fore mentioned files the windows
are established, Wnd_Log and Wnd_Op. Both windows remain invisible, because the
parameter #PB_Window_Invisible is used. Wnd_Log serves as a display for log data as log- or
award evaluation. Wnd_Op is the operational
window for all modes.
After window opening the gadgetlist is created.
Therein all gadgets are listed which are needed for CW, FM, and SSB. These are
essentially the input fields for the QSO data. Picture 1 shows the window for
CW.

Picture
1
At the upper margin we see the bar with the window
title and the system tools. Below is the menu bar with the menu titles. In the
actual window we see at the right the block Log
with the input fields and labels. The yellow fields contain the data which are
finally stored. The fields Band, Mode, and QSO Nr. are
filled program controlled and cannot be changed by keyboard input. The other
yellow fields expect inputs, which are displayed in the three fields for calls
(Call, Via, Oth. Call) in capital letters. In the light grey
fields the program writes appropriate values.
After input of the call and leaving the field by tab
or mouse click, in the field Country the
country name is shown.
With input of a locator in the correct format the distance
and direction are shown in the fields to the right.
The light grey fields only have informal function and
are not stored with the QSO data.
With the arrow buttons next to the field Power the output power is set in the steps 5W, 10W,
25W, 50W, and 100W. The checkbox QSL is
checked, when a QSL is sent.
By click on the button Log
below the log frame the QSO data are stored. Additionally to the visible date
the date and the UTC are taken over into the log. The system time of the PC is
incidentally UTC and can be queried by GetSystemTime_().
The complete log action is placed as a gadget event
under Case #Button_Log in the event loop in LogMain.
Below the Log button
we have the button Tune, which switches on the transmitter
for tuning purposes. It gets red then. Another click switches off the TX and
the button gets its normal colour again.
At the right are placed the buttons for different
fixed texts. The most frequent used one is that for CQ, but you can output as
well your personal data and the station details in Morse code. The procedure,
which enables that, MorseOutp(). In the outer of
two nested For-Next loops the as a parameter
assigned text is translated character for character into Morse code by help of
the table Tab_MorseChars(). The character space
is added.
In the inner loop the elements of the Morse code
string ("0" and "1") are changed into values. These values
are used to set the DTR line, which is used for keying the TX.
With Delay() this state
is kept for 1 element length ( = 1 dot length). The accuracy of Delay() is sufficient for this purpose. Varying dot
lengths could not be observed.
Another click on the button aborts the output.
The speed of the Morse output and of the internal
keyer is set with the arrow buttons in the block CW-Speed
with a step width of 1 WpM. The settable range spreads from 10 WpM to 30 WpM.
Below that we have the block for setting the CW-Filterwidth. After program start a width of 400 Hz is set. The text of the active button is red.
The next block is Noise
Reduction. It is off normally, the buttons for setting the level are
disabled. By click on NR1 or NR2 the buttons are enabled and the level can be set
from 0 to 9.
In the frame
The last button on our window is AGC. It is off in CW mode, but can be switched on.
Two fields are invisible yet: AwardID
and Serial Nr. AwardID
has for the different national awards different denominations. When a German
call is input, the input field appears with the denomination "DOK".
For British calls, it is "WAB", "RDA" for Russian calls,
etc.
The field Serial Nr. only appears, when it is selected
in the related menu. The number is increased after each QSO by 1 and is stored
in the log table in the column Name.
Picture 2 shows the window after input of QSO data.

Picture
2
The green script colour in the field RDA marks this RDA nr. as already confirmed. Would the
colour have been red, is would be a new RDA, and blue stands for already
worked, but no QSL yet.
A specialty we see in the lower part of the window.
Here the already executed QSOs with this station are listed in the
ListIconGadget #LI_QSOB4. It is activated, when
in the log exist already QSO data from this station. But it can be used as well
for editing QSO data, e.g. to mark received QSLs.
But now back to the process at program start.
The next statement defines the callback function,
which we already know. It traps and evaluates events, which are not covered by
PB. The parameter is the address of the function, @WindowCallBack().
This is in LogGenProc.pbi.
Now the database is initialized and opened.
Then the table Tab_Start
is opened and a value for QSL administration is retrieved. The following
command computes the length of a dot for Morse code output.
Now the parameters for the serial interface are
assigned and the interface opened. We use two interfaces, one for controlling
the radio and one for keying the TX in CW.
Afterwards the procedure SetWindow()
is called, where the basic settings are made. First of all the transceiver is
switched on. As the last command the until now invisible window is made
visible.
The program execution jumps back to Log_main, where
the occurring events are queried in an endless loop. This is the central block
of the program.
The mode PSK
When we in menu Mode
select PSK, the procedure Set_PSK() in LogWndPSK.pbi
is called.
First the window is hidden for the duration of the restructuring.
The window itself is resized, since we need more space for this mode. Then the
unneeded gadgets are hidden and the still needed moved to their new place. And
the new gadgets for PSK are created now.
Picture 3 shows the window for PSK mode.

Picture
3
The block Log is
identical to the one mentioned before. To the right we have the block Stored Text, a bit larger than with CW.
Completely new are the the editor gadgets, to the left
the receive window and to the right the transmit window. #Edit_PSKRXTxt offers as a special feature to take
over certain textparts into the appropriate input fields. Since there are no
mouse events provided in PB, we must go the way via
WindowCallback(). There from the mouse position the next character is
determined and then to the left and to the right the next non alphanumeric
character is searched. Thus we have extracted a string. From the arrangement of
figures and letters is determined, into which input field the string belongs. A
number with three figures is written into RST rec,
a combination like "AA00AA" goes into Locator,
and a combination of letters with at least one figure is written into the field
Call. A letter only string can be Name or QTH: a left
click declares it as Name, a right click as QTH.