/*
 * dxlAPRS toolchain
 *
 * Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */


#define X2C_int32
#define X2C_index32
#ifndef X2C_H_
#include "X2C.h"
#endif
#define afskmodem_C_
#ifndef soundctl_H_
#include "soundctl.h"
#endif
#ifndef udp_H_
#include "udp.h"
#endif
#include <fcntl.h>
#ifndef osi_H_
#include "osi.h"
#endif
#include <osic.h>
#ifndef mlib_H_
#include "mlib.h"
#endif
#ifndef aprsstr_H_
#include "aprsstr.h"
#endif
#include <unistd.h>
#ifndef afskmodemptt_H_
#include "afskmodemptt.h"
#endif
#include <signal.h>

/* (a)fsk -  kiss/axudp stereo soundcard afsk/fsk multimodem by OE5DXL */
/*FROM setsighandler IMPORT SIGTERM, SIGINT, setsignalproc, SIGPROC; */
#define afskmodem_MAXLEN 339

#define afskmodem_MAXMODEMS 8

#define afskmodem_TXBUFLEN 60
/* default kiss buffers */

#define afskmodem_ADCBUFLEN 4096

#define afskmodem_BAUDSAMP 65536

#define afskmodem_PLLSHIFT 4096

#define afskmodem_DEFTIMEOUT 60
/* then unsent frames are discarded */

#define afskmodem_AFIRLEN 32
/*32, 64*/

#define afskmodem_AOVERSAMP 16
/*16*/

#define afskmodem_DFIRLEN 64

#define afskmodem_DOVERSAMP 16

#define afskmodem_TFIRLEN 64
/* 2^ */
/*32/5*/

#define afskmodem_SHIFTBITS 6

#define afskmodem_TFIRFINE 128

#define afskmodem_MAXVOL 25000.0
/* max dac / overswing */

static uint32_t afskmodem_POLYNOM = 0x8408UL;

static uint32_t afskmodem_CRCINIT = 0xFFFFUL;

static uint32_t afskmodem_CRCRESULT = 0x9F0BUL;

#define afskmodem_MINFLEN 9

#define afskmodem_STUFFLEN 5

#define afskmodem_FEND 192

#define afskmodem_FESC 219

#define afskmodem_TFEND 220

#define afskmodem_TFESC 221

struct KISSBUF;

typedef struct KISSBUF * pKISSNEXT;


struct KISSBUF {
   pKISSNEXT next;
   char port;
   uint32_t time0;
   uint32_t len;
   char data[341]; /* +2 byte crc */
};

typedef float TXFIRTAB[128][64];

typedef char FILENAME[1024];

enum MONITOR {afskmodem_off, afskmodem_noinfo, afskmodem_normal,
                afskmodem_passall};


enum CHANNELS {afskmodem_LEFT, afskmodem_RIGHT};


typedef float DFIRTAB[1024];

typedef float DFIR[64];

typedef float AFIRTAB[512];

struct MPAR;


struct MPAR {
   uint8_t ch;
   int32_t baudfine;
   int32_t pllshift;
   float left;
   float tcnt;
   float freq;
   float afskmidfreq;
   float clamp;
   float squelchdcd;
   float noise;
   float afshift;
   float afmid;
   float txvolum;
   float afskhighpass;
   uint8_t monitor;
   float sqmed[2];
   uint32_t dfin;
   DFIRTAB dfirtab;
   DFIR dfir;
   AFIRTAB afirtab;
   char afsk;
   char cbit;
   char oldd;
   char plld;
   char scramb;
   char data1;
   char axudp2;
   char dcdmsgs;
   char haddcd;
   char hadtxdata;
   char haddcdrand;
   char flagbeg;
   uint32_t flagc; /* for statistic */
   uint32_t flags;
   uint32_t flage;
   uint32_t rxstuffc;
   uint32_t rxbyte;
   uint32_t rxbitc;
   uint32_t rxp;
   uint32_t demodbaud;
   uint32_t txbaud;
   uint32_t port16;
   uint32_t dcdclockm;
   uint32_t bertc;
   uint32_t berterr;
   uint32_t txdel;
   uint32_t txtail;
   uint32_t rxcrc;
   uint32_t scrambler;
   char rxbuf[339];
   pKISSNEXT txbufin;
   pKISSNEXT txbufout;
   uint32_t timeout;
   uint32_t configbaud;
   uint32_t configtxdel;
   uint32_t configtxtail;
   uint32_t txdelpattern;
   uint32_t configafskshift;
   uint32_t configafskmid;
   uint32_t confignyquist;
   int32_t configequalizer;
   int32_t leveldcd;
   int32_t bert;
   char configured;
   char kissignore;
   int32_t udpsocket;
   uint32_t udpport;
   uint32_t udpbind;
   uint32_t udpip;
   char checkip;
};

enum TXSTATE {afskmodem_receiv, afskmodem_slotwait, afskmodem_sendtxdel,
                afskmodem_senddata, afskmodem_sendtxtail};


enum DUPLEX {afskmodem_simplex, afskmodem_shiftdigi, afskmodem_fullduplex,
                afskmodem_onetx};


struct CHAN;


struct CHAN {
   uint32_t gmqtime;
   uint32_t gmcnt; /* gm900 quiet tx time after ptt */
   uint32_t dcdclock;
   uint32_t pttsoundbufs;
   uint32_t dds;
   uint32_t txbaudgen;
   uint32_t addrandom;
   uint32_t tbyte;
   uint32_t tbytec;
   int32_t txstuffc;
   int32_t tbitc;
   char * hptt;
   char pttstate;
   char tnrzi;
   uint8_t state;
   uint8_t duplex;
   float persist;
   float hipasscap;
   uint32_t tscramb;
   int32_t adcmax;
   int32_t actmodem;
   uint32_t configpersist;
   float afir[32];
};

static int32_t pipefd;

static int32_t soundfd;

static int32_t debfd;

static char esc;

static char abortonsounderr;

static char badsounddriver;

static char deb01;

static uint32_t debp;

static uint32_t getst;

static uint32_t afin;

static uint32_t soundbufs;

static uint32_t maxsoundbufs;

static uint32_t adcrate;

static uint32_t adcbuflen;

static uint32_t adcbytes;

static uint32_t fragmentsize;

static uint32_t extraaudiodelay;

static uint32_t clock0;

static uint32_t systime;

static uint8_t maxchannels;

static struct CHAN chan[2];

static pKISSNEXT pTxFree;

static pKISSNEXT pGetKiss;

static TXFIRTAB TFIR;

static struct MPAR modpar[8];

static float SIN[32768];

static uint8_t CRCL[256];

static uint8_t CRCH[256];

static FILENAME soundfn;

static char debb[81];


static void Error(char text[], uint32_t text_len)
{
   X2C_PCOPY((void **)&text,text_len);
   osi_WrStr(text, text_len);
   osi_WrStrLn(" error abort", 13ul);
   X2C_ABORT();
   X2C_PFREE(text);
} /* end Error() */

/*
PROCEDURE SetStatLine(fd:INTEGER; rts, dtr:BOOLEAN):BOOLEAN;
VAR arg : ARRAY[0..255] OF CARDINAL;
    r:INTEGER;
BEGIN
  arg[0]:=TIOCM_RTS;
  IF rts THEN r:=ioctl(fd, TIOCMBIS, ADR(arg)) ELSE r:=ioctl(fd, TIOCMBIC,
                ADR(arg)) END;
  IF r>=0 THEN
    arg[0]:=TIOCM_DTR;
    IF dtr THEN r:=ioctl(fd, TIOCMBIS, ADR(arg)) ELSE r:=ioctl(fd, TIOCMBIC,
                ADR(arg)) END;
  END;
  RETURN r>=0
END SetStatLine;
*/

static void Hamming(float f[], uint32_t f_len)
{
   uint32_t i;
   uint32_t tmp;
   tmp = f_len-1;
   i = 0UL;
   if (i<=tmp) for (;; i++) {
      f[i] = f[i]*(0.54f+0.46f*osic_cos(3.1415926535898f*(X2C_DIVR((float)
                i,(float)(1UL+(f_len-1))))));
      if (i==tmp) break;
   } /* end for */
} /* end Hamming() */


static void initdfir(DFIRTAB dfirtab, uint32_t fg)
{
   uint32_t f;
   uint32_t i;
   float t[512];
   float e;
   float f1;
   uint32_t tmp;
   for (i = 0UL; i<=511UL; i++) {
      t[i] = 0.5f;
   } /* end for */
   f1 = X2C_DIVR((float)(fg*64UL),(float)adcrate);
   tmp = (uint32_t)X2C_TRUNCC(f1,0UL,X2C_max_longcard)+1UL;
   f = 1UL;
   if (f<=tmp) for (;; f++) {
      e = 1.0f;
      if (f==(uint32_t)X2C_TRUNCC(f1,0UL,X2C_max_longcard)+1UL) {
         e = f1-(float)(uint32_t)X2C_TRUNCC(f1,0UL,X2C_max_longcard);
      }
      for (i = 0UL; i<=511UL; i++) {
         t[i] = t[i]+e*osic_cos(X2C_DIVR(3.1415926535898f*(float)(i*f),
                512.0f));
      } /* end for */
      if (f==tmp) break;
   } /* end for */
   /*
     FOR f:=1 TO fg*DFIRLEN DIV adcrate DO
       FOR i:=0 TO HIGH(t) DO t[i]:=t[i]+cos(pi*FLOAT(i*f)/FLOAT(HIGH(t)+1))
                END;
     END;
   */
   Hamming(t, 512ul);
   /*  FOR i:=0 TO HIGH(t) DO t[i]:=t[i]*(0.54+0.46*cos(pi*(FLOAT(i)
                /FLOAT(1+HIGH(t))))) END; */
   for (i = 0UL; i<=511UL; i++) {
      dfirtab[511UL+i] = t[i];
      dfirtab[511UL-i] = t[i];
   } /* end for */
/*
IO.WrLn;
FOR i:=0 TO HIGH(dfirtab) DO IO.WrFixed(dfirtab[i], 2,8) END;
IO.WrLn;
*/
/*
fd:=FIO.Create("/tmp/td.raw");
FOR i:=0 TO HIGH(dfirtab) DO n:=VAL(INTEGER, dfirtab[i]*2000.0);
                FIO.WrBin(fd,n,2) END;
FIO.Close(fd);
*/
} /* end initdfir() */


static void initafir(AFIRTAB atab, uint32_t F0, uint32_t F1,
                float eq)
{
   uint32_t f;
   uint32_t i;
   float t[256];
   float f10;
   float f00;
   float e;
   uint32_t tmp;
   f00 = X2C_DIVR((float)(F0*32UL),(float)adcrate);
   f10 = X2C_DIVR((float)(F1*32UL),(float)adcrate);
   for (i = 0UL; i<=255UL; i++) {
      t[i] = 0.0f;
   } /* end for */
   tmp = (uint32_t)X2C_TRUNCC(f10,0UL,X2C_max_longcard)+1UL;
   f = (uint32_t)X2C_TRUNCC(f00,0UL,X2C_max_longcard);
   if (f<=tmp) for (;; f++) {
      e = 1.0f+eq*((X2C_DIVR((float)f,X2C_DIVR((float)((F0+F1)*32UL),
                (float)adcrate)))*2.0f-1.0f);
      /*
          e:=1.0 + eq*(FLOAT(f)/FLOAT((F0+F1)*AFIRLEN DIV adcrate)*2.0-1.0);
      */
      if (e<0.0f) e = 0.0f;
      if (f==(uint32_t)X2C_TRUNCC(f00,0UL,X2C_max_longcard)) {
         e = e*(1.0f-(f00-(float)(uint32_t)X2C_TRUNCC(f00,0UL,
                X2C_max_longcard)));
      }
      if (f==(uint32_t)X2C_TRUNCC(f10,0UL,X2C_max_longcard)+1UL) {
         e = e*(f10-(float)(uint32_t)X2C_TRUNCC(f10,0UL,
                X2C_max_longcard));
      }
      /*
      IF eq<>0 THEN IO.WrFixed(e,2,2);IO.WrLn; END;
      */
      if (f==0UL) {
         for (i = 0UL; i<=255UL; i++) {
            t[i] = t[i]+e*0.5f;
         } /* end for */
      }
      else {
         for (i = 0UL; i<=255UL; i++) {
            t[i] = t[i]+e*osic_cos(X2C_DIVR(3.1415926535898f*(float)(i*f),
                256.0f));
         } /* end for */
      }
      if (f==tmp) break;
   } /* end for */
   Hamming(t, 256ul);
   for (i = 0UL; i<=255UL; i++) {
      atab[255UL+i] = t[i];
      atab[255UL-i] = t[i];
   } /* end for */
   if (F0>0UL) {
      /* make dc level zero */
      e = 0.0f;
      for (i = 0UL; i<=511UL; i++) {
         e = e+atab[i];
      } /* end for */
      e = X2C_DIVR(e,512.0f);
      for (i = 0UL; i<=511UL; i++) {
         atab[i] = atab[i]-e;
      } /* end for */
   }
/*
IO.WrLn;
FOR i:=0 TO HIGH(atab) DO IO.WrFixed(atab[i], 2,8) END;
IO.WrLn;
*/
/*
IF eq<>0.0 THEN
debfd:=FIO.Create("/tmp/ta.raw");
FOR i:=0 TO HIGH(atab) DO f:=VAL(INTEGER, atab[i]*1000.0);
                FIO.WrBin(debfd,f,2) END;
FIO.Close(debfd);
END;
*/
} /* end initafir() */


