Merge branch 'dl9rdz:devel' into devel

pull/212/head
eben80 2021-09-22 11:58:29 +02:00 zatwierdzone przez GitHub
commit 0291c96f50
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
13 zmienionych plików z 217 dodań i 177 usunięć

Wyświetl plik

@ -436,16 +436,16 @@ const char *createSondeHubMap() {
HTMLBODY(ptr, "map.html");
if (!sonde.config.sondehub.active) {
strcat(ptr, "<div>NOTE: SondeHub uploading is not enabled, detected sonde will not be visable on map</div>");
if ((*s->ser == 0) && ( !isnan(sonde.config.rxlat))) {
if ((*s->d.ser == 0) && ( !isnan(sonde.config.rxlat))) {
sprintf(ptr + strlen(ptr), "<iframe src=\"https://sondehub.org/#!mc=%f,%f&mz=8\" style=\"border:1px solid #00A3D3;border-radius:20px;height:95vh\"></iframe>", sonde.config.rxlat, sonde.config.rxlon);
} else {
sprintf(ptr + strlen(ptr), "<iframe src=\"https://sondehub.org/%s\" style=\"border:1px solid #00A3D3;border-radius:20px;height:95vh\"></iframe>", s-> ser);
sprintf(ptr + strlen(ptr), "<iframe src=\"https://sondehub.org/%s\" style=\"border:1px solid #00A3D3;border-radius:20px;height:95vh\"></iframe>", s->d.ser);
}
} else {
if ((*s->ser == 0) && (!isnan(sonde.config.rxlat))) {
if ((*s->d.ser == 0) && (!isnan(sonde.config.rxlat))) {
sprintf(ptr, "<iframe src=\"https://sondehub.org/#!mc=%f,%f&mz=8\" style=\"border:1px solid #00A3D3;border-radius:20px;height:98vh;width:100%%\"></iframe>", sonde.config.rxlat, sonde.config.rxlon);
} else {
sprintf(ptr, "<iframe src=\"https://sondehub.org/%s\" style=\"border:1px solid #00A3D3;border-radius:20px;height:98vh;width:100%%\"></iframe>", s-> ser);
sprintf(ptr, "<iframe src=\"https://sondehub.org/%s\" style=\"border:1px solid #00A3D3;border-radius:20px;height:98vh;width:100%%\"></iframe>", s->d.ser);
}
}
HTMLBODYEND(ptr);
@ -497,24 +497,24 @@ void addSondeStatus(char *ptr, int i)
SondeInfo *s = &sonde.sondeList[i];
strcat(ptr, "<table>");
sprintf(ptr + strlen(ptr), "<tr><td id=\"sfreq\">%3.3f MHz, Type: %s</td><tr><td>ID: %s", s->freq, sondeTypeLongStr[s->type],
s->validID ? s->id : "<?""?>");
if (s->validID && (TYPE_IS_DFM(s->type) || TYPE_IS_METEO(s->type) || s->type == STYPE_MP3H) ) {
sprintf(ptr + strlen(ptr), " (ser: %s)", s->ser);
s->d.validID ? s->d.id : "<?""?>");
if (s->d.validID && (TYPE_IS_DFM(s->type) || TYPE_IS_METEO(s->type) || s->type == STYPE_MP3H) ) {
sprintf(ptr + strlen(ptr), " (ser: %s)", s->d.ser);
}
sprintf(ptr + strlen(ptr), "</td></tr><tr><td>QTH: %.6f,%.6f h=%.0fm</td></tr>\n", s->lat, s->lon, s->alt);
const time_t t = s->time;
sprintf(ptr + strlen(ptr), "</td></tr><tr><td>QTH: %.6f,%.6f h=%.0fm</td></tr>\n", s->d.lat, s->d.lon, s->d.alt);
const time_t t = s->d.time;
ts = *gmtime(&t);
sprintf(ptr + strlen(ptr), "<tr><td>Frame# %u, Sats=%d, %04d-%02d-%02d %02d:%02d:%02d</td></tr>",
s->frame, s->sats, ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
s->d.frame, s->d.sats, ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
if (s->type == STYPE_RS41) {
sprintf(ptr + strlen(ptr), "<tr><td>Burst-KT=%d Launch-KT=%d Countdown=%d (vor %ds)</td></tr>\n",
s->burstKT, s->launchKT, s->countKT, ((uint16_t)s->frame - s->crefKT));
s->d.burstKT, s->d.launchKT, s->d.countKT, ((uint16_t)s->d.frame - s->d.crefKT));
}
sprintf(ptr + strlen(ptr), "<tr><td><a target=\"_empty\" href=\"geo:%.6f,%.6f\">GEO-App</a> - ", s->lat, s->lon);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://radiosondy.info/sonde_archive.php?sondenumber=%s\">radiosondy.info</a> - ", s->id);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://tracker.sondehub.org/%s\">SondeHub Tracker</a> - ", s->ser);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a> - ", s->lat, s->lon);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.com/maps/search/?api=1&query=%.6f,%.6f\">Google</a></td></tr>", s->lat, s->lon);
sprintf(ptr + strlen(ptr), "<tr><td><a target=\"_empty\" href=\"geo:%.6f,%.6f\">GEO-App</a> - ", s->d.lat, s->d.lon);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://radiosondy.info/sonde_archive.php?sondenumber=%s\">radiosondy.info</a> - ", s->d.id);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://tracker.sondehub.org/%s\">SondeHub Tracker</a> - ", s->d.ser);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a> - ", s->d.lat, s->d.lon);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.com/maps/search/?api=1&query=%.6f,%.6f\">Google</a></td></tr>", s->d.lat, s->d.lon);
strcat(ptr, "</table><p/>\n");
}
@ -542,7 +542,7 @@ const char *createLiveJson() {
SondeInfo *s = &sonde.sondeList[sonde.currentSonde];
sprintf(ptr + strlen(ptr), "\"sonde\": {\"rssi\": %d, \"vframe\": %d, \"time\": %d,\"id\": \"%s\", \"freq\": %3.3f, \"type\": \"%s\","
"\"lat\": %.6f, \"lon\": %.6f, \"alt\": %.0f, \"speed\": %.1f, \"dir\": %.0f, \"climb\": %.1f, \"launchsite\": \"%s\", \"res\": %d }",
s->rssi, s->vframe, s->time, s->id, s->freq, sondeTypeStr[s->type], s->lat, s->lon, s->alt, s->hs, s->dir, s->vs, s->launchsite, s->rxStat[0]);
s->rssi, s->d.vframe, s->d.time, s->d.id, s->freq, sondeTypeStr[s->type], s->d.lat, s->d.lon, s->d.alt, s->d.hs, s->d.dir, s->d.vs, s->launchsite, s->rxStat[0]);
if (gpsPos.valid) {
#if 0
@ -1295,15 +1295,15 @@ void addSondeStatusKML(char *ptr, int i)
{
SondeInfo *s = &sonde.sondeList[i];
if (!s->validID)
if (!s->d.validID)
{
return;
}
sprintf(ptr + strlen(ptr), "<Placemark id=\"%s\"><name>%s</name><Point><altitudeMode>absolute</altitudeMode><coordinates>%.6f,%.6f,%.0f</coordinates></Point><description>%3.3f MHz, Type: %s, h=%.0fm</description></Placemark>",
s->id, s->id,
s->lon, s->lat, s->alt,
s->freq, sondeTypeStr[s->type], s->alt);
s->d.id, s->d.id,
s->d.lon, s->d.lat, s->d.alt,
s->freq, sondeTypeStr[s->type], s->d.alt);
}
const char *createKMLDynamic() {
@ -1333,8 +1333,8 @@ const char *sendGPX(AsyncWebServerRequest * request) {
return "ERROR";
}
SondeInfo *si = &sonde.sondeList[index];
strcpy(si->id, "test");
si->lat = 48; si->lon = 11; si->alt = 500;
strcpy(si->d.id, "test");
si->d.lat = 48; si->d.lon = 11; si->d.alt = 500;
snprintf(ptr, 10240, "<?xml version='1.0' encoding='UTF-8'?>\n"
"<gpx version=\"1.1\" creator=\"http://rdzsonde.local\" xmlns=\"http://www.topografix.com/GPX/1/1\" "
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
@ -1344,7 +1344,7 @@ const char *sendGPX(AsyncWebServerRequest * request) {
"<author>rdzTTGOsonde</author>\n"
"</metadata>\n"
"<wpt lat=\"%f\" lon=\"%f\">\n <ele>%f</ele>\n <name>%s</name>\n <sym>Radio Beacon</sym><type>Sonde</type>\n"
"</wpt></gpx>\n", index, si->id, si->lat, si->lon, si->alt, si->id);
"</wpt></gpx>\n", index, si->d.id, si->d.lat, si->d.lon, si->d.alt, si->d.id);
Serial.println(message);
return message;
}
@ -2504,7 +2504,7 @@ void loopDecoder() {
//Send a packet with position information
// first check if ID and position lat+lonis ok
if (s->validID && ((s->validPos & 0x03) == 0x03)) {
if (s->d.validID && ((s->d.validPos & 0x03) == 0x03)) {
const char *str = aprs_senddata(s, sonde.config.call, sonde.config.udpfeed.symbol);
if (connected) {
char raw[201];
@ -2551,7 +2551,7 @@ void loopDecoder() {
Serial.println("Sending position via TCP as rdzJSON");
char raw[1024];
char gps[128];
const char *typestr = s->typestr;
const char *typestr = s->d.typestr;
if (*typestr == 0) typestr = sondeTypeStr[s->type];
// TODO: only if GPS is valid...
if (gpsPos.valid) {
@ -2597,27 +2597,27 @@ void loopDecoder() {
typestr,
(int)s->active,
s->freq,
s->id,
s->ser,
(int)s->validID,
s->d.id,
s->d.ser,
(int)s->d.validID,
s->launchsite,
s->lat,
s->lon,
s->alt,
s->vs,
s->hs,
s->dir,
s->sats,
s->validPos,
s->time,
s->frame,
(int)s->validTime,
s->d.lat,
s->d.lon,
s->d.alt,
s->d.vs,
s->d.hs,
s->d.dir,
s->d.sats,
s->d.validPos,
s->d.time,
s->d.frame,
(int)s->d.validTime,
s->rssi,
s->afc,
s->launchKT,
s->burstKT,
s->countKT,
s->crefKT,
s->d.launchKT,
s->d.burstKT,
s->d.countKT,
s->d.crefKT,
gps
);
//Serial.println("Writing rdzclient...");
@ -3603,12 +3603,12 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
uint8_t realtype = s->type;
// config setting M10 and M20 will both decode both types, so use the real type that was decoded
if (TYPE_IS_METEO(realtype)) {
realtype = s->subtype == 1 ? STYPE_M10 : STYPE_M20;
realtype = s->d.subtype == 1 ? STYPE_M10 : STYPE_M20;
}
// For DFM, s->time is data from subframe DAT8 (gps date/hh/mm), and sec is from DAT1 (gps sec/usec)
// For DFM, s->d.time is data from subframe DAT8 (gps date/hh/mm), and sec is from DAT1 (gps sec/usec)
// For all others, sec should always be 0 and time the exact time in seconds
time_t t = s->time;
time_t t = s->d.time;
int chase = conf->chase;
// automatically decided if CHASE or FIXED mode is used (for config AUTO)
@ -3627,11 +3627,11 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
}
// Check if current sonde data is valid. If not, don't do anything....
if (*s->ser == 0) return; // Don't send anything without serial number
if (((int)s->lat == 0) && ((int)s->lon == 0)) return; // Sometimes these values are zeroes. Don't send those to the sondehub
if ((int)s->alt > 50000) return; // If alt is too high don't send to SondeHub
if (*s->d.ser == 0) return; // Don't send anything without serial number
if (((int)s->d.lat == 0) && ((int)s->d.lon == 0)) return; // Sometimes these values are zeroes. Don't send those to the sondehub
if ((int)s->d.alt > 50000) return; // If alt is too high don't send to SondeHub
// M20 data does not include #sat information
if ( realtype != STYPE_M20 && (int)s->sats < 4) return; // If not enough sats don't send to SondeHub
if ( realtype != STYPE_M20 && (int)s->d.sats < 4) return; // If not enough sats don't send to SondeHub
// If not connected to sondehub, try reconnecting.
// TODO: do this outside of main loop
@ -3651,8 +3651,8 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
return;
}
if ( abs(now - (time_t)s->time) > (3600 * SONDEHUB_TIME_THRESHOLD) ) {
Serial.printf("Sonde time %d too far from current UTC time %ld", s->time, now);
if ( abs(now - (time_t)s->d.time) > (3600 * SONDEHUB_TIME_THRESHOLD) ) {
Serial.printf("Sonde time %d too far from current UTC time %ld", s->d.time, now);
return;
}
@ -3688,25 +3688,25 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
"\"type\": \"%s\",",
version_name, version_id, conf->callsign,
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
manufacturer_string[realtype], s->ser,
manufacturer_string[realtype], s->d.ser,
ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec,
(float)s->lat, (float)s->lon, (float)s->alt, (float)s->freq, (float)s->hs, (float)s->vs,
(float)s->dir, -((float)s->rssi / 2), s->vframe, sondeTypeStrSH[realtype]
(float)s->d.lat, (float)s->d.lon, (float)s->d.alt, (float)s->freq, (float)s->d.hs, (float)s->d.vs,
(float)s->d.dir, -((float)s->rssi / 2), s->d.vframe, sondeTypeStrSH[realtype]
);
w += strlen(w);
// Only send sats if not M20
if (realtype != STYPE_M20) {
sprintf(w, "\"sats\": %d,", (int)s->sats);
sprintf(w, "\"sats\": %d,", (int)s->d.sats);
w += strlen(w);
}
/* if there is a subtype (DFM only) */
if ( TYPE_IS_DFM(s->type) && s->subtype > 0 && s->subtype < 16 ) {
const char *t = dfmSubtypeStrSH[s->subtype];
if ( TYPE_IS_DFM(s->type) && s->d.subtype > 0 && s->d.subtype < 16 ) {
const char *t = dfmSubtypeStrSH[s->d.subtype];
// as in https://github.com/projecthorus/radiosonde_auto_rx/blob/e680221f69a568e1fdb24e76db679233f32cb027/auto_rx/autorx/sonde_specific.py#L84
if (t) sprintf(w, "\"subtype\": \"%s\",", t);
else sprintf(w, "\"subtype\": \"DFMx%X\",", s->subtype); // Unknown subtype
else sprintf(w, "\"subtype\": \"DFMx%X\",", s->d.subtype); // Unknown subtype
w += strlen(w);
} else if ( s->type == STYPE_RS41 ) {
char buf[11];
@ -3717,26 +3717,26 @@ void sondehub_send_data(WiFiClient * client, SondeInfo * s, struct st_sondehub *
}
// Only send temp if provided
if ((int)s->temperature != 0) {
sprintf(w, "\"temp\": %.3f,", float(s->temperature));
if ((int)s->d.temperature != 0) {
sprintf(w, "\"temp\": %.1f,", float(s->d.temperature));
w += strlen(w);
}
// Only send humidity if provided
if ((int)s->relativeHumidity != 0) {
sprintf(w, "\"humidity\": %.3f,", float(s->relativeHumidity));
if ((int)s->d.relativeHumidity != 0) {
sprintf(w, "\"humidity\": %.1f,", float(s->d.relativeHumidity));
w += strlen(w);
}
// Only send burst timer if RS41 and fresh within the last 51s
if ((realtype == STYPE_RS41) && (s->crefKT > 0) && (s->vframe - s->crefKT < 51)) {
sprintf(w, "\"burst_timer\": %d,", (int)s->countKT);
if ((realtype == STYPE_RS41) && (s->d.crefKT > 0) && (s->d.vframe - s->d.crefKT < 51)) {
sprintf(w, "\"burst_timer\": %d,", (int)s->d.countKT);
w += strlen(w);
}
// Only send battery if provided
if (s->batteryVoltage > 0) {
sprintf(w, "\"batt\": %.2f,", s->batteryVoltage);
if (s->d.batteryVoltage > 0) {
sprintf(w, "\"batt\": %.2f,", s->d.batteryVoltage);
w += strlen(w);
}

Wyświetl plik

@ -5,11 +5,11 @@ extern const char *sondeTypeStrSH[];
int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) {
char buf[1024];
struct tm tim;
time_t t = si->time;
time_t t = si->d.time;
gmtime_r(&t, &tim);
uint8_t realtype = si->type;
if (TYPE_IS_METEO(realtype)) {
realtype = si->subtype == 1 ? STYPE_M10 : STYPE_M20;
realtype = si->d.subtype == 1 ? STYPE_M10 : STYPE_M20;
}
sprintf(buf, "{ \"type\": \"PAYLOAD_SUMMARY\","
"\"callsign\": \"%s\","
@ -22,16 +22,16 @@ int Chasemapper::send(WiFiUDP &udp, SondeInfo *si) {
"\"model\": \"%s\","
"\"freq\": \"%.3f MHz\","
"\"temp\": %g }",
si->ser,
si->lat,
si->lon,
(int)si->alt,
(int)(si->hs * 1.9438445), // m/s into knots
(int)si->dir,
si->d.ser,
si->d.lat,
si->d.lon,
(int)si->d.alt,
(int)(si->d.hs * 1.9438445), // m/s into knots
(int)si->d.dir,
tim.tm_hour, tim.tm_min, tim.tm_sec,
sondeTypeStrSH[realtype],
si->freq,
si->temperature);
si->d.temperature);
Serial.printf("Sending chasemapper json: %s\n", buf);
udp.beginPacket(sonde.config.cm.host, sonde.config.cm.port);
udp.write((const uint8_t *)buf, strlen(buf));

Wyświetl plik

@ -209,6 +209,7 @@ void DFM::finddfname(uint8_t *b)
uint32_t i;
uint8_t ix;
uint16_t d;
SondeData *sd = &(sonde.si()->d);
st = b[0]; /* frame start byte */
ix = b[3]; /* hi/lo part of ser; (LSB due to our bitsToBytes...) */
@ -242,10 +243,10 @@ void DFM::finddfname(uint8_t *b)
chkid >>= 4;
}
if(i==6) {
snprintf(sonde.si()->id, 10, "D%x ", id);
sonde.si()->validID = true;
sonde.si()->subtype = (st>>4)&0x0F;
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
snprintf(sd->id, 10, "D%x ", id);
sd->validID = true;
sd->subtype = (st>>4)&0x0F;
strncpy(sd->typestr, typestr[ (st>>4)&0x0F ], 5);
return;
}
dfmstate.lastfrcnt = 0;
@ -291,12 +292,12 @@ void DFM::finddfname(uint8_t *b)
dfmstate.idcnt1 = dfmstate.cnt[2*i+1];
dfmstate.nameregok = i;
// generate id.....
snprintf(sonde.si()->id, 10, "D%d", ((dfmstate.dat[2*i]<<16)|dfmstate.dat[2*i+1])%100000000);
snprintf(sd->id, 10, "D%d", ((dfmstate.dat[2*i]<<16)|dfmstate.dat[2*i+1])%100000000);
Serial.print("\nNEW AUTOID:");
Serial.println(sonde.si()->id);
sonde.si()->validID = true;
sonde.si()->subtype = (st>>4)&0x0F;
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
Serial.println(sd->id);
sd->validID = true;
sd->subtype = (st>>4)&0x0F;
strncpy(sd->typestr, typestr[ (st>>4)&0x0F ], 5);
}
if(dfmstate.nameregok==i) {
Serial.print(" ID OK");
@ -327,7 +328,7 @@ void DFM::decodeCFG(uint8_t *cfg)
// new ID
finddfname(cfg);
// new aprs ID (dxlaprs, autorx) is now "D" + serial (8 digits) by consensus
memcpy(sonde.si()->ser, sonde.si()->id+1, 9);
memcpy(sonde.si()->d.ser, sonde.si()->d.id+1, 9);
}
static int bitCount(int x) {
@ -342,7 +343,8 @@ uint16_t MON[]={0,0,31,59,90,120,151,181,212,243,273,304,334};
void DFM::decodeDAT(uint8_t *dat)
{
SondeInfo *si = sonde.si();
// 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...

Wyświetl plik

@ -1125,41 +1125,41 @@ void Display::drawString(DispEntry *de, const char *str) {
void Display::drawLat(DispEntry *de) {
rdis->setFont(de->fmt);
if(!sonde.si()->validPos) {
if(!sonde.si()->d.validPos) {
drawString(de,"<?""?> ");
return;
}
snprintf(buf, 16, "%2.5f", sonde.si()->lat);
snprintf(buf, 16, "%2.5f", sonde.si()->d.lat);
drawString(de,buf);
}
void Display::drawLon(DispEntry *de) {
rdis->setFont(de->fmt);
if(!sonde.si()->validPos) {
if(!sonde.si()->d.validPos) {
drawString(de,"<?""?> ");
return;
}
snprintf(buf, 16, "%2.5f", sonde.si()->lon);
snprintf(buf, 16, "%2.5f", sonde.si()->d.lon);
drawString(de,buf);
}
void Display::drawAlt(DispEntry *de) {
rdis->setFont(de->fmt);
if(!sonde.si()->validPos) {
if(!sonde.si()->d.validPos) {
drawString(de," ");
return;
}
float alt = sonde.si()->alt;
float alt = sonde.si()->d.alt;
//testing only.... alt += 30000-454;
snprintf(buf, 16, alt>=1000?" %5.0fm":" %3.1fm", alt);
drawString(de,buf+strlen(buf)-6);
}
void Display::drawHS(DispEntry *de) {
rdis->setFont(de->fmt);
if(!sonde.si()->validPos) {
if(!sonde.si()->d.validPos) {
drawString(de," ");
return;
}
boolean is_ms = (de->extra && de->extra[0]=='m')?true:false; // m/s or km/h
float hs = sonde.si()->hs;
float hs = sonde.si()->d.hs;
if(!is_ms) hs = hs * 3.6;
boolean has_extra = (de->extra && de->extra[1]!=0)? true: false;
snprintf(buf, 16, hs>99?" %3.0f":" %2.1f", hs);
@ -1169,11 +1169,11 @@ void Display::drawHS(DispEntry *de) {
}
void Display::drawVS(DispEntry *de) {
rdis->setFont(de->fmt);
if(!sonde.si()->validPos) {
if(!sonde.si()->d.validPos) {
drawString(de," ");
return;
}
snprintf(buf, 16, " %+2.1f", sonde.si()->vs);
snprintf(buf, 16, " %+2.1f", sonde.si()->d.vs);
DebugPrintf(DEBUG_DISPLAY, "drawVS: extra is %s width=%d\n", de->extra?de->extra:"<null>", de->width);
if(de->extra) { strcat(buf, de->extra); }
drawString(de, buf+strlen(buf)-5- (de->extra?strlen(de->extra):0) );
@ -1181,29 +1181,29 @@ void Display::drawVS(DispEntry *de) {
}
void Display::drawID(DispEntry *de) {
rdis->setFont(de->fmt);
if(!sonde.si()->validID) {
if(!sonde.si()->d.validID) {
drawString(de, "nnnnnnnn ");
return;
}
if(de->extra && de->extra[0]=='n') {
// real serial number, as printed on sonde, can be up to 11 digits long
drawString(de, sonde.si()->ser);
drawString(de, sonde.si()->d.ser);
} else if (de->extra && de->extra[0]=='s') {
// short ID, max 8 digits (no initial "D" for DFM, "M" instead of "ME" for M10)
if( TYPE_IS_DFM(sonde.si()->type) ) {
drawString(de, sonde.si()->id+1);
drawString(de, sonde.si()->d.id+1);
} else if (TYPE_IS_METEO(sonde.si()->type)) {
char sid[9];
sid[0]='M';
memcpy(sid+1, sonde.si()->id+2, 8);
memcpy(sid+1, sonde.si()->d.id+2, 8);
sid[8] = 0;
drawString(de, sid);
} else {
drawString(de, sonde.si()->id);
drawString(de, sonde.si()->d.id);
}
} else {
// dxlAPRS sonde number, max 9 digits, as used on aprs.fi and radiosondy.info
drawString(de, sonde.si()->id);
drawString(de, sonde.si()->d.id);
}
}
void Display::drawRSSI(DispEntry *de) {
@ -1230,7 +1230,7 @@ void Display::drawQS(DispEntry *de) {
void Display::drawType(DispEntry *de) {
rdis->setFont(de->fmt);
const char *typestr = sonde.si()->typestr;
const char *typestr = sonde.si()->d.typestr;
if(*typestr==0) typestr = sondeTypeStr[sonde.si()->type];
drawString(de, typestr);
}
@ -1289,9 +1289,9 @@ void Display::drawKilltimer(DispEntry *de) {
rdis->setFont(de->fmt);
uint16_t value=0;
switch(de->extra[0]) {
case 'l': value = sonde.si()->launchKT; break;
case 'b': value = sonde.si()->burstKT; break;
case 'c': value = sonde.si()->countKT; break;
case 'l': value = sonde.si()->d.launchKT; break;
case 'b': value = sonde.si()->d.burstKT; break;
case 'c': value = sonde.si()->d.countKT; break;
}
// format: 4=h:mm; 6=h:mm:ss; s=sssss
uint16_t h=value/3600;
@ -1362,17 +1362,17 @@ static int tmpc=0;
#endif
#endif
// distance
if( gpsPos.valid && (sonde.si()->validPos&0x03)==0x03 && (layout->usegps&GPSUSE_DIST)) {
gpsDist = (int)calcLatLonDist(gpsPos.lat, gpsPos.lon, sonde.si()->lat, sonde.si()->lon);
if( gpsPos.valid && (sonde.si()->d.validPos&0x03)==0x03 && (layout->usegps&GPSUSE_DIST)) {
gpsDist = (int)calcLatLonDist(gpsPos.lat, gpsPos.lon, sonde.si()->d.lat, sonde.si()->d.lon);
} else {
gpsDist = -1;
}
// bearing
if( gpsPos.valid && (sonde.si()->validPos&0x03)==0x03 && (layout->usegps&GPSUSE_BEARING)) {
if( gpsPos.valid && (sonde.si()->d.validPos&0x03)==0x03 && (layout->usegps&GPSUSE_BEARING)) {
float lat1 = radians(gpsPos.lat);
float lat2 = radians(sonde.si()->lat);
float lat2 = radians(sonde.si()->d.lat);
float lon1 = radians(gpsPos.lon);
float lon2 = radians(sonde.si()->lon);
float lon2 = radians(sonde.si()->d.lon);
float y = sin(lon2-lon1)*cos(lat2);
float x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(lon2-lon1);
float dir = atan2(y, x)/PI*180;
@ -1425,7 +1425,7 @@ void Display::drawGPS(DispEntry *de) {
{
// distance
// equirectangular approximation is good enough
if( (sonde.si()->validPos&0x03)!=0x03 ) {
if( (sonde.si()->d.validPos&0x03)!=0x03 ) {
snprintf(buf, 16, "no pos ");
if(de->extra && *de->extra=='5') buf[5]=0;
} else if(!gpsPos.valid) {
@ -1448,7 +1448,7 @@ void Display::drawGPS(DispEntry *de) {
break;
case 'I':
// dIrection
if( (!gpsPos.valid) || ((sonde.si()->validPos&0x03)!=0x03 ) ) {
if( (!gpsPos.valid) || ((sonde.si()->d.validPos&0x03)!=0x03 ) ) {
drawString(de, "---");
break;
}
@ -1460,7 +1460,7 @@ void Display::drawGPS(DispEntry *de) {
break;
case 'B':
// relative bearing
if( (!gpsPos.valid) || ((sonde.si()->validPos&0x03)!=0x03 ) ) {
if( (!gpsPos.valid) || ((sonde.si()->d.validPos&0x03)!=0x03 ) ) {
drawString(de, "---");
break;
}
@ -1491,9 +1491,9 @@ void Display::drawGPS(DispEntry *de) {
int angN, angA, angB; // angle of north, array, bullet
int validA, validB; // 0: no, 1: yes, -1: old
if(circinfo->arr=='C') { angA=gpsPos.course; validA=disp.gpsCourseOld?-1:1; }
else { angA=disp.gpsDir; validA=sonde.si()->validPos?(rxgood?1:-1):0; }
else { angA=disp.gpsDir; validA=sonde.si()->d.validPos?(rxgood?1:-1):0; }
if(circinfo->bul=='C') { angB=gpsPos.course; validB=disp.gpsCourseOld?-1:1; }
else { angB=disp.gpsDir; validB=sonde.si()->validPos?(rxgood?1:-1):0; }
else { angB=disp.gpsDir; validB=sonde.si()->d.validPos?(rxgood?1:-1):0; }
if(circinfo->top=='N') {
angN = 0;
} else {

Wyświetl plik

@ -280,7 +280,9 @@ int M10M20::decodeframeM10(uint8_t *data) {
if(data[1]==0x9F && data[2]==0x20) {
Serial.println("Decoding...");
SondeInfo *si = sonde.si();
//SondeInfo *si = sonde.si();
SondeData *si = &(sonde.si()->d);
// Its a M10
// getid...
char ids[12];
@ -295,7 +297,7 @@ int M10M20::decodeframeM10(uint8_t *data) {
ids[7] = hex(id/16);
ids[8] = hex(id);
ids[9] = 0;
strncpy(sonde.si()->id, ids, 10);
strncpy(si->id, ids, 10);
ids[0] = hex(data[95]/16);
ids[1] = dez((data[95]&0x0f)/10);
ids[2] = dez((data[95]&0x0f));
@ -396,15 +398,15 @@ void M10M20::processM10data(uint8_t dt)
// 45 20 7x => M20
if(rxp==2 && dataptr[0]==0x45 && dataptr[1]==0x20) { isM20 = true; }
if(isM20) {
memcpy(sonde.si()->typestr, "M20 ", 5);
sonde.si()->subtype = 2;
memcpy(sonde.si()->d.typestr, "M20 ", 5);
sonde.si()->d.subtype = 2;
if(rxp>=M20_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeM20(dataptr);
}
} else {
memcpy(sonde.si()->typestr, "M10 ", 5);
sonde.si()->subtype = 1;
memcpy(sonde.si()->d.typestr, "M10 ", 5);
sonde.si()->d.subtype = 1;
if(rxp>=M10_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeM10(dataptr);
@ -472,7 +474,8 @@ int M10M20::decodeframeM20(uint8_t *data) {
int repl = 0;
bool crcok = false;
bool crcbok = false;
SondeInfo *si = sonde.si();
//SondeInfo *si = sonde.si();
SondeData *si = &(sonde.si()->d);
// error correction, inspired by oe5dxl's sondeudp
// check first block
uint8_t s[200];
@ -569,7 +572,7 @@ int M10M20::decodeframeM20(uint8_t *data) {
uint32_t tow = getint24(data+15);
uint16_t week = getint16(data+26);
si->time = (tow+week*604800+315964800)-18;
si->vframe = sonde.si()->time - 315964800;
si->vframe =si->time - 315964800;
si->validTime = true;
}

Wyświetl plik

@ -199,7 +199,8 @@ extern double atang2(double x, double y);
void calcgps(uint8_t *buf) {
SondeInfo *si = sonde.si();
//SondeInfo *si = sonde.si();
SondeData *si =&(sonde.si()->d);
double wx = i4(buf+pos_GPSecefX) * 0.01;
double wy = i4(buf+pos_GPSecefY) * 0.01;
double wz = i4(buf+pos_GPSecefZ) * 0.01;
@ -237,7 +238,8 @@ static uint32_t getgpstime(uint8_t *buf) {
}
// unix time stamp from date and time info in frame.
static void getmp3htime(uint8_t *buf) {
SondeInfo *si = sonde.si();
//SondeInfo *si = sonde.si();
SondeData *si =&(sonde.si()->d);
// gpsdate from CFG frame 15 (0 if not yet received)
uint32_t gpsdate = mp3hstate.gpsdate;
@ -285,7 +287,8 @@ int MP3H::decodeframeMP3H(uint8_t *data) {
}
// data is a frame with correct CRC
SondeInfo *si = sonde.si();
//SondeInfo *si = sonde.si();
SondeData *si =&(sonde.si()->d);
uint8_t cnt = data[pos_CNT1] & 0x0F;
uint32_t cfg = u4(data+pos_CFG);
if(cnt==15) {
@ -307,7 +310,7 @@ int MP3H::decodeframeMP3H(uint8_t *data) {
// get id
if((mp3hstate.idok&3) == 3) {
//...
si->type = STYPE_MP3H;
//si->type = STYPE_MP3H;
uint32_t n = mp3hstate.id1*100000 + mp3hstate.id2;
si->id[0] = 'M';
si->id[1] = 'R';
@ -504,13 +507,13 @@ int MP3H::receive() {
if(haveNewFrame != 1) {
Serial.printf("hNF: %d (ERROR)\n", haveNewFrame);
retval = RX_ERROR;
} else if (sonde.si()->time == lastFrame) { // same frame number as seen before => skip
} else if (sonde.si()->d.time == lastFrame) { // same frame number as seen before => skip
Serial.printf("Skipping frame with frame# %d\n", lastFrame);
// nothing, wait for next, "new" frame
} else { // good and new frame, return it.
Serial.println("Good frame");
haveNewFrame = 0;
lastFrame = sonde.si()->time;
lastFrame = sonde.si()->d.time;
return RX_OK;
}
haveNewFrame = 0;

Wyświetl plik

@ -431,7 +431,7 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
double z;
double y;
double x;
SondeInfo *si = sonde.si();
SondeData *si = &(sonde.si()->d);
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;
@ -594,7 +594,7 @@ float GetRAHumidity( uint32_t humCurrent, uint32_t humMin, uint32_t humMax, floa
float Cp = ( C / calibration->value.calibU[0] - 1.0f) * calibration->value.calibU[1];
/* Compensation for low temperature and pressure at altitude */
float estimatedPressure = 1013.25f * expf(-1.18575919e-4f * sonde.si()->alt );
float estimatedPressure = 1013.25f * expf(-1.18575919e-4f * sonde.si()->d.alt );
float Tp = (sensorTemp - 20.0f) / 180.0f;
float sum = 0;
@ -640,7 +640,7 @@ int RS41::decode41(byte *data, int maxlen)
{
char buf[128];
int crcok = 0;
SondeInfo *si = sonde.si();
SondeData *si = &(sonde.si()->d);
int32_t corr = reedsolomon41(data, 560, 131); // try short frame first
if(corr<0) {
@ -680,6 +680,12 @@ int RS41::decode41(byte *data, int maxlen)
switch(typ) {
case 'y': // name
{
if(strncmp(si->id, (const char *)(data+p+2), 8)) {
// ID changed, i.e. new sonde on same frequency. clear calibration and all other data
sonde.clearAllData(sonde.si());
struct subframeBuffer *sub = (struct subframeBuffer *)sonde.si()->extra;
if(sub) { sub->valid = 0; }
}
Serial.print("#");
uint16_t fnr = data[p]+(data[p+1]<<8);
Serial.print(fnr);
@ -687,13 +693,8 @@ int RS41::decode41(byte *data, int maxlen)
Serial.print("; RS41 ID ");
snprintf(buf, 10, "%.8s ", data+p+2);
Serial.print(buf);
sonde.si()->batteryVoltage = data[p+10] / 10.0f;
si->type=STYPE_RS41;
if(strncmp(si->id, (const char *)(data+p+2), 8)) {
// ID changed, i.e. new sonde on same frequency. clear calibration data
struct subframeBuffer *sub = (struct subframeBuffer *)si->extra;
if(sub) { sub->valid = 0; }
}
si->batteryVoltage = data[p+10] / 10.0f;
// not needed, if we end up here, the type has to be RS41.... si->type=STYPE_RS41;
strncpy(si->id, (const char *)(data+p+2), 8);
si->id[8]=0;
strncpy(si->ser, (const char *)(data+p+2), 8);
@ -761,7 +762,7 @@ int RS41::decode41(byte *data, int maxlen)
Serial.printf( "Humid sensor: tempHumiMain = %ld, tempHumiRef1 = %ld, tempHumiRef2 = %ld\n", tempHumiMain, tempHumiRef1, tempHumiRef2 );
Serial.printf( "Pressure sens: pressureMain = %ld, pressureRef1 = %ld, pressureRef2 = %ld\n", pressureMain, pressureRef1, pressureRef2 );
#endif
struct subframeBuffer *calibration = (struct subframeBuffer *)si->extra;
struct subframeBuffer *calibration = (struct subframeBuffer *)(sonde.si()->extra);
// check for bits 3 through 20 set and 37 through 46
bool validExternalTemperature = calibration!=NULL && (calibration->valid & 0xF8) == 0xF8;
bool validHumidity = calibration!=NULL && (calibration->valid & 0x7FE0001FFFF8) == 0x7FE0001FFFF8;

Wyświetl plik

@ -552,7 +552,7 @@ int RS92::waitRXcomplete() {
Serial.printf("decoding frame %d\n", lastFrame);
print_frame(lastFrame==1?data1:data2, 240);
SondeInfo *si = sonde.sondeList+rxtask.receiveSonde;
SondeData *si = &( (sonde.sondeList+rxtask.receiveSonde)->d );
si->lat = gpx.lat;
si->lon = gpx.lon;
si->alt = gpx.alt;

Wyświetl plik

@ -65,7 +65,7 @@ void ShFreqImport::populate(char *id, float lat, float lon, float freq, const ch
// update label if its a dynamic SH entry
int i;
for(i=0; i<sonde.config.maxsonde; i++) {
if( abs(sonde.sondeList[i].freq-freq)<0.0015 ) { // exists already, max error 1500 Hz
if( abs(sonde.sondeList[i].freq-freq)<0.003 ) { // exists already, max error 3000 Hz
Serial.printf("id %s close to %d\n", id, i);
if( sonde.sondeList[i].type == stype) {
char *l = sonde.sondeList[i].launchsite;
@ -103,6 +103,8 @@ void ShFreqImport::cleanup() {
//Serial.println("Cleanup called ********");
for(int i=0; i<sonde.config.maxsonde; i++) {
if( (((inuse[i/8]>>(i&7))&1) == 0) && *sonde.sondeList[i].launchsite=='@' ) {
// Don't remove the currently active entry
if(i==sonde.currentSonde) continue;
Serial.printf("removing #%d\n", i);
sonde.sondeList[i].launchsite[0] = 0;
sonde.sondeList[i].active = 0;

Wyświetl plik

@ -362,9 +362,10 @@ void Sonde::addSonde(float frequency, SondeType type, int active, char *launchsi
if(sondeList[nSonde].extra) free(sondeList[nSonde].extra);
memset(&sondeList[nSonde], 0, sizeof(SondeInfo));
sondeList[nSonde].type = type;
sondeList[nSonde].typestr[0] = 0;
sondeList[nSonde].d.typestr[0] = 0;
sondeList[nSonde].freq = frequency;
memcpy(sondeList[nSonde].rxStat, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", 18); // unknown/undefined
clearAllData(sondeList+nSonde);
}
sondeList[nSonde].active = active;
strncpy(sondeList[nSonde].launchsite, launchsite, 17);
@ -667,6 +668,14 @@ uint8_t Sonde::updateState(uint8_t event) {
return 0xFF;
}
void Sonde::clearAllData(SondeInfo *si) {
// set everything to 0
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;
}
void Sonde::updateDisplayPos() {
disp.updateDisplayPos();
}

Wyświetl plik

@ -14,7 +14,13 @@ extern uint8_t debug;
// RX_TIMEOUT: no header detected
// RX_ERROR: header detected, but data not decoded (crc error, etc.)
// RX_OK: header and data ok
// RX_PARTIAL: header detected, some data ok, some with errors
// For RS41: Some blocks with CRC error, some blocks ok in a single frame
// For DFM: In +- 1s, some but not all DAT-subframes 1,2,3,4,5,6,7,8 received
// For RS92 ??? unclear
// For M10/M20 its always all or nothing, no PARTIAL data
// For MP3H its alway all or nothing, no PARTIAL data
// RX_OK: header and all data ok
enum RxResult { RX_OK, RX_TIMEOUT, RX_ERROR, RX_UNKNOWN, RX_NOPOS };
#define RX_UPDATERSSI 0xFFFE
@ -68,18 +74,13 @@ extern const char *manufacturer_string[NSondeTypes];
#define TYPE_IS_DFM(t) ( (t)==STYPE_DFM )
#define TYPE_IS_METEO(t) ( (t)==STYPE_M10 || (t)==STYPE_M20 )
typedef struct st_sondeinfo {
// receiver configuration
bool active;
SondeType type;
int8_t subtype; /* 0 for none/unknown, hex type for dfm, 1/2 for M10/M20 */
float freq;
typedef struct st_sondedata {
// decoded ID
char typestr[5]; // decoded type (use type if *typestr==0)
char id[10];
char ser[12];
bool validID;
char launchsite[18];
char typestr[5]; // decoded type (use type if *typestr==0)
int8_t subtype; /* 0 for none/unknown, hex type for dfm, 1/2 for M10/M20 */
// decoded position
float lat; // latitude
float lon; // longitude
@ -94,6 +95,25 @@ typedef struct st_sondeinfo {
uint32_t frame;
uint32_t vframe; // vframe==frame if frame is unique/continous, otherweise vframe is derived from gps time
bool validTime;
// shut down timers, currently only for RS41; -1=disabled
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 batteryVoltage = -1;
} SondeData;
typedef struct st_sondeinfo {
// First part: static configuration, not decoded data.
// receiver configuration
bool active;
SondeType type;
float freq;
char launchsite[18];
// Second part: internal decoder state. no need to clear this on new sonde
// RSSI from receiver
int rssi; // signal strength
int32_t afc; // afc correction value
@ -103,15 +123,12 @@ typedef struct st_sondeinfo {
uint32_t norxStart; // millis() timestamp of continuous no rx start
uint32_t viewStart; // millis() timestamp of viewinf this sonde with current display
int8_t lastState; // -1: disabled; 0: norx; 1: rx
// shut down timers, currently only for RS41; -1=disabled
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)
// Third part: decoded data. Clear if reception of a new sonde has started
SondeData d;
// Decoder-specific data, dynamically allocated (for RS41: calibration data)
void *extra;
float temperature = -300.0; // platinum resistor temperature
float tempRHSensor = -300.0; // temperature of relative humidity sensor
float relativeHumidity = -1.0; // relative humidity
float batteryVoltage = -1;
} SondeInfo;
// rxStat: 3=undef[empty] 1=timeout[.] 2=errro[E] 0=ok[|] 4=no valid position[°]
@ -319,6 +336,7 @@ public:
uint16_t waitRXcomplete();
SondeInfo *si();
void clearAllData(SondeInfo *si);
uint8_t timeoutEvent(SondeInfo *si);
uint8_t updateState(uint8_t event);

Wyświetl plik

@ -258,8 +258,9 @@ static uint32_t dao91(double x)
char b[201];
//char raw[201];
char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
char *aprs_senddata(SondeInfo *si, const char *usercall, const char *sym) {
// float lat, float lon, float alt, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym, const char *comm)
SondeData *s = &(si->d);
*b=0;
aprsstr_append(b, usercall);
aprsstr_append(b, ">");
@ -299,9 +300,9 @@ char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
strcat(b, "&");
char comm[100];
snprintf(comm, 100, "Clb=%.1fm/s %.3fMHz Type=%s", s->vs, s->freq, sondeTypeStr[s->type]);
snprintf(comm, 100, "Clb=%.1fm/s %.3fMHz Type=%s", s->vs, si->freq, sondeTypeStr[si->type]);
strcat(b, comm);
if( TYPE_IS_DFM(s->type) || TYPE_IS_METEO(s->type) ) {
if( TYPE_IS_DFM(si->type) || TYPE_IS_METEO(si->type) ) {
snprintf(comm, 100, " ser=%s", s->ser);
strcat(b, comm);
}

Wyświetl plik

@ -51,8 +51,9 @@ void MQTT::publishUptime()
mqttClient.publish(topic, 1, 1, payload);
}
void MQTT::publishPacket(SondeInfo *s)
void MQTT::publishPacket(SondeInfo *si)
{
SondeData *s = &(si->d);
mqttClient.connect(); // ensure we've got connection
char payload[1024];
@ -86,12 +87,12 @@ void MQTT::publishPacket(SondeInfo *s)
"\"countKT\": %d,"
"\"crefKT\": %d"
"}",
(int)s->active,
s->freq,
(int)si->active,
si->freq,
s->id,
s->ser,
(int)s->validID,
s->launchsite,
si->launchsite,
s->lat,
s->lon,
s->alt,
@ -103,13 +104,13 @@ void MQTT::publishPacket(SondeInfo *s)
s->time,
s->frame,
(int)s->validTime,
s->rssi,
s->afc,
s->rxStat,
s->rxStart,
s->norxStart,
s->viewStart,
s->lastState,
si->rssi,
si->afc,
si->rxStat,
si->rxStart,
si->norxStart,
si->viewStart,
si->lastState,
s->launchKT,
s->burstKT,
s->countKT,