AGWPE TCP/IP API Tutorial

 

by Ing. Pedro E. Colla (LU7DID) and George Rossopoulos (SV2AGW)

 

Last Revised December 12, 2000

Copyright © P.E. Colla (LU7DID) and G.Rossopoulos (SV2AGW) 2000

 

 

Abstract

 

 

The AGW Packet Engine (AGWPE) by George Rossopoulos (SV2AGW) is a powerful AX.25 Layer 2 Manager running under Windows 95/98/NT as a “state-of-the-art” 32 bits application.

 

At this time AGWPE supports two completely different APIs; one based in DDE (Dynamic Data Exchange) which is the first implemented and another based on TCP/IP which has been introduced recently.

 

This document would concentrate on the TCP/IP API, the intended audience for it are application programmers looking for information on how to write (or adapt) their programs  to take advantage of the AGWPE services.

 

The information provided in this document is valid as per AGWPE Version 2000.20 or higher; previous versions might not support some of the functions so checking the release information is necessary; it’s likely that future versions would be backward compatible unless documented otherwise.

 


 

Table of Contents

 

Abstract 1

Table of Contents. 2

Overview.. 3

Communicating with AGWPE using the TCP/IP API 4

Enabling. 4

AGWPE GUI 4

AGWPE.INI 4

Communications. 4

API Application Interface Security. 5

TCP/IP Exchange. 5

AGWPE API Reference. 7

Frame Structure. 7

Frames sent by the Application to AGWPE. 8

Application Login (‘P’ frame) 8

Register CallSign (‘X’ frame) 8

Unregister CallSign (‘x’ frame) 9

Ask Port Information (‘G’ frame) 10

Enable Reception of Monitoring Frames (‘m’ frame) 10

AGWPE Version Info (‘R’ frame) 11

Ask Port Capabilities (‘g’ frame) 11

Callsign Heard on a Port (‘H’ frame) 12

Ask Outstanding frames waiting on a Port (‘y’ Frame) 12

Ask Outstanding frames waiting for a connection (‘Y’ frame) 13

Send UNPROTO Information (‘M’ frame) 13

Connect, Start an AX.25 Connection (‘C’ frame) 14

Send Connected Data (‘D’ frame) 15

Disconnect, Terminate an AX.25 Connection (‘d’ frame) 16

Connect VIA, Start an AX.25 circuit thru digipeaters (‘v’ frame) 16

Send UNPROTO VIA (‘V’ frame) 17

Non-Standard Connections, Connection with PID (‘c’ frame) 18

Send Data in “raw” AX.25 format (‘K’ frame) 19

Activate reception of Frames in “raw” format (‘k’ Frame) 19

Frames Sent by AGWPE to the Application. 21

Version Number (‘R’ frame) 21

Callsign Registration (‘X’ Frame) 21

Port Information (‘G’ Frame) 22

Capabilities of a Port (‘g’ Frame) 23

Frames Outstanding on a Port (‘y’ Frame) 23

Frames Outstanding on a Connection (‘Y’ Frame) 24

Heard Stations on a Port (‘H’ Frame) 25

AX.25 Connection Received (‘C’ Frame) 26

Connected AX.25 Data (‘D’ Frame) 26

Monitored Connected Information (‘I’ Frame) 27

Monitored Supervisory Information (‘S’ Frames) 29

Monitored Unproto Information (‘U’ Frames) 30

Monitoring Own Information (‘T’ Frames) 31

Monitored Information in Raw Format (‘K’ Frames) 32

Frame  Cross-Reference. 33

Programming Hints, Tips and Techniques. 34

Programming Language. 34

Talking with AGWPE. 35

Using C++. 35

Using Delphi4/5. 35

Overall Communication Cycle. 35

Frames Fiesta. 36

Sending Frames. 36

Receive Frames. 40

Format VIA Areas. 47

Parsing Port Information. 49

Port Capabilities. 50

Heard Information for a Port 50

Raw Frames. 51

Tracking Frames. 52

Managing Connections. 54

One Callsign, Many Connections. 54

Many CallSigns, Many Connections. 55

Down the Tubes, Climb the Ladder 55

Credits and other stuff 56

 

 

 

Overview

 

As an AX.25 Layer 2 (L2 for short) Manager it could control a huge number of AX.25 devices such as many TNC models (most of the commonly used), BayCom modems (most incarnations), quite a few really specialized high speed modems and the SoundBlaster card as a Packet device, AGWPE also provides an special “internal” port called loopback that could be used to interchange information among different applications running under the same AGWPE or (very useful) for test purposes; moreover, an almost unlimited number of them could be used at the same time each one being a “port” (well, sort of, George claims a maximum number of 100 ports, which is “unlimited” under every stretch of the concept).

 

As a manager, AGWPE is not functional per-se, meaning, the end user need to have it loaded but doesn’t make any direct use of it other than to configure it or to get a glimpse of the current status of the different AX.25 links and ports.

 

What uses the AGW Packet Engine are applications enabled to talk with it which in turn are used by end users to sustain activity over Packet Radio.

 

AGWPE comes with a basic “suite” of applications comprising a Packet Terminal program (AGWTerm), a monitor program (AGWMonitor), a mail client (AGWBBS/AGWFWD), a cluster program (AGWCLU) and a digipeater (AGWDigipeater), all of them written by George (SV2AGW); this suite is a complete albeit somewhat limited set for any end-user to sustain Packet operations.

 

A growing number of third party applications are starting to support the AGWPE also either directly (i.e.WinPack) or thru additional libraries (Tsthwin, WinFBB,etc); a fair number of authors had announced the future support of this platform with new versions of their programs.

 

The applications would see the AGWPE as a provider of services, those services are accessed thru a set of conventions named collectively the Application Program Interface (API).

 

The application request services thru blocks of information called API Frames (or Frames, for short, but don’t confuse them with AX.25 L2 Frames), those blocks are just a chunk of data with a predefined length

and contents.

 

The frames are always composed by a section named header (36 bytes long) and depending on the action required another section named data (any length).

 

Frames could be generated for the application and sent to AGWPE to request an specific service (such as sending data or to configure a particular aspect of the AGWPE functional behaviour or to require information about the current status).

 

AGWPE could, in turn, send also frames to the application; either as an answer to a given service (i.e. query of some value) or as an unsolicited block of information (i.e. a block of data just received at some port).

 

Both the frames sent to AGWPE by the application and the frames sent by AGWPE to the application has the same format.


 

Communicating with AGWPE using the TCP/IP API

 

Enabling

 

The TCP/IP API must be enabled to be functional, the default API for AGWPE still is the old DDE based.

 

There are basicall two ways to enable the TCP/IP API, thru the AGWPE GUI or modifying the AGWPE.INI configuration file.

 

Both methods would be useful, the AGWPE GUI for manual configurations while the AGWPE.INI modification could be seen more appropriate for automatic setups.

 

AGWPE GUI

 

Upon loading click on the AGWPE icon at the task bar, go to the “Setup Interfaces” entry and  on the “WinSock Interface Security” tab be sure the menu item “Enable Winsock TCP/IP Application Interface” is checked, verify which is the TCP Port where AGWPE listen for applications (should be 8000 unless you changed it).

 

Once checked the change would be Accepted to be effective.

AGWPE.INI

 

The following entry has to be added on the configuration file AGWPE.INI (usually at the same directory than the executable).

 

[TCPIPINTERCONNECT]

ENABLE=1

 

Without this entry AGWPE will not operate with the TCP/IP API, it is recommended that an application program willing to use the TCP/IP API should check this configuration value to ensure it is set properly and either set it directly or provide instructions to the end-user on the need to set it as a part of the installation.

 

AGWPE doesn’t provide a way to activate the TCP/IP API thru any of the GUI dialogs, so the entry on the AGWPE.INI must be configured and it is the only way to activate the TCP/IP API.

 

AGWPE reads this information only at startup, so for any change to be made effective the program has to be stopped and started.

 

Communications

 

When starting with this configuration AGWPE starts to serve the TCP/IP port 8000 for incoming requests from applications, see the previous section (AGWPE GUI) on how to change it..

 

Every application would start as many TCP/IP connections (sockets) with the AGWPE as required (usually one will be enough), multiple applications could have open connections with AGWPE at the same time; the limit on the number of sockets or connections AGWPE could sustain is defined by the TCP/IP stack of the machine where AGWPE is running (every socket “tax” the system resources, mostly in terms of memory and CPU cycles, till eventually no additional sockets could be opened).

 

On machines with a modern configuration this limit is not easily achievable under practical uses; AGWPE itself is extremely efficient in terms of the memory used and CPU cycles taken by itself([1]).

 

Each application is a typical TCP/IP client, as usually referred to in the bibliography, while AGWPE itself is a TCP/IP server.

 

In order to open a socket the application should start a TCP/IP connection to the IP address of the machine where AGWPE is running and the TCP Port 8000.

 

One of the very powerful aspects of the TCP/IP API is the fact that no restriction bounds the application and AGWPE to be run on the same machine, as long as a TCP/IP connection could be established the AGWPE and the Application program could be run on the same machine, on close machines operating in some LAN or half a world appart.

 

The traffic (amount of data transferred between AGWPE and the Application) is quite substantial indeed, in order to achieve reasonable performance the bandwidth between AGWPE and the Application (each application) should be in the order of 3 to 4 times the combined bandwidth of all the AX.25 ports being serviced, this could be served in excess either running AGWPE and the application on the same machine or being linked by some Ethernet or TokenRing LAN (typical speeds between 10 and 100 Mbps), dial-up connections might be marginal depending on the total load planned to be serviced([2]).

 

An interesting (theoretical) possiblity is to run AGWPE and an application program being appart and linked thru Packet Radio (TCP/IP over AX.25), albeit most current networks won’t have enough speed to provide even a minimum functionality in real world terms.

 

In order for the TCP/IP communication to be established the IP Address of the machine where AGWPE is running is assumed to be known (this is a pre-requisite), the TCP port is as stated usually 8000.

 

The IP address of the machine could be easily obtained with the Windows utility named winipcfg, in case the machine has many adapters (each one eventually having one different IP address) you could use the one associated with the adapter that could “see” the machine running AGWPE.

 

In the case the application and AGWPE runs on the same machine the definition of the IP address becomes trivial since the IP loopback address (127.0.0.1) should be always used; since most of the time AGWPE and the application will be run on the same machine the loopback IP address should be the one used by the application by default[3]

 

API Application Interface Security

 

Starting on version 2000.78 AGWPE brings security features that must be taken into account, the behaviour of the security model is controlled by the settings at the “WinSock Interface Security” tab on the “Interface Setup” menu entry.

 

 

 

 

 

Be aware that if the application is being ran from a machine that doesn’t comply with the security setting it has to provide a frame of type “P” to be able to interact with

AGWPE, more on this later.

 

This security model allows flexibility to make visible a node to a big (uncontrolled) environment such as the Internet and still enable the system operator to control who

is using his resources.

 

TCP/IP Exchange

 

The basics on how to program using TCP/IP are far beyond the scope of this document to explain, however, TCP/IP is a technology so pervasive and widely used that no modern language intended for the Windows environment lacks support for it.

 

Usually this support is in the form of a set of calls (the TCP/IP API or the programming conventions to use TCP/IP, do not get confused with the AGWPE TCP/IP API which is the way to communicate with AGWPE using TCP/IP).

 

The implementation varies from programming language to programming language, and even within them there are often many alternative implementations based on different vendors.

 

TCP/IP programming could be a mind boggling exercise at its limits, fortunately only a subset of all the functions are needed to establish and maintain a successful connection with AGWPE under most circumstances.

 

Communications with AGWPE will use TCP sockets only, so on most implementations of TCP/IP you would require to use just 3 API calls:

 

 

Your program would also need to handle a minimum of 3 events related to an open socket:

 

 

Depending on the language and the library the above basic elements might vary, through this document all techniques would be explained conceptually based on this set.

 

The sequence of a dialog between an application and AGWPE always steps thru the following major activities:

 

 

 

 

 


AGWPE API Reference

 

Frame Structure

 

Information between AGWPE and the application flows in both directions using an overall format composed by a header (fixed) and a variable data area depending on the particular frame being sent (many frames are just formed by a header).

 

 

Field

Length

Meaning

AGWPE Port

1 Bytes

[0..n] the least significant value comes in the first byte while the most significant in the second. I.E. Port 2 would be expressed as

0x01 ([4])

 

Reserved

3 Bytes

Usually 0x00 0x00 0x00

DataKind

1 Byte

Is the frame code, reflects the purpose of the frame. The meaning of the DataKind DO VARY depending on whether the frame flows from the application to AGWPE or viceversa.

Reserved

1 Byte

Usually 0x00

PID

1 Byte

Frame PID, it’s usage is valid only under certain frames only. Should be 0x00 when not used.

Reserved

1 Byte

Usually 0x00

CallFrom

10 Bytes

CallSign FROM of the packet, in ASCII, using the format {CALLSIGN}-{SSID}

(i.e. LU7DID-8)

it is “null terminated” (it ends with 0x00). ([5])

The field ALWAYS is 10 bytes long.

It’s filled on packets where it has some meaning.

CallTo

10 Bytes

CallSign TO of the packet, same as above.

DataLen

4 Bytes

Data Length as a 32 bits unsigned integer.

If zero means no data follows the header.

User (Reserved)

4 Bytes

32 bits unsigned integer, not used. Reserved for future use.

 

All reserved fields must not be used by application programs in any form, they should be initialized to

binary zeros (0x00) on frames sent by the application to AGWPE, undefined values could be present on frames sent by AGWPE to the application on those frames.

 

AGWPE is fairly tolerant on unused fields (either reserved or not used on a particular frame format) to held almost anything, so the need for proper initialize them reflected on this documentation aims towards proper programming practices rather than actual needs from AGWPE.

 

Frames sent from the Application to AGWPE where the CallFrom/CallTo values are relevant must contain our callsign in the CallFrom and the other end callsign in the CallTo fields. The other way around, frames from AGWPE to the Application would contain the other end callsign+SSID in the CallFrom and our callsign+SSID in the CallTo fields.

 

When the frame has data associated with it (DataLen <> 0) the bytes up to the number expressed by DataLen follows inmediately after the last byte of the header.

 

In order to allow for a fully transparent transport of data no delimiters of any kind are used on the data area, so binary information of any kind could be effectively transported.

 

A typical example of a frame (header+data) sent by AGWPE looks like this:

 

 

 |01 00 00 00 4D 00 CF 00 4C 55 37 44 49 44 2D 34 |....M...LU7DID-4

 |00 00 4E 4F 44 45 53 00 00 00 00 00 07 00 00 00 |..NODES.........

 |00 00 00 00 FF 41 42 52 4F 57 4E -- -- -- -- -- |.....ABROWN

 

An example of a frame with just a header sent by AGWPE looks like this:

 

|00 00 00 00 58 00 00 00 4C 55 37 44 49 44 2D 34 |....X...LU7DID-4

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....

 

It’s worth to notice that in the second example not all fields (actually the ones not relevant to the function requested as we would see) have not been completed.

 

 

Frames sent by the Application to AGWPE

 

Application might (or must, sometimes) send data to AGWPE in order to retrieve configuration information or to sustain communication over any of the ports.

 

Colloquially, the frames are identified by it’s DataKind (so a frame with a DataKind=’X’ is referred in this documentation as an ‘X’ frame).

 

The same DataKind could be used on a frame sent by the application to AGWPE or from AGWPE to the application (however, the meaning of a DataKind is unique in any given direction), usually the frames with the same DataKind on both directions are Query-Answer pairs (so, i.e.,  a ‘G’ frame sent by the application is replied by AGWPE with a ‘G’ frame filled with the information required).

 

