/* Generated by XDS Modula-2 to ANSI C v4.20 translator */ #define X2C_int32 #define X2C_index32 #ifndef X2C_H_ #include "X2C.h" #endif #define pocenc_C_ #ifndef udp_H_ #include "udp.h" #endif #ifndef osi_H_ #include "osi.h" #endif #include #ifndef aprsstr_H_ #include "aprsstr.h" #endif #include /* encode pocsag from stdin text to rawbits in udp from https://github.com/faithanalog/pocsag-encoder */ #define pocenc_LF "\012" #define pocenc_CRCBITS 10 #define pocenc_TEXTBITSPERCHAR 7 #define pocenc_FLAGMESSAGE 0x100000 #define pocenc_BATCHSIZE 16 #define pocenc_SYNC 0x7CD215D8 #define pocenc_FRAMESIZE 2 #define pocenc_PREAMBLELENGTH 576 #define pocenc_IDLE 0x7A89C197 #define pocenc_FLAGTEXTDATA 3 #define pocenc_FLAGNUMERICDATA 0 #define pocenc_TEXTBITSPERWORD 20 static uint32_t udpip; static uint32_t udpport; static int32_t udpfd; static uint32_t func; static uint32_t addr; static char inv; static char text[301]; static void Error(const char text0[], uint32_t text_len) { osi_Werr(text0, text_len); osi_Werr(" error abort\012", 14ul); X2C_ABORT(); } /* end Error() */ static int32_t GetIp(char h[], uint32_t h_len, uint32_t * p0, uint32_t * ip, uint32_t * port) { uint32_t n; uint32_t i0; char ok0; int32_t GetIp_ret; X2C_PCOPY((void **)&h,h_len); *p0 = 0UL; h[h_len-1] = 0; *ip = 0UL; for (i0 = 0UL; i0<=4UL; i0++) { n = 0UL; ok0 = 0; while ((uint8_t)h[*p0]>='0' && (uint8_t)h[*p0]<='9') { ok0 = 1; n = (n*10UL+(uint32_t)(uint8_t)h[*p0])-48UL; ++*p0; } if (!ok0) { GetIp_ret = -1L; goto label; } if (i0<3UL) { if (h[*p0]!='.' || n>255UL) { GetIp_ret = -1L; goto label; } *ip = *ip*256UL+n; } else if (i0==3UL) { *ip = *ip*256UL+n; if (h[*p0]!=':' || n>255UL) { GetIp_ret = -1L; goto label; } } else if (n>65535UL) { GetIp_ret = -1L; goto label; } *port = n; ++*p0; } /* end for */ GetIp_ret = 0L; label:; X2C_PFREE(h); return GetIp_ret; } /* end GetIp() */ static void Parms(void) { char h[4096]; uint32_t i0; for (;;) { osi_NextArg(h, 4096ul); if (h[0U]==0) break; if (h[0U]=='-' && h[1U]) { if (h[1U]=='h') { osi_WrStrLn("", 1ul); osi_WrStrLn("encode text on stdin to POCSAG bits sent in UDP, 0 is fsk high freq", 68ul); osi_WrStrLn(" -a address RIC 0..2097151 (0)", 47ul); osi_WrStrLn(" -f funcion 0..3 (3)", 37ul); osi_WrStrLn(" -h this", 25ul); osi_WrStrLn(" -i invert fsk", 31ul); osi_WrStrLn(" -t if not message text from stdin, use quotes for blanks", 74ul); osi_WrStrLn(" -u : send encoded bits to tx", 44ul); X2C_ABORT(); } if (h[1U]=='u') { osi_NextArg(h, 4096ul); if (GetIp(h, 4096ul, &i0, &udpip, &udpport)) Error("-u ip:port", 11ul); udpfd = openudp(); if (udpfd<0L) Error("-u cannot open udp", 19ul); } else if (h[1U]=='a') { osi_NextArg(h, 4096ul); if (!aprsstr_StrToCard(h, 4096ul, &addr) || addr>2097152UL) { Error("-a <=2097151", 19ul); } } else if (h[1U]=='i') inv = 1; else if (h[1U]=='f') { osi_NextArg(h, 4096ul); if (!aprsstr_StrToCard(h, 4096ul, &func) || func>3UL) Error("-f 0..3", 14ul); } else if (h[1U]=='t') osi_NextArg(text, 301ul); else { h[2U] = 0; aprsstr_Append(h, 4096ul, "? use -h", 9ul); Error(h, 4096ul); } h[0U] = 0; } else { h[1U] = 0; aprsstr_Append(h, 4096ul, "? use -h", 9ul); Error(h, 4096ul); } } } /* end Parms() */ /* * Calculate the CRC error checking code for the given word. * Messages use a 10 bit CRC computed from the 21 data bits. * This is calculated through a binary polynomial long division, returning * the remainder. * See https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Computation * for more information. */ static uint32_t pocenc_CRC_GENERATOR = 0x769UL; static uint32_t crc(uint32_t msg) { uint32_t m; uint32_t denominator; uint32_t column; char mb; denominator = 0x76900000UL; /*Message is right-padded with zeroes to the message length + crc length */ m = X2C_LSH(msg,32,10); /*We iterate until denominator has been right-shifted back to it's original value. */ for (column = 0UL; column<=20UL; column++) { /*Bit for the column we're aligned to */ mb = X2C_IN(30UL-column,32,m); /*If the current bit is zero, we don't modify the message this iteration */ if (mb) m = m^denominator; denominator = X2C_LSH(denominator,32,-1); } /* end for */ /*At this point 'm' contains the CRC value we've calculated */ return m&0x3FFUL; } /* end crc() */ /** * Calculates the even parity bit for a message. * If the number of bits in the message is even, return 0, else return 1. */ static char parity(uint32_t x) { char p0; uint32_t i0; p0 = 0; for (i0 = 0UL; i0<=31UL; i0++) { p0 = p0!=X2C_IN(i0,32,x); } /* end for */ return p0; } /* end parity() */ /** * Encodes a 21-bit message by calculating and adding a CRC code and parity bit. */ static uint32_t encodeCodeword(uint32_t msg) { uint32_t fullCRC; fullCRC = X2C_LSH(msg,32,10)|crc(msg); return (uint32_t)fullCRC*2UL+(uint32_t)parity(fullCRC); } /* end encodeCodeword() */ /** * ASCII encode a null-terminated string as a series of codewords, written * to *out. Returns the number of codewords written. Caller should ensure * that enough memory is allocated in *out to contain the message * * initial_offset indicates which word in the current batch the function is * beginning at, so that it can insert SYNC words at appropriate locations. */ static void encodeASCII(uint32_t initialoffset, const char s[], uint32_t s_len, uint32_t o[], uint32_t o_len, uint32_t * out) { uint32_t str; uint32_t wordPosition; uint32_t currentNumBits; uint32_t currentWord; uint32_t i0; char c; /*Number of words written to *out */ currentWord = 0UL; /*Nnumber of bits we've written so far to the current word */ currentNumBits = 0UL; /*Position of current word in the current batch */ wordPosition = initialoffset; str = 0UL; while (s[str]) { c = s[str]; ++str; /*Encode the character bits backwards */ for (i0 = 0UL; i0<=6UL; i0++) { currentWord = currentWord*2UL+(uint32_t)X2C_IN(i0,8,(uint8_t)(uint8_t)c); ++currentNumBits; if (currentNumBits==20UL) { /*Add the MESSAGE flag to our current word and encode it. */ o[*out] = encodeCodeword((uint32_t)currentWord|0x100000UL); ++*out; currentWord = 0UL; currentNumBits = 0UL; ++wordPosition; if (wordPosition==16UL) { /*We've filled a full batch, time to insert a SYNC word */ /*and start a new one. */ o[*out] = 2094142936UL; ++*out; wordPosition = 0UL; } } } /* end for */ } /*Write remainder of message */ if (currentNumBits>0UL) { /*Pad out the word to 20 bits with zeroes */ currentWord = (uint32_t)X2C_LSH((uint32_t)currentWord,32,(int32_t)(20UL-currentNumBits)); o[*out] = encodeCodeword((uint32_t)currentWord|0x100000UL); ++*out; ++wordPosition; if (wordPosition==16UL) { /*We've filled a full batch, time to insert a SYNC word */ /*and start a new one. */ o[*out] = 2094142936UL; ++*out; wordPosition = 0UL; } } return; } /* end encodeASCII() */ /** * An address of 21 bits, but only 18 of those bits are encoded in the address * word itself. The remaining 3 bits are derived from which frame in the batch * is the address word. This calculates the number of words (not frames!) * which must precede the address word so that it is in the right spot. These * words will be filled with the idle value. */ static uint32_t addressOffset(uint32_t address) { return (address&7UL)*2UL; } /* end addressOffset() */ /** * Encode a full text POCSAG transmission addressed to (address). * *message is a null terminated C string. * *out is the destination to which the transmission will be written. */ static void encodeTransmission(uint32_t address, const char m[], uint32_t m_len, uint32_t o[], uint32_t o_len, uint32_t * out) { uint32_t message; uint32_t start; uint32_t i0; uint32_t prefixLength; uint32_t written; uint32_t padding; int32_t j1; int32_t tmp; /*Encode preamble */ /*Alternating 1,0,1,0 bits for 576 bits, used for receiver to synchronize */ /*with transmitter */ *out = 0UL; message = 0UL; for (i0 = 0UL; i0<=17UL; i0++) { o[*out] = 0x0AAAAAAAAUL; ++*out; } /* end for */ start = *out; /*Sync */ o[*out] = 2094142936UL; ++*out; /*Write out padding before address word */ prefixLength = addressOffset(address); tmp = (int32_t)prefixLength-1L; j1 = 0L; if (j1<=tmp) for (;; j1++) { o[*out] = 2055848343UL; ++*out; if (j1==tmp) break; } /* end for */ /*Write address word. */ /*The last two bits of word's data contain the message type */ /*The 3 least significant bits are dropped, as those are encoded by the */ /*word's location. */ o[*out] = encodeCodeword(X2C_LSH(X2C_LSH((uint32_t)address&0x1FFFFFUL,32,-3),32,2)|(uint32_t)(func&3UL)); ++*out; /*Encode the message itself */ encodeASCII(addressOffset(address)+1UL, m, m_len, o, o_len, out); /*Finally, write an IDLE word indicating the end of the message */ o[*out] = 2055848343UL; ++*out; /*Pad out the last batch with IDLE to write multiple of BATCH_SIZE + 1 */ /*words (+ 1 is there because of the SYNC words) */ written = *out-start; padding = 17UL-written%17UL; tmp = (int32_t)padding-1L; j1 = 0L; if (j1<=tmp) for (;; j1++) { o[*out] = 2055848343UL; ++*out; if (j1==tmp) break; } /* end for */ } /* end encodeTransmission() */ static uint32_t bitbuf[1501]; static char ubuf[1501]; static uint32_t len; static uint32_t j; static uint32_t i; static uint32_t p; static int32_t ret; X2C_STACK_LIMIT(8000000l) extern int main(int argc, char **argv) { uint32_t tmp; X2C_BEGIN(&argc,argv,1,20000000l,32000000l); aprsstr_BEGIN(); osi_BEGIN(); addr = 0UL; text[0] = 0; func = 3UL; inv = 0; Parms(); if (text[0U]==0) { ret = osi_RdBin(0L, (char *)text, 301u/1u, 301UL); if (ret<0L) Error("missing text", 13ul); if ((uint32_t)ret<=300UL) text[ret] = 0; } encodeTransmission(addr, text, 301ul, bitbuf, 1501ul, &len); /*WrInt(len,1); WrStrLn("=len"); */ /*bitbuf[22]:=CAST(CARDINAL, CAST(SET32, bitbuf[22])/SET32{27, 24}); */ tmp = len-1UL; i = 0UL; if (i<=tmp) for (;; i++) { for (j = 0UL; j<=31UL; j++) { osic_WrINT32((uint32_t)X2C_IN(31UL-j,32,(uint32_t)bitbuf[i]), 1UL); } /* end for */ osi_WrStrLn("", 1ul); if (i==tmp) break; } /* end for */ /* INC(len, 4); */ if (len>=375UL) { osic_WrINT32(len*4UL, 1UL); osi_WrStrLn("=frame too long ", 17ul); len = 375UL; } tmp = len-1UL; i = 0UL; if (i<=tmp) for (;; i++) { p = bitbuf[i]; for (j = 3UL;; j--) { if (inv) ubuf[i*4UL+j] = (char)(p&255UL); else ubuf[i*4UL+j] = (char)(255UL-(p&255UL)); p = p/256UL; if (j==0UL) break; } /* end for */ if (i==tmp) break; } /* end for */ if (udpsend(udpfd, ubuf, (int32_t)(len*4UL), udpport, udpip)<0L) osi_WrStrLn("udp send error", 15ul); X2C_EXIT(); return 0; } X2C_MAIN_DEFINITION