tarpn_logo
 home    builders  

G8BPQ Debug Window Output

Document is under construction! -- April 28, 2015

Some material co-opted from John Wiseman, G8BPQ. These documents are useful starting points for the manual pages on debugging:
Netrom protocol
AX.25 protocol

Decoding the Monitor Window output

The Monitor window verbosely interprets the packets being sent and received by the node.

There are several key pieces of information displayed and there are some subtleties. This is a list of the data displayed and a key to try to understand it.

Source code for G8BPQ monitor display code

int IntDecodeFrame(MESSAGE * msg, char * buffer, int Stamp, UINT Mask, BOOL APRS)
{
   UCHAR * ptr;
   int n;
   MESSAGE * ADJBUFFER;
   UINT Work;
   UCHAR CTL;
   BOOL PF = 0;
   char CRCHAR[3] = "  ";
   char PFCHAR[3] = "  ";
   int Port;
   int MSGFLAG = 0;     //CR and V1 flags
   char * Output = buffer;
   int HH, MM, SS;
   char TR = 'R';
   char From[10], To[10];
   BOOL Info = 0;
   BOOL FRMRFLAG = 0;
   int MsgLen = msg->LENGTH;

   // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED

   n = 8;                  // MAX DIGIS
   ptr = &msg->ORIGIN[6];  // End of Address bit

   while ((*ptr & 1) == 0)
   {
      // MORE TO COME
   
      ptr += 7;
      n--;

      if (n == 0)
      {
         return 0;                  // Corrupt - no end of address bit
      }
   }

   // Reached End of digis

   Work = (UINT)&msg->ORIGIN[6];
   ptr -=   Work;                   // ptr is now length of digis

   MsgLen -= (UINT)ptr;

   Work = (UINT)msg;
   ptr += Work;

   ADJBUFFER = (MESSAGE * )ptr;        // ADJBUFFER points to CTL, etc. allowing for digis

   CTL = ADJBUFFER->CTL;

   if (CTL & PFBIT)
      PF = TRUE;

   CTL &= ~PFBIT;

   if (MUIONLY)
      if (CTL != 3)
         return 0;

   if ((CTL & 1) == 0 || CTL == FRMR || CTL == 3)
   {
   }
   else
   {
      if (MCOM == 0)
         return 0;                  // Dont do control
   }


   Port = msg->PORT;
   
   if (Port & 0x80)
   {
      if (MTX == 0)
         return 0;                     // TRANSMITTED FRAME - SEE IF MTX ON
      
      TR = 'T';
   }

   Port &= 0x7F;

   if (((1 << (Port - 1)) & Mask) == 0)      // Check MMASK
      return 0;
   

   Stamp = Stamp % 86400;     // Secs
   HH = Stamp / 3600;

   Stamp -= HH * 3600;
   MM = Stamp  / 60;

   SS = Stamp - MM * 60;

   Output += sprintf(Output, "%02d:%02d:%02d%c ", HH, MM, SS, TR);

   From[ConvFromAX25(msg->ORIGIN, From)] = 0;
   To[ConvFromAX25(msg->DEST, To)] = 0;

   Output += sprintf(Output, "%s>%s", From, To);

   // Display any Digi-Peaters   

   n = 8;               // Max number of digi-peaters
   ptr = &msg->ORIGIN[6];  // End of Address bit

   while ((*ptr & 1) == 0)
   {
      // MORE TO COME

      From[ConvFromAX25(ptr + 1, From)] = 0;
      Output += sprintf(Output, ",%s", From);
   
      ptr += 7;
      n--;

      if (n == 0)
         break;

      // See if digi actioned - put a * on last actioned

      if (*ptr & 0x80)
      {
         if (*ptr & 1)                 // if last address, must need *
            *(Output++) = '*';
         else
            if ((ptr[7] & 0x80) == 0)     // Repeased by next?
               *(Output++) = '*';         // No, so need *
      }
   }     
   
   Output += sprintf(Output, " Port=%d ", Port);

   // Set up CR and PF

   CRCHAR[0] = 0;
   PFCHAR[0] = 0;

   if (msg->DEST[6] & 0x80)
   {
      if (msg->ORIGIN[6] & 0x80)       // Both set, assume V1
         MSGFLAG |= VER1;
      else
      {
         MSGFLAG |= CMDBIT;
         CRCHAR[0] = ' ';
         CRCHAR[1] = 'C';
         if (PF)                    // If FP set
         {
            PFCHAR[0] = ' ';
            PFCHAR[1] = 'P';
         }
      }
   }
   else
   {
      if (msg->ORIGIN[6] & 0x80)       // Only Origin Set
      {
         MSGFLAG |= RESP;
         CRCHAR[0] = ' ';
         CRCHAR[1] = 'R';
         if (PF)                    // If FP set
         {
            PFCHAR[0] = ' ';
            PFCHAR[1] = 'F';
         }
      }
      else
         MSGFLAG |= VER1;           // Neither, assume V1
   }

   if ((CTL & 1) == 0)                 // I frame
   {
      int NS = (CTL >> 1) & 7;         // ISOLATE RECEIVED N(S)
      int NR = (CTL >> 5) & 7;

      Info = 1;

      Output += sprintf(Output, "", CRCHAR, PFCHAR, NS, NR);
   }
   else if (CTL == 3)
   {
      // Un-numbered Information Frame 

      Output += sprintf(Output, "", CRCHAR);
      Info = 1;
   }
   else if (CTL & 2)
   {
      // UN Numbered
            
      char SUP[5] = "??";

      switch (CTL)
      {
      case SABM:

         strcpy(SUP, "C");
         break;

      case DISC:

         strcpy(SUP, "D");
         break;

      case DM:

         strcpy(SUP, "DM");
         break;
      
      case UA:

         strcpy(SUP, "UA");
         break;
      

      case FRMR:

         strcpy(SUP, "FRMR");
         FRMRFLAG = 1;
         break;
      }

      Output += sprintf(Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR);
   }
   else
   {
      // Super

      int NR = (CTL >> 5) & 7;
      char SUP[4] = "??";

      switch (CTL & 0x0F)
      {
      case RR:

         strcpy(SUP, "RR");
         break;

      case RNR:

         strcpy(SUP, "RNR");
         break;

      case REJ:

         strcpy(SUP, "REJ");
         break;
      }

      Output += sprintf(Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR);

   }

   if (FRMRFLAG)
      Output += sprintf(Output, " %02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); 

   if (Info)
   {
      // We have an info frame

      switch (ADJBUFFER->PID)
      {
      case 0xF0:     // Normal Data
      {
         char Infofield[257];
         char * ptr1 = Infofield;
         char * ptr2 = ADJBUFFER->L2DATA;
         UCHAR C;
         int len;

         MsgLen = MsgLen - 23;

         if (MsgLen < 0 || MsgLen > 257)
            return 0;            // Duff

         while (MsgLen--)
         {
            C = *(ptr2++);

            if (APRS)
               *(ptr1++) = C;    // MIC-E needs all chars
            else
            {
               // Convert to printable

               C &= 0x7F;

               if (C == 13 || C == 10 || C > 31)
                  *(ptr1++) = C; 
            }
         }

         len = ptr1 - Infofield;
   
         Output[0] = ':';
         Output[1] = 13;
         memcpy(&Output[2], Infofield, len);
         Output += (len + 2);

         break;
      }
      case NETROM_PID:
         
         Output = DISPLAY_NETROM(ADJBUFFER, Output, MsgLen);
         break;

      case IP_PID:

         Output += sprintf(Output, " \r");
         Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, MsgLen);
         break;

      case ARP_PID:
         
         Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output);
         break;

      case 8:              // Fragmented IP

         Output += sprintf(Output, "");
         break;   
      }
   }

   if (Output[-1] != 13)
      Output += sprintf(Output, "\r");

   return Output - buffer;

}
//      Display NET/ROM data                                                 