Care has to be taken by the application program to handle sent and received frames separately.

 

Follows all the frame formats supported for the application to send to  AGWPE.

 

Application Login (‘P’ frame)

 

An application needs to login when the “WinSock Interface Security” setting rules doesn’t allow the machine where the application is being ran to access the AGWPE directly; it should not bother applications running on the same machine where AGWPE is executing. Still applications should allow the user to define this security setting and be flexible to be run on machines other than the one AGWPE is running (and thus, potentially not enabled directly by the security settings to access AGWPE).

 

This frame is mandatory when the application is being run from a machine that doesn’t comply with the security rules, without it AGWPE will not accept nor send frames

to the application.

 

The login is made with a frame with  an empty  header and the login data into the Information part of the frame with the following format.

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘P’ (ASCII 0x50)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

Length of User+Password including 0x00’s

User & Password

N Bytes

UserId ended with 0x00 filled till 255 bytes

Password ended with 0x00 filled with 255 bytes

 

AGWPE  do not inform the application about the success.

 

Please note an application must send one of the UserId/Password combinations as stated in the “WinSock Interface Security” tab “User Setup” section in order to be validated and allowed by AGWPE to interact with it thru the WinSocks API.

 

Follows a sample content of the information area of the frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

 |4C 55 37 44 49 44 00 00 00 00 00 00 00 00 00 00 |LU7DID..........

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4C |...............L

 |49 5A 41 52 44 00 00 00 00 00 00 00 00 00 00 00 |IZARD...........

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 |00 00 00 00 00 00 00 00 00 00 00 00 00 00 -- -- |..............

 

 

Register CallSign (‘X’ frame)

 

An application needs to register at least one callsign with AGWPE as a pre-requisite to be able to send data thru any AX.25 port or to sustain any connection and before any attempt on doing so.

 

To receive (monitor) information heard at the different ports the “m” frame should be used instead.

 

There is no limits on the number of callsigns that could be registered by a single application, each registration would require a separate frame.

 

When an application registers a callsign AGWPE “listen” on the radio ports for any packet frame directed to that callsign and when detected it would be sent to the application using the suitable frame format (depending on the type).

 

The registration is made with a frame with just a header (no data) with the following format.

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘X’ (ASCII 0x58)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

CallSign-SSID to register

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

A given callsign and SSID combination is allowed to be registered just once by an application (actually among all the applications connected to the same AGWPE at any given moment).

AGWPE informs the application about the success (callsign+SSID registered) or failure (callsign+SSID already in use) by means of an “X” frame sent to the application in response of this one.

 

Please note an application could register almost “anything” as a callsign+SSID (not necessarily a true callsign), so if for some reason is relevant to the application to receive frames directed to (i.e.) the “NODES” destination that could be accomplished registering the “NODES” callsign, in a way that any frames (likely UI frames) directed to the “NODES” destination (NODES-0 actually) would be directed to the application who registered it. As in with the case of true callsigns a given “destination”+SSID is allowed to be registered just once.

 

A registration could be performed at any time by the application.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

|00 00 00 00 58 00 00 00 4C 55 37 44 49 44 2D 34 |....X...LU7DID-4

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Unregister CallSign (‘x’ frame)

 

This is the opposite function than to register a callsign, it means the callsign and SSID combination is not longer used by the application and it’s free for further use, from the moment of the application become unregistered and till it’s registred again all activity heard by AGWPE on the AX.25 ports directed to that callsign is ignored.

 

Also, all information sent by the application to AGWPE involving the unregistered callsign is ignored.

 

The overall format is very similar to the registration frame, just the DataKind is changed, as follows:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘x’ (ASCII 0x78)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

CallSign-SSID to unregister

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

As a difference with the registration frame the application should not expect any answer from AGWPE as a confirmation of the successful unregistration.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 78 00 00 00 4C 55 37 44 49 44 2D 34 |....x...LU7DID-4

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Ask Port Information (‘G’ frame)

 

Using this frame the application could query AGWPE to provide information about the currently defined ports.

 

This information is usually handy at the start of the application program in order to know the number of ports available and eventually use that information for functional or presentation purposes, the port information could not be changed dynamically on AGWPE (it requires AGWPE to be stopped and re-started) so this information should also be queried every time the TCP/IP connection is re-established.

 

The frame format comprises a header only with the following information.

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘G’ (ASCII 0x47)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

AGWPE answer this request with a “G” frame.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 47 00 00 00 00 00 00 00 00 00 00 00 |....G...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

 

 

 

Enable Reception of Monitoring Frames (‘m’ frame)

 

In order for monitoring frames to be sent to the application this condition has to be signaled to AGWPE using this frame.

 

From the moment this frame is sent activity at all ports would be made available to the application (Frames S,I and U).

 

This function could be used even if the application didn’t registered any callsign.

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘m’ (ASCII 0x6D)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

AGWPE didn’t confirm specifically this frame, however, the flow of monitored information should start inmediately after it has been sent by the application.

 

This frame acts like a switch, the first time issued it enables the reception of monitoring frames while the second disables it and so on; in general on odd times it would enable and on even times it would disable.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 6D 00 00 00 00 00 00 00 00 00 00 00 |....m...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

 

 

AGWPE Version Info (‘R’ frame)

 

It’s sometimes important (at least it’s is a good programming recommended practice) to care about the level of the AGWPE which the application is connecting to.

 

Several reasons support that practice, but the most important is to be sure the AGWPE will support all the frames and functions the application program would require to work properly; as a fast evolving platform AGWPE is being continuously upgraded with new functions and fixes for old ones.

 

The application programmer should not be surprised to find almost all version historically released of AGWPE thru the time, not all of them supporting the full set of frames documented here (which are valid as per version 2000.20 or higher).

 

The AGWPE version is queried with a frame with the following format:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘R’ (ASCII 0x52)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

In any case, this frame should be sent at least once per execution by the application program (even if the AGWPE connection could be stopped and restarted it’s not unreasonable to assume the version didn’t changed, doesn’t hurt to query and confirm the version on each connection with AGWPE though).

 

This frame is answerd by AGWPE with an ‘R’ frame.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 52 00 00 00 00 00 00 00 00 00 00 00 |....R...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Ask Port Capabilities (‘g’ frame)

 

An useful complement of the “G” frame (Ask Port Information) is to query AGWPE about the particular configuration for every specific port.

 

Albeit AGWPE doesn’t allow an application to change its configuration thru the API it’s usually necessary or useful to get that information anyway for (mostly) presentation purposes.

 

This frame has the following format

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to query

0=Port1,1=Port2,…

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘g’ (ASCII 0x6D)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

AGWPE answers this request with a “g” frame.

 

Some values are static and could not be changed without re-starting AGWPE, but others reflects dynamically the current status of a given port in terms of traffic.

 

This function should be called at least once every time a connection with AGWPE is established, there is no limit on how many times this information could be queried, however, a practical limit from the performance (and usefulness) standpoint should limit this query to be performed once every minute or so.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

|00 00 00 00 67 00 00 00 00 00 00 00 00 00 00 00 |....g...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Callsign Heard on a Port (‘H’ frame)

 

A very useful service required (or nice to have) on most applications is a list of the stations “heard” on a given port; this could be achieved by the application just collecting monitoring information.

 

However, this is not required since AGWPE helds such a list and makes it available to the application upon request (at any time).

 

In order to request the updated list of stations heard on a given port the following frame has to be sent.

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to query

0=Port1,1=Port2,…

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘H’ (ASCII 0x48)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

This function makes AGWPE to answer the Heard information thru an “H” frame.

 

This frame could be sent as many times as required during the lifespan of a connection, every time the information provided will be updated to reflect the traffic actually heard.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 48 00 00 00 00 00 00 00 00 00 00 00 |....H...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Ask Outstanding frames waiting on a Port (‘y’ Frame)

 

This frame could be used by the application at any time to query AGWPE about the number of frames (from all sources, not only this application) that are queued and waiting to be transmitted by AGWPE thru a given port.

 

This would be useful to regulate the rate used to send information to AGWPE and to keep it realistic with the actual bandwidth of the destination port.

 

The information could be queried using the following frame:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to query

0=Port1,1=Port2,…

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘y’ (ASCII 0x79)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

This frame is answered by AGWPE with an ‘y’ frame.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 79 00 00 00 00 00 00 00 00 00 00 00 |....y...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Ask Outstanding frames waiting for a connection (‘Y’ frame)

 

This frame could be used with similar purposes than the ‘y’ frame but to query AGWPE about the outstanding frames waiting sourced on a given (and specific) connection as opposed to the overall activity of a port without any clue on how it had been sourced.

 

The information could be queried using the following frame:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to query

0=Port1,1=Port2,…

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘Y’ (ASCII 0x59)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00)

CallTo

10 Bytes

Other CallSign-SSID

i.e. SV2AGW-14 ended with

null (0x00)

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

AGWPE would answer this frame with a ‘Y’ frame himself if the connection referred by the CallFrom/CallTo fields do exists currently.

 

Careful must be exercised to fill correctly both the CallFrom and CallTo fields to match the ones of an existing connection, otherwise AGWPE won’t return any information at all from this query.

 

The order of the CallFrom and CallTo is not trivial, it should reflect the order used to start the connection,

so

 

 

Please refer to the ‘C’ frame sent by AGWPE upon connection to understand how to identify who initiated a connection.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 59 00 00 00 4C 55 37 44 49 44 2D 34 |....Y...LU7DID-4

|00 00 4C 55 37 44 49 44 00 00 00 00 00 00 00 00 |..LU7DID........

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Send UNPROTO Information (‘M’ frame)

 

This frame could be used by the application when an AX.25 unproto (UI) frame must be sent by the application.

 

For an application to send unproto information no registration is needed, however unproto information heard on the ports directed to it won’t be made available by AGWPE and information exchange won’t be possible (unless the application extract frames directed to it thru the inspection of monitoring frames, which is not utterly practical but still possible).

 

Typical uses for an unproto frame are beacon or any other broadcast message, it’s also widely used by NETROM L3 broadcast, TCP/IP over AX.25 and the FBB mail client protocol among others.

 

In order to send an unproto frame the header to be used is

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the unproto frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘M’ (ASCII 0x4D)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID

0x00 or 0xF0 for AX.25

0xCF NETROM

and others

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used

CallTo

10 Bytes

Destination of the unproto

frame. Not necessarily a

callsign+SSID (could be i.e. CQ, ID, another callsign+SSID), etc…

DataLen

4 Bytes

Number of Bytes to be sent

User (Reserved)

4 Bytes

0

 

Following the header the (exact) amount of bytes indicated in DataLen should follow.

 

AGWPE would indirectly inform the success of the unproto send thru both an ‘I’ frame, a ‘U’ frame and a ‘T’ frame (if monitoring is enabled thru the ‘m’ frame).

 

Even if AGWPE handles AX.25 frames larger than 255 bytes not so many other programs could over the air, so it’s a reasonably programming practice to ensure than the length of the data to be transferred is equal to or less than 255 bytes.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 4D 00 F0 00 4C 55 37 44 49 44 2D 34 |....M...LU7DID-4

|00 00 4E 45 54 4D 45 00 00 00 00 00 39 00 00 00 |..NETME.....9...

|00 00 00 00 0D 0A 42 65 61 63 6F 6E 20 64 65 20 |......Beacon de

|4E 6F 64 6F 20 4C 55 37 44 49 44 2D 34 20 41 64 |Nodo LU7DID-4 Ad

|72 6F 67 75 65 20 42 41 20 41 72 67 65 6E 74 69 |rogue BA Argenti

|6E 61 20 5B 47 46 30 35 54 45 5D 0D 0A -- -- -- |na [GF05TE]..  

 

 

 

Connect, Start an AX.25 Connection (‘C’ frame)

 

This frame is sent to AGWPE when an AX.25 connection with other station is required.

 

The station originating the connection (CallFrom) must had been previously registered with AGWPE (‘X’ frame) for the connection to be successfully established.

 

The connection started with this frame would always be a normal AX.25 connection (information frames with PID=0xF0).

 

The application is responsible to identify the port to be used for this connection and to properly inform it on the frame.

 

The format of the frame follows:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the connection request frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘C’ (ASCII 0x43)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID (0xF0 or 0x00)

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used. must had been previously registered

CallTo

10 Bytes

Destination callsign+SSID of the connection.

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

AGWPE would start inmediately to connect the destination callsign-SSID (it could be monitored, if monitoring has been enabled) thru the ‘S’ frames.

 

Upon connection or failure the application would receive a ‘C’ frame or a “Retryout message”.

 

An application could sustain one connection per distinctive callsigns+SSID for both origin and destination pairs (only one connection by a given callsign+SSID on origin and destination is allowed by the AX.25 protocol).

 

No practical limit do exists on the number of connections an application could sustain with different destinations, even from the same originating callsign+SSID. This concept, of course, is extended when many callsigns+SSID are registered by the same application.

 

It is an application duty, as we’ll see on the relevant frames sent by AGWPE, to discriminate among data coming from diferent connections.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 43 00 00 00 4C 55 37 44 49 44 2D 34 |....C...LU7DID-4

|00 00 4C 55 37 44 49 44 00 00 00 00 00 00 00 00 |..LU7DID........

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Send Connected Data (‘D’ frame)

 

Once a connection had been successfully established data could be exchanged, the application could then send data to the other end by means of data frames.

 

The format of the frame would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the data frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘D’ (ASCII 0x44)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID (0xF0 or 0x00)

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used. must had been previously registered

CallTo

10 Bytes

Destination callsign+SSID of the connection.

DataLen

4 Bytes

Number of Data Bytes to be transferred.

User (Reserved)

4 Bytes

0

 

Following the header the (exact) amount of bytes indicated in DataLen should follow.

 

AGWPE would indirectly inform the success of the unproto send thru both an ‘I’ frame, a ‘U’ frame and a ‘T’ frame (if monitoring is enabled thru the ‘m’ frame).

 

Data exchanged would be under a standard AX.25 Information PID (0xF0) unless the connection had been specifically started signalling AGWPE about a non-standard PID (connection started with the ‘c’ frame instead of the ‘C’ frame), on such situations the application must place the relevant PID on the respective field of the header. In all other situations the PID field is ignored by AGWPE and 0xF0 is used instead.

 

If a ‘D’ frame is sent by the application without an established connection the frame is ignored by AGWPE.

 

It is the application responsibility to keep using the proper AGWPort and CallFrom/CallTo values on all the frames of a given connection than the used to establish it.

 

Even if AGWPE handles AX.25 frames larger than 255 bytes not so many other programs could over the air, so it’s a reasonably programming practice to ensure than the length of the data to be transferred is equal to or less than 255 bytes.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 44 00 F0 00 4C 55 37 44 49 44 00 00 |....D...LU7DID..

|F0 15 4C 55 37 44 49 44 2D 34 00 00 02 00 00 00 |..LU7DID-4......

|A8 6D 45 00 3F 0D -- -- -- -- -- -- -- -- -- -- |.mE.?.

 

 

Disconnect, Terminate an AX.25 Connection (‘d’ frame)

 

When an AX.25 connection (started with a ‘C’ frame) needs to be terminated a disconnection frame must be sent by the application.

 

To send data between a connection and a disconnection is, of course, optional; however, the main purpose of a connection would be most of the time to exchange data with another station.

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the data frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘d’ (ASCII 0x64)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID (0xF0 or 0x00)

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used. must had been previously registered

CallTo

10 Bytes

Destination callsign+SSID of the connection.

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

No data is associated with this frame.

 