static void initTFIR(void)
{
   uint32_t b;
   uint32_t f;
   uint32_t j;
   uint32_t i;
   float sym[384];
   float sum[128];
   float s[774];
   float max0;
   float k;
   float fr;
   for (i = 0UL; i<=383UL; i++) {
      sym[i] = 0.5f;
   } /* end for */
   fr = 2.88f;
   for (f = 1UL; f<=3UL; f++) {
      if (f==(uint32_t)X2C_TRUNCC(fr,0UL,X2C_max_longcard)+1UL) {
         k = fr-(float)(uint32_t)X2C_TRUNCC(fr,0UL,X2C_max_longcard);
      }
      else k = 1.0f;
      for (i = 0UL; i<=383UL; i++) {
         sym[i] = sym[i]+k*osic_cos(X2C_DIVR(3.1415926535898f*(float)(i*f)
                ,384.0f));
      } /* end for */
   } /* end for */
   Hamming(sym, 384ul);
   for (i = 0UL; i<=773UL; i++) {
      s[i] = 0.0f;
   } /* end for */
   max0 = 0.0f;
   for (i = 0UL; i<=383UL; i++) {
      s[383UL+i+3UL] = sym[i];
      s[(383UL-i)+3UL] = sym[i];
      if ((float)fabs(sym[i])>max0) max0 = (float)fabs(sym[i]);
   } /* end for */
   max0 = X2C_DIVR(2.0f,max0);
   for (j = 0UL; j<=63UL; j++) {
      for (i = 0UL; i<=127UL; i++) {
         sum[i] = 0.0f;
      } /* end for */
      for (b = 0UL; b<=5UL; b++) {
         for (i = 0UL; i<=127UL; i++) {
            sum[i] = sum[i]+s[i+b*128UL]*((float)(uint32_t)X2C_IN(b,32,
                (uint32_t)j)-0.5f);
         } /* end for */
      } /* end for */
      for (i = 0UL; i<=127UL; i++) {
         TFIR[i][j] = sum[i]*max0;
      } /* end for */
   } /* end for */
} /* end initTFIR() */


static void SetMixer(char mixfn[], uint32_t mixfn_len,
                uint32_t chan0, uint32_t left, uint32_t right)
{
   int32_t fd;
   X2C_PCOPY((void **)&mixfn,mixfn_len);
   fd = osi_OpenRW(mixfn, mixfn_len);
   if (fd>=0L) {
      if (chan0==255UL) chan0 = recnum();
      setmixer(fd, chan0, (right<<8)+left);
   }
   else {
      osi_WrStr(mixfn, mixfn_len);
      Error(" open", 6ul);
   }
   X2C_PFREE(mixfn);
} /* end SetMixer() */


static void OpenSound(void)
{
   int32_t s;
   int32_t i;
   soundfd = osi_OpenRW(soundfn, 1024ul);
   if (soundfd>=0L) {
      i = samplesize(soundfd, 16UL); /* 8, 16 */
      i = channels(soundfd, (uint32_t)maxchannels+1UL); /* 1, 2  */
      i = setfragment(soundfd, fragmentsize); /* 2^bufsize * 65536*bufs*/
      if (i) {
         osi_WrStr("sound setfragment returns ", 27ul);
         osic_WrINT32((uint32_t)i, 1UL);
         osic_WrLn();
      }
      i = sampelrate(soundfd, adcrate); /* 8000..48000 */
      s = (int32_t)getsampelrate(soundfd);
      if (s!=(int32_t)adcrate) {
         osi_WrStr("sound device returns ", 22ul);
         osic_WrINT32((uint32_t)s, 1UL);
         osi_WrStrLn("Hz!", 4ul);
      }
   }
   else if (abortonsounderr) {
      /*
            IF s>=0 THEN Error("") END;
      */
      osi_WrStr(soundfn, 1024ul);
      Error(" open", 6ul);
   }
} /* end OpenSound() */


static void ttypar(char fn[], uint32_t fn_len)
{
   struct termios term;
   int32_t fd;
   int32_t res;
   X2C_PCOPY((void **)&fn,fn_len);
   fd = osi_OpenNONBLOCK(fn, fn_len);
   if (fd>=0L) {
      res = tcgetattr(fd, &term);
      /*
          term.c_lflag:=CAST(CARDINAL, CAST(BITSET,
                term.c_lflag) - CAST(BITSET, ECHO));
      */
      term.c_lflag = 0UL;
      res = tcsetattr(fd, 0L, &term);
   }
   /*
       res:=tcsetattr (fd, 0, term);
   */
   osic_Close(fd);
   X2C_PFREE(fn);
} /* end ttypar() */


static int32_t Opentty(char linkname[], uint32_t linkname_len)
{
   int32_t fd;
   char voidok;
   char ptsname[4096];
   int32_t Opentty_ret;
   X2C_PCOPY((void **)&linkname,linkname_len);
   fd = osi_OpenNONBLOCK("/dev/ptmx", 10ul);
   if (fd<0L) Error("/dev/ptmx open", 15ul);
   if (osi_getptsname(fd, (char *)ptsname, 4096UL)) {
      Error("no ttyname", 11ul);
   }
   /*
     IO.WrStr(ptsname); IO.WrLn;
   */
   if (osic_grantpts(fd)) Error("ptty grant", 11ul);
   if (osic_unlockpts(fd)) Error("ptty unlock", 12ul);
   ttypar(ptsname, 4096ul);
   /*make link*/
   osi_Erase(linkname, linkname_len, &voidok);
   if (osi_symblink((char *)ptsname, (char *)linkname)) {
      osi_WrStr("cannot create link <", 21ul);
      osi_WrStr(linkname, linkname_len);
      osi_WrStrLn(">, starting without kiss interface", 35ul);
      osic_Close(fd);
      fd = -1L;
   }
   Opentty_ret = fd;
   X2C_PFREE(linkname);
   return Opentty_ret;
} /* end Opentty() */


static void Makekissbufs(uint32_t n)
{
   pKISSNEXT pt;
   uint32_t i;
   pTxFree = 0;
   pGetKiss = 0;
   i = 0UL;
   for (;;) {
      if (i>=n) break;
      osic_alloc((char * *) &pt, sizeof(struct KISSBUF));
      ++i;
      if (pt==0) break;
      pt->next = pTxFree;
      pTxFree = pt;
   }
} /* end Makekissbufs() */


static void StoBuf(int32_t m, pKISSNEXT p)
{
   struct MPAR * anonym;
   { /* with */
      struct MPAR * anonym = &modpar[m];
      if (anonym->configured && pTxFree) {
         p->next = 0;
         if (anonym->txbufin==0) anonym->txbufin = p;
         else anonym->txbufout->next = p;
         anonym->txbufout = p;
      }
      else {
         p->next = pTxFree; /* discard frame, out of memory */
         pTxFree = p;
      }
   }
} /* end StoBuf() */


static void ExtractWord(char w[], uint32_t w_len, char s[],
                uint32_t s_len)
{
   uint32_t j;
   uint32_t i;
   w[0UL] = 0;
   i = 0UL;
   while ((i<=s_len-1 && s[i]) && s[i]!=':') {
      if (i<=w_len-1) w[i] = s[i];
      ++i;
   }
   if (i<=w_len-1) w[i] = 0;
   j = 0UL;
   if (i<=s_len-1 && s[i]) {
      ++i;
      while (i<=s_len-1 && s[i]) {
         s[j] = s[i];
         ++i;
         ++j;
      }
   }
   s[j] = 0;
} /* end ExtractWord() */


static void Config(void)
{
   int32_t i;
   uint8_t c;
   struct CHAN * anonym;
   struct MPAR * anonym0;
   for (c = afskmodem_LEFT;; c++) {
      { /* with */
         struct CHAN * anonym = &chan[c];
         anonym->persist = (float)
                ((anonym->configpersist*adcrate*((uint32_t)maxchannels+1UL)
                )/adcbuflen)*0.001f; /* ms */
      }
      if (c==afskmodem_RIGHT) break;
   } /* end for */
   for (i = 0L; i<=7L; i++) {
      { /* with */
         struct MPAR * anonym0 = &modpar[i];
         anonym0->txbaud = (anonym0->configbaud*65536UL)/adcrate;
         anonym0->demodbaud = anonym0->txbaud*2UL;
         anonym0->txdel = (anonym0->configbaud*anonym0->configtxdel)/8000UL;
         anonym0->txtail = 2UL+2UL*(uint32_t)
                anonym0->scramb+(anonym0->configbaud*anonym0->configtxtail)
                /8000UL;
         anonym0->afskmidfreq = X2C_DIVR((float)
                anonym0->configafskmid*2.0f,(float)adcrate);
         anonym0->afshift = X2C_DIVR(0.5f*(float)
                (anonym0->configafskshift*32768UL),(float)adcrate);
         anonym0->afmid = X2C_DIVR((float)(anonym0->configafskmid*32768UL)
                ,(float)adcrate);
         if (anonym0->afsk) {
            initafir(anonym0->afirtab,
                (anonym0->configafskmid-anonym0->configafskshift/2UL)
                -anonym0->configbaud/4UL,
                anonym0->configafskmid+anonym0->configafskshift/2UL+anonym0->configbaud/4UL,
                 X2C_DIVR((float)anonym0->configequalizer,100.0f));
            initdfir(anonym0->dfirtab,
                (anonym0->configbaud*anonym0->confignyquist)/100UL);
         }
         else {
            initafir(anonym0->afirtab, 0UL,
                (anonym0->configbaud*anonym0->confignyquist)/100UL,
                X2C_DIVR((float)anonym0->configequalizer,100.0f));
         }
         anonym0->baudfine = 0L;
         anonym0->left = 0.0f;
         anonym0->tcnt = 0.0f;
         anonym0->freq = 0.0f;
         anonym0->dfin = 0UL;
         anonym0->cbit = 0;
         anonym0->rxp = 0UL;
         anonym0->rxbitc = 0UL;
         anonym0->rxcrc = 0xFFFFUL;
      }
   } /* end for */
} /* end Config() */

