<*+M2EXTENSIONS *> <*-CHECKDIV *> <*-CHECKRANGE *> <*-GENFRAME*> <*-COVERFLOW *> <*-IOVERFLOW*> <*-NOPTRALIAS*> <*-DOREORDER*> <*-PROCINLINE*> <*-GENPTRINIT*> <*+STORAGE *> <* IF __GEN_C__ THEN *> <*GENWIDTH="120"*> <*+STORAGE *> <*-GENCTYPES*> <*+COMMENT*> <*-GENHISTORY*> <*-GENDEBUG*> <*-GENDATE*> <*-LINENO*> <*-CHECKINDEX*> <*-CHECKDINDEX*> <*+GENCDIV*> <*-GENKRC*> <*+NOOPTIMIZE*> <*-GENSIZE*> <*-ASSERT*> <*-CHECKNIL*> <*-COVERFLOW*> <*-IOVERFLOW*> <*-CHECKRANGE*> <*-CHECKSET*> <*-CHECKDIV*> <*-GENCONSTENUM*> <*-ASSERT*> <* ELSE *> <*HEAPLIMIT="100000000"*> <*+GENHISTORY*> <*+GENDEBUG*> <*-GENDATE*> <*+LINENO*> <*+CHECKINDEX*> <*+CHECKDNDEX*> <*-NOOPTIMIZE*> <*-GENSIZE*> <*-ASSERT*> <*-CHECKNIL*> <*-COVERFLOW*> <*-IOVERFLOW*> <*-CHECKRANGE*> <*-CHECKSET*> <*-CHECKDIV*> <*-GENCONSTENUM*> <* END *> <*NEW WITHMESHCORE*> <*+WITHMESHCORE*> <*NEW WITHUDP*> <*+WITHUDP*> <*NEW WITHMESHCOM4*> <*+WITHMESHCOM4*> <*NEW WITHFANET*> <*+WITHFANET*> <*NEW WITHLORAWAN*> <*+WITHLORAWAN*> <*NEW WITHMESHTASTIC*> <*+WITHMESHTASTIC*> IMPLEMENTATION MODULE loraprotocols; (* monitor lora frames and try to decode known content protocols *) FROM SYSTEM IMPORT FILL, MOVE, ADR, CAST, CARD8, CARD16, INT16, SHIFT, BYTE, INT8; FROM math IMPORT sin, cos, log, sqrt, atan, pow; FROM osi IMPORT Werr, WrStr, WrStrLn, WrInt, File, OpenRead, Close, WrFixed, ALLOCATE, NextArg, RdBin, ln, arctan, time, OpenWrite, Seekend, WrBin, OpenNONBLOCK, OpenAppend, WrCard, realcard; FROM aprsstr IMPORT Length, DateToStr, IntToStr; <* IF WITHUDP THEN *> FROM osi IMPORT openudp, SOCKET, udpsend; FROM aprsstr IMPORT mon2raw, AppCRC; <* END *> FROM aprsstr IMPORT StrToCard, StrToInt, StrToFix, Append, CardToStr, TimeToStr, FixToStr; FROM signal IMPORT signal, SIGPIPE; CONST LF=12C; TYPE SET8=SET OF [0..7]; SET16=SET OF [0..15]; SET32=SET OF [0..31]; PROCEDURE hex(n:CARDINAL; cap:BOOLEAN):CHAR; BEGIN n:=n MOD 16; IF n<=9 THEN RETURN CHR(n+ORD("0")) END; IF cap THEN RETURN CHR(n+(ORD("A")-10)) END; RETURN CHR(n+(ORD("a")-10)) END hex; PROCEDURE HexStr(x, digits, len:CARDINAL; cap:BOOLEAN; VAR s:ARRAY OF CHAR); VAR i:CARDINAL; BEGIN IF digits>HIGH(s) THEN digits:=HIGH(s) END; i:=digits; WHILE (i0 DO DEC(digits); s[digits]:=hex(x, cap); x:=x DIV 16; END; END HexStr; PROCEDURE WrHex(x, digits, len:CARDINAL); VAR s:ARRAY[0..255] OF CHAR; BEGIN HexStr(x, digits, len, FALSE, s); WrStr(s); END WrHex; PROCEDURE WrHexCap(x, digits, len:CARDINAL); VAR s:ARRAY[0..255] OF CHAR; BEGIN HexStr(x, digits, len, TRUE, s); WrStr(s); END WrHexCap; PROCEDURE ChHex(c:CHAR; VAR s:ARRAY OF CHAR); BEGIN IF (c>=177C) OR (c<" ") THEN s[0]:="["; s[1]:=hex(ORD(c) DIV 16, FALSE); s[2]:=hex(ORD(c), FALSE); s[3]:="]"; s[4]:=0C; ELSE s[0]:=c; s[1]:=0C; END; END ChHex; PROCEDURE WrChHex(c:CHAR); VAR s:ARRAY[0..10] OF CHAR; BEGIN ChHex(c, s); WrStr(s); END WrChHex; PROCEDURE WrdB(volt:REAL); BEGIN IF volt>0.0001 THEN WrFixed(log(volt)*8.685889638, 1,4) ELSE WrStr("----") END; END WrdB; PROCEDURE WrdBw(volt, fact, corr:REAL); BEGIN IF volt>0.0001 THEN WrFixed(log(volt)*(8.685889638)*fact+corr, 1,1) ELSE WrStr("----") END; END WrdBw; <* IF WITHUDP THEN *> PROCEDURE sendaxudp2(ipnum:IPNUM; udpport:UDPPORT; udp2:BOOLEAN; mon:ARRAY OF CHAR; dlen:CARDINAL; txd:INTEGER; level, snrr:REAL; afc, qual:INTEGER; longcall, dcdon:BOOLEAN); PROCEDURE cleantext(VAR text:ARRAY OF CHAR; len:CARDINAL):BOOLEAN; (* remove heading junk *) VAR i,j:CARDINAL; BEGIN i:=0; WHILE (i"Z")) & ((mon[i]<"0") OR (mon[i]>"9")) DO INC(i) END; IF (i<=3) & (len>2) THEN j:=0; WHILE (j0) & (text[j-1]0C DO b[p]:=CHR(ORD(s[i])<<0); INC(p); INC(i) END; b[p]:=" "; INC(p); END app; BEGIN snr:=VAL(INTEGER, snrr+0.5); IF snr>127 THEN snr:=127 ELSIF snr<-127 THEN snr:=-127 END; lev:=VAL(INTEGER, level+0.5); IF lev>127 THEN lev:=127 ELSIF lev<-255 THEN lev:=-255 END; IF NOT longcall THEN IF cleantext(mon, dlen) THEN mon2raw(mon, data, datalen); ELSE IF verb THEN WrStrLn("axudp encode: too much junk in data (-y for prefilter aprs)") END; datalen:=0; END; IF datalen=0 THEN longcall:=TRUE END; END; IF longcall THEN datalen:=Length(mon); i:=0; REPEAT data[i]:=mon[i]; INC(i); UNTIL (i>HIGH(data)) OR (VAL(INTEGER, i)>=datalen); END; IF datalen>2 THEN p:=0; IF udp2 THEN IF NOT longcall THEN DEC(datalen, 2) END; (* remove crc *) b[0]:=1C; b[1]:=CHR(30H + ORD(dcdon)*2); p:=2; app("T", txd); app("V", lev); app("S", snr); app("A", afc); app("Q", qual); IF longcall THEN app("X", 2) END; (* call are normal text *) b[p]:=0C; INC(p); (* end of axudp2 header *) END; i:=0; REPEAT b[p]:=data[i]; INC(p); INC(i); UNTIL VAL(INTEGER, i)>=datalen; IF udp2 THEN AppCRC(b, p); INC(p, 2); END; IF udpport<>0 THEN ret:=udpsend(udpsock, b, p, udpport, ipnum); END; ELSIF verb THEN WrStrLn("axudp: beacon encode error") END; END sendaxudp2; <* END *> PROCEDURE sendjson(udps:pJDESTS; id:CARDINAL; text-:ARRAY OF CHAR; dlen:CARDINAL; hascrc, crc, invert:BOOLEAN; sf, cr, txd, frametime:CARDINAL; level:REAL; n, peakn, snr, dre:REAL; df, qual, fecc:INTEGER); PROCEDURE b64(c:CARDINAL):CHAR; BEGIN c:=c MOD 64; IF c<26 THEN RETURN CHR(c+ORD("A")) ELSIF c<52 THEN RETURN CHR(c+(ORD("a")-26)) ELSIF c<62 THEN RETURN CHR(VAL(INTEGER, c)+(VAL(INTEGER,ORD("0"))-52)) ELSIF c=62 THEN RETURN "+" ELSE RETURN "/" END; END b64; PROCEDURE enc64(b, n:CARDINAL; VAR s:ARRAY OF CHAR); VAR i:CARDINAL; BEGIN FOR i:=n TO 2 DO b:=b*256 END; s[2]:="="; s[3]:="="; s[4]:=0C; s[0]:=b64(b DIV 40000H); s[1]:=b64(b DIV 1000H); IF n>=2 THEN s[2]:=b64(b DIV 40H) END; IF n=3 THEN s[3]:=b64(b) END; END enc64; VAR s,h:ARRAY[0..999] OF CHAR; ret:INTEGER; i,b:CARDINAL; BEGIN s:="{"; Append(s, '"net":'); IntToStr(id, 1, h); Append(s, h); Append(s, ',"crc":'); IntToStr(VAL(INTEGER, ORD(hascrc)+ORD(crc))-1, 1, h); Append(s, h); Append(s, ',"invers":'); IntToStr(ORD(invert), 1, h); Append(s, h); Append(s, ',"bw":'); FixToStr(MAXBW/FLOAT(BWTAB[bwnum]), 2, h); Append(s, h); Append(s, ',"sf":'); IntToStr(sf, 1, h); Append(s, h); Append(s, ',"cr":'); IntToStr(cr, 1, h); Append(s, h); Append(s, ',"preamb":'); IntToStr(txd, 1, h); Append(s, h); Append(s, ',"duration":'); IntToStr(frametime, 1, h); Append(s, h); Append(s, ',"level":'); FixToStr(level, 2, h); Append(s, h); Append(s, ',"afc":'); IntToStr(df, 1, h); Append(s, h); Append(s, ',"dre":'); FixToStr(dre, 2, h); Append(s, h); Append(s, ',"eye":'); IntToStr(qual, 1, h); Append(s, h); Append(s, ',"nfloor":'); FixToStr(n, 2, h); Append(s, h); Append(s, ',"pknfloor":'); FixToStr(peakn, 2, h); Append(s, h); Append(s, ',"snr":'); FixToStr(snr, 2, h); Append(s, h); Append(s, ',"fec":'); IntToStr(fecc, 1, h);; Append(s, h); IF jmhz<>0.0 THEN Append(s, ',"rxmhz":'); FixToStr(jmhz+0.00005, 4, h); Append(s, h) END; Append(s, ',"ver":"lorarx"'); Append(s, ',"payload":"'); b:=0; i:=0; WHILE i0 THEN enc64(b, i MOD 3, h); Append(s, h) END; Append(s, '"}'+LF); IF jpipename[0]<>0C THEN IF jsonfd<0 THEN jsonfd:=OpenNONBLOCK(jpipename); IF jsonfd<0 THEN jsonfd:=OpenWrite(jpipename) ELSE Seekend(jsonfd, 0) END; (* no file and no pipe *) END; IF jsonfd>=0 THEN WrBin(jsonfd, s, Length(s)) ELSE WrStrLn("cannot write json-file") END; END; WHILE udps<>NIL DO IF udps^.judpport<>0 THEN ret:=udpsend(udpsock, s, Length(s), udps^.judpport, udps^.jipnum) END; udps:=udps^.next; END; END sendjson; PROCEDURE senddcd(sf:INTEGER; inv, on:BOOLEAN); VAR s,h:ARRAY[0..99] OF CHAR; ret:INTEGER; jd:pJDESTS; BEGIN jd:=jdests; WHILE jd<>NIL DO IF jd^.judpport<>0 THEN IF inv THEN sf:=-sf END; s:="{"; Append(s, '"dcd":'); IntToStr(ORD(on), 1, h); Append(s, h); Append(s, ',"sf":'); IntToStr(sf, 1, h); Append(s, h); Append(s, '}'+LF); ret:=udpsend(udpsock, s, Length(s), jd^.judpport, jd^.jipnum) END; jd:=jd^.next; END; END senddcd; PROCEDURE decodepr(raw:ARRAY OF CHAR; rawlen:CARDINAL; port:CHAR; VAR join:BOOLEAN; VAR axpart:ARRAY OF CHAR; VAR fb:ARRAY OF CHAR; VAR fblen:CARDINAL); PROCEDURE ShowFrame(f:ARRAY OF CHAR; len:CARDINAL; port:CHAR); PROCEDURE WCh(c:CHAR); BEGIN IF c<>15C THEN IF (c<" ") OR (c>=177C) THEN WrStr(".") ELSE WrStr(c) END; END; END WCh; PROCEDURE ShowCall(VAR f:ARRAY OF CHAR; pos:CARDINAL); VAR i,e:CARDINAL; BEGIN e:=pos; FOR i:=pos TO pos+5 DO IF f[i]<>100C THEN e:=i END; END; FOR i:=pos TO e DO WCh(CHR(ASH(ORD(f[i]), -1))) END; i:=ASH(ORD(f[pos+6]), -1) MOD 16; IF i<>0 THEN WrStr("-"); IF i>=10 THEN WrStr(CHR(i DIV 10 + ORD("0"))) END; WrStr(CHR(i MOD 10 + ORD("0"))); END; END ShowCall; PROCEDURE Showctl(com, cmd:CARDINAL); CONST UA = {0,1,5,6}; DM = {0,1,2,3}; SABM={0,1,2,3,5}; DISC={0,1,6}; FRMR={0,1,2,7}; UI = {0,1}; RR = {0}; REJ= {0,3}; RNR= {0,2}; VAR cm:BITSET; PF:ARRAY[0..3] OF CHAR; BEGIN WrStr(" ctl "); cm:=CAST(BITSET, cmd) - {4}; IF cm * {0,1,2,3} = RR THEN WrStr("RR"); WrStr(CHR(48+ASH(cmd, -5))); ELSIF cm * {0,1,2,3} = RNR THEN WrStr("RNR"); WrStr(CHR(48+ASH(cmd, -5))); ELSIF cm * {0,1,2,3} = REJ THEN WrStr("REJ"); WrStr(CHR(48+ASH(cmd, -5))); ELSIF cm * {0} = {} THEN WrStr("I"); WrStr(CHR(48+ASH(cmd, -5))); WrStr(CHR(48+ASH(cmd, -1) MOD 8)); ELSIF cm = UI THEN WrStr("UI") ELSIF cm = DM THEN WrStr("DM"); ELSIF cm = SABM THEN WrStr("SABM") ELSIF cm = DISC THEN WrStr("DISC") ELSIF cm = UA THEN WrStr("UA") ELSIF cm = FRMR THEN WrStr("FRMR") ELSE WrHexCap(cmd,2,0) END; PF:="v^-+"; IF (com=0) OR (com=3) THEN WrStr("v1") ELSE WrStr(PF[com MOD 2 + 2*ORD(4 IN CAST(BITSET, cmd))]) END; END Showctl; VAR i:CARDINAL; v, d:BOOLEAN; h:ARRAY[0..20] OF CHAR; BEGIN WrStr(port); i:=0; WHILE NOT ODD(ORD(f[i])) DO INC(i); IF i>len THEN WrStrLn(" no ax.25 (no address end mark)"); RETURN (* no address end mark found *) END; END; IF i MOD 7 <> 6 THEN WrStrLn(" no ax.25 (address field size not multiples of 7)"); RETURN (* address end not modulo 7 error *) END; WrStr(":fm "); ShowCall(f, 7); WrStr(" to "); ShowCall(f, 0); i:=14; v:=TRUE; WHILE (i+6=128) & (ODD(ORD(f[i+6])) OR (ORD(f[i+13])<128)) THEN WrStr("*") END; INC(i,7); END; Showctl(ORD(7 IN CAST(SET8, f[6])) + 2*ORD(7 IN CAST(SET8,f[13])), ORD(f[i])); INC(i); IF i15C THEN WCh(f[i]); d:=TRUE; ELSIF d THEN WrStrLn(""); d:=FALSE END; INC(i); END; IF d THEN WrStrLn("") END; -- END; END ShowFrame; VAR i:CARDINAL; BEGIN fblen:=0; IF rawlen>0 THEN IF join THEN IF verb THEN WrStrLn("ax25 part 2") END; IF rawlen+2<=HIGH(raw) THEN AppCRC(raw, rawlen); IF (raw[rawlen]=axpart[253]) & (raw[rawlen+1]=axpart[254]) THEN (* join hash fits *) FOR i:=0 TO 252 DO fb[i]:=axpart[i] END; FOR i:=0 TO rawlen-1 DO fb[i+253]:=raw[i] END; fblen:=rawlen+253; ELSIF verb THEN WrStrLn("ax25 chain hash missmatch") END; END; END; join:=FALSE; IF fblen=0 THEN i:=0; WHILE (i=13) & (i<=69) THEN (* is pr, not 2 to 10 shift up calls *) IF rawlen=255 THEN FOR i:=0 TO HIGH(axpart) DO axpart[i]:=raw[i] END; join:=TRUE; IF verb THEN WrStrLn("ax25 part 1") END; ELSE FOR i:=0 TO rawlen-1 DO fb[i]:=raw[i] END; fblen:=rawlen; END; END; END; END; IF fblen>=2 THEN IF verb THEN WrStr("AX25:"); ShowFrame(fb, fblen-2, port) END; END; END decodepr; <* IF WITHFANET OR WITHMESHCORE THEN *> PROCEDURE encodeaprs(ipnum:IPNUM; udpport:UDPPORT; udp2:BOOLEAN; tocall, mycall-, comment-, sym-:ARRAY OF CHAR; lat-, long-:LONGREAL; speed, course, alt, clb, snrr, gust, temp, hum, baro:REAL; fecc:INTEGER; dcd:BOOLEAN; txd:CARDINAL; level:REAL; afc:INTEGER; qual:CARDINAL); CONST KNOTS=1.851984; FEET=1.0/0.3048; WKNOTS=1.609; (* wx knots *) PROCEDURE num(n:CARDINAL):CHAR; BEGIN RETURN CHR(n MOD 10 + ORD("0")) END num; PROCEDURE dao91(x:LONGREAL):CARDINAL; (* radix91(xx/1.1) of dddmm.mmxx *) VAR a:LONGREAL; BEGIN a:=ABS(x); RETURN (TRUNC((a-LFLOAT(TRUNC(a)))*600000.0) MOD 100*20+11) DIV 22 END dao91; VAR b,h:ARRAY[0..500] OF CHAR; i,n:CARDINAL; a:LONGREAL; v:REAL; sig:BOOLEAN; BEGIN b:=""; Append(b, mycall); Append(b, ">"); Append(b, tocall); Append(b, ":!"); i:=Length(b); a:=ABS(lat); n:=realcard(a); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); n:=realcard((a-LFLOAT(n))*6000.0); b[i]:=num(n DIV 1000); INC(i); b[i]:=num(n DIV 100); INC(i); b[i]:="."; INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); IF lat>=0.0 THEN b[i]:="N" ELSE b[i]:="S" END; INC(i); b[i]:=sym[0]; INC(i); a:=ABS(long); n:=realcard(a); b[i]:=num(n DIV 100); INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); n:=realcard((a-LFLOAT(n))*6000.0); b[i]:=num(n DIV 1000); INC(i); b[i]:=num(n DIV 100); INC(i); b[i]:="."; INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); IF long>=0.0 THEN b[i]:="E" ELSE b[i]:="W" END; INC(i); b[i]:=sym[1]; INC(i); IF (speed>0.0) OR (gust<1000.0) THEN n:=realcard(course+1.5); b[i]:=num(n DIV 100); INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); b[i]:="/"; INC(i); IF (sym[0]="/") & (sym[1]="_") THEN n:=realcard(speed*(1.0/WKNOTS)+0.5); ELSE n:=realcard(speed*(1.0/KNOTS)+0.5) END; b[i]:=num(n DIV 100); INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); IF gust<1000.0 THEN n:=realcard(gust*(1.0/WKNOTS)+0.5); b[i]:="g"; INC(i); b[i]:=num(n DIV 100); INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); END; END; IF ABS(temp)<1000.0 THEN v:=temp*1.8+32.0; sig:=v<0.0; n:=realcard(ABS(v)+0.5); b[i]:="t"; INC(i); IF sig THEN b[i]:="-" ELSE b[i]:=num(n DIV 100) END; INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); END; IF hum<=100.0 THEN b[i]:="h"; INC(i); n:=realcard(hum+0.5); IF n=100 THEN n:=0 END; b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); END; IF baro<10000.0 THEN b[i]:="b"; INC(i); n:=realcard(baro*10+0.5); b[i]:=num(n DIV 10000); INC(i); b[i]:=num(n DIV 1000); INC(i); b[i]:=num(n DIV 100); INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); END; IF alt>0.5 THEN b[i]:="/"; INC(i); b[i]:="A"; INC(i); b[i]:="="; INC(i); n:=realcard(ABS(alt*FEET+0.5)); IF alt>=0.0 THEN b[i]:=num(n DIV 100000) ELSE b[i]:="-" END; INC(i); b[i]:=num(n DIV 10000); INC(i); b[i]:=num(n DIV 1000); INC(i); b[i]:=num(n DIV 100); INC(i); b[i]:=num(n DIV 10); INC(i); b[i]:=num(n); INC(i); END; b[i]:="!"; INC(i); (* DAO *) b[i]:="w"; INC(i); b[i]:=CHR(33 + dao91(lat)); INC(i); b[i]:=CHR(33 + dao91(long)); INC(i); b[i]:="!"; INC(i); b[i]:=0C; IF ABS(clb)<1000.0 THEN Append(b, "Clb="); FixToStr(clb, 2, h); Append(b, h); Append(b, "m/s "); END; Append(b, comment); sendaxudp2(ipnum, udpport, udp2, b, Length(b), txd, level, snrr, afc, qual, TRUE, dcd); END encodeaprs; <* END *> <* IF WITHMESHCORE THEN *> PROCEDURE decodemeshcore(ipnum:IPNUM; udpport:UDPPORT; udp2:BOOLEAN; raw:ARRAY OF CHAR; dlen, fecbits:CARDINAL; txd:CARDINAL; level,snrr:REAL; qual,afc:INTEGER); VAR pr,pt,ph,app,i,j,hl,t,pl:CARDINAL; lat,long:LONGREAL; s,h,pn,ps,pp,nt,ms,sym:ARRAY[0..300] OF CHAR; trace:BOOLEAN; BEGIN (* 0e00d29c136d Meshcore:DIRECT/ACK Path: 26 02 1d f2 78 db 4a620000000000729272 Meshcore:DIRECT/TRACE Path:1d f2 26 02 f7 fb f1 17 b1b4000000000072a972 Meshcore:DIRECT/TRACE Path:f7 fb 26 02 31 f4 dc 78 744f000000000072a973a972 26 02 34 16 cc dd f1bd00000000000ec00e Meshcore:DIRECT/TRACE Path:34 16 Meshcore:DIRECT/TRACE Path:31 f4 26 02 3a00 f60d8d26000000000072b002b072 26 03 3a00e2 f60d8d26000000000072b002b072 i 26 04 3a00e2e6 f60d8d26000000000072b002b072 *) ps:="Meshcore:"; pr:=ORD(raw[0]); i:=1; trace:=FALSE; CASE pr MOD 4 OF 0: i:=5; Append(ps, "TRANSPORT_FLOOD"); |1: Append(ps, "FLOOD"); |2: Append(ps, "DIRECT"); ELSE i:=5; Append(ps, "TRANSPORT_DIRECT") END; Append(ps, "/"); pt:=pr DIV 4 MOD 16; CASE pt OF 0: Append(ps, "REQ"); |1: Append(ps, "RESPONSE"); |2: Append(ps, "TXT_MSG"); |3: Append(ps, "ACK"); |4: Append(ps, "ADVERT"); |5: Append(ps, "GRP_TXT"); |6: Append(ps, "GRP_DATA"); |7: Append(ps, "ANON_REQ"); |8: Append(ps, "PATH"); |9: Append(ps, "TRACE"); trace:=pr MOD 4=2; |10: Append(ps, "MULTIPART"); |11: Append(ps, "CONTROL"); |15: Append(ps, "RAW_CUSTOM"); ELSE Append(ps, "UNDEF") END; IF i=5 THEN Append(ps, " TRANS:"); i:=1; WHILE i<5 DO HexStr(ORD(raw[i]), 2, 2, TRUE, h); Append(ps, h); INC(i); END; END; pl:=ORD(raw[i]); INC(i); IF trace THEN Append(ps, " Trace[") ELSE Append(ps, " Path[") END; j:=0; hl:=pl; WHILE (hl>0) & (i0 THEN Append(ps, ",") END; HexStr(ORD(raw[i]), 2, 2, TRUE, h); Append(ps, h); END; INC(j); INC(i); DEC(hl); END; Append(ps, "]"); pp:=""; pn:=""; nt:=""; IF pt=4 THEN (* advert *) ph:=i; Append(ps, " Hash:"); HexStr(ORD(raw[i]), 2, 2, TRUE, h); Append(ps, h); INC(i,32); Append(ps, " Time:"); DateToStr(ORD(raw[i]) + ORD(raw[i+1])*100H + ORD(raw[i+2])*10000H + ORD(raw[i+3])*1000000H, h); Append(ps, h); INC(i,4+64); app:=ORD(raw[i]); INC(i); Append(ps, " App:"); HexStr(app,2,2,TRUE,h);Append(ps,h); lat:=0.0; long:=0.0; IF (i+890.0) OR (ABS(long)>180.0) THEN lat:=0.0; long:=0.0 END; END; IF app>=128 THEN (* bit 7 = has name *) pn:=" ["; j:=i; WHILE j0.0) OR (long<>0.0) THEN pp:=" pos:"; FixToStr(lat,5,h); Append(pp, h); Append(pp, ","); FixToStr(long,5,h); Append(pp, h); END; sym:="\g"; CASE app MOD 16 OF 1: nt:="ChatNode"; sym:="/l"; |2: nt:="Repeater"; sym:="/r"; |3: nt:="RoomServer"; sym:="/B"; |4: nt:="Sensor"; ELSE END; ----aprs IF pp[0]<>0C THEN HexStr(ORD(raw[ph+3]) + ORD(raw[ph+2])*100H + ORD(raw[ph+1])*10000H + ORD(raw[ph])*1000000H, 8, 0, TRUE, ms); (* mycall *) FixToStr(snrr,2,s); Append(s,"dB afc:"); IntToStr(afc,1,h); Append(s,h); Append(s,"Hz "); Append(s, ps); Append(s," "); Append(s, nt); Append(s, pn); encodeaprs(ipnum, udpport, udp2, "MSHCOR", ms, s, sym, lat, long, 0.0, 0.0, 0.0, 100000.0, snrr, 100000.0, 100000.0, 100000.0, 100000.0, fecbits, FALSE, txd, level, afc, qual); END; ----aprs ELSIF pt=5 THEN (* grp_txt *) Append(ps, " Hash:"); HexStr(ORD(raw[i]), 2, 2, TRUE, h); Append(ps, h); -- Append(ps, " Mac:"); HexStr(ORD(raw[i+1])*100H + ORD(raw[i+2]), 4, 4, TRUE, h); -- Append(ps, h); ELSIF pt=2 THEN (* txt *) Append(ps, " Dst:"); HexStr(ORD(raw[i]), 2, 2, TRUE, h); Append(ps, h); Append(ps, " Src:"); HexStr(ORD(raw[i+1]), 2, 2, TRUE, h); Append(ps, h); Append(ps, " Mac:"); HexStr(ORD(raw[i+2])*100H + ORD(raw[i+3]), 4, 4, TRUE, h); Append(ps, h); INC(i, 5); Append(ps, " ["); WHILE i <* IF WITHLORAWAN THEN *> PROCEDURE decodelorawan(text-:ARRAY OF CHAR; textlen:CARDINAL); PROCEDURE WH(title-,text-:ARRAY OF CHAR; from,too:CARDINAL); VAR i:CARDINAL; BEGIN WrStr(title); i:=from; WHILE (i<=too) & (i<=HIGH(text)) DO WrHex(ORD(text[i]),2,0); INC(i); END; END WH; VAR mtype, fctl, p:CARDINAL; BEGIN IF verb & (textlen>0) THEN mtype:=ORD(text[0]) DIV 32; (* MHDR *) CASE mtype OF 0:WrStr("Join-request"); |1:WrStr("Join-accept"); |2:WrStr("Unconfirmed Data Up"); |3:WrStr("Unconfirmed Data Down"); |4:WrStr("Confirmed Data Up"); |5:WrStr("Confirmed Data Down"); |6:WrStr("Rejoin-request"); ELSE WrStr("Proprietary"); END; WrStr(" RFU:"); WrInt(ORD(text[0]) DIV 4 MOD 8,1); WrStr(" Major:");WrInt(ORD(text[0]) MOD 4,1); IF (mtype=0) & (textlen>=18) THEN WH(" AppEUI:", text, 1,8); WH(" DevEUI:", text, 9,16); WH(" DevNonce:", text, 17,18); ELSIF (mtype=1) & (textlen>=12) THEN WH(" DevNonce:", text, 1,3); WH(" NetID:", text, 4,6); WH(" DevAddr:", text, 7,10); WH(" DLSettings:", text, 11,11); WH(" RxDelay:", text, 12,12); IF textlen>18+16 THEN WH(" CFList:", text, 13,28) END; ELSIF (mtype>=2) & (mtype<=5) THEN (* data frame *) fctl:=ORD(text[5]); WH(" DevAddr:", text, 1,4); WrStr(" ADR:"); WrInt(fctl DIV 128,1); IF ODD(ORD(mtype)) THEN WrStr(" RFU:") ELSE WrStr(" ADRACKReq:") END; WrInt(fctl DIV 64 MOD 2,1); WrStr(" ACK:"); WrInt(fctl DIV 32 MOD 2,1); IF ODD(ORD(mtype)) THEN WrStr(" FPending:") ELSE WrStr(" ClassB:") END; WrInt(fctl DIV 16 MOD 2,1); WrStr(" FOptsLen:"); WrInt(fctl MOD 16,1); WrStr(" FCnt:"); WrInt(ORD(text[6])+ORD(text[7])*256, 1); p:=0; IF fctl MOD 16>0 THEN WrStr(" FOpts:"); WHILE (p=6 THEN WrStr(" FPort:"); WrInt(ORD(text[p+8]),1); IF p+9<=textlen-5 THEN WH(" Payload:", text, p+9,textlen-5) END; WH(" MIC:", text, textlen-4,textlen-1); END; END; WrStrLn(""); END; END decodelorawan; <* END *> <* IF WITHMESHTASTIC THEN *> PROCEDURE decodemeshtastic(text-:ARRAY OF CHAR; textlen:CARDINAL); PROCEDURE WH(title-,text-:ARRAY OF CHAR; from,too:CARDINAL; revers:BOOLEAN); VAR i,j:CARDINAL; BEGIN WrStr(title); i:=from; WHILE i<=too DO j:=i; IF revers THEN j:=too-i+from END; IF j<=HIGH(text) THEN WrHex(ORD(text[j]),2,0) END; INC(i); END; END WH; VAR i:CARDINAL; BEGIN IF (textlen>=15) & verb THEN WH("Meshtastic: Dest:", text, 0, 3, TRUE); WH(" SRC:", text, 4,7, TRUE); WH(" MID:", text, 8,11, TRUE); i:=ORD(text[12]); WrStr(" HopLim:"); WrInt(i MOD 8,1); WrStr(" HopStart:"); WrInt(i DIV 32,1); IF ODD(i DIV 8) THEN WrStr(" WantAck") END; IF ODD(i DIV 16) THEN WrStr(" viaMQTT") END; WH(" Hash:", text, 13,13, FALSE); WH(" NextHop:", text, 14,14, FALSE); WH(" RelayNode:", text, 15,15, FALSE); WH(" Msg:[", text, 16,textlen-1, FALSE); WrStrLn("]"); END; END decodemeshtastic; <* END *> <* IF WITHMESHCOM4 THEN *> PROCEDURE decodemeshcom4(text-:ARRAY OF CHAR; textlen:CARDINAL); (* MESHCOM4 *) VAR i, te:CARDINAL; c:CHAR; BEGIN IF verb THEN ---fcs i:=0; te:=0; WHILE (te+30C)) DO INC(i, ORD(text[te])); INC(te) END; INC(i, ORD(text[te+1])); INC(i, ORD(text[te+2])); ---fcs IF (te>0) & (te+4 <* IF WITHFANET THEN *> PROCEDURE decodefanet(text-:ARRAY OF CHAR; textlen:CARDINAL; ipnum:IPNUM; udpport:UDPPORT; udp2:BOOLEAN; txd:INTEGER; level, snrr:REAL; afc, qual, fecc, cr:INTEGER; dcd:BOOLEAN); TYPE FANETPR=RECORD lat, long, speed, clb, gust, temp, hum, baro:REAL; alt, dir:CARDINAL; scall:ARRAY[0..8] OF CHAR; s:ARRAY[0..250] OF CHAR; END; VAR fp:FANETPR; PROCEDURE wcsv(n:CARDINAL; s-:ARRAY OF CHAR); VAR i:CARDINAL; BEGIN i:=0; WHILE n>0 DO IF i>=HIGH(s) THEN RETURN END; IF s[i]="," THEN DEC(n) END; INC(i); END; WHILE (i<=HIGH(s)) & (s[i]<>",") DO Append(fp.s, s[i]); INC(i) END; END wcsv; PROCEDURE spd(i:CARDINAL; scale:REAL; VAR v:REAL); BEGIN IF i>=128 THEN i:=i MOD 128*5 END; v:=FLOAT(i)*scale; END spd; PROCEDURE clb(p:CARDINAL; VAR v:REAL); VAR i:CARDINAL; ii:INTEGER; BEGIN i:=ORD(text[p]); ii:=i MOD 128; IF ii>=64 THEN ii:=64-ii END; IF i>=128 THEN ii:=ii*5 END; v:=VAL(REAL, ii)*0.1; END clb; PROCEDURE latlong(p:CARDINAL; scale:REAL; VAR d:REAL); VAR ii:INTEGER; BEGIN ii:=ORD(text[p+2])*10000H + ORD(text[p+1])*100H + ORD(text[p]); IF ii>=800000H THEN ii:=800000H-ii END; d:=VAL(REAL, ii)*scale; END latlong; PROCEDURE pos(VAR p:CARDINAL; VAR lat, long:REAL); BEGIN latlong(p, 1.0/93206.0, lat); (* lat *) INC(p,3); latlong(p, 1.0/46603.0, long); (* long *) INC(p,3); END pos; PROCEDURE apppos(lat, long:REAL; VAR s:ARRAY OF CHAR); VAR h:ARRAY[0..99] OF CHAR; BEGIN IF (lat<>0.0) & (long<>0.0) THEN FixToStr(lat, 6, h); Append(s, h); Append(s, ","); FixToStr(long,6,h); Append(s, h); Append(s, " "); END; END apppos; PROCEDURE manufact(b:CARDINAL); VAR s:ARRAY[0..99] OF CHAR; BEGIN s:=""; IF b=01H THEN s:="Skytraxx"; ELSIF b=003H THEN s:="BitBroker.eu"; ELSIF b=004H THEN s:="AirWhere"; ELSIF b=005H THEN s:="Windline"; ELSIF b=006H THEN s:="Burnair.ch"; ELSIF b=007H THEN s:="SoftRF"; ELSIF b=008H THEN s:="GXAircom"; ELSIF b=009H THEN s:="Airtribune"; ELSIF b=010H THEN s:="alfapilot"; --ELSIF b=011H THEN s:="FANET+ (incl FLARM. Currently Skytraxx, Naviter, and Skybean)"; ELSIF b=011H THEN s:="FANET+"; ELSIF b=00AH THEN s:="FLARM"; ELSIF b=020H THEN s:="XC Tracer"; ELSIF b=0E0H THEN s:="OGN Tracker"; ELSIF b=0E4H THEN s:="4aviation"; ELSIF b=0FAH THEN s:="Various"; --ELSIF b=0FBH THEN s:="Espressif based base stations, address is last 2bytes of MAC"; ELSIF b=0FBH THEN s:="Espressif based base station"; ELSIF b=0FCH THEN s:="Unregistered Devices"; ELSIF b=0FDH THEN s:="Unregistered Devices"; ELSIF b=0FEH THEN s:="[Multicast]"; END; Append(fp.s, "fw:["); Append(fp.s, s); Append(fp.s, "] "); END manufact; PROCEDURE srcdest(b:CARDINAL; VAR s:ARRAY OF CHAR); VAR h:ARRAY[0..9] OF CHAR; BEGIN s[0]:=0C; Append(s, "FNT"); HexStr(ORD(text[b])*10000H + ORD(text[b+1]) + ORD(text[b+2])*100H,6,7, TRUE, h); Append(s, h); END srcdest; PROCEDURE alt25(p:CARDINAL); VAR ii:INTEGER; h:ARRAY[0..9] OF CHAR; BEGIN ii:=ORD(text[p]); IF ii>=128 THEN ii:=128-ii END; ii:=(ii+109)*125; Append(fp.s, "alt:"); IntToStr(ii, 1, h); Append(fp.s, h); Append(fp.s, "m "); END alt25; CONST ATY="Other,Paraglider,Hangglider,Balloon,Glider,Powered Aircraft,Helicopter,UAV"; GNDVEHICLES="Other,Walking,Vehicle,Bike,Boot,Need a ride,Landed well,Need technical support,Need medical help,Distress call,Distress call automatically"; LANDM="Text,Line,Arrow,Area,Area Filled,Circle,Circle Filled,3D Line,3D Area,3D Cylinder"; LANDLAYER="Info,Warning,Keep out,Touch down,No airspace warn zone"; prATY="///g/g/O/g/'/X/n"; prGNDVEHICLES="///[/>/b/s/e\o/'/a\!\!"; VAR p,i:CARDINAL; ii:INTEGER; ss:SET32; typ, s:SET8; exth, signature, unicast:BOOLEAN; h:ARRAY[0..99] OF CHAR; v, lat, long:REAL; sym:ARRAY[0..1] OF CHAR; BEGIN sym:="//"; FILL(ADR(fp), 0C, SIZE(fp)); FixToStr(snrr, 2, h); Append(fp.s, "snr:"); Append(fp.s, h); Append(fp.s, "dB "); IF fecc>0 THEN IntToStr(fecc,1,h); Append(fp.s, "fec:"); Append(fp.s, h); Append(fp.s, " ") END; IntToStr(cr,1,h); Append(fp.s, "cr:"); Append(fp.s, h); Append(fp.s, " "); IF jmhz<>0.0 THEN FixToStr(jmhz, 4, h); Append(fp.s, h); Append(fp.s, "MHz ") END; fp.gust:=10000.0; fp.clb:=10000.0; fp.dir:=10000; fp.temp:=10000.0; fp.hum:=10000.0; fp.baro:=100000.0; typ:=CAST(SET8,text[0]); exth:=7 IN typ; IF exth THEN Append(fp.s, "Ext Header ") END; IF 6 IN typ THEN Append(fp.s,"Forward ") END; srcdest(1, fp.scall); manufact(ORD(text[1])); p:=4; IF exth THEN s:=CAST(SET8,text[p]); Append(fp.s,"ACK "); IF s*SET8{6,7}=SET8{} THEN Append(fp.s, "none "); ELSIF s*SET8{6,7}=SET8{6} THEN Append(fp.s,"requested "); ELSIF s*SET8{6,7}=SET8{7} THEN Append(fp.s,"requested via forward "); ELSE Append(fp.s,"unknown ") END; unicast:=5 IN s; IF unicast THEN Append(fp.s,"unicast ") ELSE Append(fp.s,"broadcast ") END; signature:=4 IN s; IF 3 IN s THEN Append(fp.s,"geobased fwd ") END; INC(p); IF unicast THEN Append(fp.s,"Dst:"); srcdest(p, h); Append(fp.s, h); manufact(p); INC(p,3); END; IF signature THEN Append(fp.s,"Signature:"); HexStr(ORD(text[p+3])*1000000H+ORD(text[p+2])*10000H+ORD(text[p+1])*100H+ORD(text[p]),8,9, FALSE, h); Append(fp.s, h); INC(p,4); END; END; typ:=typ*SET8{0..5}; IF typ=SET8{} THEN Append(fp.s,"no payload "); ELSIF typ=SET8{0} THEN (* tracking *) Append(fp.s,"tracking:"); pos(p, fp.lat, fp.long); ss:=CAST(SET32, ORD(text[p+1])*100H + ORD(text[p])); INC(p,2); IF 15 IN ss THEN Append(fp.s,"Online Tracking ") END; i:=CAST(CARDINAL, SHIFT(ss,-12)*SET32{0..2}); wcsv(i, ATY); IF i*2>=HIGH(prATY) THEN sym[0]:=prATY[i*2]; sym[1]:=prATY[i*2+1] END; Append(fp.s," "); i:=CAST(CARDINAL, ss*SET32{0..10}); IF 11 IN ss THEN i:=i*4 END; fp.alt:=i; spd(ORD(text[p]), 0.5, fp.speed); (* speed *) INC(p); clb(p, fp.clb); INC(p); fp.dir:=ORD(text[p])*360 DIV 256; INC(p); IF p=64 THEN ii:=-ii END; IF i>=128 THEN ii:=ii*4 END; FixToStr(VAL(REAL, ii)*0.25,2,h); Append(fp.s, h); Append(fp.s,"deg/s "); (* turn rate *) END; IF p=64 THEN ii:=-ii END; IF i>=128 THEN ii:=ii*4 END; Append(fp.s, "QNE:"); FixToStr(VAL(REAL,ii),2,h); Append(fp.s, h); Append(fp.s," "); (* QNE *) END; ELSIF typ=SET8{1} THEN (* text *) Append(fp.s,"Message:"); WHILE p=HIGH(prGNDVEHICLES) THEN sym[0]:=prGNDVEHICLES[i*2]; sym[1]:=prGNDVEHICLES[i*2+1] END; ELSIF typ=SET8{1,2} THEN (* remote config *) Append(fp.s, "Remote configuration:"); i:=ORD(text[p]); INC(p); IF i=0 THEN Append(fp.s, "Acknowledge configuration "); HexStr(ORD(text[p]),2,3, FALSE, h); Append(fp.s, h); INC(p); ELSIF i=1 THEN Append(fp.s, "Request "); HexStr(ORD(text[p]),2,3, FALSE,h); Append(fp.s,h); INC(p); ELSIF i=2 THEN pos(p, lat, long); alt25(p); INC(p); IntToStr(ORD(text[p])*360 DIV 256,1,h); Append(fp.s, h); Append(fp.s,"deg "); (* heading *) INC(p); ELSIF (i>=4) & (i<=8) THEN Append(fp.s, "Geofence "); alt25(p); INC(p); --- ELSIF (i>=9) & (i<=33) THEN Append(fp.s,"Broadcast Reply "); --- END; ELSIF typ=SET8{0,2} THEN (* remote config *) Append(fp.s,"Landmarks "); s:=CAST(SET8, ORD(text[p])); INC(p); wcsv(CAST(CARDINAL,s*SET8{0..3}),LANDM); Append(fp.s, " "); s:=CAST(SET8, ORD(text[p])); INC(p); wcsv(CAST(CARDINAL,s*SET8{0..3}),LANDLAYER); Append(fp.s," "); IF 4 IN s THEN INC(p); --- END; --- END; i:=Length(fp.s); WHILE (i>0) & (fp.s[i-1]=" ") DO DEC(i) END; IF i<=HIGH(fp.s) THEN fp.s[i]:=0C END; (* remove trailing blanks *) IF ((fp.lat<>0.0) OR (fp.long<>0.0)) & (udpport<>0) THEN encodeaprs(ipnum, udpport, udp2, "FANET", fp.scall, fp.s, sym, fp.lat, fp.long, fp.speed, VAL(REAL, fp.dir), VAL(REAL, fp.alt), fp.clb, snrr, fp.gust, fp.temp, fp.hum, fp.baro, fecc, dcd, txd, level, afc, qual); END; IF verb THEN WrStr("Fanet:"); WrStr(fp.scall); WrStr(" "); h:=""; apppos(fp.lat, fp.long, h); WrStr(h); IF fp.alt<>0 THEN WrInt(fp.alt,1); WrStr("m ") END; IF fp.speed<>0.0 THEN WrFixed(fp.speed,1,1); WrStr("km/h ") END; IF fp.dir<=360 THEN WrInt(fp.dir,1); WrStr("deg ") END; IF ABS(fp.clb)<1000.0 THEN WrStr("Clb:"); WrFixed(fp.clb,1,1); WrStr("m/s ") END; WrStrLn(fp.s); END; END decodefanet; <* END *> PROCEDURE hasjunk(h-:ARRAY OF CHAR):BOOLEAN; BEGIN RETURN (h[0]=CHR(3CH)) & (h[1]=CHR(0FFH)) & (h[2]=CHR(01H)) END hasjunk; PROCEDURE sendframe(text:ARRAY OF CHAR; textlen:CARDINAL; fpar:FPAR; axpart:ARRAY OF CHAR; notchlist, finf:ARRAY OF CHAR; ipnum, ipnumraw:IPNUM; udpport, udprawport:UDPPORT; udp2:BOOLEAN); VAR i, axlen:CARDINAL; ret:INTEGER; s:ARRAY[0..511] OF CHAR; BEGIN WITH fpar DO IF verb & (NOT quietcrc OR hascrc & crc) THEN TimeToStr(time() MOD 86400, s); WrStr(s); WrStr(" "); WrInt(cfgsf,1); IF invers THEN WrStr("-") ELSE WrStr(":") END; WrInt(label,1); WrStr(":"); IF illid=0 THEN WrStr("id:");WrHexCap(idfound,2,0); ELSE WrStr(" ill-id:"); WrHexCap(illid*256+idfound,4,0) END; WrStr(" cr:");WrInt(cr,1); WrStr(" len:");WrInt(textlen,1); IF dcdlost THEN WrStr(" dcd-lost") END; WrStr(" crc:"); IF hascrc THEN IF crc THEN WrStr("ok") ELSE WrStr("err") END; ELSE WrStr("no") END; WrStr(" lev:"); WrFixed(level, 1, 1); WrStr("("); WrFixed(minlev, 1, 1);WrStr("/"); WrFixed(maxlev, 1, 1); WrStr(")dB snr:");WrFixed(snr, 1, 1); WrStr("dB nf:");WrFixed(nf, 1, 1); WrStr("("); WrFixed(minnf, 1, 1);WrStr("/"); WrFixed(maxnf, 1, 1); WrStr(")dB txd:");WrInt(txd, 1); WrStr(" t:");WrInt(frametime, 1); WrStr(" q:");WrInt(qual, 1); WrStr("%"); IF fasecorrs<>0 THEN WrStr(" fc:"); WrInt(fasecorrs, 1) END; IF fecbits>0 THEN WrStr(" fec:");WrInt(fecbits, 1) END; WrStr(" afc:");WrInt(truedf, 1); WrStr("Hz"); WrStr(" dre:");WrFixed(drift, 1,1); WrStr("ppm"); IF jmhz<>0.0 THEN WrStr(" "); WrFixed(jmhz, 3, 1); WrStr("MHz") END; WrStr(notchlist); WrStrLn(""); WrStr("[");WrStr(finf); WrStr("]"); IF textlen>0 THEN FOR i:=0 TO textlen-1 DO IF allwaysascii THEN WrChHex(text[i]) ELSE WrHex(ORD(text[i]),2,0) END; END; END; WrStrLn(""); END; IF ((synfilter MOD 256 DIV 16=0) OR (idfound DIV 16=synfilter MOD 256 DIV 16)) & ((synfilter MOD 16=0) OR (idfound MOD 16=synfilter MOD 16)) THEN IF NOT ax25long & (udprawport<>0) & (NOT udpcrcok OR hascrc & crc & NOT dcdlost) THEN ret:=udpsend(udpsock, text, textlen, udprawport, ipnumraw); END; ELSIF verb & (NOT quietcrc OR hascrc & crc) THEN WrStr("frame deleted, wrong sync word "); WrHexCap(idfound,2,0); WrStrLn(""); END; IF (jdests<>NIL) OR (jpipename[0]<>0C) THEN sendjson(jdests, idfound, text, textlen, hascrc, crc, invers, cfgsf, cr, txd, frametime, level, nf, maxnf, snr, drift, truedf, qual, fecbits); END; IF hascrc & crc & (textlen>0) THEN <* IF WITHLORAWAN THEN *> IF idfound=34H THEN decodelorawan(text, textlen) END; (* LORAWAN *) <* END *> IF idfound MOD 256=12H THEN decodepr(text, textlen, "0", axjoin, axpart, s, axlen); (* PR *) IF ax25long & (axlen>0) & (udprawport<>0) THEN ret:=udpsend(udpsock, s, axlen, udprawport, ipnumraw); ELSIF (udpport<>0) & NOT dcdlost & (NOT needsjunk OR hasjunk(text)) THEN sendaxudp2(ipnum, udpport, udp2, text, textlen, txd, level, snr, truedf, qual, FALSE, dcd); END; <* IF WITHMESHCORE THEN *> IF NOT hamview THEN decodemeshcore(ipnum, udpport, udp2, text, textlen, fecbits, txd, level, snr, qual, truedf) END; (* else might be aprs *) <* END *> END; <* IF WITHMESHTASTIC THEN *> IF NOT hamview & (idfound MOD 256=2BH) THEN decodemeshtastic(text, textlen) END; (* MESHTASTIC *) <* END *> <* IF WITHFANET THEN *> IF idfound=0F1H THEN decodefanet(text, textlen, ipnum, udpport, udp2, txd, level, snr, truedf, qual, fecbits, cr, dcd); (* FANET *) END; <* END *> <* IF WITHMESHCOM4 THEN *> IF hamview & (idfound DIV 16=2) & ((text[0]="!") OR (text[0]=":")) THEN decodemeshcom4(text, textlen); (* MESHCOM4 *) END; <* END *> END; IF newline & verb & (NOT quietcrc OR hascrc & crc) THEN WrStrLn("") END; END; END sendframe; BEGIN allwaysascii:=FALSE; jsonfd:=-1; END loraprotocols.