diff --git a/README.md b/README.md index 8d16990..022c404 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ for details on supported boards, and additional setup instructions. Manufacturer | Model | Position | Temperature | Humidity | Pressure -------------|-------|----------|-------------|----------|---------- Vaisala | RS92-SGP/NGP | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: -Vaisala | RS41-SG/SGP/SGM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: +Vaisala | RS41-SG/SGP/SGM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: Graw | DFM06/09/17 | :heavy_check_mark: | :x: | :x: | :x: Meteomodem | M10 | :heavy_check_mark: | :x: | :x: | Not Sent Meteomodem | M20 | :heavy_check_mark: | :x: | :x: | Not Sent diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 535f0b8..68f2bd7 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -1,4 +1,3 @@ - #include "features.h" #include "version.h" @@ -90,6 +89,8 @@ WiFiClient tncclient; // JSON over TCP for communicating with my kotlin andoird test stuff WiFiServer rdzserver(14570); WiFiClient rdzclient; +// APRS over TCP for radiosondy.info etc +AsyncClient tcpclient; #if FEATURE_MQTT unsigned long lastMqttUptime = 0; @@ -2510,7 +2511,7 @@ void loopDecoder() { // first check if ID and position lat+lonis ok if (s->d.validID && ((s->d.validPos & 0x03) == 0x03)) { - const char *str = aprs_senddata(s, sonde.config.call, sonde.config.udpfeed.symbol); + char *str = aprs_senddata(s, sonde.config.call, sonde.config.udpfeed.symbol); if (connected) { char raw[201]; int rawlen = aprsstr_mon2raw(str, raw, APRS_MAXLEN); @@ -2527,6 +2528,22 @@ void loopDecoder() { Serial.print("sending: "); Serial.println(raw); tncclient.write(raw, rawlen); } + if(sonde.config.tcpfeed.active) { + static unsigned long lasttcp = 0; + static bool loginok = false; + if( tcpclient.disconnected()) { + tcpclient.connect(sonde.config.tcpfeed.host, sonde.config.tcpfeed.port); + } + else if( tcpclient.connected() ) { + unsigned long now = millis(); + if( (now-lasttcp) > sonde.config.tcpfeed.highrate*1000L ) { + strcat(str,"\r\n"); + Serial.print(str); + tcpclient.write(str, strlen(str)); + lasttcp = now; + } + } + } #if FEATURE_CHASEMAPPER if (sonde.config.cm.active) { Chasemapper::send(udp, s); @@ -2759,6 +2776,15 @@ void enableNetwork(bool enable) { MDNS.end(); connected = false; } + tcpclient.onConnect([](void *arg, AsyncClient *s) { + Serial.write("APRS: TCP connected\n"); + char buf[128]; + snprintf(buf, 128, "user %s pass %d vers %s %s\r\n", sonde.config.call, sonde.config.passcode, version_name, version_id); + s->write(buf, strlen(buf)); + }); + tcpclient.onData([](void *arg, AsyncClient *c, void *data, size_t len) { + Serial.write((const uint8_t *)data, len); + }); Serial.println("enableNetwork done"); } @@ -3578,7 +3604,7 @@ void sondehub_reply_handler(WiFiClient * client) { // also handle periodic station updates here... // interval check moved to sondehub_station_update to avoid having to calculate distance in auto mode twice - if (shState == SH_CONN_IDLE) { + if (shState == SH_CONN_IDLE || shState == SH_DISCONNECTED ) { // (do not set station update while a telemetry report is being sent sondehub_station_update(&shclient, &sonde.config.sondehub); } @@ -3749,6 +3775,12 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub * w += strlen(w); } + // Only send pressure if provided + if (!isnan(s->d.pressure)) { + sprintf(w, "\"pressure\": %.2f,", s->d.pressure); + w += strlen(w); + } + // Only send burst timer if RS41 and fresh within the last 51s if ((realtype == STYPE_RS41) && (s->d.crefKT > 0) && (s->d.vframe - s->d.crefKT < 51)) { sprintf(w, "\"burst_timer\": %d,", (int)s->d.countKT); diff --git a/RX_FSK/data/cfg.js b/RX_FSK/data/cfg.js index bcda035..39bba11 100644 --- a/RX_FSK/data/cfg.js +++ b/RX_FSK/data/cfg.js @@ -69,7 +69,7 @@ var cfgs = [ [ "sondehub.fiactive", "SondeHub frequency import active (0=disabled, 1=active)" ], [ "sondehub.fiinterval", "Import frequency (minutes, ≥ 5)" ], [ "sondehub.fimaxdist", "Import maximum distance (km, ≤ 500)" ], -[ "sondehub.fimaxage", "Import maximum age (hours, ≤ 24)" ], +[ "sondehub.fimaxage", "Import maximum age (hours, ≤ 48)" ], [ "", "Hardware configuration (requires reboot)", "https://github.com/dl9rdz/rdz_ttgo_sonde/wiki/Hardware-configuration"], [ "disptype", "Display type (0=OLED/SSD1306, 1=ILI9225, 2=OLED/SH1106, 3=ILI9341, 4=ILI9342)"], [ "oled_sda", "OLED SDA/TFT SDA"], diff --git a/RX_FSK/data/config.txt b/RX_FSK/data/config.txt index 7babac7..73f7250 100644 --- a/RX_FSK/data/config.txt +++ b/RX_FSK/data/config.txt @@ -85,8 +85,8 @@ dfm.rxbw=10400 # s1: igs.bkg.bund.de/IGS/BRDC/ # s2: www.ngs.noaa.gov/cors/rinex/ #-------------------------------# -ephftp=www.ngs.noaa.gov/cors/rinex/ -#ephftp=igs.bkg.bund.de/IGS/BRDC/ +#ephftp=www.ngs.noaa.gov/cors/rinex/ +ephftp=igs.bkg.bund.de/IGS/BRDC/ #-------------------------------# # axudp for sending to aprsmap #-------------------------------# diff --git a/RX_FSK/src/Chasemapper.cpp b/RX_FSK/src/Chasemapper.cpp index 7dddeee..166abaf 100644 --- a/RX_FSK/src/Chasemapper.cpp +++ b/RX_FSK/src/Chasemapper.cpp @@ -13,8 +13,8 @@ int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) { } sprintf(buf, "{ \"type\": \"PAYLOAD_SUMMARY\"," "\"callsign\": \"%s\"," - "\"latitude\": %g," - "\"longitude\": %g," + "\"latitude\": %.5f," + "\"longitude\": %.5f," "\"altitude\": %d," "\"speed\": %d," "\"heading\": %d," @@ -31,7 +31,7 @@ int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) { sondeTypeStrSH[realtype], si->freq); if( !isnan(si->d.temperature) ) { - sprintf(buf + strlen(buf), ", \"temp\": %g", si->d.temperature); + sprintf(buf + strlen(buf), ", \"temp\": %.1f", si->d.temperature); } strcat(buf, "}"); Serial.printf("Sending chasemapper json: %s\n", buf); diff --git a/RX_FSK/src/RS41.cpp b/RX_FSK/src/RS41.cpp index 06e8bc6..ce6768d 100644 --- a/RX_FSK/src/RS41.cpp +++ b/RX_FSK/src/RS41.cpp @@ -435,11 +435,15 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p) x = (double)getint32(b, b_len, p)*0.01; y = (double)getint32(b, b_len, p+4UL)*0.01; z = (double)getint32(b, b_len, p+8UL)*0.01; - if(x==0 && y==0 && z==0) { + uint8_t sats = getcard16(b, b_len, p+18UL)&255UL; + Serial.printf("x:%g, y:%g, z:%g sats:%d\n", x, y, z, sats); + if( sats<4 || (x==0 && y==0 && z==0) ) { // RS41 sometimes sends frame with all 0 + // or, if sats<4, data is simply garbage. do not use. if(si->validPos) si->validPos |= 0x80; // flag as old return; } + si->sats = sats; wgs84r(x, y, z, &lat, &long0, &heig); Serial.print(" "); si->lat = (float)(X2C_DIVL(lat,1.7453292519943E-2)); @@ -471,10 +475,6 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p) Serial.print((float)vu); si->vs = vu; Serial.print("m/s "); - uint8_t sats = getcard16(b, b_len, p+18UL)&255UL; - Serial.print(sats); - Serial.print("Sats"); - si->sats = sats; si->alt = heig; if( 0==(int)(lat*10000) && 0==(int)(long0*10000) ) { if(si->validPos) { @@ -554,6 +554,36 @@ static float _RS41_waterVaporSaturationPressure (float Tcelsius) return p / 100.0f; } +#define PM(x) calibration->value.matrixP[x] +// CALIB_P: matrixP (frames 0x25..0x2A) and type (frame 0x21) +#define CALIB_P ((0x3Fll<<0x25)|(1ll<<0x21)) +float GetRAP( uint32_t m, uint32_t m1, uint32_t m2, int16_t ptraw) { + struct subframeBuffer *calibration = (struct subframeBuffer *)sonde.si()->extra; + float pt = (float)ptraw*0.01; + float pw[6]; + pw[0] = PM(0) + pt*PM(7) + pt*pt*PM(11) + pt*pt*pt*PM(15); + pw[1] = PM(1) + pt*PM(8) + pt*pt*PM(12) + pt*pt*pt*PM(16); + pw[2] = PM(2) + pt*PM(9) + pt*pt*PM(13) + pt*pt*pt*PM(17); + pw[3] = PM(3) + pt*PM(10)+ pt*pt*PM(14); + pw[4] = PM(4); + pw[5] = PM(5); + float f = (float)m; //meas[9]; + float f1 = (float)m1; //meas[10]; + float f2 = (float)m2; //meas[11]; + float r = f-f1; + if(r!=0.0) { + r = (f2-f1) * PM(6) / r; + float xx = 1.0; + float p = 0.0; + for(int i=0; i<=5; i++) { + p += pw[i] * xx; + xx = xx * r; + } + return p; + } + return NAN; +} + // taken from https://github.com/einergehtnochrein/ra-firmware float GetRATemp( uint32_t measuredCurrent, uint32_t refMin, uint32_t refMax, float calT, float taylorT[3], float polyT[6] ) { struct subframeBuffer *calibration = (struct subframeBuffer *)sonde.si()->extra; @@ -639,7 +669,7 @@ float GetRAHumidity( uint32_t humCurrent, uint32_t humMin, uint32_t humMax, floa int RS41::decode41(byte *data, int maxlen) { char buf[128]; - int crcok = 0; + int crcok = 1; SondeData *si = &(sonde.si()->d); int32_t corr = reedsolomon41(data, 560, 131); // try short frame first @@ -675,8 +705,8 @@ int RS41::decode41(byte *data, int maxlen) // check CRC if(!crcrs(data, 560, p, p+len)) { Serial.println("###CRC ERROR###"); + crcok = 0; } else { - crcok = 1; switch(typ) { case 'y': // name { @@ -751,11 +781,10 @@ int RS41::decode41(byte *data, int maxlen) uint32_t tempHumiMain = getint24(data, 560, p+18); uint32_t tempHumiRef1 = getint24(data, 560, p+21); uint32_t tempHumiRef2 = getint24(data, 560, p+24); - #if 0 uint32_t pressureMain = getint24(data, 560, p+27); uint32_t pressureRef1 = getint24(data, 560, p+30); uint32_t pressureRef2 = getint24(data, 560, p+33); - #endif + int16_t ptraw = getint16(data, 560, p+38); #if 0 Serial.printf( "External temp: tempMeasMain = %ld, tempMeasRef1 = %ld, tempMeasRef2 = %ld\n", tempMeasMain, tempMeasRef1, tempMeasRef2 ); Serial.printf( "Rel Humidity: humidityMain = %ld, humidityRef1 = %ld, humidityRef2 = %ld\n", humidityMain, humidityRef1, humidityRef2 ); @@ -767,6 +796,13 @@ int RS41::decode41(byte *data, int maxlen) bool validExternalTemperature = calibration!=NULL && (calibration->valid & 0xF8) == 0xF8; bool validHumidity = calibration!=NULL && (calibration->valid & 0x7FE0001FFFF8) == 0x7FE0001FFFF8; + bool validPressure = calibration!=NULL && (calibration->valid & CALIB_P)==CALIB_P && calibration->value.names.variant[7]=='P'; + + if ( validPressure ) { + si->pressure = GetRAP( pressureMain, pressureRef1, pressureRef2, ptraw ); + Serial.printf("Pressure sensor = %f\n", si->pressure); + } + if ( validExternalTemperature ) { si->temperature = GetRATemp( tempMeasMain, tempMeasRef1, tempMeasRef2, calibration->value.calT, calibration->value.taylorT, calibration->value.polyT ); @@ -791,7 +827,7 @@ int RS41::decode41(byte *data, int maxlen) p += len; Serial.println(); } - return crcok ? 0 : -1; + return crcok ? 0 : RX_ERROR; } void RS41::printRaw(uint8_t *data, int len) { diff --git a/RX_FSK/src/ShFreqImport.cpp b/RX_FSK/src/ShFreqImport.cpp index 3bd7aa9..4da63b1 100644 --- a/RX_FSK/src/ShFreqImport.cpp +++ b/RX_FSK/src/ShFreqImport.cpp @@ -90,6 +90,7 @@ void ShFreqImport::populate(char *id, float lat, float lon, float freq, const ch return; } // no more free slots + sonde.clearAllData(&sonde.sondeList[ppos]); sonde.sondeList[ppos].active = 1; sonde.sondeList[ppos].freq = freq; sonde.sondeList[ppos].type = (SondeType)stype; diff --git a/RX_FSK/src/Sonde.cpp b/RX_FSK/src/Sonde.cpp index ed4f3f7..318accd 100644 --- a/RX_FSK/src/Sonde.cpp +++ b/RX_FSK/src/Sonde.cpp @@ -273,6 +273,7 @@ void Sonde::checkConfig() { if(config.maxsonde > MAXSONDE) config.maxsonde = MAXSONDE; if(config.sondehub.fiinterval<5) config.sondehub.fiinterval = 5; if(config.sondehub.fimaxdist>500) config.sondehub.fimaxdist = 500; + if(config.sondehub.fimaxage>48) config.sondehub.fimaxage = 48; if(config.sondehub.fimaxdist==0) config.sondehub.fimaxdist = 150; if(config.sondehub.fimaxage==0) config.sondehub.fimaxage = 2; } @@ -675,7 +676,7 @@ void Sonde::clearAllData(SondeInfo *si) { memset(&(si->d), 0, sizeof(SondeData)); // set floats to NaN si->d.lat = si->d.lon = si->d.alt = si->d.vs = si->d.hs = si->d.dir = NAN; - si->d.temperature = si->d.tempRHSensor = si->d.relativeHumidity = si->d.batteryVoltage = NAN; + si->d.temperature = si->d.tempRHSensor = si->d.relativeHumidity = si->d.pressure = si->d.batteryVoltage = NAN; } void Sonde::updateDisplayPos() { diff --git a/RX_FSK/src/Sonde.h b/RX_FSK/src/Sonde.h index 01db603..8381f0a 100644 --- a/RX_FSK/src/Sonde.h +++ b/RX_FSK/src/Sonde.h @@ -99,9 +99,10 @@ typedef struct st_sondedata { uint16_t launchKT, burstKT, countKT; uint16_t crefKT; // frame number in which countKT was last sent // sonde specific extra data, NULL if unused or not yet initialized, currently used for RS41 subframe data (calibration) - float temperature = -300.0; // platinum resistor temperature - float tempRHSensor = -300.0; // temperature of relative humidity sensor - float relativeHumidity = -1.0; // relative humidity + float temperature; // platinum resistor temperature + float tempRHSensor; // temperature of relative humidity sensor + float relativeHumidity; // relative humidity + float pressure; float batteryVoltage = -1; } SondeData; diff --git a/RX_FSK/src/aprs.cpp b/RX_FSK/src/aprs.cpp index f827b78..913a1a5 100644 --- a/RX_FSK/src/aprs.cpp +++ b/RX_FSK/src/aprs.cpp @@ -298,7 +298,7 @@ char *aprs_senddata(SondeInfo *si, const char *usercall, const char *sym) { i=strlen(b); snprintf(b+i, APRS_MAXLEN-i, "!w%c%c!", 33+dao91(s->lat), 33+dao91(s->lon)); - strcat(b, "&"); + // ??? strcat(b, "&"); char comm[100]; snprintf(comm, 100, "Clb=%.1fm/s %.3fMHz Type=%s", s->vs, si->freq, sondeTypeStr[si->type]); strcat(b, comm); diff --git a/RX_FSK/version.h b/RX_FSK/version.h index 4fa1af3..9107600 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20210922"; +const char *version_id = "devel20210923"; const int SPIFFS_MAJOR=2; const int SPIFFS_MINOR=16;