/*
PROCEDURE GetIp(h:ARRAY OF CHAR; VAR ip:IPNUM; VAR dp, lp:UDPPORT;
                VAR fd:INTEGER; VAR check:BOOLEAN):INTEGER;
VAR i, p, n:CARDINAL;
    ok:BOOLEAN;
BEGIN
  p:=0;
  h[HIGH(h)]:=0C;
  ip:=0;
  n:=0;
  FOR i:=0 TO 5 DO
    n:=0;
    ok:=FALSE;
    WHILE (h[p]>="0") & (h[p]<="9") DO
      ok:=TRUE;
      n:=n*10+ORD(h[p])-ORD("0");
      INC(p);
    END;
    IF NOT ok THEN RETURN -1 END;
    IF i<3 THEN
      IF (h[p]<>".") OR (n>255) THEN RETURN -1 END;
      ip:=ip*256+n;
    ELSIF i=3 THEN
      ip:=ip*256+n;
      IF (h[p]<>":")OR (n>255) THEN RETURN -1 END;
    ELSIF i=4 THEN
      check:=h[p]<>"/";
      IF (h[p]<>":") & (h[p]<>"/") OR (n>65535) THEN RETURN -1 END;
      dp:=n;
    ELSIF n>65535 THEN RETURN -1 END;
    lp:=n;
    INC(p);
  END;  

  fd:=udp.openudp();
  IF (fd<0) OR (udp.bindudp(fd, lp)<0) (*OR (udp.udpnonblock(fd)<0)*) THEN RETURN -1 END;
  RETURN 0

END GetIp;
*/

static int32_t GetIp(char h[], uint32_t h_len, uint32_t * ip,
                uint32_t * dp, uint32_t * lp, int32_t * fd,
                char * check)
{
   int32_t GetIp_ret;
   X2C_PCOPY((void **)&h,h_len);
   if (aprsstr_GetIp2(h, h_len, ip, dp, lp, check)<0L) {
      GetIp_ret = -1L;
      goto label;
   }
   *fd = openudp();
   if (*fd<0L || bindudp(*fd, *lp)<0L) {
      /*OR (udp.udpnonblock(fd)<0)*/
      GetIp_ret = -1L;
      goto label;
   }
   GetIp_ret = 0L;
   label:;
   X2C_PFREE(h);
   return GetIp_ret;
} /* end GetIp() */


static void bertstart(void)
{
   pKISSNEXT p;
   uint32_t i;
   struct MPAR * anonym;
   for (i = 0UL; i<=7UL; i++) {
      { /* with */
         struct MPAR * anonym = &modpar[i];
         if (anonym->bert>0L && pTxFree) {
            p = pTxFree;
            pTxFree = pTxFree->next;
            p->port = (char)i;
            p->len = 0UL;
            p->time0 = X2C_max_longcard;
            StoBuf((int32_t)i, p);
         }
      }
   } /* end for */
} /* end bertstart() */


static void Parms(void)
{
   char err;
   FILENAME pipefn;
   FILENAME mixfn;
   FILENAME h1;
   FILENAME h;
   char ptth[4096];
   uint32_t right;
   uint32_t left;
   uint32_t cnum;
   uint32_t kissbufs;
   int32_t inum;
   int32_t modem;
   int32_t channel;
   uint8_t c;
   struct CHAN * anonym;
   struct MPAR * anonym0;
   struct MPAR * anonym1;
   err = 0;
   abortonsounderr = 0;
   adcrate = 16000UL;
   adcbytes = 2UL;
   adcbuflen = 256UL;
   fragmentsize = 9UL;
   maxchannels = afskmodem_LEFT;
   extraaudiodelay = 1UL;
   debfd = -1L;
   deb01 = 0;
   debp = 0UL;
   badsounddriver = 0;
   for (cnum = 0UL; cnum<=32767UL; cnum++) {
      SIN[cnum] = osic_cos(X2C_DIVR((float)cnum*2.0f*3.1415926535898f,
                32768.0f));
   } /* end for */
   for (c = afskmodem_LEFT;; c++) {
      { /* with */
         struct CHAN * anonym = &chan[c];
         anonym->state = afskmodem_receiv;
         anonym->pttsoundbufs = 0UL;
         anonym->dds = 0UL;
         anonym->duplex = afskmodem_simplex;
         anonym->persist = 800.0f;
         anonym->actmodem = -1L;
         anonym->hptt = 0;
         anonym->configpersist = 800UL;
         anonym->tscramb = 0x1UL; /* for bert */
         anonym->gmqtime = 0UL;
      }
      if (c==afskmodem_RIGHT) break;
   } /* end for */
   for (cnum = 0UL; cnum<=7UL; cnum++) {
      { /* with */
         struct MPAR * anonym0 = &modpar[cnum];
         anonym0->configured = cnum==0UL;
         anonym0->configbaud = 1200UL;
         anonym0->configtxdel = 300UL;
         anonym0->configtxtail = 20UL;
         anonym0->configafskshift = 1000UL;
         anonym0->configafskmid = 1700UL;
         anonym0->confignyquist = 75UL;
         anonym0->configequalizer = 0L;
         anonym0->kissignore = 0;
         anonym0->leveldcd = 10L;
         anonym0->pllshift = 4096L;
         anonym0->ch = afskmodem_LEFT;
         anonym0->afsk = 1;
         anonym0->monitor = afskmodem_normal;
         anonym0->scramb = 0;
         anonym0->clamp = 0.996f;
         anonym0->port16 = cnum*16UL;
         anonym0->squelchdcd = 0.14f;
         anonym0->txbufin = 0;
         anonym0->bert = 0L;
         anonym0->txdelpattern = 126UL;
         anonym0->txvolum = 25000.0f;
         anonym0->afskhighpass = 0.0f;
         anonym0->timeout = 60UL;
         anonym0->udpsocket = -1L;
      }
   } /* end for */
   kissbufs = 60UL;
   channel = -1L;
   modem = -1L;
   X2C_COPY("/dev/dsp",9ul,soundfn,1024u);
   pipefn[0U] = 0;
   X2C_COPY("/dev/mixer",11ul,mixfn,1024u);
   maxsoundbufs = 10UL;
   for (;;) {
      osi_NextArg(h, 1024ul);
      if (h[0U]==0) break;
      if ((h[0U]=='-' && h[1U]) && h[2U]==0) {
         if (h[1U]=='a') {
            if (modem>=0L) modpar[modem].afsk = 0;
            else if (channel<=0L) abortonsounderr = 1;
            else Error("need modem number -M before -a", 31ul);
         }
         else if (h[1U]=='b') {
            osi_NextArg(h, 1024ul);
            if (modem>=0L) {
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               if (cnum>=adcrate) {
                  Error("sampelrate -f too low for this baud", 36ul);
               }
               modpar[modem].configbaud = cnum;
            }
            else {
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               maxsoundbufs = cnum;
            }
         }
         else if (h[1U]=='B') {
            if (modem<0L && channel<0L) {
               badsounddriver = 1;
            }
            else {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToInt(h, 1024ul, &inum)) err = 1;
               if (modem>=0L) modpar[modem].bert = inum*8000L;
               else Error("need modem number -M before -B", 31ul);
            }
         }
         else if (h[1U]=='C') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (cnum>1UL) Error("channel 0 to 1", 15ul);
            channel = (int32_t)cnum;
         }
         else if (h[1U]=='c') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) {
               if (cnum>(uint32_t)maxchannels) {
                  Error("channel too high", 17ul);
               }
               modpar[modem].ch = (uint8_t)cnum;
            }
            else {
               if (cnum<1UL || cnum>2UL) Error("maxchannels 1..2", 17ul);
               maxchannels = (uint8_t)(cnum-1UL);
            }
         }
         else if (h[1U]=='D') {
            osi_NextArg(h1, 1024ul);
            debfd = creat(h1, 420L);
            inum = 0L;
            while (inum<1023L && h1[inum]) ++inum;
            deb01 = (((inum>=4L && h1[inum-4L]=='.') && h1[inum-3L]=='t')
                && h1[inum-2L]=='x') && h1[inum-1L]=='t';
         }
         else if (h[1U]=='d') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) {
               modpar[modem].squelchdcd = X2C_DIVR((float)cnum,400.0f);
            }
            else Error("need modem number -M before -d", 31ul);
         }
         else if (h[1U]=='e') {
            osi_NextArg(h, 1024ul);
            if (modem>=0L) {
               if (!aprsstr_StrToInt(h, 1024ul, &inum)) err = 1;
               if (labs(inum)>999L) Error("equalizer -999..999", 20ul);
               modpar[modem].configequalizer = inum;
            }
            else {
               if (channel>=0L) {
                  Error("need modem number -M before -e", 31ul);
               }
               if (!aprsstr_StrToCard(h, 1024ul, &extraaudiodelay)) err = 1;
            }
         }
         else if (h[1U]=='f') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) modpar[modem].configafskmid = cnum;
            else if (channel>=0L) {
               if (cnum>3UL) Error("duplex 0..3", 12ul);
               chan[channel].duplex = (uint8_t)cnum;
            }
            else {
               if (cnum<8000UL || cnum>96000UL) {
                  Error("sampelrate 8000..96000", 23ul);
               }
               adcrate = cnum;
            }
         }
         else if (h[1U]=='g') {
            if (modem>=0L) modpar[modem].scramb = 1;
            else if (channel>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum) || cnum>999UL) {
                  Error("-g <ms>", 8ul);
               }
               chan[channel].gmqtime = (cnum*adcrate)/1000UL;
            }
            else Error("need modem number -M before -g", 31ul);
         }
         else if (h[1U]=='H') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) modpar[modem].afskhighpass = (float)cnum*0.01f;
            else Error("need modem number -M before -H", 31ul);
         }
         else if (h[1U]=='i') {
            if (modem>=0L) modpar[modem].kissignore = 1;
            else osi_NextArg(pipefn, 1024ul);
         }
         else if (h[1U]=='k') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) {
               if (cnum>7UL) Error("kissports 0..7", 15ul);
               modpar[modem].port16 = cnum*16UL;
            }
            else kissbufs = cnum;
         }
         else if (h[1U]=='l') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (cnum>=16UL && cnum<=4096UL) adcbuflen = cnum;
            else Error("sound buffer out of range", 26ul);
         }
         else if (h[1U]=='M') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (cnum>7UL) Error(">maxmodems", 11ul);
            modem = (int32_t)cnum;
            modpar[modem].configured = 1;
         }
         else if (h[1U]=='m') {
            osi_NextArg(h, 1024ul);
            if (modem<0L && channel<0L) {
               while (h[0U]) {
                  ExtractWord(h1, 1024ul, h, 1024ul);
                  if (h1[0U]=='/') X2C_COPY(h1,1024u,mixfn,1024u);
                  else {
                     if (!aprsstr_StrToCard(h1, 1024ul, &cnum)) err = 1;
                     ExtractWord(h1, 1024ul, h, 1024ul);
                     if (!aprsstr_StrToCard(h1, 1024ul, &left)) err = 1;
                     ExtractWord(h1, 1024ul, h, 1024ul);
                     if (!aprsstr_StrToCard(h1, 1024ul, &right)) err = 1;
                     if (!err) SetMixer(mixfn, 1024ul, cnum, left, right);
                  }
               }
            }
            else {
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               if (cnum>3UL) Error("monitor 0..3", 13ul);
               if (modem>=0L) modpar[modem].monitor = (uint8_t)cnum;
               else Error("need modem number -M before -m", 31ul);
            }
         }
         else if (h[1U]=='n') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) modpar[modem].confignyquist = cnum;
            else Error("need modem number -M before -n", 31ul);
         }
         else if (h[1U]=='o') osi_NextArg(soundfn, 1024ul);
         else if (h[1U]=='p') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               modpar[modem].pllshift = (int32_t)(cnum*256UL);
            }
            else if (channel>=0L) {
               osi_NextArg(h1, 1024ul);
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToInt(h, 1024ul, &inum)) err = 1;
               inum = labs(inum)+1L;
               if (h[0U]=='-') inum = -inum;
               chan[channel].hptt = pttinit((char *)h1, inum);
            }
         }
         else if (h[1U]=='q') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               modpar[modem].leveldcd = (int32_t)cnum;
            }
            else Error("need modem number -M before -q", 31ul);
         }
         else if (h[1U]=='r') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (channel>=0L) {
               if (!aprsstr_StrToCard(h, 1024ul,
                &chan[channel].configpersist)) err = 1;
            }
            else Error("need channel number -C before -r", 33ul);
         }
         else if (h[1U]=='s') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) modpar[modem].configafskshift = cnum;
            else fragmentsize = cnum;
         }
         else if (h[1U]=='t') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               modpar[modem].configtxdel = cnum;
            }
         }
         else if (h[1U]=='u') {
            if (channel>=0L) pttSetclaim(chan[channel].hptt, 1L);
         }
         else if ((h[1U]=='U' || h[1U]=='L') || h[1U]=='P') {
            if (modem>=0L) {
               modpar[modem].axudp2 = h[1U]!='U'; /* switch on axudp2 */
               modpar[modem].dcdmsgs = h[1U]=='P';
                /* send dcd change & txbuffer empty messages*/
               osi_NextArg(h, 1024ul);
               { /* with */
                  struct MPAR * anonym1 = &modpar[modem];
                  if (GetIp(h, 1024ul, &anonym1->udpip, &anonym1->udpport,
                &anonym1->udpbind, &anonym1->udpsocket,
                &anonym1->checkip)<0L) Error("cannot open udp socket", 23ul);
               }
            }
            else Error("need modem number -M before -U", 31ul);
         }
         else if (h[1U]=='v') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               if (cnum>100UL) cnum = 100UL;
               modpar[modem].txvolum = (float)cnum*250.0f;
            }
            else Error("need modem number -M before -w", 31ul);
         }
         else if (h[1U]=='T') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               modpar[modem].timeout = cnum;
            }
            else Error("need modem number -M before -T", 31ul);
         }
         else if (h[1U]=='w') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               modpar[modem].txdelpattern = cnum;
            }
            else Error("need modem number -M before -w", 31ul);
         }
         else if (h[1U]=='x') {
            if (modem>=0L) {
               osi_NextArg(h, 1024ul);
               if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
               modpar[modem].configtxtail = cnum;
            }
            else Error("need modem number -M before -x", 31ul);
         }
         else if (h[1U]=='z') {
            osi_NextArg(h, 1024ul);
            if (!aprsstr_StrToCard(h, 1024ul, &cnum)) err = 1;
            if (modem>=0L) {
               modpar[modem].clamp = X2C_DIVR((float)cnum,1000.0f);
            }
            else Error("need modem number -M before -z", 31ul);
         }
         else {
            if (h[1U]=='h') {
               osi_WrStrLn("first for all modems", 21ul);
               osi_WrStrLn(" -a             abort on sounddevice error else r\
etry to open", 62ul);
               osi_WrStrLn(" -b <num>       tx dacbuffers (10) (more to avoid\
 underruns)", 61ul);
               osi_WrStrLn(" -B             bad sound driver repair, sending \
continuous quietness to avoid receive", 87ul);
               osi_WrStrLn("                sample loss on start/stop of soun\
d output. Use for stereo or fullduplex)", 89ul);
               osi_WrStrLn(" -c <num>       maxchannels (1) (1=mono, 2=stereo\
)", 51ul);
               osi_WrStrLn(" -D <filename>  (debug) write raw soundcard input\
 data to file or pipe", 71ul);
               osi_WrStrLn(" -D <filename>.txt  (debug) write demodulated bit\
s as \"01..\" to file or pipe", 77ul);
               osi_WrStrLn(" -e <num>       additional ptt hold time (if soun\
dsystem has delay) unit=adcbuffers (1)", 88ul);
               osi_WrStrLn(" -f <num>       adcrate (16000) (8000..96000)",
                46ul);
               osi_WrStrLn(" -h             help", 21ul);
               osi_WrStrLn(" -i <filename>  kiss pipename (/dev/kiss/soundmod\
em)", 53ul);
               osi_WrStrLn(" -k <num>       tx kiss bufs (60)", 34ul);
               osi_WrStrLn(" -l <num>       adcbuffer length (256) more: lowe\
r system load but slower reaction", 83ul);
               osi_WrStrLn(" -m [<mixername>:]<channel>:<left>:<right> (0..25\
5) ossmixer (/dev/mixer)", 74ul);
               osi_WrStrLn(" -o <filename>  oss devicename (/dev/dsp)",
                42ul);
               osi_WrStrLn(" -s <num>       fragment size in 2^n (9)", 41ul);
               osic_WrLn();
               osi_WrStrLn("repeat for each channel -C ... :", 33ul);
               osi_WrStrLn("  -C <num>              (0..1) channel parameters\
 follow (repeat for each channel)", 83ul);
               pttHelp((char *)ptth, 4096UL);
               osi_WrStrLn(ptth, 4096ul);
               osi_WrStrLn("  -f <num>              (0) (0=halfduplex, 1=mast\
er fullduplex, 2=all fullduplex,", 82ul);
               osi_WrStrLn("                        3=simplex \'stereo never \
both tx same time\')", 68ul);
               osi_WrStrLn("  -g <ms>               GM900 audio quiet time af\
ter ptt on (0)", 64ul);
               osi_WrStrLn("  -r <num>              max random wait time afte\
r dcd before start tx (ms) (800)", 82ul);
               osic_WrLn();
               osi_WrStrLn("repeat for each modem -M ... :", 31ul);
               osi_WrStrLn("   -M <num> (0..7) modem parameters follow (repea\
t for each modem)", 67ul);
               osi_WrStrLn("   -a       afsk off, fsk on (on)", 34ul);
               osi_WrStrLn("   -b <num> baud (1200) (1..32000)", 35ul);
               osi_WrStrLn("   -B <kbyte> send BERT, (negative bytes receive \
only)", 55ul);
               osi_WrStrLn("   -c <num> use stereo channel 0=left (or mono), \
1=right", 57ul);
               osi_WrStrLn("   -d <num> dcdlevel, 0 no dcd (56) (0..100)",
                45ul);
               osi_WrStrLn("   -e <num> demod equalizer (0) 100=6db/oct highp\
ass   (-999..999)", 67ul);
               osi_WrStrLn("   -f <num> afsk mid frequency, tx and rx (hz) (1\
700)", 54ul);
               osi_WrStrLn("   -g       g3ruh scrambler on (off)", 37ul);
               osi_WrStrLn("   -H <num> afsk tx highpass (0) (0..100)",
                42ul);
               osi_WrStrLn("   -i       ignore modem parameters from kiss (of\
f)", 52ul);
               osi_WrStrLn("   -k <num> received data send to this kiss port \
(0) (0..7)", 60ul);
               /*
                       WrStrLn("#  -l <num> filterlow (hz)");
               */
               osi_WrStrLn("   -m <num> monitor (2) (0=off, 1=header, 2=all, \
3=passall)", 60ul);
               osi_WrStrLn("   -n <num> nyquist filter baseband -6db point in\
 % of baudrate (65) (0..100)", 78ul);
               osi_WrStrLn("   -P same as -U but use AXUDP v2, send dcd and t\
xbuffer empty messages", 72ul);
               osi_WrStrLn("   -p <num> receive clock pll aquisition speed (1\
6) (num/256 of bit time)", 74ul);
               osi_WrStrLn("   -q <num> quiet adc level to save cpu or avoid \
reciption of channel crosstalk (0)", 84ul);
               /*
                       WrStrLn("#  -r       rzi -1 1");
               */
               osi_WrStrLn("   -s <num> afsk shift tx (for rx bandfilter widt\
h) (1000) (hz)", 64ul);
               osi_WrStrLn("   -t <num> txdelay (300) (ms)", 31ul);
               osi_WrStrLn("   -T <seconds> timeout for tx buffered frame (60\
) (s)", 55ul);
               /*
                       WrStrLn("#  -u       filterup  (hz)");
               */
               osi_WrStrLn("   -U <[x.x.x.x]:destport:listenport> use axudp i\
nstead of kiss /listenport check ip", 85ul);
               osi_WrStrLn("   -L same as -U but use AXUDP v2 (no dcd and txb\
uffer empty messages)", 71ul);
               osi_WrStrLn("   -v <num> tx loudness (100)", 30ul);
               osi_WrStrLn("   -w <num> txdelay pattern before 1 flag (126) (\
0..255)", 57ul);
               osi_WrStrLn("   -x <num> txtail (20) (ms), ptt hold for fulldu\
plex", 54ul);
               osi_WrStrLn("   -z <num> fsk rx baseband dc regeneration clamp\
 speed (996) (0=off, 1=fast, 999=slow)", 88ul);
               osi_WrStrLn("example: ./afskmodem -f 22050 -c 2 -C 0 -p /dev/t\
tyS0 0 -M 0 -c 0 -b 1200 -M 1 -c 1 -b 9600 -a -g -U 127.0.0.1:6001:1093",
                121ul);
               X2C_ABORT();
            }
            err = 1;
         }
      }
      else {
         /*
               h[0]:=0C;
         */
         err = 1;
      }
      if (err) break;
   }
   if (err) {
      osi_WrStr(">", 2ul);
      osi_WrStr(h, 1024ul);
      osi_WrStrLn("< use -h", 9ul);
      X2C_ABORT();
   }
   Config();
   OpenSound();
   pipefd = -1L;
   if (pipefn[0U]) pipefd = Opentty(pipefn, 1024ul);
   Makekissbufs(kissbufs);
   bertstart();
} /* end Parms() */