AGWPE will inform the completion of this request thru a ‘d’ frame[6]

 

If a ‘d’ frame is sent by the application without an established connection the frame is ignored by AGWPE.

 

It is the application responsibility to keep using the proper AGWPort and CallFrom/CallTo values on all the frames of a given connection than the used to establish it.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 64 00 00 00 4C 55 37 44 49 44 2D 34 |....d...LU7DID-4

|00 00 4C 55 37 44 49 44 00 00 00 00 00 00 00 00 |..LU7DID........

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

Connect VIA, Start an AX.25 circuit thru digipeaters (‘v’ frame)

 

This frame is used with similar purposes than the ‘C’ frame, but as it creates a “direct” connection this frame must be used when intermediate AX.25 digipeaters must be used to establish a connection.

 

It’s the application responsibility to determine, based on it’s functionality and user interface, whether a given connection should be started direct (‘C’ frame) or thru digipeaters (‘v’ frame).

 

Once the connection is established data is transferred between both ends with the same frame (‘D’ frame) and disconnection is started also with the same frame (‘d’ frame) on both.

 

The format of this frame would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the data frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘v’ (ASCII 0x76)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID (0xF0 or 0x00)

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used. must had been previously registered

CallTo

10 Bytes

Destination callsign+SSID of the connection.

DataLen

4 Bytes

Length of the VIA information

User (Reserved)

4 Bytes

0

 

The VIA (number and sequence of digipeaters to be used) is informed in the data part of the frame inmediately following the header, the length of this area would vary depending on the number of

digipeaters to be used, the exact length must be informed in the DataLen field.

 

The data area must contain the VIA information in the following format

 

Offset

Length

Meaning

+00

1 Bytes

Total number of digipeaters to be used (max 7)

+01

10 Bytes

CallSign+SSID of the first digipeater ended with null (0x00)

+11

10 Byte

CallSign+SSID of the 2nd digipeater ended with null (0x00)

……

……

…..

+10XN+1

10 Byte

CallSign+SSID of the Nnd digipeater ended with null (0x00)

 

Of course, only the number of needed digipeaters has to be informed (but at least ONE must be informed, otherwise a direct connection should be used instead).

 

The successful completion of the connection is informed by AGWPE thru the ‘C’ frame.

 

Send UNPROTO VIA (‘V’ frame)

 

When the application needs to send unproto information (as in the ‘M’ frame) but using a chain of repeaters to do so this frame format should be used instead.

 

The frame format is as follows:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the unproto frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘V’ (ASCII 0x76)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID (0xF0 or 0x00)

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used. must had been previously registered

CallTo

10 Bytes

Destination callsign+SSID or ID of the unproto frame (i.e. CQ, ID, MAIL, etc)

DataLen

4 Bytes

Length of the VIA information and the data to be sent

User (Reserved)

4 Bytes

0

 

Right after the header the chain of digipeaters to be used is sent as the first part of the data area using the format already discussed for the Connect VIA (‘v’ frame)

 

Offset

Length

Meaning

+00

1 Bytes

Total number of digipeaters to be used (max 7)

+01

10 Bytes

CallSign+SSID of the first digipeater ended with null (0x00)

+11

10 Byte

CallSign+SSID of the 2nd digipeater ended with null (0x00)

……

……

…..

+10XN+1

10 Byte

CallSign+SSID of the Nnd digipeater ended with null (0x00)

 

As before, only the number of digipeaters to be used needs to be included (with at least one being informed).

 

After the VIA information the actual data to be sent is included, please note the DataLen field on the header should reflect the exact size of both the VIA information and the data information to be sent.

 

Even if AGWPE handles AX.25 frames larger than 255 bytes not so many other programs could over the air, so it’s a reasonably programming practice to ensure than the length of the data to be transferred is equal to or less than 255 bytes.

 

However, nothing prevents the SUM of the VIA information and the data information to be sent to be larger than 255 bytes (the VIA information is internally decoded and used by AGWPE to build the AX.25 header but only the data information is included on the AX.25 information part).

 

Non-Standard Connections, Connection with PID (‘c’ frame)

 

On special situations the application might need to interchange information with a destination thru frames using a non-standard PID (standard AX.25 PID for Information Frames is 0xF0), examples of such a need are NETROM connections and frames related to a connected TCP/IP over AX.25 circuit.

 

On such ocassions AGWPE must be signaled of this singularity starting the connection with this frame instead of a “normal” ‘C’ frame as documented before; the application is also responsible to fill the PID field of the header of all data frames sent during the connection with the appropriate value (even if the PID is informed during the connection it has to be “repeated” on every data frame).

 

The format of the frame would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the connection request frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘c’ (ASCII 0x63)

Reserved

1 Byte

0x00

PID

1 Byte

Non Standard PID to use

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Our CallSign-SSID

i.e. LU7DID-11 ended with

null (0x00) used. must had been previously registered

CallTo

10 Bytes

Destination callsign+SSID of the connection.

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

 

AGWPE would start inmediately to connect the destination callsign-SSID (it could be monitored, if monitoring has been enabled) thru the ‘S’ frames.

 

Upon connection or failure the application would receive a ‘C’ frame or a “RETRYOUT” message.

 

An application could sustain one connection per distinctive callsigns+SSID for both origin and destination pairs (only one connection by a given callsign+SSID on origin and destination is allowed by the AX.25 protocol).

 

No practical limit do exists on the number of connections an application could sustain with different destinations, even from the same originating callsign+SSID. This concept, of course, is extended when many callsigns+SSID are registered by the same application.

 

It is an application duty, as we’ll see on the relevant frames sent by AGWPE, to discriminate among data coming from diferent connections.

 

Please note that the destination application must know exactly how to handle data frames with non-standard PID in order for a data exchange to take place, the connection would succeed even with destinations not truly aware of the non-standard PID (the AX.25 protocol doesn’t include PID information on a connection frame).

 


 

Send Data in “raw” AX.25 format (‘K’ frame)

 

On special situations when the application needs to control the exact content of a given frame (as when applications needs to deal with a hardware TNC in KISS mode) the complete frame could be built and sent using this frame.

 

This facility should be used on very special applications only.

 

The format of the frame follows:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port to send the data frame thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘K’ (ASCII 0x4B)

Reserved

1 Byte

0x00

PID

1 Byte

 0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

Actual origin is stated in the

raw frame

CallTo

10 Bytes

10 0x00

Actual destination is stated in

the raw frame

DataLen

4 Bytes

Number of Data Bytes to be transferred.

User (Reserved)

4 Bytes

0

 

The complete frame (AX.25 header followed by data if applicable) in raw format must follow as data, the exact length of it must be reflected on the DataLen field.

 

Full knowledge of the intricancies of the AX.25 must be mastered by the brave programmer trying to use this frame, for what is worth the heartfull recommendation is to try to identify other frames or combination of frames most suitable for a given purpose, so use this frame format as an absolute last resort.

 

For those brave souls in need to still use it the following recommendations should be used (refer to the AX.25 Protocol documentation for the naming conventions).

 

Field

Length

Description

Flag

1 byte

Will not be the standard 0b01111110 flag but

the “TNC” to use

00=Port 1

16=Port 2

Address

112/360 bits

AX.25 coded Origin, Destination and (optionally) digipeaters.

Control

1 byte

AX.25 Control Field

PID

1 byte

AX.25 PID

Info

N bytes

AX.25 Information Area

 

Please note than the AX.25 FCS and the ending Flag are NOT included. No KISS escape codes nor bit stuffing is required to be performed (AGWPE would add them as needed).

 

As per AGWPE Version 2000.20 this frame should be used to send only unproto information. This is a general recommendation, still it could be used to send both connected

and unconnected information. When connected information is sent using this frame the application will not receive monitor frames (T frames) with the frames sent. Connected frames will be both received as “K” frames and the appropriate monitoring frame.

 

Even if AGWPE handles AX.25 frames larger than 255 bytes not so many other programs could over the air, so it’s a reasonably programming practice to ensure than the length of the data to be transferred is equal to or less than 255 bytes.

 

 

Activate reception of Frames in “raw” format (‘k’ Frame)

 

AGWPE send to the application all data using several frame formats (D or U for actual connected or unconnected data, I or S for monitored information, T for information the application sent, etc).

 

In particular, the I and S frames (as we would see) provides some “decoding” of the information as part of the data area of the frame; things such as the AX.25 header components, NETROM circuit control and TCP/IP connection control data are parsed and included in plain ASCII before the actual data.

 

The application program could, for light usages, process and decode the contents of either the ‘I’ (information) or ‘S’ (supervisory) frames.

 

For more serious usages it’s likely the application would need the complete AX.25 frame and process it by it’s own means.

 

This is accomplished with this frame, the application signals AGWPE that from this moment on all relevant information should be sent also in raw format; AGWPE will still continue to provide information with the regular frames (D/U/I/S) and it would also send the raw version of them using ‘K’ frames.

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘k’ (ASCII 0x6B)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

0

User (Reserved)

4 Bytes

0

 

AGWPE doesn’t recognize this frame in any particular way, however, ‘K’ frames should start to flow into the application reflecting any activity at the ports.

 

This frame acts like a switch, the first time issued it enables the reception of raw frames while the second disables it and so on; in general on odd times it would enable and on even times it would disable.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 6B 00 00 00 00 00 00 00 00 00 00 00 |....k...........

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

|00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- |....           

 

 

 


 

Frames Sent by AGWPE to the Application

 

AGWPE might send information to the application basically for two main reasons:

 

 

The frame format is exactly the same than the previously seen used by the application side, in fact, many frames shares the same datakind, so the meaning might differ depending on the direction of the information flow.

 

A cross reference among frames sent by the application and by AGWPE could be seen in a later section of this document (See Frame  Cross-Reference on page 42)

Version Number (‘R’ frame)

 

This frame is sent by AGWPE to the application in response of an ‘R’ frame sent to AGWPE carrying the information about the current AGWPE version.

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘R’ (ASCII 0x52)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

8

User (Reserved)

4 Bytes

0

 

 

8 bytes of data would follow (as indicated by the DataLen field) containing the AGWPE version with the following contents:

 

 

Offset (Byte or Characters) into the Data Area

Meaning

+00

LSB of Major Version

+01

MSB of Major Version

+02

not used

+03

not used

+04

LSB of Minor Version

+05

MSB of Minor Version

+06

not used

+07

not used

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

 

 

|00 00 00 00 52 00 00 00 00 00 00 00 00 00 00 00 |....R...........

|00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 |................

|00 00 00 00 D0 07 00 00 14 00 00 00 -- -- -- -- |............   

 

Callsign Registration (‘X’ Frame)

 

This frame would be sent by AGWPE in response for a callsign registration (‘X’ frame) sent by the application.

 

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘X’ (ASCII 0x58)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Registered CallSign-SSID ended with null (0x00)

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

1

User (Reserved)

4 Bytes

0

 

 

1 byte of data would follow (as indicated by the DataLen field) containing the result of the registration:

 

 

Offset (Byte or Characters) into the Data Area

Meaning

+00

0x00 Registration Failed

0x01 Registration Successful

 

The application must refrain any further use of the callsign if the registration failed because it means that callsign is already in use (already registered) with AGWPE.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 58 00 00 00 4C 55 37 44 49 44 2D 34 |....X...LU7DID-4

|00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................

|00 00 00 00 01 -- -- -- -- -- -- -- -- -- -- -- |.....       

 

 

Port Information (‘G’ Frame)

 

This frame would be sent by AGWPE in response of a query for Port information (‘G’ frame) sent by the application.

 

 

The format of the frame would be:

 

 

Field

Length

Meaning

AGWPE Port

1 Bytes

0x00

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘G’ (ASCII 0x47)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

Length of port info

User (Reserved)

4 Bytes

0

 

 

A stream of bytes would follow (as indicated by the DataLen field) containing the port data using the following format:

 

An example of a typical data area showing this information for two ports is

 

2;Port1 with KPC3 on COM1: 145.03 Mhz;Port2 with Loopback Port;

 

The first port information belongs to the Port1, the second to Port2 and so on. The application could rely on the “;” character to “parse” the successive components; AGWPE guarantees that the text information won’t contain ‘;’ characters other than the ones to separate succesive information pieces.

 

The total length of the stream would depend on the number of ports defined with AGWPE

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 47 00 00 00 00 00 00 00 00 00 00 00 |....G...........

|00 00 00 00 00 00 00 00 00 00 00 00 5C 00 00 00 |............\...

|00 00 00 00 32 3B 50 6F 72 74 31 20 77 69 74 68 |....2;Port1 with

|20 4B 50 43 33 20 4F 6E 20 43 4F 4D 31 3A 20 31 | KPC3 On COM1: 1

|34 35 2E 30 33 30 4D 68 7A 20 31 32 30 30 62 61 |45.030Mhz 1200ba

|75 64 3B 50 6F 72 74 32 20 77 69 74 68 20 4C 6F |ud;Port2 with Lo

|6F 70 42 61 63 6B 20 50 6F 72 74 3B 00 00 00 00 |opBack Port;....

|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................

 

 

 

 

Capabilities of a Port (‘g’ Frame)

 

This frame is generated by AGWPE in response of a ‘g’ frame sent by the application and contain static configuration information as well as dynamically updated values for the particular port being queried.

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port being queried

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘g’ (ASCII 0x67)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

12

User (Reserved)

4 Bytes

0

 

 

12 bytes of data would follow (as indicated by the DataLen field) containing the following information about the particular port referenced by the header’s AGWPEPort field :

 

 

Offset (Byte or Characters) into the Data Area

Meaning

+00

On air baud rate (0=1200/1=2400/2=4800/3=9600…)

+01

Traffic level (if 0xFF the port is not in autoupdate mode)

+02

TX Delay

+03

TX Tail

+04

Persist

+05

SlotTime

+06

MaxFrame

+07

How Many connections are active on this port

+08 LSB Low Word

+09 MSB Low Word

+10 LSB High Word

+11 MSB High Word

HowManyBytes (received in the last 2 minutes) as a 32 bits (4 bytes) integer. Updated every two minutes.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 67 00 00 00 4C 55 37 44 49 44 2D 34 |....g...LU7DID-4

|00 00 00 00 00 00 00 00 00 00 00 00 0C 00 00 00 |................

|00 00 00 00 00 01 19 04 C8 04 07 00 01 00 00 00 |................

 

 

Frames Outstanding on a Port (‘y’ Frame)

 

This frame is generated by AGWPE in response of a ‘y’ frame sent by the application and contains how many frames are waiting to be transmitted by AGWPE thru the indicated port (from all sources, not only from the application that makes the queries).

 

It could (should) be used by the application to introduce a “reality check” into the amount of data being sent to AGWPE for transmission in order to accommodate the real bandwidth of the port.

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port being queried

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘y’ (ASCII 0x79)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

4

User (Reserved)

4 Bytes

0

 

 

4 bytes of data would follow (as indicated by the DataLen field) containing a 32 bits integer with the total number of frames waiting to be transmitted (outstanding frames) :

 

 

Offset (Byte or Characters) into the Data Area

Meaning

+00 LSB Low Word

+01 MSB Low Word

+02 LSB High Word

+03 MSB High Word

Number of Frames waiting to be transmitted on the queried port.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 79 00 00 00 9C 09 58 00 A0 BE 9D 01 |....y.....X.....

|98 BE 9D 01 00 00 00 00 24 00 00 00 04 00 00 00 |........$.......

|98 02 BE 00 01 00 00 00 -- -- -- -- -- -- -- -- |........       

 

 

Frames Outstanding on a Connection (‘Y’ Frame)

 

