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 10 Jun 2006 in BPSK31. In Tab_EPC_Members there is a column Area. The link between the tables is the column Call, which exists in both of them.

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 Germany, DDFM for France, WAB for the UK, RDA for Russia, etc. So we have to extract via the calls the QSOs with German stations.

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.

Reading from CommPort

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 Split we have the related button, which is off normally. By click on the button the XIT of the transceiver is switched on, the setting of the offset frequency is done at the rig. The following button TF-Set works together with Split. If Split is off, TF-Set is disabled. If Split is on, a click on TF-Set causes that additionally the RIT is switched on and the bandwidth is increased to 2000 Hz. With this procedure it should be accomplished, that one (hopefully) hear the counter station of the DX station and get on its frequency. When TF-Set is on, Split is disabled and cannot be switched off until TF-Set is off.

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.