static void AppCRC(char frame[], uint32_t frame_len, int32_t size)
{
   uint8_t h;
   uint8_t l;
   uint8_t b;
   int32_t i;
   int32_t tmp;
   l = 0U;
   h = 0U;
   tmp = size-1L;
   i = 0L;
   if (i<=tmp) for (;; i++) {
      b = (uint8_t)((uint8_t)(uint8_t)frame[i]^l);
      l = CRCL[b]^h;
      h = CRCH[b];
      if (i==tmp) break;
   } /* end for */
   frame[size] = (char)l;
   frame[size+1L] = (char)h;
} /* end AppCRC() */


static void Kisscmd(void)
{
   uint32_t x;
   uint32_t cmd;
   int32_t modem;
   struct MPAR * anonym;
   cmd = (uint32_t)(uint8_t)pGetKiss->port;
   modem = (int32_t)(cmd>>4&7UL);
   cmd = cmd&15UL;
   osi_WrStr("p=", 3ul);
   osic_WrINT32((uint32_t)modem, 1UL);
   osic_WrLn();
   { /* with */
      struct MPAR * anonym = &modpar[modem];
      if (anonym->configured && !anonym->kissignore) {
         x = (uint32_t)(uint8_t)pGetKiss->data[0U];
         if (cmd==1UL) {
            anonym->configtxdel = x*10UL;
            osi_WrStr("txdel=", 7ul);
            osic_WrINT32(anonym->configtxdel, 1UL);
            osic_WrLn();
         }
         else if (cmd==2UL) {
            chan[modpar[modem].ch].configpersist = 10UL*(255UL-x);
            osi_WrStr("persist=", 9ul);
            osic_WrINT32(chan[modpar[modem].ch].configpersist, 1UL);
            osic_WrLn();
         }
         else if (cmd==4UL) {
            /*3 SlotTime*/
            anonym->configtxtail = x*10UL;
            osi_WrStr("txtail=", 8ul);
            osic_WrINT32(anonym->configtxtail, 1UL);
            osic_WrLn();
         }
         else if (cmd==5UL) {
            if (x<=2UL) chan[modpar[modem].ch].duplex = (uint8_t)x;
            osi_WrStr("duplex=", 8ul);
            osic_WrINT32(x, 1UL);
            osic_WrLn();
         }
         Config();
      }
   }
} /* end Kisscmd() */


static void getkiss(void)
{
   char b[1024];
   int32_t i;
   int32_t l;
   int32_t m;
   int32_t tmp;
   for (;;) {
      if (pipefd<0L) break;
      l = read(pipefd, (char *)b, 1024UL);
      if (l<=0L) break;
      tmp = l-1L;
      i = 0L;
      if (i<=tmp) for (;; i++) {
         /*allocate buffer*/
         if (pGetKiss==0) {
            if (pTxFree==0) goto loop_exit;
            pGetKiss = pTxFree;
            pTxFree = pTxFree->next;
         }
         /*allocate buffer*/
         if (b[i]=='\300') {
            esc = 0;
            if (getst>2UL) {
               pGetKiss->len = getst-2UL;
               getst = 0UL;
               /*store frame*/
               m = (int32_t)((uint32_t)(uint8_t)pGetKiss->port>>4&7UL);
               if (m<=7L) {
                  if (((uint32_t)(uint8_t)pGetKiss->port&15UL)==0UL) {
                     /* data frame */
                     pGetKiss->port = (char)m;
                     AppCRC(pGetKiss->data, 341ul, (int32_t)pGetKiss->len);
                     pGetKiss->time0 = systime+modpar[m].timeout;
                     StoBuf(m, pGetKiss);
                     pGetKiss = 0;
                  }
                  else Kisscmd();
               }
            }
            else {
               /*store frame*/
               getst = 1UL;
            }
         }
         else if (b[i]=='\333' && getst>0UL) esc = 1;
         else {
            if (esc) {
               if (b[i]=='\335') b[i] = '\333';
               else if (b[i]=='\334') b[i] = '\300';
               esc = 0;
            }
            if (getst==1UL) {
               pGetKiss->port = b[i];
               getst = 2UL;
            }
            else if (getst>=2UL && getst-2UL<339UL) {
               pGetKiss->data[getst-2UL] = b[i];
               ++getst;
            }
         }
         if (i==tmp) break;
      } /* end for */
   }
   loop_exit:;
} /* end getkiss() */