This frame is conceptually similar to the previous one but referring to a particular AX.25 L2 connection (CallFrom/CallTo pair) on a given port; it’s returned by AGWPE when queried thru a ‘Y’ frame over an existing connection.

 

As in the ‘y’ frame this information could (should) be used by the application to control the pace of information delivery on a given connection in order to adjust it realistically to the port bandwidth.

 

The format of the frame would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port being queried

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘Y’ (ASCII 0x59)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign-SSID ended with null (0x00)

CallTo

10 Bytes

CallSign-SSID ended with null (0x00)

DataLen

4 Bytes

4

User (Reserved)

4 Bytes

0

 

 

4 bytes of data would follow (as indicated by the DataLen field) containing a 32 bits integer with the total number of frames waiting to be transmitted (outstanding frames) :

 

 

Offset (Byte or Characters) into the Data Area

Meaning

+00 LSB Low Word

+01 MSB Low Word

+02 LSB High Word

+03 MSB High Word

Number of Frames waiting to be transmitted on the given AX.25 connection

 

 

 

 

Heard Stations on a Port (‘H’ Frame)

 

This frame is produced by AGWPE in response of an ‘H’ frame sent by the application and contains information about the stations heard by AGWPE on a given port.

 

Upon a single ‘H’ frame sent by the application AGWPE would produce 20 sucessive ‘H’ frames, one for each station heard.

 

If on a given port more than 20 stations were heard only the 20 most recently heard would be sent, if less than 20 stations were heard AGWPE would send as many “empty” ‘H’ frames as required to make the total number sent as 20.

 

The frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Queried Port

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘H’ (ASCII 0x48)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

CallTo

10 Bytes

10 0x00

DataLen

4 Bytes

Length of heard info

User (Reserved)

4 Bytes

0

 

 

A stream of bytes would follow (as indicated by the DataLen field) containing the heard data using the following format:

 

 

An example of a typical data area showing this information would be

 

LU7DID-4 Mon,21Feb2000 11:14:30 Mon,21Feb2000 12:18:22

 

After the null (0x00) signaling the end of the “plain ASCII” heard information follows two SYSTEMTIME structures containing the timestamp of the first hearing and the last hearing (see the Windows SDK help for information about this standard structure).

 

The application could “parse” the timestamps and get the individual components such as day, month, year, hour, minute and seconds of both the first hearing and last hearing information or to process the information using the SYSTEMTIME structures as it best suit the programmer’s preferences.

 

Take into consideration this information is stored by AGWPE from the moment it had been started the last time and it’s not preserved by AGWPE across succesive starts (meaning, the heard information would be empty just after AGWPE starts).

 

The empty entries (used to complete up to 20 entries) would have the callsign and timestamp information

not filled, so it would look like

 

 

                 00:00:00         00:00:00

 

Unexpected data might be expected by the application on an empty frame, it’s on the application responsibility to define when an entry contain valid information or it’s just empty and should be discarded.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

|-- -- -- -- -- -- -- -- 01 00 00 00 48 00 00 00 |        ....H...

|4C 55 37 44 49 44 00 00 74 BD 9D 01 00 00 00 00 |LU7DID..t.......

|F0 BE 9D 01 5A 00 00 00 68 A0 F7 BF 20 20 20 4C |....Z...h...   L

|55 37 44 49 44 20 54 75 65 2C 32 32 46 65 62 32 |U7DID Tue,22Feb2

|30 30 30 20 31 30 3A 35 32 3A 31 32 20 20 54 75 |000 10:52:12  Tu

|65 2C 32 32 46 65 62 32 30 30 30 20 31 30 3A 35 |e,22Feb2000 10:5

|36 3A 30 38 00 27 11 D0 07 02 00 02 00 16 00 0A |6:08.'..........

|00 34 00 0C 00 32 00 D0 07 02 00 02 00 16 00 0A |.4...2..........

|00 38 00 08 00 3E -- -- -- -- -- -- -- -- -- -- |.8...>

 

AX.25 Connection Received (‘C’ Frame)    

 

This frame is sent by AGWPE to the application when an AX.25 connection has been made, either started from the application from a registered callsign+SSID or initiated by a remote node with a registered callsign+SSID.

 

The frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the connection had been made

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘C’ (ASCII 0x43)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign+SSID who the connection has been made to

(usually the remote end) ended by null (0x00)

CallTo

10 Bytes

CallSign+SSID receiving the connection (usually one of our registered callsigns) ended by null (0x00)

DataLen

4 Bytes

Length of connect info

User (Reserved)

4 Bytes

0

 

 

A stream of bytes would follow (as indicated by the DataLen field) containing the connection message.

 

Depending on who started the connection the connection message could be:

 

*** CONNECTED With {Callsign-SSID}

 

*** CONNECTED To Station {Callsign-SSID}

 

The application might “parse” the message to detect whether a given connection is the result of our connection request (thru a ‘C’ frame) or initiated independently the the other end; this verification has to always be made since we could not rule out the other station independently started a connection even simultaneously with our connection request (at the very least, it should be a good programming practice to perform that verification whenever a ‘C’ frame is received).

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 43 00 00 00 4C 55 37 44 49 44 2D 33 |....C...LU7DID-3

|00 2F 4C 55 37 44 49 44 2D 34 00 00 23 00 00 00 |./LU7DID-4..#...

|D3 73 F7 BF 2A 2A 2A 20 43 4F 4E 4E 45 43 54 45 |.s..*** CONNECTE

|44 20 54 6F 20 53 74 61 74 69 6F 6E 20 4C 55 37 |D To Station LU7

|44 49 44 2D 33 0D 00 -- -- -- -- -- -- -- -- -- |DID-3..

 

 

Connected AX.25 Data (‘D’ Frame)

 

This is a frame sent by AGWPE to the application when an information frame part of an established AX.25 connection directed to a registered station is detected.

 

The frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the connection had been made

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘D’ (ASCII 0x44)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign+SSID who sends the information(usually the remote end) ended by null (0x00)

CallTo

10 Bytes

CallSign+SSID receiving the information (usually one of our registered callsigns) ended by null (0x00)

DataLen

4 Bytes

Length of data info

User (Reserved)

4 Bytes

0

 

A stream of bytes would follow (as indicated by the DataLen field) containing the connected data in a fully transparent way (binary information, no delimiters, bit stuffing or escape codes), the data is as sent by the other end and could be inmediately used by the application without further processing.

 

Note that the PID reflected on the frame would be 0xF0 if the connection has been established by us using the ‘C’ command, could be any non-standard PID if the connection had been established by us using the ‘c’ command and could be anything if the connection has been established by the other end.

 

AGWPE guarantees the information is sent just once to the application in the right sequence (all the retries of information and resending of it due to link conditions is hidden from the application perspective), the frame doesn’t provide any information about the frame sequence as received on the AX.25 link, in case the application needs that information pairing of the ‘D’ frame with other monitoring information should be made by the application by it’s own means (albeit, this need should be extremely infrequent on normal uses).

 

Note there is no limit on the amount of data sent by AGWPE to the application with this frame since it’s not necessarily related to a concrete AX.25 frame; so the application should not expect any given length to be used (i.e. several AX.25 frames could be bound together on a single ‘D’ frame sent by AGWPE).

 

Since AGWPE supports the latest AX.25 specification no guarantee the frame is limited to 256 bytes do actually exist and the application should be able to process data of any arbitrary length.

 

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 44 00 F0 00 4C 55 37 44 49 44 2D 33 |....D...LU7DID-3

|00 47 4C 55 37 44 49 44 2D 34 00 00 02 00 00 00 |.GLU7DID-4......

|A8 6D 45 00 62 0D -- -- -- -- -- -- -- -- -- -- |.mE.b.

 

 

Monitored Connected Information (‘I’ Frame)

 

This frame is sent by AGWPE to the application whenever any exchange of connected information is detected among any pair of stations on any port, for AGWPE to send this information the monitoring must be previously activated by the application thru the sending of a ‘m’ frame to AGWPE.

 

 

 

The frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the frame has been heard

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘I’ (ASCII 0x49)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign+SSID who sends the information(usually the remote end) ended by null (0x00)

CallTo

10 Bytes

CallSign+SSID receiving the information (usually one of our registered callsigns) ended by null (0x00)

DataLen

4 Bytes

Length of monitored info

User (Reserved)

4 Bytes

0

 

A stream of bytes would follow (as indicated by the DataLen field) containing the decoded headers of the AX.25 Frames and the connected data as sent by the transmitting end and could be inmediately used by the application without further processing.

 

AGWPE also includes a decode NETROM header or a decoded TCP/IP header when the respective frame types are detected.

 

The application might parse the information to extract both information relevant to the connected link and the actual data being interchanged.

 

The AX.25 headers included in the data area of the frame usually follows the format (example)

 

 1:Fm LU7DID-4 To SV2AGW-2 <I R3 S1 pid=F0 Len=29 >[12:23:49]

 

Followed by the actual data being exchanged (binary information).

 

The application might choose to process the information and extract the relevant components, if so, the following things must be considered.

 

 

After the frame AX.25 header a CR (0x0D) follows and then the actual data in binary form.

 

 

 

Please note the application should handle TWO different lengths when handling this frame, the one stated on the header (DataLen) refers to the total amount of data transferred after the AGWPE header (which includes BOTH the decoded AX.25 header and the binary data).

 

A second length is the one stated on the decoded AX.25 header (Len=…) which referes to the actual amount of data transferred AFTER the header.

 

The application, in order to process this frame, should

 

 

 

 

 

The application should be aware on the fact that the data transmitted (or received) by it would be communicated by AGWPE using the appropriate frame AND also thru an ‘I’ frame, so if the ‘I’ frames sent by AGWPE are used with any functional purpose the redundancy has to be considered and solved by the application.

 

The conceptual thinking behind an ‘I’ frame is to provide the application with a way to provide it with “presentation” ready monitored information rather than to rely on them for any functional purpose (altrough, this could be done provided appropriate caution is taken as show above).

 

Take note that the correct PID of the monitored frame is correctly reflected on the “decoded” header provided by AGWPE rather than on the relevant field on the AGWPE header.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 49 00 00 00 4C 55 37 44 49 44 2D 34 |....I...LU7DID-4

|00 E7 4C 55 37 44 49 44 00 FA 6A 00 6F 00 00 00 |..LU7DID..j.o...

|2C 0E 45 00 20 32 3A 46 6D 20 4C 55 37 44 49 44 |,.E. 2:Fm LU7DID

|2D 34 20 54 6F 20 4C 55 37 44 49 44 20 3C 49 20 |-4 To LU7DID <I

|50 20 52 31 20 53 34 20 70 69 64 3D 46 30 20 4C |P R1 S4 pid=F0 L

|65 6E 3D 34 39 20 3E 5B 31 30 3A 35 35 3A 35 35 |en=49 >[10:55:55

|5D 0D 0D 5B 4C 55 37 44 49 44 40 4C 55 37 44 49 |]..[LU7DID@LU7DI

|44 2D 34 5D 20 42 2C 43 2C 44 2C 45 2C 58 2C 49 |D-4] B,C,D,E,X,I

|2C 4D 2C 3F 2C 4E 2C 50 2C 55 2C 4A 2C 52 3A 20 |,M,?,N,P,U,J,R:

|0D 0D 00 -- -- -- -- -- -- -- -- -- -- -- -- -- |...

 

Monitored Supervisory Information (‘S’ Frames)

 

Conceptually similar to the ‘I’ frames discussed before AGWPE sends the application information regarding supervisory frames interchanged among any two stations as a part of the AX.25 connected session as stated on the AX.25 protocol in order to administer a given link.

 

Those frames are usually

 

 

Those frames doesn’t transport information other than their own meaning in the context of a given connection.

 

 

The frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the frame has been heard

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘S’ (ASCII 0x53)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign+SSID who sends the information(usually the remote end) ended by null (0x00)

CallTo

10 Bytes

CallSign+SSID receiving the information (usually one of our registered callsigns) ended by null (0x00)

DataLen

4 Bytes

Length of supervisory info

User (Reserved)

4 Bytes

0

 

A stream of bytes would follow (as indicated by the DataLen field) containing the decoded headers of the AX.25 Supervisory Frames sent by the transmitting end and could be inmediately used by the application without further processing.

 

The usual format of the information is as follows

 

 1:Fm LU7DID-2 To SV2AGW-11 <RR P/F R2 >[12:11:19]

 

In the above example a “RR” frame is shown; all information provided by AGWPE is formatted as plain ASCII text (no binary elements) and is “presentation ready” for the application; the application could choose to “parse” that information and use it functionally if needed.

 

Most of the programming recommendations provided for the ‘I’ frames still does apply with the exception of no data to be associated with the frame after the header.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 53 00 00 00 4C 55 37 44 49 44 2D 33 |....S...LU7DID-3

|00 E7 4C 55 37 44 49 44 2D 34 00 00 2F 00 00 00 |..LU7DID-4../...

|2C 0E 45 00 20 32 3A 46 6D 20 4C 55 37 44 49 44 |,.E. 2:Fm LU7DID

|2D 33 20 54 6F 20 4C 55 37 44 49 44 2D 34 20 3C |-3 To LU7DID-4 <

|53 41 42 4D 20 50 3E 5B 31 30 3A 35 37 3A 35 32 |SABM P>[10:57:52

|5D 0D 00 -- -- -- -- -- -- -- -- -- -- -- -- -- |]..            

 

 

Monitored Unproto Information (‘U’ Frames)

 

Conceptually similar to the ‘I’ frames discussed before AGWPE sends the application information regarding unnumbered (unproto) frames frames interchanged among any two stations as a part of the AX.25 connected session as stated on the AX.25 protocol in order to administer a given link.

 

Those frames are usually related to beacons or broadcasted data of some sort or in more advanced uses convey NETROM or TCP/IP links related information.

 

 

The frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the frame has been heard

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘U’ (ASCII 0x55)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign+SSID who sends the information(usually the remote end) ended by null (0x00)

CallTo

10 Bytes

CallSign+SSID receiving the information (usually one of our registered callsigns) ended by null (0x00)

DataLen

4 Bytes

Length of unproto info

User (Reserved)

4 Bytes

0

 

A stream of bytes would follow (as indicated by the DataLen field) containing the decoded headers of the AX.25 Unproto Frames sent by the transmitting end and could be inmediately used by the application without further processing.

 

The usual format of the information is as follows

 

 1:Fm LU7DID-2 To SV2AGW-11 <UI pid=F0 Len=57 >[12:11:19]

 

The application might choose to process the information and extract the relevant components, if so, the following things must be considered.

 

 

After the frame AX.25 header a CR (0x0D) follows and then the actual data in binary form.

 

Please note the application should handle TWO different lengths (as if the ‘I’ frame) when handling this frame, the one stated on the header (DataLen) refers to the total amount of data transferred after the AGWPE header (which includes BOTH the decoded AX.25 header and the binary data).

 

A second length is the one stated on the decoded AX.25 header (Len=…) which referes to the actual amount of data transferred AFTER the header.

 

The application, in order to process this frame, should

 

 

 

 

 

 

The application should be aware on the fact that the data transmitted (or received) by it would be communicated by AGWPE using the appropriate frame AND also thru an ‘U’ frame, so if the ‘U’ frames sent by AGWPE are used with any functional purpose the redundancy has to be considered and solved by the application.

 

The conceptual thinking behind an ‘U’ frame is to provide the application with a way to provide it with “presentation” ready monitored information rather than to rely on them for any functional purpose (altrough, this could be done provided appropriate caution is taken as show above).

 

