; DFM enhancements

pull/207/head
Hansi, dl9rdz 2021-09-30 17:53:57 +02:00
rodzic ef9ae86969
commit fdf5132fa6
4 zmienionych plików z 97 dodań i 58 usunięć

Wyświetl plik

@ -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<<dat[6]);
switch(dat[6]) {
case 0:
Serial.print("Packet counter: "); Serial.print(dat[3]);
si->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);

Wyświetl plik

@ -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 ) {

Wyświetl plik

@ -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();

Wyświetl plik

@ -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];