static float dB(uint32_t v)
{
   if (v>0UL) return osic_ln((float)v)*8.685889638f-96.5f;
   else return 0.0f;
   return 0;
} /* end dB() */


static float noiselevel(uint32_t m)
/* 0.0 perfect, ~0.25 noise only*/
{
   struct MPAR * anonym;
   { /* with */
      struct MPAR * anonym = &modpar[m];
      if (anonym->sqmed[1]==anonym->sqmed[0]) return 0.0f;
      else return X2C_DIVR(anonym->noise,anonym->sqmed[1]-anonym->sqmed[0]);
   }
   return 0;
} /* end noiselevel() */


static void app(uint32_t * i, uint32_t * p, char b[501], char c,
                int32_t v)
{
   char s[51];
   b[*p] = c;
   ++*p;
   aprsstr_IntToStr(v, 0UL, s, 51ul);
   *i = 0UL;
   while (s[*i]) {
      b[*p] = s[*i];
      ++*p;
      ++*i;
   }
   b[*p] = ' ';
   ++*p;
} /* end app() */


static void sendaxudp2(uint32_t modem, uint32_t datalen,
                char parms, char data[], uint32_t data_len)
{
   char b[501];
   int32_t ret;
   uint32_t ff;
   uint32_t i;
   uint32_t p;
   float q;
   struct MPAR * anonym;
   X2C_PCOPY((void **)&data,data_len);
   { /* with */
      struct MPAR * anonym = &modpar[modem];
      if (anonym->udpsocket>=0L) {
         b[0U] = '\001';
         b[1U] = (char)(48UL+(uint32_t)
                anonym->haddcdrand*2UL+(uint32_t)
                anonym->hadtxdata*4UL+(uint32_t)chan[anonym->ch].pttstate);
         p = 2UL;
         if (parms) {
            app(&i, &p, b, 'D', (int32_t)(uint32_t)(chan[anonym->ch].duplex==afskmodem_fullduplex));
            app(&i, &p, b, 'B', (int32_t)anonym->configbaud);
            app(&i, &p, b, 't', (int32_t)anonym->configtxdel);
         }
         if (datalen>0UL) {
            /* with data */
            ff = (anonym->flags*1000UL)/anonym->configbaud;
            if (ff>0UL) app(&i, &p, b, 'T', (int32_t)ff);
            app(&i, &p, b, 'V', (int32_t)X2C_TRUNCI(dB((uint32_t)chan[anonym->ch].adcmax),
                X2C_min_longint,X2C_max_longint)); /* volume in dB */
            q = noiselevel(modem);
            if (q>0.0f) {
               q = 100.5f-q*200.0f;
               if (q<1.0f) q = 1.0f;
               app(&i, &p, b, 'Q', (int32_t)X2C_TRUNCI(q,X2C_min_longint,
                X2C_max_longint)); /* quality in % */
            }
            b[p] = 0; /* end of axudp2 header */
            ++p;
            i = 0UL;
            do {
               b[p] = data[i];
               ++p;
               ++i;
            } while (i<datalen);
         }
         else {
            b[p] = 0;
            ++p;
         }
         AppCRC(b, 501ul, (int32_t)p);
         ret = udpsend(anonym->udpsocket, b, (int32_t)(p+2UL),
                anonym->udpport, anonym->udpip);
      }
   }
   X2C_PFREE(data);
} /* end sendaxudp2() */


static void getudp(void)
{
   pKISSNEXT p;
   uint32_t i;
   int32_t ulen;
   uint32_t fromport;
   uint32_t fromip;
   char crc2;
   char crc1;
   char udp2[100];
   struct MPAR * anonym;
   for (i = 0UL; i<=7UL; i++) {
      { /* with */
         struct MPAR * anonym = &modpar[i];
         if (anonym->udpsocket>=0L && pTxFree) {
            ulen = udpreceive(anonym->udpsocket, pTxFree->data, 341L,
                &fromport, &fromip);
            if ((ulen>2L && ulen<341L)
                && (!anonym->checkip || fromip==anonym->udpip)) {
               crc1 = pTxFree->data[ulen-2L];
               crc2 = pTxFree->data[ulen-1L];
               AppCRC(pTxFree->data, 341ul, ulen-2L);
               if (crc1==pTxFree->data[ulen-2L]
                && crc2==pTxFree->data[ulen-1L]) {
                  if (pTxFree->data[0U]=='\001') {
                     aprsstr_extrudp2(pTxFree->data, 341ul, udp2, 100ul,
                &ulen);
                     AppCRC(pTxFree->data, 341ul, ulen-2L);
                  }
                  /* axudp2 */
                  if (ulen>2L) {
                     p = pTxFree;
                     pTxFree = pTxFree->next;
                     p->port = (char)i;
                     p->len = (uint32_t)(ulen-2L);
                     p->time0 = systime+anonym->timeout;
                     StoBuf((int32_t)i, p);
                  }
                  else if (udp2[1U]=='?' && udp2[2U]==0) {
                     sendaxudp2(i, 0UL, 1, udp2, 100ul);
                /* on axudp2 header only send dcd & txbuf status */
                  }
               }
            }
         }
      }
   } /* end for */
/* else crc error */
/* else wrong len or source ip */
} /* end getudp() */


static void sendkiss(char data[], uint32_t data_len, int32_t len,
                uint32_t port16)
{
   char b[683];
   int32_t i;
   int32_t l;
   char d;
   uint32_t po;
   struct MPAR * anonym;
   int32_t tmp;
   /*
   FOR i:=0 TO len-1 DO IO.WrHex(ORD(data[i]),3) END; IO.WrLn; 
   */
   po = port16>>4&7UL;
   if (po<=7UL && modpar[po].udpsocket>=0L) {
      { /* with */
         struct MPAR * anonym = &modpar[po];
         if (anonym->axudp2) {
            sendaxudp2(po, (uint32_t)len, 0, data, data_len);
                /* makes new crc */
         }
         else {
            i = udpsend(anonym->udpsocket, data, len+2L, anonym->udpport,
                anonym->udpip);
         }
      }
   }
   else if (pipefd>=0L) {
      b[0U] = '\300';
      b[1U] = (char)port16;
      l = 2L;
      tmp = len-1L;
      i = 0L;
      if (i<=tmp) for (;; i++) {
         d = data[i];
         if (d=='\300') {
            b[l] = '\333';
            ++l;
            b[l] = '\334';
         }
         else if (d=='\333') {
            b[l] = '\333';
            ++l;
            b[l] = '\335';
         }
         else b[l] = d;
         ++l;
         if (i==tmp) break;
      } /* end for */
      b[l] = '\300';
      ++l;
      i = write(pipefd, (char *)b, (uint32_t)l);
   }
} /* end sendkiss() */


static void WrQuali(float q)
{
   if (q>0.0f) {
      q = 100.5f-q*200.0f;
      if (q<0.0f) q = 0.0f;
      osi_WrStr(" q:", 4ul);
      osic_WrINT32((uint32_t)X2C_TRUNCC(q,0UL,X2C_max_longcard), 1UL);
   }
} /* end WrQuali() */


static void WrdB(int32_t volt)
{
   if (volt>0L) {
      osic_WrFixed(dB((uint32_t)volt), 1L, 6UL);
      osi_WrStr("dB", 3ul);
   }
} /* end WrdB() */


static void WrTXD(uint32_t ms)
{
   osi_WrStr(" txd:", 6ul);
   osic_WrINT32(ms, 1UL);
   osi_WrStr("ms", 3ul);
} /* end WrTXD() */


static void WCh(char c)
{
   if (c!='\015') {
      if ((uint8_t)c<' ' || (uint8_t)c>='\177') osi_WrStr(".", 2ul);
      else osi_WrStr((char *) &c, 1u/1u);
   }
} /* end WCh() */


static void ShowCall(char f[], uint32_t f_len, uint32_t pos)
{
   uint32_t e;
   uint32_t i;
   uint32_t tmp;
   e = pos;
   tmp = pos+5UL;
   i = pos;
   if (i<=tmp) for (;; i++) {
      if (f[i]!='@') e = i;
      if (i==tmp) break;
   } /* end for */
   tmp = e;
   i = pos;
   if (i<=tmp) for (;; i++) {
      WCh((char)((uint32_t)(uint8_t)f[i]>>1));
      if (i==tmp) break;
   } /* end for */
   if ((uint32_t)(uint8_t)f[pos+6UL]>>1&15UL) {
      osi_WrStr("-", 2ul);
      osic_WrINT32((uint32_t)(uint8_t)f[pos+6UL]>>1&15UL, 1UL);
   }
} /* end ShowCall() */

static uint32_t afskmodem_UA = 0x63UL;

static uint32_t afskmodem_DM = 0xFUL;

static uint32_t afskmodem_SABM = 0x2FUL;

static uint32_t afskmodem_DISC = 0x43UL;

static uint32_t afskmodem_FRMR = 0x87UL;

static uint32_t afskmodem_UI = 0x3UL;

static uint32_t afskmodem_RR = 0x1UL;

static uint32_t afskmodem_REJ = 0x9UL;

static uint32_t afskmodem_RNR = 0x5UL;


static void Showctl(uint32_t com, uint32_t cmd)
{
   uint32_t cm;
   char PF[4];
   char tmp;
   osi_WrStr(" ctl ", 6ul);
   cm = (uint32_t)cmd&~0x10UL;
   if ((cm&0xFUL)==0x1UL) {
      osi_WrStr("RR", 3ul);
      osi_WrStr((char *)(tmp = (char)(48UL+(cmd>>5)),&tmp), 1u/1u);
   }
   else if ((cm&0xFUL)==0x5UL) {
      osi_WrStr("RNR", 4ul);
      osi_WrStr((char *)(tmp = (char)(48UL+(cmd>>5)),&tmp), 1u/1u);
   }
   else if ((cm&0xFUL)==0x9UL) {
      osi_WrStr("REJ", 4ul);
      osi_WrStr((char *)(tmp = (char)(48UL+(cmd>>5)),&tmp), 1u/1u);
   }
   else if ((cm&0x1UL)==0UL) {
      osi_WrStr("I", 2ul);
      osi_WrStr((char *)(tmp = (char)(48UL+(cmd>>5)),&tmp), 1u/1u);
      osi_WrStr((char *)(tmp = (char)(48UL+(cmd>>1&7UL)),&tmp),
                1u/1u);
   }
   else if (cm==0x3UL) osi_WrStr("UI", 3ul);
   else if (cm==0xFUL) osi_WrStr("DM", 3ul);
   else if (cm==0x2FUL) osi_WrStr("SABM", 5ul);
   else if (cm==0x43UL) osi_WrStr("DISC", 5ul);
   else if (cm==0x63UL) osi_WrStr("UA", 3ul);
   else if (cm==0x87UL) osi_WrStr("FRMR", 5ul);
   else osi_WrHex(cmd, 1UL);
   strncpy(PF,"v^-+",4u);
   if (com==0UL || com==3UL) osi_WrStr("v1", 3ul);
   else {
      osi_WrStr((char *) &PF[(com&1UL)+2UL*(uint32_t)
                ((0x10UL & (uint32_t)cmd)!=0)], 1u/1u);
   }
} /* end Showctl() */