Take note that the correct PID of the monitored frame is correctly reflected on the “decoded” header provided by AGWPE rather than on the relevant field on the AGWPE header.

 

If the unproto frame transport additional information on known formats used by other protocols such as NETROM or TCPIP AGWPE would attempt to “decode’ them also and provide a “plain text” version of them.

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 55 00 00 00 4C 55 37 44 49 44 2D 34 |....U...LU7DID-4

|00 E7 4E 45 54 4D 45 00 C8 FA 6A 00 6F 00 00 00 |..NETME...j.o...

|2C 0E 45 00 20 32 3A 46 6D 20 4C 55 37 44 49 44 |,.E. 2:Fm LU7DID

|2D 34 20 54 6F 20 4E 45 54 4D 45 20 3C 55 49 20 |-4 To NETME <UI

|70 69 64 3D 46 30 20 4C 65 6E 3D 35 37 20 3E 5B |pid=F0 Len=57 >[

|31 30 3A 35 37 3A 34 32 5D 0D 0D 42 65 61 63 6F |10:57:42]..Beaco

|6E 20 64 65 20 4E 6F 64 6F 20 4C 55 37 44 49 44 |n de Nodo LU7DID

|2D 34 20 41 64 72 6F 67 75 65 20 42 41 20 41 72 |-4 Adrogue BA Ar

|67 65 6E 74 69 6E 61 20 5B 47 46 30 35 54 45 5D |gentina [GF05TE]

|0D 0D 00 -- -- -- -- -- -- -- -- -- -- -- -- -- |...

 

Monitoring Own Information (‘T’ Frames)

 

All information sent unproto by the application thru the ‘M’ frame is returned by AGWPE once transmitted as a ‘T’ frame, this frame could be used for confirmation purposes.

 

The format would be:

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the connection had been made

0x00 Port1

0x01 Port2 ….

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘T’ (ASCII 0x54)

Reserved

1 Byte

0x00

PID

1 Byte

AX.25 PID

Reserved

1 Byte

0x00

CallFrom

10 Bytes

Callsign+SSID who sends the information(usually the remote end) ended by null (0x00)

CallTo

10 Bytes

CallSign+SSID receiving the information (usually one of our registered callsigns) ended by null (0x00)

DataLen

4 Bytes

Length of data info

User (Reserved)

4 Bytes

0

 

A stream of bytes would follow (as indicated by the DataLen field) containing the sent data in a fully transparent way (binary information, no delimiters, bit stuffing or escape codes), the data is as sent by the application and could be (eventually) used inmediately by the application without further processing.

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|00 00 00 00 54 00 00 00 4C 55 37 44 49 44 00 00 |....T...LU7DID..

|38 E7 4C 57 35 44 47 4D 00 FA 6A 00 44 00 00 00 |8.LW5DGM..j.D...

|2C 0E 45 00 20 31 3A 46 6D 20 4C 55 37 44 49 44 |,.E. 1:Fm LU7DID

|20 54 6F 20 4C 57 35 44 47 4D 20 3C 55 49 20 70 | To LW5DGM <UI p

|69 64 3D 46 30 20 4C 65 6E 3D 31 32 20 3E 5B 31 |id=F0 Len=12 >[1

|30 3A 35 39 3A 31 30 5D 0D 3F 20 30 30 30 38 36 |0:59:10].? 00086

|31 34 31 61 61 0D 0D 00 -- -- -- -- -- -- -- -- |141aa...       


 

Monitored Information in Raw Format (‘K’ Frames)

 

When enabled to do so thru the ‘r’ Frame AGWPE would send to the application a “raw” version of every monitored frame (on top of the respective I/S/U frames if enabled to).

 

This frame is basically a representation of the AX.25 frame actually received on the port.

 

The AGWPE frame format would be

 

Field

Length

Meaning

AGWPE Port

1 Bytes

Port where the data frame had been received thru {0=Port1,1=Port2,…}

Reserved

3 Bytes

0x00 0x00 0x00

DataKind

1 Byte

‘K’ (ASCII 0x4B)

Reserved

1 Byte

0x00

PID

1 Byte

0x00

Reserved

1 Byte

0x00

CallFrom

10 Bytes

10 0x00

Actual origin is stated in the

raw frame

CallTo

10 Bytes

10 0x00

Actual destination is stated in

the raw frame

DataLen

4 Bytes

Number of Data Bytes to be transferred.

User (Reserved)

4 Bytes

0

 

The complete frame (AX.25 header followed by data if applicable) in raw format follows as data, the exact length of it must be reflected on the DataLen field.

 

Full knowledge of the intricancies of the AX.25 must be mastered by the brave programmer trying to use this frame, for what is worth the heartfull recommendation is to try to identify other frames or combination of frames most suitable for a given purpose, so use this frame format as an absolute last resort.

 

Probably, for most light uses it would suffice to decode the equivalent information on the I/S/U frames.

 

For those brave souls in need to still use it the following recommendations should be used (refer to the AX.25 Protocol documentation for the naming conventions).

 

Field

Length

Description

Flag

1 byte

Will not be the standard 0b01111110 flag but

the “TNC” to use

00=Port 1

16=Port 2

Address

112/360 bits

AX.25 coded Origin, Destination and (optionally) digipeaters.

Control

1 byte

AX.25 Control Field

PID

1 byte

AX.25 PID

Info

N bytes

AX.25 Information Area

 

Please note than the AX.25 FCS and the ending Flag are NOT included. No KISS escape codes nor bit stuffing is required to be performed (AGWPE would add them as needed).

 

 

Follows a sample frame using a dump format of this frame (16 hexadecimal formatted bytes at the left and the ASCII, interpretation when feasible at the right), this sample could be used for study and comparation purposes.

 

 

|01 00 00 00 4B 00 00 00 4C 55 37 44 49 44 00 00 |....K...LU7DID..

|38 E7 4C 55 37 44 49 44 2D 34 00 00 42 00 00 00 |8.LU7DID-4..B...

|2C 0E 45 00 00 98 AA 6E 88 92 88 80 98 AA 6E 88 |,.E....n......n.

|92 88 69 38 F0 0D 0A 5B 4C 55 37 44 49 44 40 4C |..i8...[LU7DID@L

|55 37 44 49 44 2D 34 5D 20 42 2C 43 2C 44 2C 45 |U7DID-4] B,C,D,E

|2C 58 2C 49 2C 4D 2C 3F 2C 4E 2C 50 2C 55 2C 4A |,X,I,M,?,N,P,U,J

|2C 52 3A 20 0D 0A -- -- -- -- -- -- -- -- -- -- |,R: ..         

 

 

 

Frame  Cross-Reference

 

Follows a cross-reference of all frame types supported by AGWPE and it’s meaning whether they are sent by either the application to AGWPE or AGWPE to the Application (N/A means frame not supported on this particular end or no answer frame produced).

 

 

DataKind

Description

Application

AGWPE

‘P’

Application Login (User/Password)

Login into AGWPE

N/A

‘R’

AGWPE Version

Query AGWPE Version

Answer AGWPE Version

‘G’

Port Information

Query AGWPE Ports

Answer AGWPE Port Information

‘g’

Port Capabilities

Query AGWPE Port Capability

Answer queried Port capabilities

‘X’

Register CallSign

Register CallSign

Success/Failure of Registration

‘x’

Unregister CallSign

Unregister CallSign

N/A

‘y’

Outstanding frames on a Port

Query outstanding frames waiting to be transmitted on a port

(all sources)

Answer outstanding frames waiting to be transmitted on a port (all sources)

‘Y’

Outstanding frames on a connection

Query outstanding frames waiting to be transmitted on an AX.25 connection (From/To pair)

Answer outstanding frames waiting to be transmitted on a existing AX.25 connection (From/To pair).

‘H’

Heard Stations on a Port

Query heard stations on a port

Answer heard stations on the requested port (20 frames would be generated)

‘m’

Start Monitoring Data

Start/Stop the flow of monitoring data

(first time switch on, second time switch off, and so on).

N/A

‘M’

Send Unproto Info

Send Unproto Info

N/A

‘V’

Send Unproto Info VIA

Send Unproto Info VIA

N/A

‘C’

Start an AX.25 Connection

Start an AX.25 connection

Success or Failure of AX.25 Connection Request

‘v’

Start an AX.25 Connection using digipeaters

Start an AX.25 connection using digipeaters

N/A

answered as in ‘C’

‘c’

Start a non-standard AX.25 connection (data frames would have PID not 0xF0)

Start a non standard AX.25 Connection

N/A

answered as in ‘C’

‘D’

Connected Data

Send Connected Data

Receives Connected Data

‘d’

Disconnect an AX.25 connection

Start disconnection of AX.25 connection

Success or Failure of AX.25 disconnection

‘U’

Unnumbered Information (UI) frame received for a registered application.

N/A

Unnumbered Information (UI) frame received for a registered application.

‘I’

Information Frame between any two connected station (not necessarily the ones registered by the application)

N/A

Information Frame monitored

‘S’

Supervisory Frame

(SABM/UA/DISC/DM/RR/REJ)

N/A

Supervisory Frame monitored

‘T’

Monitored frame sent by this application

N/A

Monitored Frame sent by this application.

‘K’

Raw Frame

Raw Frame To be sent

Raw Frame Received

‘k’

Start monitoring using raw frames

Start/Stop sending information in raw format.

(First time switch on, Second switch off, and so on).

N/A

 

 


 

Programming Hints, Tips and Techniques

 

AGWPE could be truly despicted as a “middleware” application, a very powerful one, or in plain language a component that other applications uses as an enabler to perform “things”, in this case the “thing” is to exchange information thru AX.25 Packet Radio.

 

In one hand, AGWPE is extremely easy to use and powerful compared with the alternative which is to deal directly to the intrincancies of a myriad of different hardware components not to mention the arcane AX.25 protocol itself.

 

In the other hand, AGWPE could have a rather step learning curve for the novel programmer; after all it’s a rather complex piece of software doing a rather complex activity, and as any such software it has it’s idiosincracyes and “dark spots” that the application programmer should plan for.

 

No middleware application (no matter how brilliantly conceived and implemented) is more successful than the applications written or adapted to it, applications is what the final user “uses” and likes (or dislikes).

 

Applications could be written by seasoned programmers, fluent on the world of TCP/IP sockets, used to deal thru different APIs and aware of the fact that no perfect platform (perfectly coherent and bug free) has ever been written; this audience would flip thru this manual and probably felt at home right away, with perhaps more attention to the colloquial sidenotes than to the overall technical details.

 

But applications could also be written by novel programmers with great ideas for whom all those factors could be truly disabling.

 

The following sections aims for the second audience, some general references and known tricks/workarounds would be documented to aid a novel programmer on their first steps; it could be also useful for the seasoned ones when some part of the reference didn’t fulfill it’s goal to try to clarify a given funcion (so a programming example could spoke aloud by itself).

 

 

Programming Language

 

The issue of the programming language is easy to fix, Applications programs could be written on almost anything under the sun as long as

 

 

AGWPE itself has been written in MS C++, many examples do exist in the form of short pieces of code or sample programs provided by the author using that language; this is of course a matter of convenience and not a pre-requisite by any stretch of the concept that Applications intended to be run with AGWPE has to be written in C++.

 

Some rather major pieces of code to work with AGWPE has been written using Delphi 4/5 (Object Pascal) and surely Visual Basic programmers could extensively benefit from good “helpers” to implement TCP/IP oriented applications available for free or at reasonable costs.

 

Those are the languages more common in the Windows world, but any other language satisfying the requirements could be used as well.

 

Nothing prevents that a given application could be implemented on MORE than one language, in fact, there are some experiences writing a part of an application in (say) Delphi4 and other part of it (as a DLL) in (say) C++; given the proper “bound” conditions are met nothing prevents such projects to take place.

 

While a multilanguage implementation could only make sense on the context of a program really big, most likely programs will not be that complex to make that a reasonable decision; however, helpers and other supporting libraries could be developed in the form of shareable components (such as ActiveX controls or Windows DLLs) that could be shared among almost anything platform making the usage of AGWPE easier for others to use.

 

When outlining the prerequisites the need of the Application to run on Windows was made as a relative statement; AGWPE must run on a Windows environment but the Application doesn’t needs to run on the same machine as long as it could have TCP/IP connectivity with the machine where AGWPE is running.

 

No actual part of the AGWPE API depends on Windows itself (with the exception of the SYSTEMTIME structure present on the ‘H’ Frames, and that is quite solvable as a problem) and thus an application for AGWPE could truly be written on any platform as long as it supports some form of standard TCP/IP, quite extraneous things come to my mind after this statement.

 

Nothing prevents an AGWPE application to be run under a 8086 DOS, on a Cray machine or a big IBM mainframe under OS/390 ; hopefully those possibilities could be seen as extreme and certainly not recommended.

 

However, a much more down to the ground possibility is for applications either written, adapted or ported to be run under the very popular Linux environment.

 

The only drawback of the “multiplatform” approach is the need of a minimum of two machines, one to run AGWPE (which must run under Windows) and other to run the application; this might or might not be very exotic as a proposition given the falling prices for hardware.

 

All the remaining of this document would assume the Application program is implemented under Windows though.

 

 

Talking with AGWPE

 

The first requirement to use AGWPE from an application is to be able to talk with it, and for that the application must be able to open, sustain and close a TCP/IP socket.

 

Countless libraries do exist either free or at reasonable cost to allow almost any language to accomplish that, most modern versions of the most widely used languages comes from the factory with support for TCP/IP right inside.

 

Unfortunately, even if the description at a conceptual level of programming with TCP/IP are common across any language used, the details might differ from application to application; so it’s sort of difficult to provide a single and definitive recommendation on how to program that part.

 

Each programmer should take the general guidelines provided here or in other places pointed, check with the information provided by the particular libraries and implementation of the compiler at hand and make the necessary adjustments.

 

 

Using C++

 

George (SV2AGW) provides a simple sample monitoring program written in C++ for the AGWPE on his Web site at http://www.forthnet.gr/SV2AGW

 

While limited in functionality a monitor frame shows a great deal of the most commonly used aspects of the AGWPE API, but more important, it has ALL the TCP/IP components needed on any program of any complexity.

 

A monitor program should open a socket, send some information to AGWPE (i.e. the ‘m’ frame to start monitoring), receive information thru the socket and eventually close it.

 

For a detailed information the download of this sample is recommended, it also contains many of the tips outlined in this document.

 

Using Delphi4/5

 

Delphi comes with Visual Component Library (VCL) components to support a TCP/IP socket client.

 

The application needs to sets the minimum properties (IP Address/TCP Port), make the component active and be sure handlers are written for the events OnConnect, OnRead,OnError and OnClose.

 

The connection with AGWPE is quite similar (despite the different information exchanged and the port number used) to a Telnet application, so check one of the many examples of a Telnet application on how to handle a connection like this.

 

 

Overall Communication Cycle

 

The communication between an application and AGWPE could take many different “logics” or “implementations” but the recommended one should implement the following topics.

 

 

 

Frames Fiesta

 

The absolute key point to manage the communication with AGWPE is to be able to send to it and receive from it properly formatted frames to accomplish a given activity.

 

Sending Frames

 

This is a relatively easy activity to do if properly organized.

 

The AGWPE frames are composed by a fixed formatted header with some data added to it depending on the particular frame being sent.

 

The development of a particular routine that enable the application to send frames from whatever part of the program logic when it’s needed is absolutely recommended (in case Object Oriented Programming is used the equivalent action is to define AGWPE as an object and to provide a method to send frames to it).

 

The function/method should be something like the following prototype (in Pascal)

 

