#include "../features.h" #if FEATURE_RS92 /* RS92 decoder functions */ #include "RS92.h" #include "SX1278FSK.h" #include "rsc.h" #include "Sonde.h" #include // well... #include "rs92gps.h" #define RS92_DEBUG 1 #if RS92_DEBUG #define RS92_DBG(x) x #else #define RS92_DBG(x) #endif uint16_t *CRCTAB = NULL; #define X2C_DIVR(a, b) ((b) != 0.0f ? (a)/(b) : (a)) #define X2C_DIVL(a, b) ((a)/(b)) static uint32_t X2C_LSH(uint32_t a, int32_t length, int32_t n) { uint32_t m; m = 0; m = (length == 32) ? 0xFFFFFFFFl : (1 << length) - 1; if (n > 0) { if (n >= (int32_t)length) return 0; return (a << n) & m; } if (n <= (int32_t)-length) return 0; return (a >> -n) & m; } static void Gencrctab(void) { uint16_t j; uint16_t i; uint16_t crc; if(!CRCTAB) { CRCTAB=(uint16_t *)malloc(256*sizeof(uint16_t)); } for (i = 0U; i<=255U; i++) { crc = (uint16_t)(i*256U); for (j = 0U; j<=7U; j++) { if ((0x8000U & crc)) crc = X2C_LSH(crc,16,1)^0x1021U; else crc = X2C_LSH(crc,16,1); } /* end for */ CRCTAB[i] = X2C_LSH(crc,16,-8)|X2C_LSH(crc,16,8); } /* end for */ } /* end Gencrctab() */ static byte data1[512]={0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x10}; static byte data2[512]={0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x10}; static byte *dataptr=data1; static uint8_t rxbitc; static int32_t asynst[10]={0}; static uint16_t rxbyte; int rxp=0; static int haveNewFrame = 0; static int lastFrame = 0; static int headerDetected = 0; decoderSetupCfg rs92SetupCfg = { .bitrate = 4800, .rx_cfg = 0x1E, .sync_cfg = 0x70, .sync_len = 2, .sync_data = (const uint8_t *)"\x66\x65", .preamble_cfg = 0xA8, }; int RS92::setup(float frequency, int /*type*/) { #if RS92_DEBUG Serial.println("Setup sx1278 for RS92 sonde"); #endif if(!initialized) { Gencrctab(); initrsc(); // not here for now.... get_eph("/brdc.19n"); initialized = true; } if(sx1278.ON()!=0) { RS92_DBG(Serial.println("Setting SX1278 power on FAILED")); return 1; } if(DecoderBase::setup(rs92SetupCfg, sonde.config.rs92.rxbw, sonde.config.rs92.rxbw)!=0) { return 1; } #if 0 if(sx1278.setFSK()!=0) { RS92_DBG(Serial.println("Setting FSJ mode FAILED")); return 1; } if(sx1278.setBitrate(4800)!=0) { RS92_DBG(Serial.println("Setting bitrate 4800bit/s FAILED")); return 1; } #if RS92_DEBUG float br = sx1278.getBitrate(); Serial.print("Exact bitrate is "); Serial.println(br); #endif if(sx1278.setAFCBandwidth(sonde.config.rs92.rxbw)!=0) { RS92_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs92.rxbw)); return 1; } if(sx1278.setRxBandwidth(sonde.config.rs92.rxbw)!=0) { RS92_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs92.rxbw)); return 1; } // Enable auto-AFC, auto-AGC, RX Trigger by preamble if(sx1278.setRxConf(0x1E)!=0) { RS92_DBG(Serial.println("Setting RX Config FAILED")); return 1; } // Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte //char header[] = "0110.0101 0110.0110 1010.0101 1010.1010"; //const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8"; //const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F"; // was 0x57 //const char *SYNC="\x99\x9A"; // version 1, working with continuous RX const char *SYNC="\x66\x65"; if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) { RS92_DBG(Serial.println("Setting SYNC Config FAILED")); return 1; } if(sx1278.setPreambleDetect(0xA8)!=0) { RS92_DBG(Serial.println("Setting PreambleDetect FAILED")); return 1; } #endif #if 0 // version 2, with per-packet rx start, untested // header is 2a 10 65, i.e. with lsb first // 0 0101 0100 1 0 0000 1000 1 0 1010 0110 1 // 10 10011001 10011010 01 10 10101010 01101010 01 10 01100110 10010110 01 // preamble 0x6A 0x66 0x6A // i.e. preamble detector on (0x80), preamble detector size 1 (0x00), preample chip errors??? (0x0A) // after 2a2a2a2a2a1065 if(sx1278.setPreambleDetect(0xA8)!=0) { RS92_DBG(Serial.println("Setting PreambleDetect FAILED")); return 1; } // sync config: ato restart (01), preamble polarity AA (0), sync on (1), resevered (0), syncsize 2+1 (010) => 0x52 const char *SYNC="\x6A\x66\x69"; if(sx1278.setSyncConf(0x52, 3, (const uint8_t *)SYNC)!=0) { RS92_DBG(Serial.println("Setting SYNC Config FAILED")); return 1; } // payload length is ((240 - 7)*10 +6)/8 = 292 #endif // Packet config 1: fixed len, no mancecer, no crc, no address filter // Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0) if(sx1278.setPacketConfig(0x08, 0x40)!=0) { RS92_DBG(Serial.println("Setting Packet config FAILED")); return 1; } Serial.print("RS92: setting RX frequency to "); Serial.println(frequency); int res = sx1278.setFrequency(frequency); sx1278.clearIRQFlags(); // enable RX sx1278.setPayloadLength(0); // infinite for now... //sx1278.setPayloadLength(292); sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); #if RS92_DEBUG RS92_DBG(Serial.println("Setting SX1278 config for RS92 finished\n"); Serial.println()); #endif return res; } uint32_t RS92::bits2val(const uint8_t *bits, int len) { uint32_t val = 0; for (int j = 0; j < len; j++) { val |= (bits[j] << (len-1-j)); } return val; } RS92::RS92() { } /* RS92 reed solomon decoder, from dxlAPRS */ #if 0 static char crcrs(const byte frame[], uint32_t frame_len, int32_t from, int32_t to) { uint16_t crc; int32_t i; int32_t tmp; crc = 0xFFFFU; tmp = to-3L; i = from; if (i<=tmp) for (;; i++) { crc = X2C_LSH(crc,16,-8)^CRCTAB[(uint32_t)((crc^(uint16_t)(uint8_t)frame[i])&0xFFU)]; if (i==tmp) break; } /* end for */ return frame[to-1L]==(char)crc && frame[to-2L]==(char)X2C_LSH(crc, 16,-8); } /* end crcrs() */ static int32_t getint32(const byte frame[], uint32_t frame_len, uint32_t p) { uint32_t n; uint32_t i; n = 0UL; for (i = 3UL;; i--) { n = n*256UL+(uint32_t)(uint8_t)frame[p+i]; if (i==0UL) break; } /* end for */ return (int32_t)n; } /* end getint32() */ static uint32_t getcard16(const byte frame[], uint32_t frame_len, uint32_t p) { return (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t) frame[p+1UL]; } /* end getcard16() */ static int32_t getint16(const byte frame[], uint32_t frame_len, uint32_t p) { uint32_t n; n = (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t) frame[p+1UL]; if (n>=32768UL) return (int32_t)(n-65536UL); return (int32_t)n; } /* end getint16() */ static void wgs84r(double x, double y, double z, double * lat, double * long0, double * heig) { double sl; double ct; double st; double t; double rh; double xh; double h; h = x*x+y*y; if (h>0.0) { rh = (double)sqrt((float)h); xh = x+rh; *long0 = atang2(xh, y)*2.0; if (*long0>3.1415926535898) *long0 = *long0-6.2831853071796; t = (double)atan((float)(X2C_DIVL(z*1.003364089821, rh))); st = (double)sin((float)t); ct = (double)cos((float)t); *lat = (double)atan((float) (X2C_DIVL(z+4.2841311513312E+4*st*st*st, rh-4.269767270718E+4*ct*ct*ct))); sl = (double)sin((float)*lat); *heig = X2C_DIVL(rh,(double)cos((float)*lat))-(double)(X2C_DIVR(6.378137E+6f, sqrt((float)(1.0-6.6943799901413E-3*sl*sl)))); } else { *lat = 0.0; *long0 = 0.0; *heig = 0.0; } /* lat:=atan(z/(rh*(1.0 - E2))); */ /* heig:=sqrt(h + z*z) - EARTHA; */ } /* end wgs84r() */ #endif static int32_t reedsolomon92(uint8_t *buf, uint32_t buf_len) { uint32_t i; int32_t res; uint8_t b[256]; uint32_t eraspos[24]; for (i = 0UL; i<=255UL; i++) { b[i] = 0; } /* end for */ for (i = 0UL; i<=209UL; i++) { b[230UL-i] = buf[i+6UL]; } /* end for */ for (i = 0UL; i<=23UL; i++) { b[254UL-i] = buf[i+216UL]; } /* end for */ res = decodersc((char *)b, eraspos, 0L); if (res>0L && res<=12L) { for (i = 0UL; i<=209UL; i++) { buf[i+6UL] = b[230UL-i]; } /* end for */ for (i = 0UL; i<=23UL; i++) { buf[i+216UL] = b[254UL-i]; } /* end for */ } return res; } /* end reedsolomon92() */ void printRaw(uint8_t *data, int len) { char buf[3]; int i; for(i=0; i240) { Serial.print("RS92 frame too long: "); Serial.println(len); break; } ++p; j=0; uint16_t crc = 0xFFFF; while(j240) { Serial.println("eof"); return; } } if ( (((uint8_t)(crc&0xff)) != data[p-2]) || (((uint8_t)(crc>>8)) != data[p-1])) { Serial.printf("************ crc error: expected %04x\n",crc); continue; } switch(typ) { case 'e': Serial.println("cal "); //docalib(sf, 256, objname, 9, &contextr9, &mhz, &frameno); // ... break; case 'i': if(calok && calibok==0xffffffff) { //domes(sf, 256, &hp, &hyg, &temp) mesok = 1; } break; case 'g': Serial.println("gps "); if(1||calok) { //dogps(data+p-len, 256, &contextr9, &contextr9.timems, &gpstime); } break; case 'h': Serial.println("data "); break; if(data[p+2]!=3) Serial.println("aux "); // .. break; } } #endif } /* end decodeframe92() */ void RS92::printRaw(uint8_t *data, int len) { char buf[3]; int i; for(i=0; i=5 || b=='*') rxp++; else rxp=0; if(rxp==6) { // header detected headerDetected = 1; } if(rxp>=240) { // frame complete... (240 byte) rxp=0; //printRaw(data, 240); decodeframe92(dataptr); haveNewFrame = 1; } } /* end stobyte92() */ uint32_t rxdata; bool rxsearching=true; // search for // 101001100110011010011010011001100110100110101010100110101001 // 1010011001100110100110100110 0110.0110 1001.1010 1010.1001 1010.1001 => 0x669AA9A9 void RS92::process8N1data(uint8_t dt) { for(int i=0; i<8; i++) { uint8_t d = (dt&0x80)?1:0; rxdata = (rxdata<<1) | d; if((rxbitc&1)==1) { rxbyte = (rxbyte>>1) + (d<<9); } // mancester decoded data dt <<= 1; // if(rxsearching) { if(rxdata == 0x669AA9A9) { rxsearching = false; rxbitc = 0; rxp = 6; int rssi=sx1278.getRSSI(); int fei=sx1278.getFEI(); int afc=sx1278.getAFC(); Serial.print("Test: RSSI="); Serial.print(rssi); Serial.print(" FEI="); Serial.print(fei); Serial.print(" AFC="); Serial.println(afc); sonde.si()->rssi = rssi; sonde.si()->afc = afc; } } else { rxbitc = (rxbitc+1)%20; if(rxbitc == 0) { // got startbit, 8 data bit, stop bit //Serial.printf("%03x ",rxbyte); dataptr[rxp++] = (rxbyte>>1)&0xff; if(rxp==7 && dataptr[6] != 0x65) { Serial.printf("wrong start: %02x\n",dataptr[6]); rxsearching = true; } if(rxp>=240) { rxsearching = true; decodeframe92(dataptr); haveNewFrame = 1; } } } } } void process8N1dataOrig(uint8_t data) { // data contains 8 bits (after mancester encoding; 4 real bit), big endian for(int i=0; i<4; i++) { uint8_t d = (data&0x80)?1:0; data = data << 2; rxbyte = (rxbyte>>1) + (d<<8); int maxk = 0; int max0 = 0; for(int k = 0; k< 10; k++) { int n = asynst[k] - asynst[(k+1)%10]; if(abs(n)>abs(max0)) { max0 = n; maxk = k; } } //Serial.printf("<%d,%d,%d>",max0,maxk,rxbitc); if(rxbitc == maxk) { if(max0<0) { rxbyte = rxbyte ^ 0xFF; } /////TODO stobyte92( rxbyte&0xff ); } //Serial.printf("%d:",asynst[rxbitc]); if(d) { asynst[rxbitc] += (32767-asynst[rxbitc])/16; } else { asynst[rxbitc] -= (32767+asynst[rxbitc])/16; } //Serial.printf("%d ",asynst[rxbitc]); rxbitc = (rxbitc+1) % 10; } } int RS92::receive() { unsigned long t0 = millis(); Serial.printf("RS92::receive() start at %ld\n",t0); while( millis() - t0 < 1000 ) { uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2); if ( bitRead(value, 7) ) { Serial.println("FIFO full"); } if ( bitRead(value, 4) ) { Serial.println("FIFO overflow"); } if ( bitRead(value, 2) == 1 ) { Serial.println("FIFO: ready()"); sx1278.clearIRQFlags(); } if(bitRead(value, 6) == 0) { // while FIFO not empty byte data = sx1278.readRegister(REG_FIFO); //Serial.printf("%02x",data); process8N1data(data); value = sx1278.readRegister(REG_IRQ_FLAGS2); } else { if(headerDetected) { t0 = millis(); // restart timer... don't time out if header detected... headerDetected = 0; } if(haveNewFrame) { Serial.printf("RS92::receive(): new frame complete after %ldms\n", millis()-t0); haveNewFrame = 0; return RX_OK; } delay(2); } } Serial.printf("RS92::receive() timed out\n"); return RX_TIMEOUT; // TODO RX_OK; } #define RS92MAXLEN (240) int RS92::waitRXcomplete() { // called after complete... Serial.printf("decoding frame %d\n", lastFrame); print_frame(lastFrame==1?data1:data2, 240); SondeData *si = &( (sonde.sondeList+rxtask.receiveSonde)->d ); si->lat = gpx.lat; si->lon = gpx.lon; si->alt = gpx.alt; si->vs = gpx.vU; si->hs = gpx.vH; si->dir = gpx.vD; si->validPos = 0x3f; memcpy(si->id, gpx.id, 9); memcpy(si->ser, gpx.id, 9); si->validID = true; si->vframe = si->frame = gpx.frnr; si->sats = gpx.k; si->time = (gpx.gpssec/1000) + 86382 + gpx.week*604800 + 315878400UL; si->validTime = true; return 0; } RS92 rs92 = RS92(); #endif