static void ShowFrame(char f[], uint32_t f_len, uint32_t len,
                int32_t modem, int32_t volt, char noinfo)
{
   uint32_t ff;
   uint32_t i;
   char d;
   char v;
   char tmp;
   X2C_PCOPY((void **)&f,f_len);
   i = 0UL;
   while (!((uint32_t)(uint8_t)f[i]&1)) {
      ++i;
      if (i>len) goto label;
   }
   /* no address end mark found */
   /*
     IF i=1 THEN
       flexmon(f, len);
       i:=13;
     END;
   */
   if (i%7UL!=6UL) goto label;
   /* address end not modulo 7 error */
   osi_WrStr((char *)(tmp = (char)((modem&7L)+48L),&tmp), 1u/1u);
   osi_WrStr(":fm ", 5ul);
   ShowCall(f, f_len, 7UL);
   osi_WrStr(" to ", 5ul);
   ShowCall(f, f_len, 0UL);
   i = 14UL;
   v = 1;
   while (i+6UL<len && !((uint32_t)(uint8_t)f[i-1UL]&1)) {
      if (v) {
         osi_WrStr(" via", 5ul);
         v = 0;
      }
      osi_WrStr(" ", 2ul);
      ShowCall(f, f_len, i);
      if ((uint32_t)(uint8_t)f[i+6UL]>=128UL && (((uint32_t)(uint8_t)
                f[i+6UL]&1) || (uint32_t)(uint8_t)f[i+13UL]<128UL)) {
         osi_WrStr("*", 2ul);
      }
      i += 7UL;
   }
   /*
     IO.WrStr(" ctl "); IO.WrHex(ORD(f[i]),1);
   */
   Showctl((uint32_t)((0x80U & (uint8_t)(uint8_t)f[6UL])!=0)
                +2UL*(uint32_t)((0x80U & (uint8_t)(uint8_t)f[13UL])!=0)
                , (uint32_t)(uint8_t)f[i]);
   ++i;
   if (i<len) {
      osi_WrStr(" pid ", 6ul);
      osi_WrHex((uint32_t)(uint8_t)f[i], 1UL);
   }
   ++i;
   if (volt>0L) {
      WrQuali(noiselevel((uint32_t)modem));
      WrdB(volt);
      ff = (modpar[modem].flags*1000UL)/modpar[modem].configbaud;
      if (ff>0UL) WrTXD(ff);
   }
   /*
   IO.WrCard(bufree(), 3);
   */
   osic_WrLn();
   if (!noinfo) {
      d = 0;
      while (i<len) {
         if (f[i]!='\015') {
            WCh(f[i]);
            d = 1;
         }
         else if (d) {
            osic_WrLn();
            d = 0;
         }
         ++i;
      }
      if (d) osic_WrLn();
   }
   label:;
   X2C_PFREE(f);
/*
FOR i:=0 TO len-1 DO WrStr("\"); WrInt(ASH(ORD(f[i]), -6),1);
 IO.WrCard(ORD(f[i]) DIV 8 MOD 8,1);IO.WrCard(ORD(f[i]) MOD 8,1);
END;
IO.WrLn;
*/
} /* end ShowFrame() */

#define afskmodem_POLINOM 0x8408 


static void Gencrctab(void)
{
   uint32_t c;
   uint32_t crc;
   uint32_t i;
   for (c = 0UL; c<=255UL; c++) {
      crc = 255UL-c;
      for (i = 0UL; i<=7UL; i++) {
         if ((crc&1)) crc = (uint32_t)((uint32_t)(crc>>1)^0x8408UL);
         else crc = crc>>1;
      } /* end for */
      CRCL[c] = (uint8_t)crc;
      CRCH[c] = (uint8_t)(255UL-(crc>>8));
   } /* end for */
} /* end Gencrctab() */


static float Fir(uint32_t in, uint32_t sub, uint32_t step,
                float fir[], uint32_t fir_len, float firtab[],
                uint32_t firtab_len)
{
   float s;
   uint32_t i;
   s = 0.0f;
   i = sub;
   do {
      s = s+fir[in]*firtab[i];
      ++in;
      if (in>fir_len-1) in = 0UL;
      i += step;
   } while (i<=firtab_len-1);
   return s;
} /* end Fir() */


static void demodbit(int32_t m, char d)
{
   char xor;
   struct MPAR * anonym;
   { /* with */
      struct MPAR * anonym = &modpar[m];
      /*descrambler*/
      if (anonym->scramb) {
         /*
               scrambler:=CAST(BITSET, CAST(CARDINAL, scrambler)*2 + ORD(d));
                 
         */
         anonym->scrambler = X2C_LSH(anonym->scrambler,32,1);
         if (d) anonym->scrambler |= 0x1UL;
         d = ((0x1UL & anonym->scrambler)!=0)
                ==(((0x1000UL & anonym->scrambler)!=0)
                ==((0x20000UL & anonym->scrambler)!=0));
                /*result is xor bit 0 12 17*/
      }
      /*nrzi*/
      xor = d;
      d = d==anonym->data1;
      anonym->data1 = xor;
      if (deb01) {
         debb[debp] = (char)(48UL+(uint32_t)d);
         ++debp;
         if (debp>=80UL) {
            debb[80U] = '\012';
            debp = (uint32_t)write(debfd, (char *)debb, debp+1UL);
            debp = 0UL;
         }
      }
      /*bert*/
      if (anonym->bert) {
         if (!d) ++anonym->berterr;
         ++anonym->bertc;
         if (anonym->bert<0L) {
            /* receive only */
            ++anonym->bert;
            if (anonym->bert==0L) osi_WrStrLn("---- end BERT", 14ul);
         }
         if (anonym->bertc>2000UL) {
            osic_WrINT32(anonym->berterr, 4UL);
            osic_WrINT32((uint32_t)m, 2UL);
            WrQuali(noiselevel((uint32_t)m));
            WrdB(chan[anonym->ch].adcmax);
            osic_WrLn();
            anonym->bertc = 0UL;
            anonym->berterr = 0UL;
         }
      }
      if (anonym->rxstuffc<5UL) {
         /*bit to byte*/
         anonym->rxbyte = anonym->rxbyte/2UL+((uint32_t)d<<7);
         ++anonym->rxbitc;
         /*crc*/
         xor = d!=((0x1UL & anonym->rxcrc)!=0); /*databit xor crcbit0*/
         /* 
               rxcrc:=CAST(BITSET, CAST(CARDINAL, rxcrc) DIV 2);
                (*shift right crc register*)
         */
         anonym->rxcrc = X2C_LSH(anonym->rxcrc,32,-1);
         if (xor) anonym->rxcrc = anonym->rxcrc^0x8408UL;
         /*byte to frame*/
         if (anonym->rxbitc>=8UL) {
            if (anonym->rxp<339UL) {
               anonym->rxbuf[anonym->rxp] = (char)anonym->rxbyte;
               ++anonym->rxp;
            }
            /*else frame too long error*/
            anonym->rxbitc = 0UL;
            if (anonym->flagbeg) {
               /* start of data */
               if (anonym->flage>2UL) anonym->flagc = 0UL;
               anonym->flags = anonym->flagc;
               anonym->flagbeg = 0;
            }
            else if (anonym->rxbyte && anonym->flagc>64UL) {
               ++anonym->flage; /* looks like data not txdel patterns */
            }
         }
      }
      else if (anonym->rxstuffc>5UL) {
         /*flag*/
         /*flag*/
         if (((!d && anonym->rxbitc==6UL) && anonym->rxp>=9UL)
                && anonym->rxp<339UL) {
            /*0111111x 0 is flag else abort*/
            /*bits modulo 8 ?*/
            /*frame long enough ?*/
            if (anonym->rxcrc==0x9F0BUL) {
               sendkiss(anonym->rxbuf, 339ul, (int32_t)(anonym->rxp-2UL),
                anonym->port16);
            }
            if (anonym->monitor==afskmodem_passall || anonym->rxcrc==0x9F0BUL && anonym->monitor)
                 {
               ShowFrame(anonym->rxbuf, 339ul, anonym->rxp-2UL, m,
                chan[anonym->ch].adcmax, anonym->monitor==afskmodem_noinfo);
               anonym->flagc = 0UL;
               anonym->flage = 0UL;
            }
         }
         anonym->rxp = 0UL;
         anonym->rxbitc = 0UL;
         anonym->rxcrc = 0xFFFFUL; /*init crc register*/
         anonym->flagbeg = 1;
      }
      if (d) ++anonym->rxstuffc;
      else anonym->rxstuffc = 0UL;
   }
/*destuffing*/
} /* end demodbit() */


static void demod(float u, int32_t m)
{
   char d;
   struct MPAR * anonym;
   d = u>=0.0f;
   { /* with */
      struct MPAR * anonym = &modpar[m];
      if (anonym->cbit) {
         demodbit(m, d);
         if (d!=anonym->oldd) {
            if (d==anonym->plld) anonym->baudfine += anonym->pllshift;
            else anonym->baudfine -= anonym->pllshift;
            anonym->oldd = d;
         }
         else if (d!=anonym->plld) {
            anonym->flagc = 0UL;
            anonym->flage = 0UL;
            anonym->flagbeg = 0;
         }
         /*squelch*/
         anonym->sqmed[d] = anonym->sqmed[d]+(u-anonym->sqmed[d])*0.05f;
         anonym->noise = anonym->noise+((float)fabs(u-anonym->sqmed[d])
                -anonym->noise)*0.05f;
         /*squelch*/
         ++anonym->flagc;
      }
      else anonym->plld = d;
      anonym->cbit = !anonym->cbit;
   }
} /* end demod() */


static void Afsk(int32_t m)
{
   float ff;
   float b;
   float a;
   float d;
   float mid;
   float right;
   struct MPAR * anonym;
   { /* with */
      struct MPAR * anonym = &modpar[m];
      right = Fir(afin, 0UL, 16UL, chan[anonym->ch].afir, 32ul,
                anonym->afirtab, 512ul);
      if (anonym->left<0.0f!=right<0.0f) {
         d = X2C_DIVR(anonym->left,anonym->left-right);
         a = (float)(uint32_t)X2C_TRUNCC(d*16.0f+0.5f,0UL,
                X2C_max_longcard);
         b = a*0.0625f;
         if ((uint32_t)X2C_TRUNCC(a,0UL,
                X2C_max_longcard)>0UL && (uint32_t)X2C_TRUNCC(a,0UL,
                X2C_max_longcard)<16UL) {
            mid = Fir(afin, 16UL-(uint32_t)X2C_TRUNCC(a,0UL,
                X2C_max_longcard), 16UL, chan[anonym->ch].afir, 32ul,
                anonym->afirtab, 512ul);
            if (anonym->left<0.0f!=mid<0.0f) {
               d = (X2C_DIVR(anonym->left,anonym->left-mid))*b;
            }
            else d = b+(X2C_DIVR(mid,mid-right))*(1.0f-b);
         }
         if (anonym->tcnt+d!=0.0f) {
            anonym->freq = X2C_DIVR(1.0f,anonym->tcnt+d);
         }
         anonym->tcnt = 0.0f-d;
      }
      anonym->tcnt = anonym->tcnt+1.0f;
      anonym->left = right;
      anonym->dfir[anonym->dfin] = anonym->freq-anonym->afskmidfreq;
      anonym->dfin = anonym->dfin+1UL&63UL;
      anonym->baudfine += (int32_t)anonym->demodbaud;
      if (anonym->baudfine>=65536L) {
         anonym->baudfine -= 65536L;
         if (anonym->baudfine<65536L) {
            /* normal alway true */
            ff = Fir(anonym->dfin, (uint32_t)(16L-anonym->baudfine/4096L),
                16UL, anonym->dfir, 64ul, anonym->dfirtab, 1024ul);
            demod(ff, m);
         }
      }
   }
} /* end Afsk() */