char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen)
{
   char Alias[7]= "";
   char Dest[10];
   char Node[10];
   UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window;
   UCHAR * ptr = &ADJBUFFER->L2DATA[0];

   if (ADJBUFFER->L2DATA[0] == NODES_SIG)
   {
      // Display NODES


      // If an INP3 RIF (type <> UI) decode as such
   
      if (ADJBUFFER->CTL != 3)      // UI
         return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - 24);

      memcpy(Alias, ++ptr, 6);

      ptr += 6;
   
      Output += sprintf(Output, " NODES broadcast from %s\r", Alias);

      MsgLen -= 30;              //Header, mnemonic and signature length

      while(MsgLen > 20)            // Entries are 21 bytes
      {
         Dest[ConvFromAX25(ptr, Dest)] = 0;
         ptr +=7;
         memcpy(Alias, ptr, 6);
         ptr +=6;
         strlop(Alias, ' ');
         Node[ConvFromAX25(ptr, Node)] = 0;
         ptr +=7;

         Output += sprintf(Output, "  %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]);
         ptr++;
         MsgLen -= 21;
      }
      return Output;
   }

   // Display normal NET/ROM transmissions 

   Output += sprintf(Output, " NET/ROM\r  ", Alias);

   Dest[ConvFromAX25(ptr, Dest)] = 0;
   ptr +=7;
   Node[ConvFromAX25(ptr, Node)] = 0;
   ptr +=7;

   TTL = *(ptr++);
   Index = *(ptr++);
   ID = *(ptr++);
   TXNO = *(ptr++);
   RXNO = *(ptr++);
   OpCode = Flags = *(ptr++);

   OpCode &= 15;           // Remove Flags

   Output += sprintf(Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID );
   MsgLen -= 20;

   switch (OpCode)
   {
   case L4CREQ:

      Window = *(ptr++);
      Dest[ConvFromAX25(ptr, Dest)] = 0;
      ptr +=7;
      Node[ConvFromAX25(ptr, Node)] = 0;
      ptr +=7;

      Output += sprintf(Output, " w=%d %s at %s", Window, Dest, Node);

      if (MsgLen > 38)           // BPQ Extended Params
      {
         short Timeout = (SHORT)*ptr;
         Output += sprintf(Output, " t/o %d", Timeout);
      }

      return Output;

   case L4CACK:

      if (Flags & L4BUSY)           // BUSY RETURNED
         return Output + sprintf(Output, "  - BUSY");

      return Output + sprintf(Output, "  w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO);

   case L4DREQ:

      return Output + sprintf(Output, " ");

   case L4DACK:

      return Output + sprintf(Output, " ");

   case L4INFO:
      {
         char Infofield[257];
         char * ptr1 = Infofield;
         UCHAR C;
         int len;
         
         Output += sprintf(Output, " ", TXNO, RXNO);
         
         if (Flags & L4BUSY)
            *(Output++) = 'B';

         if (Flags & L4NAK)
            *(Output++) = 'N';

         if (Flags & L4MORE)
            *(Output++) = 'M';
   
         MsgLen = MsgLen - 23;

         if (MsgLen < 0 || MsgLen > 257)
            return Output;          // Duff

         while (MsgLen--)
         {
            C = *(ptr++);

            // Convert to printable

            C &= 0x7F;

            if (C == 13 || C == 10 || C > 31)
               *(ptr1++) = C; 
         }

         len = ptr1 - Infofield;
   
         Output[0] = ':';
         Output[1] = 13;
         memcpy(&Output[2], Infofield, len);
         Output += (len + 2);
      }
      
      return Output;

   case L4IACK:

      Output += sprintf(Output, "  ", RXNO);
   
      if (Flags & L4BUSY)
         *(Output++) = 'B';

      if (Flags & L4NAK)
         *(Output++) = 'N';

      if (Flags & L4MORE)
         *(Output++) = 'M';

      return Output;


   case 0:

      // OPcode zero is used for several things

      if (Index == 0x0c && ID == 0x0c) // IP 
      {
//       Output =  L3IP(Output);
         return Output;
      }
   
      if (Index == 0 && ID == 1)       // NRR   
      {
         Output += sprintf(Output, " \r");

         MsgLen -= 23;

         while (MsgLen > 6)
         {
            Dest[ConvFromAX25(ptr, Dest)] = 0;

            if (ptr[7] & 0x80)
               Output += sprintf(Output, "%s* ", Dest);
            else
               Output += sprintf(Output, "%s ", Dest);

            ptr +=8;
            MsgLen -= 8;
         }

         return Output;
      }
   }

   Output += sprintf(Output, " ");
   return Output;
}
© Tadd Torborg, 2014, 2015 -- all rights reserved