Function Send(cPort : Char;

              cDataKind : Char;

              cPID : Char;

              sFrom : String ;

              sTo : String;

              iLen : Integer;

              sData : String) : Boolean;

 

This function/method should

 

 

A complete and flexible send function/method would probably be the best single investment a programmer could do at the beginning of the activity with AGWPE.

 

Once this function is available to send frames to AGWPE would be an surprinsingly trivial thing.

 

i.e. to register a callsign

 

              Send(NUL,’X’,NUL,’LU7DID-2’,’’,0,’’);

 

Would do it!!!

 

George (SV2AGW) proposes the following C++ procedure for a simple routine

 

void  SendPacket(char *ToCall,char *str,int count,int DataKind,int port)

{

if (count==0) count=lstrlen(str);

char szTemp[3000];

MoveMemory(szTemp.&port,sizeof(int));//which port to tx

MoveMemory(szTemp+sizeof(int),&DataKind,sizeof(int));//datakind here LOWORD should be 'D'

//datakind here should be 'D'

       MoveMemory(szTemp+(sizeof(int)*2),MyCall,strlen(MyCall)+1);//mycall

       MoveMemory(szTemp+(sizeof(int)*2)+10,ToCall,strlen(ToCall)+1);//other station call

       MoveMemory(szTemp+(sizeof(int)*2)+10+10,&count,4);//length of the data we send to other station

       MoveMemory(szTemp+(sizeof(int)*4)+10+10,str,count+1);//now add the actual data after leving 4 additional bytes for USER which are reserved for the moment;

TXDATA(szTemp,count+26);//send them over our socket connection

}

 

 

 

 

 

 

The following fragment shows how to use this routine to send an unproto frame to all ports

 

for (int x=0;x<HowManyPorts;x++)

{

SendPacket("BEACON",test,strlen(test),MKELONNG('M',0),MAKELONG(x,0));

}

 

A comprehensive information about this and other routines could be found on George’s authored demo monitoring program.

 

The following couple of methods written in Delphi4 are used on most (if not all) programs written by Pedro (LU7DID).

 

The first method (Write) is just a wrapper that isolates the calling routine about the details of the TCP/IP connection, basically it verify the link is ready and then pass all the information to a second (send) who actually delivers the frame.

 

This routines are used on multithreaded environments so the resources are protected from re-entrancy issues (this should not be a concern for the writer or a simple, single threaded program), some proprietary functions for selective tracing and debug are also present that should be ignored.

 

(*--->>> Write         <<<----------------------------------------------*)

{*Write information to the packet engine                                *}

{*----------------------------------------------------------------------*}

Function TLink.Write(cPort : Char; cPID : Char; cDataKind : Char; szFrom : String; szTo : String; iDataLen : DWORD; szData : String) : Boolean;

 

begin { TLink.Write}

 

                                               {*-----------------------*}

                                               {*Only write to valid    *}

                                               {*sockets                *}

                                               {*                       *}

{*----------------------------------------------------------------------*}

 

     If AGWState <> 2 then begin

        PutAGW(1,'AGWPE Not connected, ignoring Write request');

        Result := FALSE;

        Exit;

     end;

 

    Send(AGWSocket,cPort,cPID,cDataKind,szFrom,szTo,iDataLen,szData);

    Result := TRUE;

 

end; { TLink.Write}

 

(*--->>> Send          <<<----------------------------------------------*)

{* Function to send a frame to AGWPE (Low Level Routine)                *}

{*----------------------------------------------------------------------*}

Function TLink.Send     (Socket    : TCustomWinSocket ;

                         cPort     : Char;

                         cPID      : Char;

                         cDataKind : Char;

                         szFrom    : String;

                         szTo      : String;

                         iLen      : DWORD;

                         szBuffer  : String)    :  Boolean;

  Var

    Index               :       Integer;

    MSB                 :       Byte;

    LSB                 :       Byte;

    szFrame             :       String;

    szFromAux           :       String;

    szToAux             :       String;

    iBigLen             :       DWORD;

    szBigBuffer         :       String;

    dwStatus            :       DWORD;

 

begin { Send }

 

                                               {*-----------------------*}

                                               {*Re-entrancy protection *}

{*----------------------------------------------------------------------*}

 

  dwStatus := WaitForSingleObject(hAGWSend,INFINITE);

  If dwSTATUS <> WAIT_OBJECT_0 then begin

     PutAGW(1,'dwStatus <> WAIT_OBJECT_0 returned by WaitForSingleObject');

  end; {endif}

 

 

  If ((cDataKind = 'g') or (cDataKind = 'H') or (cDataKind = 'G')) then begin

     PutAGW(1,'SEND to AGWPE:Port {'+inttostr(ord(cPort))+'} DataKind ('+cDataKind+') Pid=('+inttostr(ord(cPid))+') From <'+szFrom+'> To <'+szTo+'> Len ('+inttostr(iLen)+')');

  end else begin

     PutAGW(1,'SEND to AGWPE:Port {'+inttostr(ord(cPort))+'} DataKind ('+cDataKind+') Pid=('+inttostr(ord(cPid))+') From <'+szFrom+'> To <'+szTo+'> Len ('+inttostr(iLen)+')');

  end; {endif}

 

 

 

 

  DumpHex(4,szBuffer);

 

                                               {*-----------------------*}

                                               {*does we have a live    *}

                                               {*connection already?    *}

                                               {*If NOT -> Error        *}

{*----------------------------------------------------------------------*}

  If Socket = Nil then begin

     PutAGW(1,'SEND: Socket = Nil, frame discarded');

     ReleaseSemaphore(hAGWSend,+1,Nil);

     Result  := FALSE;

     Exit;

  End;

 

                                               {*-----------------------*}

                                               {*Init buffer and temp   *}

                                               {*areas                  *}

                                               {*                       *}

{*----------------------------------------------------------------------*}

  szFrame     := '';

  szFromAux   := szFrom;

  szToAux     := szTo;

 

                                               {*-----------------------*}

                                               {*Ensure the whole header*}

                                               {*is filled with nulls   *}

                                               {*as well as the callsign*}

{*----------------------------------------------------------------------*}

  szFrame     := PadStr(szFrame,AGW_HEADER,NUL);

  szFromAux   := PadStr(szFromAux,10,NUL);

  szToAux     := PadStr(szToAux,10,NUL);

 

                                               {*-----------------------*}

                                               {*Format the frame       *}

                                               {*                       *}

                                               {*                       *}

{*----------------------------------------------------------------------*}

 

  szFrame[01] := Chr(ord(cPort)-1);            {* Port                  *}

  If szFrame[01] = Chr($FF) then begin

     szFrame[01] := Chr($00);

  end; {endif}

 

  szFrame[02] := NUL;

  szFrame[03] := NUL;

  szFrame[04] := NUL;

 

  szFrame[05] := cDataKind;                    {* LOWord(DataKind)      *}

  szFrame[06] := NUL;

  szFrame[07] := cPID;                         {* HiWord(bPID)          *}

  szFrame[08] := NUL;

 

  For Index := 1  to 10  do begin              {* From Call             *}

    szFrame[08+Index] := szFromAux[Index];

  end; {endfor}

 

  For Index := 1  to 10  do begin              {* To Call               *}

    szFrame[18+Index] := szToAux[Index];

  end; {endfor}

 

  If iLen <= (MAXFRAME-1) then begin

     MSB := Trunc(iLen/MAXFRAME);

     LSB := Trunc(iLen - (MSB*MAXFRAME));

  end else begin

     MSB := $00;

     LSB := $FF;

  end; {endif}

 

  szFrame[29] := chr(LSB);                     {* Size                  *}

  szFrame[30] := chr(MSB);

  szFrame[31] := NUL;

  szFrame[32] := NUL;

 

  szFrame[33] := NUL;                          {* User - Reserved       *}

  szFrame[34] := NUL;

  szFrame[35] := NUL;

  szFrame[36] := NUL;

 

  PutAGW(1,'Frame to send to AGWPE is');

  DumpHex(1,szFrame+szBuffer);

 

 

                                               {*-----------------------*}

                                               {*Efficiency trick       *}

                                               {*If there is data send  *}

                                               {*with the frame,othewise*}

                                               {*send just the frame    *}

                                               {*This will reduce the   *}

                                               {*chances for TCP to frag*}

{*----------------------------------------------------------------------*}

  If (iLen = 0) then begin

     PutAGW(3,'Send Header');

     DumpHex(3,szFrame);

     SendAGW(szFrame);

 

 

  end else begin

     If (iLen <= (MAXFRAME-1)) then begin

        PutAGW(3,'Send Header+Data');

        DumpHex(3,szFrame+szBuffer);

        SendAGW(szFrame+szBuffer);

     end else begin

 

                                               {*-----------------------*}

                                               {*Handles data areas     *}

                                               {*longer than 256 bytes  *}

                                               {*in sucessive frames    *}

                                               {*of up to 256 bytes     *}

{*----------------------------------------------------------------------*}

        SendAGW(szFrame+Copy(szBuffer,1,(MAXFRAME-1)));

 

        szBigBuffer := Copy(szBuffer,MAXFRAME,Length(szBuffer)-(MAXFRAME-1));

        iBigLen     := Length(szBigBuffer);

 

        ReleaseSemaphore(hAGWSend,+1,Nil);

 

        Self.Send(Socket,cPort,cPid,cDataKind,szFrom,szTo,iBigLen,szBigBuffer);

        If cDataKind <> 'Y' then begin

           Self.Send(Socket,cPort,NUL,'Y',szFrom,szTo,0,'');

        end; {endif}

 

        Result := TRUE;

        Exit;

     end; {endif}

  end; {endif}

 

  ReleaseSemaphore(hAGWSend,+1,Nil);

 

 

                                               {*-----------------------*}

                                               {*Piggyback a request    *}

                                               {*for AGW Status on every*}

                                               {*Data Frame Sent        *}

                                               {*                       *}

{*----------------------------------------------------------------------*}

 

  If cDataKind = 'D' then begin

     Self.Send(Socket,cPort,NUL,'Y',szFrom,szTo,0,'');

  end; {endif}

  Result := TRUE;

 

end; { Send }

 

(*--->>> SendAGW       <<<----------------------------------------------*)

{* Function to send at low level (TCPIP) the actual frame               *}

{*----------------------------------------------------------------------*}

Procedure TLink.SendAGW( szBuffer : String);

 

begin { TLink }

    PutAGW(1,'Send to AGWPE (TCP/IP)');

    DumpHex(1,szBuffer);

    If AGWSocket <> Nil then begin

       AGWSocket.SendText(szBuffer);

    end else begin

       PutAGW(1,'AGW Frame IGNORED because AGWPE is not connected');

    end; {endif}

end;

 

Complex?…. A little, however once invested here look what means to send an unproto beacon to all available ports….

 

     …..

     sMessage := ‘Hello World!’+Chr($0D);

     For iPort = 0 to iMaxPorts do begin

             Self.Write(Chr(iPort),Chr($F0),’M’,

                        sMyCall,’BEACON’,Length(sMessage),sMessage);

     end;

     …..

 

 

There could be another couple of zillion ways to do this work (even far more efficiently) and everybody is encouraged to find it’s own way.

 

 

 

Receive Frames

 

Receive and processing AGWPE frames isn’t quantum physics nor rocket science, it’s deceptively simple once a couple of issues are properly addressed.

 

 

In order to understand the proper way to process an AGWPE frame a golden rule must be understood.

 

TCP/IP doesn’t guarantees the data would arrive at the destination blocked in the same way than was blocked on the transmision end. In other words, AGWPE could send in one end a perfectly formatted frame complete with header and data at once on a single TCP send call.

 

At the other end, however, and due to TCP and (specially) IP fragmentation factors, the data could be made available to the application as it’s received and not necessarily as the same block that has been sent.

 

So the application must deal with the following situations.

 

 

So, you could decide to follow this advice and save yourself many hours of frustration and debugging or find it by yourself the hard way…. “never assume anything about how the data arrives to the application”

 

The recommended way to process AGWPE frames is to tackle that activity as three differenciated stages:

 

 

 

This is the method recommended by George (SV2AGW)

 

Don't assume that you will receive a complete frame,TCPIP may send to your program part of a frame or more than a frame so the procedure for reading data is like reading from a file.Read only what you need. Like

 

A complete frame is HEADER+DATA  or just HEADER with no data

1.check to see if in the stream socket there are at least HEADER bytes. If not then return

2.Examine the header and  the DataLen field

3.If the DataLen field is greater than 0 check to see if there are in the stream socket DataLen bytes.

4.If there are DataLen bytes then read exactly DataLen bytes no more, otherwise wait until DataLen bytes are available.

5.go to step 1 again till all the frames read.

 

Follow these steps carefully. If your application is running in the same machine with agw packet engine then the usal is that you will receive more than a frame ,if the monitor traffic is large.

 

Follow some of the routines used by Pedro (LU7DID) written in Delphi4 with the same purpose.

 

This routine is the OnRead event (some TCP/IP implementation calls it OnDataAvailable) handler, it’s activated anytime data is ready to be processed from TCP/IP, this is the first stage recommended previously.

 

(*--->>> AGWSocketRead        <<<---------------------------------------*)

{* Receives the OK from the connection request                          *}

{*----------------------------------------------------------------------*}

procedure TLink.AGWSocketRead(Sender: TObject;

                              Socket: TCustomWinSocket);

  Var

    szData      :       String;

 

begin

    PutAGW(3,'Data Available from AGWPE <event>');

 

    szData := Socket.ReceiveText;

    DumpHex(3,szData);

 

    If Length(szData) <> 0 then begin

       Store(szData);

    end; {endif}

 

end;

 

The following method decides whether or not completed frames are ready for processing, the Decode routine is the one actually handling the different frames, see how the routine is called recursively.

 

(*--->>> Store         <<<----------------------------------------------*)

{*Store the information from AGWPE and handles fragmentation issues     *}

{*----------------------------------------------------------------------*}

Function TLink.Store (szReceived : String) : Boolean;

  Var

    szHeader    :       String;

    MSB         :       Integer;

    LSB         :       Integer;

    Index       :       Integer;

 