static void Fsk(int32_t m)
{
   float ff;
   int32_t lim;
   struct MPAR * anonym;
   { /* with */
      struct MPAR * anonym = &modpar[m];
      lim = (int32_t)anonym->demodbaud;
      for (;;) {
         if (anonym->baudfine>=65536L) {
            anonym->baudfine -= 65536L;
            ff = Fir(afin, (uint32_t)((anonym->baudfine&65535L)/4096L),
                16UL, chan[anonym->ch].afir, 32ul, anonym->afirtab, 512ul);
            demod(ff-(anonym->left+anonym->freq)*0.5f, m);
            /*clamp*/
            if (!anonym->cbit) {
               if (ff>anonym->left) {
                  anonym->left = anonym->left+(ff-anonym->left)*1.0f;
               }
               if (ff<anonym->freq) {
                  anonym->freq = anonym->freq+(ff-anonym->freq)*1.0f;
               }
               anonym->left = anonym->left*anonym->clamp;
               anonym->freq = anonym->freq*anonym->clamp;
            }
         }
         /*clamp*/
         anonym->baudfine += lim;
         lim = 0L;
         if (anonym->baudfine<131072L) break;
      }
   }
} /* end Fsk() */

/*
(*pp065*)
PROCEDURE setppout(fd:INTEGER; mask:SET32):BOOLEAN;
CONST PORTCFG=40026206H;

VAR pc:SET32;
    r:CARDINAL;
BEGIN
  r:=100;
  LOOP
    pc:=SET32{30};
    IF ioctl(fd, CAST(INTEGER, PORTCFG), ADR(pc))=0 THEN EXIT END;
    DEC(r);
    IF r=0 THEN pc:=SET32{}; EXIT END;
  END;
  pc:=pc*SET32{0..15}+SET32{31}+mask+SHIFT(mask,1);
  RETURN ioctl(fd, CAST(INTEGER, PORTCFG), ADR(pc))=0;
END setppout;

PROCEDURE ppbitset(fd:INTEGER; port:CARDINAL; on:BOOLEAN);
CONST PARWR=40026207H;
VAR res:INTEGER;
BEGIN
--IO.WrInt(port, 3); IO.WrInt(ORD(on) , 3); IO.WrStrLn("ppout");
  res:=ioctl(fd, CAST(INTEGER, PARWR), CAST(ADDRESS,SET32{port+ORD(NOT on)*8}
                ));
END ppbitset;
(*pp065*)



PROCEDURE Ptt;
VAR lptfd, b:INTEGER;
    map, mask:SET8;
    c:CHANNELS;
    ppmask:SET32;
BEGIN
--WrInt(ORD(chan[LEFT].pttstate),2); WrInt(ORD(chan[RIGHT].pttstate),1);
                WrStrLn("=ptts");
(*tty*)
  IF ttyfn[0]<>0C THEN
    IF ttyfd<0 THEN ttyfd:=open(ttyfn, 0) END;
    IF ttyfd>=0 THEN

      IF NOT SetStatLine(ttyfd, chan[LEFT].pttstate, chan[RIGHT].pttstate)
      OR closetty THEN
        Close(ttyfd);
        ttyfd:=-1;
      END;                                      (* else keep open for alzheimer usb devices *)
    ELSE WrStrLn("tty port open error") END;
  END;
(*tty*)

  IF parporttyp=1 THEN
(*parport*)
    mask:=SET8{};
    map:=SET8{};
    FOR c:=LEFT TO RIGHT DO 
      b:=chan[c].lptpttbit;
      IF b<>0 THEN
        IF chan[c].pttstate=(b>=0) THEN INCL(map, ABS(b)-1) END;
        INCL(mask, ABS(b)-1);
      END;
    END; 
      IF mask<>SET8{} THEN
      lptfd:=OpenRW(lptfn);
      IF (lptfd<0) OR (lpt.ppclaim(lptfd)<0)
                THEN WrStrLn("lpt port open error");
      ELSE 
        map:=(CAST(SET8, lpt.ppdevrdata(lptfd))-mask) + map;
        lpt.ppdevdata(lptfd, CAST(CHAR, map));
      END;
      IF (lptfd>=0) THEN Close(lptfd) END;
    END;
(*parport*)

  ELSIF parporttyp=2 THEN
(*pp065*)
    lptfd:=OpenRW(lptfn);
    IF lptfd>=0 THEN 
      ppmask:=SET32{};
      FOR c:=LEFT TO RIGHT DO
        b:=chan[c].lptpttbit;
        IF b<>0 THEN INCL(ppmask, (ABS(b)-1)*2) END;
      END;
      IF setppout(lptfd, ppmask) THEN
        FOR c:=LEFT TO RIGHT DO
          b:=chan[c].lptpttbit;
          IF b<>0 THEN
            ppbitset(lptfd, ABS(b)-1, chan[c].pttstate=(b>=0));
          END;
        END;
      END;
      Close(lptfd);
    ELSE WrStr(lptfn); WrStrLn(" port open error") END;
  END; 
(*pp065*)


END Ptt;
*/

static void startrandom(uint8_t ch)
{
   struct CHAN * anonym;
   { /* with */
      struct CHAN * anonym = &chan[ch];
      anonym->addrandom = 2UL+(uint32_t)X2C_TRUNCC(osic_Random()
                *(double)anonym->persist,0UL,X2C_max_longcard);
                /* store ramdom wait */
      anonym->dcdclock = clock0; /* start txwait after we sent */
   }
} /* end startrandom() */


static char CheckRandom(int32_t modem)
{
   uint32_t clk;
   struct MPAR * anonym;
   struct CHAN * anonym0;
   { /* with */
      struct MPAR * anonym = &modpar[modem];
      { /* with */
         struct CHAN * anonym0 = &chan[anonym->ch];
         if (anonym0->duplex==afskmodem_shiftdigi) {
            clk = modpar[modem].dcdclockm; /* use dcd of this modulation */
         }
         else clk = anonym0->dcdclock;
         return anonym0->duplex!=afskmodem_fullduplex && (clock0-clk<=anonym0->addrandom || anonym0->duplex==afskmodem_onetx)
                ;
      }
   }
} /* end CheckRandom() */

/* (usb) soundcard died */

static void repairsound(void)
{
   /*  pttok:=FALSE; */
   ptt(chan[afskmodem_LEFT].hptt, -1L);
   ptt(chan[afskmodem_RIGHT].hptt, -1L);
   if (abortonsounderr) Error("Sounddevice Failure", 20ul);
   osic_Close(soundfd);
   usleep(100000UL);
   /*WrStrLn("openA"); */
   OpenSound();
} /* end repairsound() */


static void getadc(void)
{
   short buf[4096];
   int32_t minr;
   int32_t maxr;
   int32_t minl;
   int32_t maxl;
   int32_t sl;
   int32_t m;
   int32_t i;
   int32_t l;
   uint8_t c;
   char ndcd;
   struct MPAR * anonym;
   struct MPAR * anonym0;
   l = read(soundfd, (char *)buf, adcbuflen*adcbytes);
   for (m = 0L; m<=7L; m++) {
      modpar[m].noise = modpar[m].noise*0.99f; /* clear dcd on silence */
   } /* end for */
   if (l<=0L) {
      repairsound();
      return;
   }
   if (debfd>=0L && !deb01) {
      i = write(debfd, (char *)buf, (uint32_t)l);
   }
   l = (int32_t)((uint32_t)l/adcbytes);
   for (c = afskmodem_LEFT;; c++) {
      chan[c].adcmax = chan[c].adcmax*15L>>4;
      if (c==afskmodem_RIGHT) break;
   } /* end for */
   maxl = -32768L;
   maxr = -32768L;
   minl = 32767L;
   minr = 32767L;
   i = 0L;
   while (i<l) {
      sl = (int32_t)buf[i];
      chan[afskmodem_LEFT].afir[afin] = (float)sl;
      if (sl>maxl) maxl = sl;
      if (sl<minl) minl = sl;
      if (maxl-minl>chan[afskmodem_LEFT].adcmax) {
         chan[afskmodem_LEFT].adcmax = maxl-minl;
      }
      if (maxchannels>afskmodem_LEFT) {
         sl = (int32_t)buf[i+1L];
         chan[afskmodem_RIGHT].afir[afin] = (float)sl;
         if (sl>maxr) maxr = sl;
         if (sl<minr) minr = sl;
         if (maxr-minr>chan[afskmodem_RIGHT].adcmax) {
            chan[afskmodem_RIGHT].adcmax = maxr-minr;
         }
      }
      afin = afin+1UL&31UL;
      for (m = 0L; m<=7L; m++) {
         { /* with */
            struct MPAR * anonym = &modpar[m];
            if ((anonym->configured && chan[anonym->ch]
                .adcmax>=anonym->leveldcd)
                && (!chan[anonym->ch].pttstate || chan[anonym->ch]
                .duplex && chan[anonym->ch].duplex!=afskmodem_onetx)) {
               /* save cpu and echo reception */
               if (anonym->afsk) Afsk(m);
               else Fsk(m);
               /*
               IF (m=0) & (clock MOD 64=0)
                THEN IO.WrInt(chan[modpar[m].ch].adcmax,6); IO.WrLn; END;
               */
               if (noiselevel((uint32_t)m)<anonym->squelchdcd) {
                  chan[anonym->ch].dcdclock = clock0; /* tx wise dcd */
                  anonym->dcdclockm = clock0;
                /* modem wise dcd for shift digi*/
               }
            }
            else anonym->rxp = 0UL;
         }
      } /* end for */
      i += (int32_t)((uint32_t)maxchannels+1UL);
   }
   for (m = 0L; m<=7L; m++) {
      { /* with */
         struct MPAR * anonym0 = &modpar[m];
         if (anonym0->configured) {
            ndcd = chan[anonym0->ch]
                .adcmax>=anonym0->leveldcd && noiselevel((uint32_t)m)
                <anonym0->squelchdcd;
            if (ndcd) {
               chan[anonym0->ch].dcdclock = clock0; /* tx wise dcd */
               anonym0->dcdclockm = clock0;
                /* modem wise dcd for shift digi*/
            }
            if (ndcd!=anonym0->haddcd) {
               if (ndcd) startrandom(anonym0->ch);
               anonym0->haddcd = ndcd;
            }
            ndcd = CheckRandom(m);
            if (ndcd!=anonym0->haddcdrand) {
               anonym0->haddcdrand = ndcd;
               if (anonym0->dcdmsgs) {
                  sendaxudp2((uint32_t)m, 0UL, 0, "", 1ul);
               }
            }
         }
      }
   } /* end for */
} /* end getadc() */


static void txmon(pKISSNEXT pf)
{
   if (pf && modpar[(uint8_t)pf->port].monitor) {
      ShowFrame(pf->data, 341ul, pf->len,
                (int32_t)(uint32_t)(uint8_t)pf->port, 0L,
                modpar[(uint8_t)pf->port].monitor==afskmodem_noinfo);
   }
} /* end txmon() */


static float Phasemod(float * uc, float u, float hp)
{
   float ud;
   ud = u-*uc;
   *uc = *uc+ud*hp;
   return ud;
} /* end Phasemod() */


static void Free(pKISSNEXT * tb)
{
   pKISSNEXT ph;
   if (*tb) {
      ph = (*tb)->next;
      (*tb)->next = pTxFree;
      pTxFree = *tb;
      *tb = ph;
   }
} /* end Free() */


static char frames2tx(int32_t modem)
{
   char txo;
   char tx;
   tx = 0;
   if (modpar[modem].txbufin) {
      if (modpar[modem].txbufin->time0<=systime) {
         Free(&modpar[modem].txbufin); /* frame too old */
      }
      else tx = 1;
   }
   if (modpar[modem].dcdmsgs) {
      /* axudp2 check if txbuffer run empty*/
      txo = modpar[modem].hadtxdata;
      modpar[modem].hadtxdata = tx;
      if (txo && !tx) {
         sendaxudp2((uint32_t)modem, 0UL, 0, "", 1ul);
                /* send tx ready msg */
      }
   }
   return tx;
} /* end frames2tx() */


