diff --git a/RX_FSK/src/DFM.cpp b/RX_FSK/src/DFM.cpp index 1b4803a..89c1ede 100644 --- a/RX_FSK/src/DFM.cpp +++ b/RX_FSK/src/DFM.cpp @@ -14,6 +14,18 @@ #define DFM_FRAMELEN 33 + +/* + * observed DAT patterns for DFM-9: + * A: 0+1; 2+3; 4+5; 6+7; 8+0 => keep frame in shadowFrame + * B: 1+2; 3+4; 5+6; 7+8 => all good => keep date in shadowDate + * C: 0+1; 2+3; 4+5; 6+7; 8+15 => all good => keep date in shadowDate + * D: 0+1; 2+3; 4+5; 6+7; 0+1 => use shadowDate + * not seen:5+6; 7+1 + * values: + * 0:packet counter; 1:utc-msec; 2:lat,vh; 3:lon,dir, 4:alt,vv, 8=utc-date(day-hh-mm) + */ + // single data structure, search restarts after decoder change static struct st_dfmstat { int idcnt0; @@ -24,10 +36,13 @@ static struct st_dfmstat { uint16_t dat[50*2]; uint8_t cnt[50*2]; uint16_t good; + uint32_t datesec; + uint8_t frame; uint16_t msec; uint8_t nameregok; uint8_t nameregtop; uint8_t lastdat; + uint8_t cycledone; // 0=no; 1=OK, 2=partially/with errors } dfmstate; int DFM::setup(float frequency, int type) @@ -332,6 +347,8 @@ void DFM::decodeCFG(uint8_t *cfg) memcpy(sonde.si()->d.ser, sonde.si()->d.id+1, 9); } +#if 0 +// not used any more static int bitCount(int x) { int m4 = 0x1 | (0x1<<8) | (0x1<<16) | (0x1<<24); int m1 = 0xFF; @@ -339,6 +356,7 @@ static int bitCount(int x) { int s1 = (s4&m1) + ((s4>>8)&m1) + ((s4>>16)&m1) + ((s4>>24)&m1); return s1; } +#endif uint16_t MON[]={0,0,31,59,90,120,151,181,212,243,273,304,334}; @@ -347,24 +365,64 @@ void DFM::decodeDAT(uint8_t *dat) // TODO: Here we need to work on a shadow copy of SondeData in order to prevent concurrent changes while using data in main loop SondeData *si = &(sonde.si()->d); Serial.print(" DAT["); Serial.print(dat[6]); Serial.print("]: "); - // We can have a 8 and 0 subframe in a single frame. So do the reset only for dat>0 - if( !(dat[6]==0 && dfmstate.lastdat==8) ) { // if we have DAT8 + DAT0, don't reset before returing the 8 frame... - if(dat[6] < dfmstate.lastdat) dfmstate.good = 0; // next iteration detected - } - dfmstate.lastdat = dat[6]; + + // We handle this case already here, because we need to update dfmstate.datesec before the cycle complete handling + if( dat[6]==8 ) { + int y = (dat[0]<<4) + (dat[1]>>4); + int m = dat[1]&0x0F; + int d = dat[2]>>3; + int h = ((dat[2]&0x07)<<2) + (dat[3]>>6); + int mi = (dat[3]&0x3F); + Serial.printf("Date: %04d-%02d-%02d %02d:%02dz ", y, m, d, h, mi); + si->sats = dat[4]; + si->validPos |= 0x40; + // convert to unix time + int tt = (y-1970)*365 + (y-1969)/4; // days since 1970 + if(m<=12) { tt += MON[m]; if((y%4)==0 && m>2) tt++; } + tt = (tt+d-1)*(60*60*24) + h*3600 + mi*60; + dfmstate.datesec = tt; + dfmstate.good |= 0x100; + } + else if( dat[6]>8 ) return; // we ignore those... + + /* Here we update data that should be updated only after receiving a "complete" frame (mainly for consistent SondeHub exports) + * We do this (a) when there is a DAT8 block and (b) when there is wrap from DAT7 to DAT0, for these fields: + * => frame + * => vframe (only used for SondeHub as virtual frame number) + * => time (calculated with using date and msec) + * [assuming that if there is no DAT8, then the date value did not change.] + */ + + if( dat[6]==8 || dat[6] < dfmstate.lastdat) { // After DAT8, or after a "warp around" + if( dfmstate.good&1 ) si->frame = dfmstate.frame; + if( (dfmstate.good&0x102)==0x102 ) { + si->time = dfmstate.datesec + dfmstate.msec/1000; + // Lets be consistent with autorx: the timestamp uses the msec value truncated to seconds, + // whereas the virtual frame number for DFM uses the msec value rounded to full seconds. + // Actually, tt is real UTC, and the transformation to GPS seconds lacks adjusting for leap seconds + si->vframe = dfmstate.datesec + (dfmstate.msec+500)/1000 - 315964800; + } + // All fields updated? 1=OK, 2=with errors + Serial.printf("Cycle done: good is %x\n", dfmstate.good); + dfmstate.cycledone = ((dfmstate.good&0x11F)==0x11F) ? 1 : 2; + dfmstate.good = 0; + dfmstate.lastdat = 0; + } else { + dfmstate.lastdat = dat[6]; + } dfmstate.good |= (1<frame = dat[3]; + dfmstate.frame = dat[3]; break; case 1: { int val = (((uint16_t)dat[4])<<8) + (uint16_t)dat[5]; Serial.print("UTC-msec: "); Serial.print(val); dfmstate.msec = val; - uint32_t tmp = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]); - si->sats = bitCount(tmp); + //uint32_t tmp = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]); + //si->sats = bitCount(tmp); } break; case 2: @@ -376,7 +434,7 @@ void DFM::decodeDAT(uint8_t *dat) Serial.print(", hor-V: "); Serial.print(vh*0.01); si->lat = lat*0.0000001; si->hs = vh*0.01; - si->validPos |= 0x11; + if(lat!=0 || vh!=0) si->validPos |= 0x11; else si->validPos &= ~0x11; } break; case 3: @@ -384,11 +442,11 @@ void DFM::decodeDAT(uint8_t *dat) float lon, dir; lon = (int32_t)(((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + (uint32_t)dat[3]); dir = ((uint16_t)dat[4]<<8) + dat[5]; - Serial.print("GPS-lon: "); Serial.print(lon*0.0000001); - Serial.print(", dir: "); Serial.print(dir*0.01); si->lon = lon*0.0000001; si->dir = dir*0.01; - si->validPos |= 0x42; + Serial.print("GPS-lon: "); Serial.print(si->lon); + Serial.print(", dir: "); Serial.print(si->dir); + if(lon != 0 || dir != 0) si->validPos |= 0x22; else si->validPos &= ~0x22; } break; case 4: @@ -400,32 +458,11 @@ void DFM::decodeDAT(uint8_t *dat) Serial.print(", vv: "); Serial.print(vv*0.01); si->alt = alt*0.01; si->vs = vv*0.01; - si->validPos |= 0x0C; + if(alt!=0 || vv != 0) si->validPos |= 0x0C; else si->validPos &= ~0x0C; } break; case 8: - { - int y = (dat[0]<<4) + (dat[1]>>4); - int m = dat[1]&0x0F; - int d = dat[2]>>3; - int h = ((dat[2]&0x07)<<2) + (dat[3]>>6); - int mi = (dat[3]&0x3F); - char buf[100]; - snprintf(buf, 100, "%04d-%02d-%02d %02d:%02dz", y, m, d, h, mi); - Serial.print("Date: "); Serial.print(buf); - // convert to unix time - int tt = (y-1970)*365 + (y-1969)/4; // days since 1970 - if(m<=12) { tt += MON[m]; if((y%4)==0 && m>2) tt++; } - tt = (tt+d-1)*(60*60*24) + h*3600 + mi*60; - si->time = tt + dfmstate.msec/1000; - // Lets be consistent with autorx: the timestamp uses the msec value truncated to seconds, - // whereas the virtual frame number for DFM uses the msec value rounded to full seconds. - // Actually, tt is real UTC, and the transformation to GPS seconds lacks adjusting for leap seconds - si->vframe = tt + (dfmstate.msec+500)/1000 - 315964800; - // maybe TODO: if we missed the type 0 frame, we still might caculate the right seconds from system time. - // but we only send time stamps to external servers (in particular to sondehub), if all - // required frame types have been correctly decoded, so this does not matter much. - } + // handled above break; default: Serial.print("(?)"); @@ -433,6 +470,7 @@ void DFM::decodeDAT(uint8_t *dat) } } + void DFM::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len) { int i; @@ -505,6 +543,7 @@ int DFM::receive() { // tentative continuous RX version... unsigned long t0 = millis(); + dfmstate.cycledone = 0; while( ( millis() - t0 ) < 1300 ) { uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2); if ( bitRead(value, 7) ) { @@ -527,22 +566,16 @@ int DFM::receive() { } else { if(haveNewFrame) { //Serial.printf("DFM::receive(): new frame complete after %ldms\n", millis()-t0); - Serial.printf("receive newframe: %d, good: %x\n", rxframes, dfmstate.good); haveNewFrame = 0; rxframes--; - if(dfmstate.good & 0x100) { - if(dfmstate.good & 0x11E) { - dfmstate.good = 0; return RX_OK; // type 8 frame has been received - } else { - dfmstate.good = 0; return RX_ERROR; - } - } - if(rxframes==0) return RX_ERROR; + // OK: All DAT frames (0/1/2/3/4/8) have been received in the last cycle + if(dfmstate.cycledone==1) return RX_OK; + if(dfmstate.cycledone>1 || rxframes==0) return RX_ERROR; } delay(2); } } - return rxframes == 5 ? RX_TIMEOUT : RX_OK; + return rxframes == 5 ? RX_TIMEOUT : RX_ERROR; } int DFM::decodeFrameDFM(uint8_t *data) { @@ -553,7 +586,7 @@ int DFM::decodeFrameDFM(uint8_t *data) { int ret0 = hamming(hamming_conf, 7, block_conf); int ret1 = hamming(hamming_dat1, 13, block_dat1); int ret2 = hamming(hamming_dat2, 13, block_dat2); - Serial.printf("Hamming returns %d %d %d -- %d\n", ret0, ret1, ret2, ret0|ret1|ret2); + //Serial.printf("Hamming returns %d %d %d -- %d\n", ret0, ret1, ret2, ret0|ret1|ret2); byte byte_conf[4], byte_dat1[7], byte_dat2[7]; bitsToBytes(block_conf, byte_conf, 7); diff --git a/RX_FSK/src/Display.cpp b/RX_FSK/src/Display.cpp index 810925a..66d063f 100644 --- a/RX_FSK/src/Display.cpp +++ b/RX_FSK/src/Display.cpp @@ -1204,7 +1204,7 @@ void Display::drawString(DispEntry *de, const char *str) { void Display::drawLat(DispEntry *de) { rdis->setFont(de->fmt); - if(!sonde.si()->d.validPos) { + if(!VALIDPOS(sonde.si()->d.validPos)) { drawString(de," "); return; } @@ -1213,7 +1213,7 @@ void Display::drawLat(DispEntry *de) { } void Display::drawLon(DispEntry *de) { rdis->setFont(de->fmt); - if(!sonde.si()->d.validPos) { + if(!VALIDPOS(sonde.si()->d.validPos)) { drawString(de," "); return; } @@ -1222,7 +1222,7 @@ void Display::drawLon(DispEntry *de) { } void Display::drawAlt(DispEntry *de) { rdis->setFont(de->fmt); - if(!sonde.si()->d.validPos) { + if(!VALIDALT(sonde.si()->d.validPos)) { drawString(de," "); return; } @@ -1233,7 +1233,7 @@ void Display::drawAlt(DispEntry *de) { } void Display::drawHS(DispEntry *de) { rdis->setFont(de->fmt); - if(!sonde.si()->d.validPos) { + if(!VALIDHS(sonde.si()->d.validPos)) { drawString(de," "); return; } @@ -1248,7 +1248,7 @@ void Display::drawHS(DispEntry *de) { } void Display::drawVS(DispEntry *de) { rdis->setFont(de->fmt); - if(!sonde.si()->d.validPos) { + if(!VALIDVS(sonde.si()->d.validPos)) { drawString(de," "); return; } @@ -1459,13 +1459,13 @@ void Display::calcGPS() { valid = true; } // distance - if( valid && (sonde.si()->d.validPos&0x03)==0x03 && (layout->usegps&GPSUSE_DIST)) { + if( valid && VALIDPOS(sonde.si()->d.validPos) && (layout->usegps&GPSUSE_DIST)) { gpsDist = (int)calcLatLonDist(mylat, mylon, sonde.si()->d.lat, sonde.si()->d.lon); } else { gpsDist = -1; } // bearing - if( valid && (sonde.si()->d.validPos&0x03)==0x03 && (layout->usegps&GPSUSE_BEARING)) { + if( valid && VALIDPOS(sonde.si()->d.validPos&0x03) && (layout->usegps&GPSUSE_BEARING)) { float lat1 = radians(mylat); float lat2 = radians(sonde.si()->d.lat); float lon1 = radians(mylon); @@ -1522,7 +1522,7 @@ void Display::drawGPS(DispEntry *de) { { // distance // equirectangular approximation is good enough - if( (sonde.si()->d.validPos&0x03)!=0x03 ) { + if( !VALIDPOS(sonde.si()->d.validPos) ) { snprintf(buf, 16, "no pos "); if(de->extra && *de->extra=='5') buf[5]=0; } else if( disp.gpsDist < 0 ) { diff --git a/RX_FSK/src/Sonde.cpp b/RX_FSK/src/Sonde.cpp index 47b06b0..c4be466 100644 --- a/RX_FSK/src/Sonde.cpp +++ b/RX_FSK/src/Sonde.cpp @@ -487,14 +487,13 @@ void Sonde::receive() { } // state information for RX_TIMER / NORX_TIMER events - if(res==0) { // RX OK - flashLed(700); + if(res==RX_OK || res==RX_ERROR) { // something was received... + flashLed( (res==RX_OK)?700:100); if(si->lastState != 1) { si->rxStart = millis(); si->lastState = 1; } - } else { // RX not ok - if(res==RX_ERROR) flashLed(100); + } else { // RX Timeout //Serial.printf("Sonde::receive(): result %d (%s), laststate was %d\n", res, (res<=3)?RXstr[res]:"?", si->lastState); if(si->lastState != 0) { si->norxStart = millis(); diff --git a/RX_FSK/src/Sonde.h b/RX_FSK/src/Sonde.h index 15007f5..36a5470 100644 --- a/RX_FSK/src/Sonde.h +++ b/RX_FSK/src/Sonde.h @@ -74,6 +74,13 @@ extern const char *manufacturer_string[NSondeTypes]; #define TYPE_IS_DFM(t) ( (t)==STYPE_DFM ) #define TYPE_IS_METEO(t) ( (t)==STYPE_M10M20 || (t)==STYPE_M10 || (t)==STYPE_M20 ) +#define VALIDPOS(x) (((x)&0x03)==0x03) +#define VALIDALT(x) ((x)&0x04) +#define VALIDVS(x) ((x)&0x08) +#define VALIDHS(x) ((x)&0x10) +#define VALIDDIR(x) ((x)&0x20) +#define VALIDSATS(x) ((x)&0x40) + typedef struct st_sondedata { // decoded ID char id[10];