begin

 

  PutAGW(5,'Received from TCPIP');

  DumpHex(5,szReceived);

 

  AGW.Buffer := AGW.Buffer + szReceived;

 

  if AGW.Pending = FALSE then begin

 

     {*----------------------------------*}

     {*This is where it comes for a fresh*}

     {*packet from AGWPE                 *}

     {*----------------------------------*}

 

     if Length(AGW.Buffer) >= AGW_HEADER then begin

 

        szHeader    := Copy(AGW.Buffer,1,AGW_HEADER);

        PutAGW(5,'Translated into Header Buffer');

        DumpHex(5,szHeader);

 

 

        If Length(AGW.Buffer) > 0 then begin

           AGW.Buffer := Copy(AGW.Buffer,AGW_HEADER+1,Length(AGW.Buffer)-AGW_HEADER);

        end; {endif}

 

        AGW.Data     := '';

        If szHeader[1] = Chr($31) then begin

           AGW.Buffer := Copy(AGW.Buffer,2,Length(AGW.Buffer)-1);

           Self.Store('');

           Result := TRUE;

           Exit;

        end; {endif}

 

        AGW.cPort    := szHeader[1];

        AGW.cPort    := Chr(ord(AGW.cPort)+1);

 

        AGW.DataKind := szHeader[5];

        AGW.cPID     := szHeader[7];

 

        AGW.CallFrom := '';

        AGW.CallTo   := '';

 

        For Index := 9  to 18  do begin

          If szHeader[Index] <> NUL then begin

             AGW.CallFrom := AGW.CallFrom + szHeader[Index];

          end else begin

             Break;

          end; {endif}

        end; {endfor}

 

        For Index := 19 to 28  do begin

          If szHeader[Index] <> NUL then begin

             AGW.CallTo  := AGW.CallTo   + szHeader[Index];

          end else begin

             Break;

          end; {endif}

        end; {endfor}

 

        LSB := ord(szHeader[29]);

        MSB := ord(szHeader[30]);

 

        AGW.DataLen := MSB*MAXFRAME + LSB;

 

       PutAGW(3,'Just Decoded as Port('+inttostr(ord(AGW.cPort))+') Kind['+AGW.DataKind+'] {'+HexByte(Ord(AGW.cPID))+'} <'+AGW.CallFrom+

              '> <'+AGW.CallTo+'> Len('+inttostr(AGW.DataLen)+') + <<--Store');

                                         {*----------------------------------*}

                                         {*If DataLen is zero there is no    *}

                                         {*data, HOWEVER other frames could  *}

                                         {*be pending as well                *}

                                         {*----------------------------------*}

        If (AGW.DataLen = 0) then begin

 

                                         {*----------------------------------*}

                                         {*A frame were received and has no  *}

                                         {*data, we might call it a complete *}

                                         {*frame so store it on the port     *}

                                         {*object.                           *}

                                         {*----------------------------------*}

 

           AGW.Pending := FALSE;

                                         {*----------------------------------*}

                                         {*Store the frame on that port obj  *}

                                         {*----------------------------------*}

 

           PutAGW(3,'Decoded Port{'+inttostr(ord(AGW.cPort))+'} DataKind['+AGW.DataKind+'] <'+AGW.CallFrom+'> <'+AGW.CallTo+'> {NO DATA}');

           Decode(AGW.cPort,AGW.cPID,AGW.DataKind,AGW.CallFrom,AGW.CallTo,0,'');

 

           Result       := TRUE;

 

                                      {*----------------------------------*}

                                      {*Wonder if something else came with*}

                                      {*that frame and still in the buffer*}

                                      {*----------------------------------*}

 

           If Length(AGW.Buffer) = 0 then begin

 

                                      {*----------------------------------*}

                                      {*Buffer is empty, see ya next time *}

                                      {*                                  *}

                                      {*----------------------------------*}

           end else begin

                                      {*----------------------------------*}

                                      {*Ooops, something else there       *}

                                      {*recurse on myself to process      *}

                                      {*----------------------------------*}

              Result := Self.Store('');

           end; {endif}

 

 

           Exit;

 

        end; {endif}

 

                                         {*----------------------------------*}

                                         {*If DataLen is NOT zero then       *}

                                         {*continue processing the buffer to *}

                                         {*see if we could extract the data  *}

                                         {*----------------------------------*}

        Result := Self.FragFrame;

        Exit;

 

     end; {endif}

 

  end else begin

 

     Result := Self.FragFrame;

     Exit;

 

  end; {endif}

  Result := TRUE;

 

end;

(*--->>> FragFrame     <<<----------------------------------------------*)

{*Method to handle the likely fragmentation of frames over a TCPIP link *}

{*----------------------------------------------------------------------*}

Function TLink.FragFrame : Boolean;

 

begin { TLink }

 

                                         {*----------------------------------*}

                                         {*If DataLen is NOT zero then       *}

                                         {*continue processing the buffer to *}

                                         {*see if we could extract the data  *}

                                         {*----------------------------------*}

  If Length(AGW.Buffer) >= AGW.DataLen then begin

 

     AGW.Data  := Copy(AGW.Buffer,1,AGW.DataLen);

 

     If Length(AGW.Data) < Length(AGW.Buffer) then begin

        AGW.Buffer:= Copy(AGW.Buffer,Length(AGW.Data)+1,Length(AGW.Buffer)-Length(AGW.Data));

     end else begin

        AGW.Buffer := '';

     end; {endif}

 

     AGW.Pending := FALSE;

                                         {*----------------------------------*}

                                         {*Store the frame on that port obj  *}

                                         {*instance                          *}

                                         {*Same solution than previous       *}

                                         {*----------------------------------*}

 

     PutAGW(3,'Decoded (Frag) Port{'+inttostr(ord(AGW.cPort))+'} DataKind['+AGW.DataKind+'] <'+AGW.CallFrom+'> <'+AGW.CallTo+'> Len=('+inttostr(AGW.DataLen)+') Data: '+AGW.Data);

     Decode(AGW.cPort,AGW.cPID,AGW.DataKind,AGW.CallFrom,AGW.CallTo,AGW.DataLen,AGW.Data);

 

     Result       := TRUE;

 

                                         {*----------------------------------*}

                                         {*Wonder if something else came with*}

                                         {*that frame and still in the buffer*}

                                         {*----------------------------------*}

 

     If Length(AGW.Buffer) = 0 then begin

 

                                         {*----------------------------------*}

                                         {*Buffer is empty, see ya next time *}

                                         {*                                  *}

                                         {*----------------------------------*}

     end else begin

                                         {*----------------------------------*}

                                         {*Ooops, something else there       *}

                                         {*recurse on myself to process      *}

                                         {*----------------------------------*}

        Result := Self.Store('');

     end; {endif}

 

 

     Exit;

 

  end else begin

 

     AGW.Pending := TRUE;

     Result      := FALSE;

     Exit;

 

  end; {endif}

  Result := TRUE;

 

end; { TLink }

 

 

Last, but not least, the actual actions for each frame received, this is a method very application specific so only the skeleton is provided as a sample (despite its length this is a conceptually simple skeleton).

 

(*--->>> Decode        <<<----------------------------------------------*)

{*Procedure to decode, validate and route a received frame              *}

{*----------------------------------------------------------------------*}

Procedure TLink.Decode  (cPort     : Char;

                         cPID      : Char;

                         cDataKind : Char;

                         szFrom    : String;

                         szTo      : String;

                         iDataLen  : DWORD;

                         szData    : String) ;

  Var

    szYourCall  :       String;

    szAuxStr    :       String;

    szInfo      :       String;

    szVIA       :       String;

 

    IsNew       :       Boolean;

    lpAux       :       PtrLink;

 

    MSB         :       Byte;

    LSB         :       Byte;

    SysDateTime :       TDatetime;

 

begin { TLink.Decode }

 

    szTo   := UpCaseStr(szTo);

    szFrom := UpCaseStr(szFrom);

 

                                              {*-----------------------*}

                                              {*Return of call registr.*}

{*---------------------------------------------------------------------*}

 

    If cDataKind = ‘X’ then begin

       {* Validate our registrations *}

       Exit;

    end; {* endif *}

 

                                              {*-----------------------*}

                                              {*Outstanding Frames Call*}

{*---------------------------------------------------------------------*}

    If cDataKind = 'Y' then begin

       {* Your code here… *}

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*Outstanding Frames Port*}