static void sendmodem(void)
{
   short buf[4096];
   int32_t i;
   float samp;
   uint8_t c;
   struct CHAN * anonym;
   struct CHAN * anonym0;
   struct MPAR * anonym1;
   struct CHAN * anonym2;
   uint8_t tmp;
   int32_t tmp0;
   if (soundbufs>0UL) --soundbufs;
   tmp = maxchannels;
   c = afskmodem_LEFT;
   if (c<=tmp) for (;; c++) {
      { /* with */
         struct CHAN * anonym = &chan[c];
         if (anonym->pttsoundbufs>0UL) --anonym->pttsoundbufs;
         if (anonym->state==afskmodem_receiv) {
            for (i = 0L; i<=7L; i++) {
               /* has any modem data? */
               /*
                         IF modpar[i].txbufin<>NIL THEN
                           IF c=modpar[i].ch THEN actmodem:=i END;  
                         END;
               */
               if (frames2tx(i) && c==modpar[i].ch) anonym->actmodem = i;
            } /* end for */
            /*
                    IF (actmodem<0) OR (modpar[actmodem].txbufin=NIL)
                THEN     (* no data to send *)
            */
            if (anonym->actmodem<0L || !frames2tx(anonym->actmodem)) {
               /* no data to send */
               /*
               IF soundctl.getoutfilled(soundfd)
                <=0 THEN soundctl.pcmsync(soundfd) END;
               IF soundctl.getoutfilled(soundfd)
                >0 THEN IO.WrInt(soundctl.getoutfilled(soundfd),1); IO.WrLn;
                END;
               */
               if (anonym->pttstate && anonym->pttsoundbufs==0UL) {
                  anonym->pttstate = 0;
                  ptt(anonym->hptt, 0L);
                /* guess all sound buffers are sent*/
                  if (modpar[anonym->actmodem].dcdmsgs) {
                     sendaxudp2((uint32_t)anonym->actmodem, 0UL, 0, "",
                1ul);
                  }
               }
            }
            else if (anonym->pttstate) {
               /* more data, ptt is on*/
               anonym->state = afskmodem_sendtxdel;
               anonym->tbitc = 0L;
               anonym->tbytec = 0UL;
               txmon(modpar[anonym->actmodem].txbufin);
            }
            else {
               /* data ptt off */
               anonym->tbytec = 0UL;
               anonym->state = afskmodem_slotwait;
               startrandom(c);
            }
         }
         if (anonym->state==afskmodem_slotwait) {
            if (!CheckRandom(anonym->actmodem) || !chan[(uint32_t)
                maxchannels-(uint32_t)c].pttstate) {
               /* onetx: tx locks tx of other channel */
               /*
                       IF duplex=shiftdigi THEN 
                         clk:=modpar[actmodem].dcdclockm;
                (* use dcd of this modulation *)
                       ELSE clk:=dcdclock END;
                (* use dcd of latest heard modulation *)
               
                       IF (duplex=fullduplex) OR (clock-clk > addrandom)
                       & ((duplex<>onetx) OR NOT chan[VAL(CHANNELS,
                ORD(maxchannels)-ORD(c))].pttstate)
                THEN  (* onetx: tx locks tx of other channel *)
               */
               if (frames2tx(anonym->actmodem)) {
                  txmon(modpar[anonym->actmodem].txbufin);
                  chan[c].pttstate = 1; /*WrInt(ORD(c),1); WrStrLn(" ptton");
                */
                  ptt(chan[c].hptt, 1L);
                  if (modpar[anonym->actmodem].dcdmsgs) {
                     sendaxudp2((uint32_t)anonym->actmodem, 0UL, 0, "",
                1ul);
                  }
                  chan[c].gmcnt = chan[c].gmqtime;
                  anonym->pttsoundbufs = soundbufs+extraaudiodelay;
                  anonym->state = afskmodem_sendtxdel;
                  anonym->tbitc = 0L;
                  anonym->tbytec = 0UL;
               }
               else anonym->state = afskmodem_receiv;
            }
         }
      }
      if (c==tmp) break;
   } /* end for */
   /*  WHILE (soundbufs<maxsoundbufs) & ((chan[LEFT].state>=sendtxdel)
                OR (chan[RIGHT].state>=sendtxdel)) DO */
   while (soundbufs<maxsoundbufs && ((badsounddriver || chan[afskmodem_LEFT]
                .state>=afskmodem_sendtxdel)
                || chan[afskmodem_RIGHT].state>=afskmodem_sendtxdel)) {
      tmp0 = (int32_t)(adcbuflen-1UL);
      i = 0L;
      if (i<=tmp0) for (;; i++) {
         if (maxchannels==afskmodem_RIGHT) c = (uint8_t)(i&1L);
         else c = afskmodem_LEFT;
         { /* with */
            struct CHAN * anonym0 = &chan[c];
            if (anonym0->state>=afskmodem_sendtxdel) {
               samp = TFIR[anonym0->txbaudgen/512UL][(uint32_t)
                anonym0->tscramb&63UL];
               if (modpar[anonym0->actmodem].afsk) {
                  anonym0->dds = anonym0->dds+(uint32_t)
                X2C_TRUNCC(samp*modpar[anonym0->actmodem]
                .afshift+modpar[anonym0->actmodem].afmid,0UL,
                X2C_max_longcard)&32767UL;
                  buf[i] = (short)(int32_t)
                X2C_TRUNCI(Phasemod(&anonym0->hipasscap,
                SIN[anonym0->dds]*modpar[anonym0->actmodem].txvolum,
                modpar[anonym0->actmodem].afskhighpass),X2C_min_longint,
                X2C_max_longint);
               }
               else {
                  buf[i] = (short)(int32_t)
                X2C_TRUNCI(samp*modpar[anonym0->actmodem].txvolum,
                X2C_min_longint,X2C_max_longint);
               }
               if (anonym0->gmcnt>0UL) {
                  buf[i] = 0;
                  --anonym0->gmcnt;
               }
               anonym0->txbaudgen = anonym0->txbaudgen+modpar[anonym0->actmodem]
                .txbaud;
               if (anonym0->txbaudgen>=65536UL) {
                  anonym0->txbaudgen -= 65536UL;
                  /*send next bit */
                  if (anonym0->tbitc<=0L) {
                     anonym0->tbitc = 8L;
                     if (anonym0->state==afskmodem_sendtxdel) {
                        if (modpar[anonym0->actmodem].bert>0L) {
                           anonym0->tbyte = 255UL;
                           modpar[anonym0->actmodem].bert -= 8L;
                        }
                        else {
                           anonym0->tbyte = modpar[anonym0->actmodem]
                .txdelpattern;
                           ++anonym0->tbytec;
                        }
                        if (anonym0->tbytec>=modpar[anonym0->actmodem].txdel)
                 anonym0->tbyte = 126UL;
                        if (anonym0->tbytec>modpar[anonym0->actmodem].txdel) {
                           anonym0->state = afskmodem_senddata;
                           anonym0->tbytec = 0UL;
                           anonym0->txstuffc = 0L;
                        }
                     }
                     if (anonym0->state==afskmodem_sendtxtail) {
                        anonym0->tbyte = 126UL;
                        ++anonym0->tbytec;
                        /*next frame*/
                        if (modpar[anonym0->actmodem].txbufin) {
                           /* same modem */
                           anonym0->tbytec = 0UL;
                           anonym0->state = afskmodem_senddata;
                           anonym0->txstuffc = 0L;
                           txmon(modpar[anonym0->actmodem].txbufin);
                        }
                        else if (anonym0->tbytec>modpar[anonym0->actmodem]
                .txtail) {
                           anonym0->state = afskmodem_receiv;
                /* no data for this modem */
                        }
                     }
                     if (anonym0->state==afskmodem_senddata) {
                        { /* with */
                           struct MPAR * anonym1 = &modpar[anonym0->actmodem]
                ;
                           if (anonym1->txbufin==0 || anonym0->tbytec>=anonym1->txbufin->len+2UL)
                 {
                              /*frame sent*/
                              Free(&anonym1->txbufin);
                              anonym0->state = afskmodem_sendtxtail;
                              anonym0->tbyte = 126UL;
                              anonym0->tbytec = 0UL;
                           }
                           else {
                              anonym0->tbyte = (uint32_t)(uint8_t)
                anonym1->txbufin->data[anonym0->tbytec];
                              ++anonym0->tbytec;
                           }
                        }
                     }
                  }
                  /*stuff*/
                  if ((anonym0->tbyte&1)
                && anonym0->state==afskmodem_senddata) {
                     ++anonym0->txstuffc;
                     if (anonym0->txstuffc>=5L) {
                        anonym0->tbyte += anonym0->tbyte-1UL;
                        ++anonym0->tbitc;
                        anonym0->txstuffc = 0L;
                     }
                  }
                  else anonym0->txstuffc = 0L;
                  /*stuff*/
                  /* nrzi */
                  if (!(anonym0->tbyte&1)) anonym0->tnrzi = !anonym0->tnrzi;
                  /*
                              IF modpar[actmodem].scramb THEN
                                tscramb:=CAST(BITSET, CAST(CARDINAL,
                tscramb)*2+ORD(tnrzi <> ((11 IN tscramb) <> (16 IN tscramb)))
                );
                              ELSE tscramb:=CAST(BITSET, CAST(CARDINAL,
                tscramb)*2+ORD(tnrzi)) END;
                  */
                  anonym0->tscramb = X2C_LSH(anonym0->tscramb,32,1);
                  if (modpar[anonym0->actmodem].scramb) {
                     if (anonym0->tnrzi!=(((0x1000UL & anonym0->tscramb)!=0)
                !=((0x20000UL & anonym0->tscramb)!=0))) {
                        anonym0->tscramb |= 0x1UL;
                     }
                  }
                  else if (anonym0->tnrzi) anonym0->tscramb |= 0x1UL;
                  anonym0->tbyte = anonym0->tbyte>>1;
                  --anonym0->tbitc;
               }
            }
            else buf[i] = 0;
         }
         if (i==tmp0) break;
      } /* end for */
      /*    WrBin(soundfd, buf, adcbuflen*adcbytes); */
      i = write(soundfd, (char *)buf, adcbuflen*adcbytes);
      for (c = afskmodem_LEFT;; c++) {
         { /* with */
            struct CHAN * anonym2 = &chan[c];
            if (anonym2->state>=afskmodem_sendtxdel) ++anonym2->pttsoundbufs;
         }
         if (c==afskmodem_RIGHT) break;
      } /* end for */
      ++soundbufs;
   }
} /* end sendmodem() */

static void afskmodemcleanup(int32_t);


static void afskmodemcleanup(int32_t signum)
{
   pttDestroy(chan[afskmodem_LEFT].hptt);
   pttDestroy(chan[afskmodem_RIGHT].hptt);
   osi_WrStr("exit ", 6ul);
   osic_WrINT32((uint32_t)signum, 0UL);
   osi_WrStrLn("!", 2ul);
   X2C_HALT((uint32_t)signum);
} /* end afskmodemcleanup() */


X2C_STACK_LIMIT(100000l)
extern int main(int argc, char **argv)
{
   X2C_BEGIN(&argc,argv,1,4000000l,8000000l);
   if (sizeof(FILENAME)!=1024) X2C_ASSERT(0);
   aprsstr_BEGIN();
   osi_BEGIN();
   signal(SIGTERM, afskmodemcleanup);
   signal(SIGINT, afskmodemcleanup);
   memset((char *)modpar,(char)0,sizeof(struct MPAR [8]));
   Parms();
   Gencrctab();
   initTFIR();
   /*
   tfd:=FIO.Create("/tmp/t.raw");
   */
   getst = 0UL;
   esc = 0;
   afin = 0UL;
   soundbufs = 0UL;
   ptt(chan[afskmodem_LEFT].hptt, -1L);
   ptt(chan[afskmodem_RIGHT].hptt, -1L);
   for (;;) {
      getadc();
      ++clock0;
      if ((clock0&63UL)==0UL) {
         ptt(chan[afskmodem_LEFT].hptt, -1L);
                /* sync ptt to hardware sometime */
         ptt(chan[afskmodem_RIGHT].hptt, -1L);
         systime = osic_time();
      }
      getkiss();
      getudp();
      sendmodem();
   }
/*
IO.WrStr("len="); IO.WrCard(txbuf[txp].len, 1); IO.WrStr("cmd=");
                IO.WrHex(ORD(txbuf[txp].cmd), 1);

FOR i:=1 TO txbuf[txp].len DO IO.WrHex(ORD(txbuf[txp].data[i-1]), 3) END;
IO.WrLn;   
*/
/*
  FIO.Close(pipefd);
*/
/*
FIO.Close(tfd);
*/
   X2C_EXIT();
   return 0;
}

X2C_MAIN_DEFINITION