{*---------------------------------------------------------------------*}

    If cDataKind = 'y' then begin

       {* Your code here … *}

       Exit;

    end; {endif}

 

 

                                              {*-----------------------*}

                                              {*MHEARD List            *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'H' then begin

 

       PutAGW(1,'AGW Message <H>');

       DumpHex(1,szData);

 

       szAuxStr := Parse(szFrom);

 

       PutAGW(1,'AGW Message <H> Parsed From is ('+szAuxStr+') Remaining('+szFrom+')');

       If szAuxStr <> '' then begin

          ParseHeard(cPort,szData);

       end; {endif}

 

       PutAGW(1,'AGW Message <H> End of Message');

 

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*PARAMS List            *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'g' then begin

       PutAGW(5,'AGW Message <g>');

       ParseParam(cPort,szData);

       PutAGW(3,'End of processing Message <g>');

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*AGWPE KISS Raw Frame   *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'K' then begin

       PutAGW(5,'AGW Message <K>');

       DumpHex(5,szData);

       {*------- Here Decode the Raw Frame ------*}

       RawDecode(szData,szFrom,szTo,szVIA,cDataKind,cPID,iDataLen,szInfo);

       szData := szInfo;

       Exit;

    end; {endif}

                                              {*-----------------------*}

                                              {*AGWPE UNPROTO          *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'U' then begin

       PutAGW(5,'AGW Message <U>');

       {* Your code here….*}

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*AGWPE Radio Ports      *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'G' then begin

       PutAGW(5,'AGW Message <G>');

       ParsePort(szData);

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*AGWPE Version          *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'R' then begin

       PutAGW(5,'AGW Message <R>');

 

       If VersionFlag = FALSE then begin

          VersionFlag := TRUE;

 

          If Length(szData) >= 8 then begin

             LSB := ord(szData[1]);

             MSB := ord(szData[2]);

             AGWVerHigh := MAXFRAME * MSB + LSB;

 

             LSB := ord(szData[5]);

             MSB := ord(szData[6]);

             AGWVerLow  := MAXFRAME * MSB + LSB;

 

             PutAGW(1,'AGWPE Version '+inttostr(AGWVerHigh)+'-'+inttostr(AGWVerLow));

 

          end; {endif}

       end; {endif}

 

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*CONNECT Event Handler  *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'C' then begin

       PutAGW(5,'AGW Message <C>');

       IsNew      := FALSE;

       szYourCall := '';

 

       If Pos('CONNECTED To',szData) <> 0 then begin

          szAuxStr  := Parse(szData);

          szAuxStr  := Parse(szData);

          szAuxStr  := Parse(szData);

          szAuxStr  := Parse(szData);

          szYourCall:= Parse(szData);

          IsNew     := TRUE;

          PutAGW(1,'Connection initiated by station '+szYourCall);

       end; {endif}

 

       If Pos('CONNECTED With',szData) <> 0 then begin

          szAuxStr  := Parse(szData);

          szAuxStr  := Parse(szData);

          szAuxStr  := Parse(szData);

          szAuxStr  := Parse(szData);

          szYourCall:= Parse(szData);

          IsNew     := FALSE;

          PutAGW(1,'Connection initiated by us with station '+szYourCall);

       end; {endif}

 

                                              {*-----------------------*}

                                              {*We are looking for a   *}

                                              {*connection we started  *}

{*---------------------------------------------------------------------*}

       If IsNew = FALSE then begin

 

          {* Your code here to handle a connection started by us *}

 

       end; {endif}

 

                                              {*-----------------------*}

                                              {*We are looking to serve*}

                                              {*an unsolicited connect *}

{*---------------------------------------------------------------------*}

       If IsNew = TRUE then begin

 

          {* Somebody connected us, so handle it….*}

       end;

 

    end; {endif} {*-- This is the Footer of the whole <C> event handler --*}

 

 

                                              {*-----------------------*}

                                              {*DISC    Event Handler  *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'd' then begin

                                              {*First look at IC link  *}

       PutAGW(1,'Disconnect Frame: '+szData);

 

       If ((Pos('DISCONNECTED',szData) <> 0)  and

           (Pos('RETRYOUT',szData)     <> 0)) then begin

           PutAGW(2,'Disconnection by Retryout detected');

 

       end else begin

          If ((Pos('DISCONNECTED',szData) <> 0)  and

              (Pos('From',szData)         <> 0)) then begin

              PutAGW(2,'Normal Disconnection detected');

          end else begin

             If ((Pos('DISCONNECTED',szData) <> 0)  and

                 (Pos('With',szData)         <> 0)) then begin

                PutAGW(2,'AbNormal Disconnection detected, swap From<->To');

                szAuxStr := szFrom;

                szFrom   := szTo;

                szTo     := szAuxStr;

             end else begin

                PutAGW(2,'Bogus Disconnection form detected, ignored');

                Exit;

             end; {endif}

          end; {endif}

       end; {endif}

 

       {* Process the remaining of the disconnection *}

       Exit;

    end; {endif}

 

                                              {*-----------------------*}

                                              {*DATA    Event Handler  *}

{*---------------------------------------------------------------------*}

    If cDataKind = 'D' then begin

 

       {* DO whatever it fits with the Data from a connected partner *}

       Exit;

    end; {endif}

 

    PutAGW(3,'AGW Message not processed <'+cDataKind+'>');

 

end; { TLink.Decode }

 

 

 

 

 

 

Format VIA Areas

 

The way a digipeater string is informed to AGWPE seems intrincated and terrible on first look, but it’s rather simple actually, see on how to create the data area in C++ (code excerpt from George, SV2AGW).

 

char str[100];

 

str[0]=HowManyDigis;

str+1=1digi;//null terminated

str+1+10=2digi;//null terminated

str+1+20=3digi//null terminated

 

A more comprehensive routine to prepare a VIA list in C++ (also from George, SV2AGW) follows

 

int PrepareViaList(char *InVia,char *OutVia)

{

//InVia string contains the via list like SV2AGW,SV2BBO,SV2DFK

//OutVia is the same list suitable prepared for AGWPE

char *token;

char temp[15];

char HowVia=0;

token=strtok(InVia,", ");

if (token)

   {

   HowVia++;

   strcpy(temp,token);

   memmove(OutVia+1,temp,10);

   }

for (;;)

        {

        token=strtok(NULL,", ");

        if (token)

        {

        strcpy(temp,token);

           memmove(OutVia+1+(HowVia*10),temp,10);

           HowVia++;

        } else break;

        }//end for

OutVia[0]=HowVia;

return((HowVia*10)+1);

}

 

The following function does about the same, only in Delphi4 and written by Pedro (LU7DID); the routine accepts a string with the list of callsigns+SSID to be used as digipeaters and returns a data area directly in the format AGWPE likes it.

 

(*--->>> FormatVIA     <<<----------------------------------------------*)

{*Routine to format the VIA path in the particular way AGWPE likes it   *}

{*----------------------------------------------------------------------*}

Function FormatVIA (InStr : String) : String;

  Var

    AuxStr      :       String;

    CountVIA    :       Byte;

    ArrayVIA    :       Array[0..256] of Char;

    Index       :       Integer;

    Token       :       String;

    PtrVIA      :       Byte;

    Jndex       :       Integer;

 

begin { FormatVIA }

 

    For Index := 0  to 256  do begin

        ArrayVIA[Index] := NUL;

    end; {endfor}

 

    AuxStr   := InStr;

    PtrVIA   := 1;

    CountVIA := 0;

 

    While AuxStr <> '' do begin

      Token := Parse(AuxStr);

      If Token <> '' then begin

         For Jndex := 1  to Length(Token)  do begin

             ArrayVIA[Jndex-1+PtrVIA] := Token[Jndex];

         end; {endfor}

         ArrayVia[Length(Token)+1+PtrVIA] := NUL;

         PtrVIA := PtrVIA + 10;

         inc(CountVIA);

      end; {endif}

    end; {endwhile}

 

    If PtrVIA <> 1 then begin

       AuxStr      := '';

       ArrayVia[0] := chr(CountVIA);

 

       For Index := 0  to PtrVIA-1  do begin

           AuxStr := AuxStr + ArrayVia[Index];

       end; {endfor}

       Result := AuxStr;

       Exit;

    end else begin

       Result := '';

       Exit;

    end; {endif}

 

end; { FormatVIA }

 

 

Parsing Port Information

 

This code excerpt written in Delphi4 shows how to “chain” port information sent by AGWPE with request for further information to AGWPE, this routine/method should be originally called from the main dispatcher switch when a frame with DataKind=’G’ is received.

 

(*--->>> ParsePort     <<<----------------------------------------------*)

{*Procedure to decode, validate and store information about ports       *}

{*----------------------------------------------------------------------*}

Procedure TLink.ParsePort(szPort : String);

  Var

  PortNum       :       Integer;

  AuxStr        :       String;

  RadioPort     :       Integer;

  cPort         :       Char;

  bPort         :       Byte;

  Index         :       Integer;

 

begin { TLink }

 

  AGWPortInit;

 

  PutAGW(3,'Port String is '+szPort);

  PortNum := strtoint(PopBang(szPort,';'));

  PutAGW(3,'Parsed Number of Ports is '+inttostr(PortNum));

  RadioPort := 1;

 

  While PortNum > 0 do begin

                                         {*----------------------------------*}

                                         {*Store the port description        *}

                                         {*----------------------------------*}

    AuxStr := PopBang(szPort,';');

    Ports[RadioPort].PortStr := GetStrZ(AuxStr);

    Ports[RadioPort].Enabled := TRUE;

 

                                         {*----------------------------------*}

                                         {*Initialize the MHEARD structure   *}

                                         {*for the port and pull a refresh   *}

                                         {*----------------------------------*}

    bPort := Trunc(RadioPort);

    cPort := chr(bPort);

 

    For Index := 1  to MAXHEARD  do begin

        Ports[RadioPort].HeardStr[Index] := '';

    end; {endfor}

                                         {*----------------------------------*}

                                         {*Pull outstanding info, heard and  *}

                                         {*capabilities info                 *}

                                         {*----------------------------------*}

 

    Send(AGWSocket,cPort,NUL,'y','','',0,'');

    Send(AGWSocket,cPort,NUL,'H','','',0,'');

    Send(AGWSocket,cPort,NUL,'g','','',0,'');

 

    PutAGW(3,'  Stored RadioPort # '+inttostr(RadioPort)+' as +Ports[RadioPort].PortStr);

    inc(RadioPort);

    dec(PortNum);

  end; {endwhile}

 

 

Port Capabilities

 

This is the C++ structure recommended by George (SV2AGW) to held and decode the data area with the port capabilities as provided in the ‘g’ Frame sent by AGWPE.

 

unsigned char OnairBaud;

unsigned char TrafficLevel;// if    this is 255 then the port is not in autoupdate mode

unsigned char TxDelay;

unsigned char TxTail;

unsigned char Persist;

unsigned char Slottime;

unsigned char maxframe;

unsigned char AX25Channels;  // How many connections we have

unsigned int HowManyBytes;// how many bytes are received the last 2 minutes

 

An alternate way, this time in Delphi4 from a code excerpt from Pedro (LU7DID) obtain this information right out of the data area as received in the ‘G’ frame from AGWPE.

 

(*--->>> ParseParam    <<<----------------------------------------------*)

{*Procedure to decode, validate and store information about ports params*}

{*----------------------------------------------------------------------*}

Procedure TLink.ParseParam(cPort : Char; szData : String);

 

  Var

    bPort       :       Byte;

    szAuxStr    :       String;

 

begin { TLink }

 

  bPort := Ord(cPort);

  If (bPort > 0) and (bPort <= MAXPORT) then begin

  end else begin

     PutAGW(1,'ParseParam exit with cPort('+inttostr(bPort)+') too high');

     Exit;

  end; {endif}

 

  PutAGW(3,'Storing Params for Port ('+inttostr(bPort)+')');

  Ports[bPort].bOnAirBaud     := ord(szData[1]);

  Ports[bPort].bTrafficLevel  := ord(szData[2]);

  Ports[bPort].bTxDelay       := ord(szData[3]);

  Ports[bPort].bTxTail        := ord(szData[4]);

  Ports[bPort].bPersist       := ord(szData[5]);

  Ports[bPort].bSlotTime      := ord(szData[6]);

  Ports[bPort].bMaxFrame      := ord(szData[7]);

  Ports[bPort].bAX25Channel   := ord(szData[8]);

  Ports[bPort].iHowManyBytes  :=    ord(szData[9])+MAXFRAME*ord(szData[10])+($10000*ord(szData[11]));

 

 

end; { TLink }

 

 

Heard Information for a Port

 

The following Delphi4 code excerpt from Pedro (LU7DID) shows a possible way to decode the heard information provided on the data area of an ‘H’ frame (note that the data associated with the binary format is just ignored).

 

(*--->>> ParseHeard    <<<----------------------------------------------*)

{*Procedure to decode, validate and store information about ports       *}

{*----------------------------------------------------------------------*}

Procedure TLink.ParseHeard(cPort : Char; szData : String);

  Var

    bPort       :       Byte;

    iPort       :       Integer;

    Index       :       Integer;

    szAuxStr    :       String;

    szHeardStr  :       String;

 

begin { TLink }

 

  bPort := Ord(cPort);

  If (bPort > 0) and (bPort <= MAXPORT) then begin

  end else begin

     PutAGW(1,'ParseHeard exit with cPort('+inttostr(bPort)+') too high');

     Exit;

  end; {endif}

  iPort := bPort;

 

  PutAGW(1,'ParseHeard:');

  DumpHex(1,szData);

 

  szAuxStr   := szData;

  szHeardStr := PopBang(szAuxStr,Chr($00));

 

  For Index := 1  to MAXHEARD  do begin

      If Ports[iPort].HeardStr[Index] = '' then begin

         Ports[iPort].HeardStr[Index] := szHeardStr;

         PutAGW(1,'Stored '+szHeardStr+' at Offset ('+IntToStr(Index)+')');

         Exit;

      end; {endif}

  end; {endfor}

  PutAGW(1,'Table Full. Not Stored '+szHeardStr);

 

end; { TLink }

 

 

 

 

 

 

 

 

 

 

Raw Frames

 

In case raw frames want to be handled the following Delphi4 routine written by Pedro (LU7DID) could give you a starting point.

 

You must enter the routine (method, actually) with the data frame as received over the air on the ‘K’ frame from AGWPE, the routine would parse and decode the components of that frame.

 

(*--->>> RawDecode     <<<----------------------------------------------*)

{*Procedure to decode a Raw KISS frame                                  *}

{*----------------------------------------------------------------------*}

Procedure TLink.RawDecode(    szData    : String;

                          Var szFrom    : String;

                          Var szTo      : String;

                          Var szVIA     : String;

                          Var cDataKind : Char;

                          Var cPID      : Char;

                          Var iDataLen  : DWORD;

                          Var szInfo    : String);

  Var

    Index       :       Integer;

    AuxChar     :       Char;

    AuxByte     :       Byte;

    szCall      :       String;

    Jndex       :       Integer;

    iCount      :       Integer;

    iFlag       :       Integer;

 

begin { TLink }

 

    Index   := 2;

    szCall  := '';

    iCount  := 1;

    iFlag   := 1;

    szVIA   := '';

    For Jndex := Index  to Length(szData)  do begin

       AuxChar := szData[Index];

       AuxByte := Ord(AuxChar);

       szCall := szCall + Chr(((AuxByte and $FE) shr 1));

       inc(iCount);

       If iCount > 7 then begin

          iCount := 1;

          If iFlag = 1 then begin

             szTo   := szCall;

             szCall := '';

             inc(iFlag);

          end else begin

             If iFlag = 2 then begin

                szFrom := szFrom;

                szCall := '';

                inc(iFlag);

             end else begin

                szVIA  := szVIA + szCall;

                szCall := '';

             end; {endif}

          end; {endif}

       end; {endif}

       inc(Index);

       If (AuxByte and $01) = $01 then begin

          Break;

       end; {endif}

    end; {endfor}

 

    szCall    := szFrom;

    szFrom    := Parse(szCall);

    szCall    := szTo;

    szTo      := Parse(szCall);

 

    AuxChar   := szData[Index];

    AuxByte   := Ord(AuxChar);

    If (AuxByte and $FE) = $00 then begin

       cDataKind := 'I';

    end else begin

       If (AuxByte and $FC) = $01 then begin

          cDataKind := 'S';

       end else begin

          cDataKind := 'U';

       end; {endif}

    end; {endif}

 

    inc(Index);

    cPid      := szData[Index];

    inc(Index);

 

    szInfo := '';

    For Jndex := Index  to Length(szData)  do begin

        szInfo := szInfo + szData[Jndex];

    end; {endfor}

 

    PutAGW(2,'Decoded KISS Frame as From=('+szFrom+') To=('+szTo+') VIA('+szVIA+') DataKind('+cDataKind+') Pid('+HexByte(Ord(cPid))+')');

    DumpHex(2,szInfo);

 

end; { TLink }

 

 

 

Tracking Frames

 

Some of the frames used for the application to communicate with AGWPE acts like a switch, the first time sent activates a function, the second de-activates it and so on (i.e. the ‘m’ and ‘k’ frames).

 

Since AGWPE doesn’t return a “confirming” frame to the requirement the application has to keep track of the current status if for functional reasons the underlying functions must be alternated as on or off.

 

A good technique of doing so is to use a “counter” to track how many frames of a given type has been sent, at any time the value of that counter could be inspected and being odd it would signal the function is activated while being even it’s inactive.

 

The general algorithm would be:

 

 

 

 

 

 

 


 

Managing Connections

 

Once the basic frame processing is mastered just one additional issue has to be understood for the would be AGWPE programmer in order to be ready to write applications of any arbitrary complexity, and that is how to manage AX.25 connections.

 

Simple applications that involves the processing of some sort of monitoring only doesn’t need to be bothered by the managing of connections at all, those applications doesn’t even need to register a call to work properly.

 

Simple terminal programs could assume that a single user could sustain a single connection using a single registered callsign, so no complex issues occurs in that scenario.

 

However, most real world applications would face the need to be able to manage multiple simultaneous connections possibly using several registered callsigns at once.

 

The basic issue to solve is assuming a single TCP/IP connection is held between the application and AGWPE different frames might arrive thru that connection which logically belongs to different connections and the application is responsible to identify to which particular connection the frame belongs and to route the relevant information to that connection.

 

Unfortunately AGWPE doesn’t provide any handle or id that uniquely identify a given connection among others; this is not needed actually since a close look of the AX.25 protocol provides such unique identifier as we would see.

 

Some suggested tactics would be discussed on the following sections.

 

One Callsign, Many Connections

 

This is the simplest case, so it’s a good starting point.

 

The application needs to sustain several AX.25 connections at the same time (i.e. a multiwindow terminal program) using a single registered callsign.

 

At first look, the initial attempt of many programmers should be to create different TCP/IP sockets one for each connection, so frames flowing from AGWPE to the application would be automatically bucketed into their destination by means of the socket where the data arrived.

 

Given the relative simplicity to establish TCP/IP sockets from the application standpoint and the fact that AGWPE could handle a limitless number of simultaneous connections (from any meaningful number required in the real world) this seems to be the right approach.

 

But it is not, the main drawback is in the fact that AGWPE only allows a given callsign+SSID to be registered once across all applications running on a given moment, AGWPE doesn’t really knows nor care if the multiple sockets were opened from a single application or from many applications.

 

So the first socket to register a callsign+SSID would take it all, the others would either fail trying to register the same callsign+SSID or be forced to use different callsign+SSID.

 

To register a different callsign+SSID for each connection  could be both extremely impractical and a very limited approach, after all AX.25 tolerates just 16 different SSID to be used by callsign (0 to 15), and with a fair number of applications running on a typical environment this would quickly become a limiting factor.

 

So, the best solution would be for the application to open just a single socket with AGWPE, register a single callsign with it and manage many connections with the same callsign+SSID.

 

A limitation of the AX.25 protocol L2 connections come to provide a sort of help, the protocol doesn’t allow more than one connected session among a pair of callsign+SSID ends, so the combination From/To (in any order) of any two callsign+SSID has to be unique. Since AGWPE could handle multiple ports at the same time the uniqueness could be obtained adding the Port to the identification key.

 

The overall logic the application should follow is despicted as follows:

 

 

Since this logic would be exercised fairly often the programmer should pay premium attention to the efficiency of the creation, search and disposal of entries on the memory structure making them as efficient and fast as possible (or buy an umpteen Mhz Pentium IX iron to run it).

 

 

 

Many CallSigns, Many Connections

 

In this case the added complexity is the need for the application to register and use any arbitrary number of callsign+SSID (i.e. on a BBS program some callsigns to accept connections and some others to start forward sessions).

 

Again, multiple sockets could be opened, in this case one for each registered callsign, so the case trivialized itself into many instances of the “One CallSign, Many Connection” just seen in the previous section.

 

However, the very logic stated in that case would still be valid for this one, as long as we add a validation on every connection started that the our involved callsign belongs to a given (and limited) authorized set or pool of callsigns.

 

This control won’t hurt, actually is highly desirable, even on the case a single callsign is used.

 

 

Down the Tubes, Climb the Ladder

 

In fantastic Wonderland bug free programs do exist; however, on Earth this is not true. Programs does have bugs, plenty of them. Some inocuous enough to survive the entire lifespan of a program and still be undetected (only triggered for a combination of factors so unlikely that it actually never happens).

 

AGWPE itself is not an exception, and certainly no application program would be out of that rule.

 

So the ideal profile of a programmer is a person that it’s moderately paranoid and expect things that might go wrong from time to time; good programs often differentiate from bad programs that provide exactly the same functionality just by the extend of how hardened it has been made by it’s author.

 

Some general guidelines:

 

 


 

Credits and other stuff

 

This document is the intellectual property of Pedro E. Colla (LU7DID) and George Rossopoulos (SV2AGW) and as such is a copyrighted piece subject to international laws covering intellectual property.

 

However, it’s usage is free for radioamateur uses as long as the reader understand is using the material contained on it at his or her own risk, authors doesn’t bear any responsibility on any damage direct or indirect been made thru the usage of this material.

 

Excerpts had been taken from other sources and the relevant copyright information provided when this has been done, the copyright over those parts remains on it’s beholder.

 

This documentation could be understood as an annotated version of the original “AGW TCPIP Socket Interface” document originally written by George Rossopoulos (SV2AGW) and included as a documentation with thw AGWPE package.

 

AGWPE is copyright© of G.Rossopoulos (SV2AGW) who held it’s property and the right to change any aspect of the functionality without prior notice.



[1] As a reference on a 233 Mhz Pentium it could take as little as 4% of the total processor time and less than 4 MBytes of memory (allocated) under a moderate to heavy port activity. Under idle port conditions the amount of CPU taken might be 0.5% or less.

 

[2] If  (as an example) two 9k6 bps packet ports are serviced the minimum bandwidth between AGWPE and each application should be between 3 X 2 X 9K6 = 57K6  and 4 X 2 X 9k6 = 76K8, so a modern 56K dialup connection would be in the lower end to service such arrangement with reasonable performance, assuming the effective speed is about 56K bps and not something lower.

[3] TCP/IP addressing is not particularly difficult once understood, however, the author of any program that forces the user to deal with them should be prepared to expend a great deal of effort to make the configuration of it as user friendly and “foolproof” as possible.

[4] Some oddity of the API, while AGWPE number the ports in the order of creation starting with Port 1 the API reflects the ports in the same order but starting with 0 (zero). So, the port reflected as Port 1 in the AGWPE Properties dialog would be reflected as 0x00 on the API, Port 2 as 0x01, and so on.

[5] When the callsign plus ssid plus the ending null (0x00) requires less than 10 bytes the remaining bytes of the field are not guaranteed to be cleared, data after the null (0x00) must be handled as “garbage” and cleared out by the application program. C programmers would handle it nicely (ASCIIZ variables), Pascal programmers should handle it more carefully.

[6] An exception to this is when the application terminates a link with a ‘d’ frame and inmediately unregisteres the callsign with a ‘x’ frame, in this situation no further information regarding the fate of the disconnection is sent to the application. In fact, if the unregistration closely follows the ‘d’ frame in such a way that the frame doesn’t have enough time to be sent the other end will be left “connected”, the connection would be terminated then either by the remote end inactivity timeout or when a data exchange attempt is made.