From 8e6d953e60ab51f8709ae6974f4ff25a8ac41246 Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Fri, 5 Feb 2021 16:51:26 +0100 Subject: [PATCH 1/3] rs92 eph: configurable ftp site --- RX_FSK/RX_FSK.ino | 3 ++- RX_FSK/data/config.txt | 8 ++++++ RX_FSK/version.h | 4 +-- libraries/SondeLib/Sonde.cpp | 3 +++ libraries/SondeLib/Sonde.h | 1 + libraries/SondeLib/geteph.cpp | 47 ++++++++++++++++++----------------- 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index b6a4394..5295244 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -498,6 +498,7 @@ struct st_configitems config_list[] = { {"dfm.rxbw", "DFM RX bandwidth", 0, &sonde.config.dfm.rxbw}, {"m10m20.agcbw", "M10/M20 AGC bandwidth", 0, &sonde.config.m10m20.agcbw}, {"m10m20.rxbw", "M10/M20 RX bandwidth", 0, &sonde.config.m10m20.rxbw}, + {"ephftp", "FTP for eph (RS92)", 39, &sonde.config.ephftp}, {"", "Data feed configuration", -5, NULL}, /* APRS settings */ {"call", "Call", 8, sonde.config.call}, @@ -1140,7 +1141,7 @@ void unkHandler(T nmea) { } } -#define DEBUG_GPS 1 +#define DEBUG_GPS 0 static bool gpsCourseOld; static int lastCourse; void gpsTask(void *parameter) { diff --git a/RX_FSK/data/config.txt b/RX_FSK/data/config.txt index 1c212aa..4e8b2e8 100644 --- a/RX_FSK/data/config.txt +++ b/RX_FSK/data/config.txt @@ -78,6 +78,14 @@ rs92.alt2d=480 dfm.agcbw=20800 dfm.rxbw=10400 #-------------------------------# +# ftp server for RINEX data (for RS92) +# YYYY/DDD/brdcDDD0.YYn.gz is appended +# 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/ +#-------------------------------# # axudp for sending to aprsmap #-------------------------------# # local use only, do not feed to public services diff --git a/RX_FSK/version.h b/RX_FSK/version.h index fa26990..45cd53b 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20210126"; +const char *version_id = "devel20210205"; const int SPIFFS_MAJOR=2; -const int SPIFFS_MINOR=8; +const int SPIFFS_MINOR=9; diff --git a/libraries/SondeLib/Sonde.cpp b/libraries/SondeLib/Sonde.cpp index bf1ca9e..6f16a14 100644 --- a/libraries/SondeLib/Sonde.cpp +++ b/libraries/SondeLib/Sonde.cpp @@ -204,6 +204,7 @@ void Sonde::defaultConfig() { config.tcpfeed.highrate = 10; config.tcpfeed.idformat = ID_DFMDXL; config.kisstnc.active = 0; + strcpy(config.ephftp,"igs.bkg.bund.de/IGS/BRDC/"); config.mqtt.active = 0; strcpy(config.mqtt.id, "rdz_sonde_server"); @@ -312,6 +313,8 @@ void Sonde::setConfig(const char *cfg) { config.dfm.rxbw = atoi(val); } else if(strcmp(cfg,"rs92.alt2d")==0) { config.rs92.alt2d= atoi(val); + } else if(strcmp(cfg,"ephftp")==0) { + strncpy(config.ephftp, val, 40); } else if(strcmp(cfg,"kisstnc.active")==0) { config.kisstnc.active = atoi(val); } else if(strcmp(cfg,"kisstnc.idformat")==0) { diff --git a/libraries/SondeLib/Sonde.h b/libraries/SondeLib/Sonde.h index 89b0bf0..189df28 100644 --- a/libraries/SondeLib/Sonde.h +++ b/libraries/SondeLib/Sonde.h @@ -209,6 +209,7 @@ typedef struct st_rdzconfig { struct st_rs92config rs92; struct st_dfmconfig dfm; struct st_m10m20config m10m20; + char ephftp[40]; // data feed configuration // for now, one feed for each type is enough, but might get extended to more? char call[10]; // APRS callsign diff --git a/libraries/SondeLib/geteph.cpp b/libraries/SondeLib/geteph.cpp index 9f86273..a898a55 100644 --- a/libraries/SondeLib/geteph.cpp +++ b/libraries/SondeLib/geteph.cpp @@ -6,11 +6,11 @@ #include #include #include "Display.h" - +#include "Sonde.h" extern WiFiClient client; -static const char *ftpserver = "www.ngs.noaa.gov"; +//static const char *ftpserver = "www.ngs.noaa.gov"; char outbuf[128]; uint8_t getreply() { @@ -72,31 +72,32 @@ void geteph() { Serial.printf("now: %s, existing: %s => updating\n", nowstr, tsstr); } status.close(); - disp.rdis->clear(); - disp.rdis->setFont(FONT_SMALL); - disp.rdis->drawString(0, 0, "FTP ngs.noaa.gov"); - // fetch rinex from server File fh = SPIFFS.open("/brdc.gz","w"); if(!fh) { Serial.println("cannot open file\n"); return; } - char buf[252]; - snprintf(buf, 128, "/cors/rinex/%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000); + char host[252]; + strcpy(host, sonde.config.ephftp); + char *buf = strchr(host, '/'); + if(!buf) { Serial.println("Invalid FTP host config"); return; } + *buf = 0; + buf++; + uint8_t dispw, disph, dispxs, dispys; + disp.rdis->getDispSize(&disph, &dispw, &dispxs, &dispys); + disp.rdis->clear(); + disp.rdis->setFont(FONT_SMALL); + disp.rdis->drawString(0, 0, host); + // fetch rinex from server + char *ptr = buf + strlen(buf); + snprintf(ptr, 128, "%04d/%03d/brdc%03d0.%02dn.gz", year, day, day, year-2000); Serial.println("running geteph\n"); - disp.rdis->drawString(0, 1, buf+21); + disp.rdis->drawString(0, 1*dispys, ptr+9); - if(!client.connect(ftpserver, 21)) { - Serial.println("FTP connection to www.ngs.noaa.gov failed"); + if(!client.connect(host, 21)) { + Serial.printf("FTP connection to %s failed\n", host); return; } -#if 0 - while(!client.available()) delay(1); - while(client.available()) { - String s = client.readStringUntil('\n'); - Serial.println(s); - } -#endif if(getreply()>='4') { Serial.println("connected failed"); return; } client.print("USER anonymous\r\n"); if(getreply()>='4') { Serial.println("USER failed"); return; } @@ -121,8 +122,8 @@ void geteph() { } uint16_t port = (array_pasv[4]<<8) | (array_pasv[5]&0xff); WiFiClient dclient; - Serial.printf("connecting to %s:%d\n", ftpserver,port); - dclient.connect(ftpserver, port); + Serial.printf("connecting to %s:%d\n", host, port); + dclient.connect(host, port); if(!dclient) { Serial.println("data connection failed"); return; @@ -149,9 +150,9 @@ void geteph() { fh.close(); snprintf(buf, 16, "Fetched %d B ",len); buf[16]=0; - disp.rdis->drawString(0,2,buf); + disp.rdis->drawString(0,2*dispys,buf); - disp.rdis->drawString(0,4,"Decompressing..."); + disp.rdis->drawString(0,4*dispys,"Decompressing..."); // decompression tinfl_decompressor *decomp = (tinfl_decompressor *)malloc(sizeof(tinfl_decompressor)); tinfl_init(decomp); @@ -219,7 +220,7 @@ void geteph() { status.close(); snprintf(buf, 16, "Done: %d B ",total); buf[16]=0; - disp.rdis->drawString(0,5,buf); + disp.rdis->drawString(0,5*dispys,buf); delay(1000); free(obuf); From 981ecc804474ba24fbea39765df7faf85c6b2d22 Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Tue, 9 Feb 2021 00:59:56 +0100 Subject: [PATCH 2/3] detect rdzclient timeout and disconnect from app --- RX_FSK/RX_FSK.ino | 13 ++++++++++--- RX_FSK/version.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 5295244..520110a 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -1141,7 +1141,7 @@ void unkHandler(T nmea) { } } -#define DEBUG_GPS 0 +//#define DEBUG_GPS static bool gpsCourseOld; static int lastCourse; void gpsTask(void *parameter) { @@ -1987,8 +1987,15 @@ void loopDecoder() { s->countKT, s->crefKT ); - - rdzclient.write(raw, len>1024?1024:len); + //Serial.println("Writing rdzclient..."); + if(len>1024) len=1024; + int wlen = rdzclient.write(raw, len); + if(wlen != len) { + Serial.println("Writing rdzClient not OK, closing connection"); + rdzclient.stop(); + rdzclient = NULL; + } + //Serial.println("Writing rdzclient OK"); } Serial.print("updateDisplay started... "); if (forceReloadScreenConfig) { diff --git a/RX_FSK/version.h b/RX_FSK/version.h index 45cd53b..e5049d5 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20210205"; +const char *version_id = "devel20210209"; const int SPIFFS_MAJOR=2; const int SPIFFS_MINOR=9; From 7db0e8ff1163675865a153527373cad030d00833 Mon Sep 17 00:00:00 2001 From: "Hansi, dl9rdz" Date: Sat, 13 Feb 2021 12:23:21 +0100 Subject: [PATCH 3/3] web ui usability enhancements --- RX_FSK/RX_FSK.ino | 65 ++++++++++++++++++++++++++++--------- RX_FSK/data/index.html | 53 ++++++++++++------------------ RX_FSK/data/rdz.js | 28 ++++++++++++++++ RX_FSK/data/style.css | 59 ++++++++++++++++++++++++++++++--- RX_FSK/version.h | 4 +-- libraries/SondeLib/RS41.cpp | 2 +- 6 files changed, 155 insertions(+), 56 deletions(-) create mode 100644 RX_FSK/data/rdz.js diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino index 520110a..00cb44c 100644 --- a/RX_FSK/RX_FSK.ino +++ b/RX_FSK/RX_FSK.ino @@ -214,9 +214,24 @@ void setupChannelList() { file.close(); } +const char *HTMLHEAD=" "; +void HTMLBODY(char *ptr, const char *which) { + strcat(ptr, "
"); +} +void HTMLBODYEND(char *ptr) { + strcat(ptr, "
"); +} +void HTMLSAVEBUTTON(char *ptr) { + strcat(ptr, "
"); +} + const char *createQRGForm() { char *ptr = message; - strcpy(ptr, ""); + strcpy(ptr, HTMLHEAD); + strcat(ptr, ""); +/* strcat(ptr, ""); - - strcat(ptr, "
"); +*/ + HTMLBODY(ptr, "qrg.html"); + //strcat(ptr, "
IDActiveFreqLaunchsiteMode
"); + strcat(ptr, "
IDActiveFreqLaunchsiteMode
"); for (int i = 0; i < sonde.config.maxsonde; i++) { //String s = sondeTypeSelect(i >= sonde.nSonde ? 2 : sonde.sondeList[i].type); String site = sonde.sondeList[i].launchsite; sprintf(ptr + strlen(ptr), "" - "" + "" "" //"", "
IDActiveFreqLaunchsiteMode
%d", @@ -258,7 +275,10 @@ const char *createQRGForm() { i + 1, i >= sonde.nSonde ? 2 : sondeTypeChar[sonde.sondeList[i].type] ); //i + 1, s.c_str()); } - strcat(ptr, "
"); + strcat(ptr, ""); + //
"); + HTMLSAVEBUTTON(ptr); + HTMLBODYEND(ptr); Serial.printf("QRG form: size=%d bytes\n", strlen(message)); return message; } @@ -353,7 +373,9 @@ void setupWifiList() { const char *createWIFIForm() { char *ptr = message; char tmp[4]; - strcpy(ptr, "
"); + strcpy(ptr, HTMLHEAD); strcat(ptr, ""); + HTMLBODY(ptr, "wifi.html"); + strcat(ptr, "
NrSSIDPassword
"); for (int i = 0; i < MAX_WIFI; i++) { sprintf(tmp, "%d", i); sprintf(ptr + strlen(ptr), "" @@ -362,7 +384,10 @@ const char *createWIFIForm() { i + 1, i < nNetworks ? networks[i].id.c_str() : "", i + 1, i < nNetworks ? networks[i].pw.c_str() : ""); } - strcat(ptr, "
NrSSIDPassword
%s
"); + strcat(ptr, ""); + //
"); + HTMLSAVEBUTTON(ptr); + HTMLBODYEND(ptr); Serial.printf("WIFI form: size=%d bytes\n", strlen(message)); return message; } @@ -436,7 +461,8 @@ void addSondeStatus(char *ptr, int i) const char *createStatusForm() { char *ptr = message; - strcpy(ptr, ""); + strcpy(ptr, HTMLHEAD); + strcat(ptr, ""); for (int i = 0; i < sonde.nSonde; i++) { int snum = (i + sonde.currentSonde) % sonde.nSonde; @@ -607,7 +633,9 @@ void addConfigInt8List(char *ptr, int idx, const char *label, int8_t *list) { const char *createConfigForm() { char *ptr = message; - strcpy(ptr, "
"); + strcpy(ptr, HTMLHEAD); strcat(ptr, ""); + HTMLBODY(ptr, "config.html"); + strcat(ptr, "
OptionValue
"); for (int i = 0; i < N_CONFIG; i++) { switch (config_list[i].type) { case -5: // Heading @@ -636,7 +664,10 @@ const char *createConfigForm() { break; } } - strcat(ptr, "
OptionValue
"); + strcat(ptr, ""); + //
"); + HTMLSAVEBUTTON(ptr); + HTMLBODYEND(ptr); Serial.printf("Config form: size=%d bytes\n", strlen(message)); return message; } @@ -695,21 +726,23 @@ const char *handleConfigPost(AsyncWebServerRequest *request) { const char *ctrlid[] = {"rx", "scan", "spec", "wifi", "rx2", "scan2", "spec2", "wifi2"}; -const char *ctrllabel[] = {"Receiver (short keypress)", "Scanner (double keypress)", "Spectrum (medium keypress)", "WiFi (long keypress)", - "Button 2 (short keypress)", "Button 2 (double keypress)", "Button 2 (medium keypress)", "Button 2 (long keypress)" +const char *ctrllabel[] = {"Receiver/next freq. (short keypress)", "Scanner (double keypress)", "Spectrum (medium keypress)", "WiFi (long keypress)", + "Button 2/next screen (short keypress)", "Button 2 (double keypress)", "Button 2 (medium keypress)", "Button 2 (long keypress)" }; const char *createControlForm() { char *ptr = message; - strcpy(ptr, "
"); + strcpy(ptr, HTMLHEAD); strcat(ptr, ""); + HTMLBODY(ptr, "control.html"); for (int i = 0; i < 8; i++) { - strcat(ptr, "
"); + strcat(ptr, "\">"); + if(i==3) { strcat(ptr, "

"); } } - strcat(ptr, "
"); + HTMLBODYEND(ptr); Serial.printf("Control form: size=%d bytes\n", strlen(message)); return message; } diff --git a/RX_FSK/data/index.html b/RX_FSK/data/index.html index e0acb1d..1bc15ae 100644 --- a/RX_FSK/data/index.html +++ b/RX_FSK/data/index.html @@ -1,64 +1,50 @@ - RDZSonde Server + rdzTTGOsonde Server + -

RDZSonde Server

- +
+

RDZSonde Server

-
+
-

QRG - Setup

- +
-

WiFi - Settings

- +
-

Data

- +
- -
-

Configuration

- +
-

Control

- +
-

About

+
%VERSION_NAME%
- Copyright © 2019-2020 by Hansi Reiser, DL9RDZ
+ Copyright © 2019-2021 by Hansi Reiser, DL9RDZ
(version %VERSION_ID%)

with contributions by Vigor and Xavier (M20 support), Meinhard Guenther, DL2MF, Johannes, Robert Stefanowicz, @@ -68,14 +54,16 @@
Autodetect info: %AUTODETECT_INFO%

- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
+ This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
- see https://www.gnu.org/licenses/gpl-2.0.txt - for details + See https://www.gnu.org/licenses/gpl-2.0.txt + for details. +
+
+ diff --git a/RX_FSK/data/rdz.js b/RX_FSK/data/rdz.js new file mode 100644 index 0000000..e041dec --- /dev/null +++ b/RX_FSK/data/rdz.js @@ -0,0 +1,28 @@ +let stypes=new Map(); +stypes.set('4', 'RS41'); +stypes.set('R', 'RS92'); +stypes.set('9', 'DFM9 (old)'); +stypes.set('6', 'DFM6 (old)'); +stypes.set('D', 'DFM'); +stypes.set('M', 'M10'); +stypes.set('2', 'M20'); + +/* Used by qrg.html in RX_FSK.ino */ +function prep() { + var stlist=document.querySelectorAll("input.stype"); + for(txt of stlist){ + var val=txt.getAttribute('value'); var nam=txt.getAttribute('name'); + var sel=document.createElement('select'); + sel.setAttribute('name',nam); + for(stype of stypes) { + var opt=document.createElement('option'); + opt.value=stype[0]; + opt.innerHTML=stype[1]; + if(stype[0]==val) { opt.setAttribute('selected','selected'); } + sel.appendChild(opt); + } + txt.replaceWith(sel); + } +} + +window.onload = prep; diff --git a/RX_FSK/data/style.css b/RX_FSK/data/style.css index 63e9b6b..e098874 100644 --- a/RX_FSK/data/style.css +++ b/RX_FSK/data/style.css @@ -4,6 +4,18 @@ body, html { font-family: Arial; } +.wrapper { + height: 100%; + display: flex; + flex-direction: column; + margin: 0; +} +.tci { + flex-grow: 1; border: none; margin: 0; padding: 0; +} +.footer { +} + table, th, td { border: 1px solid black; border-collapse: collapse; @@ -31,7 +43,8 @@ td#sfreq { border: none; outline: none; cursor: pointer; - padding: 14px 16px; + padding: 10px 10px; + width: 16vw; transition: 0.3s; } @@ -42,13 +55,21 @@ td#sfreq { .tab button.active { background-color: #ccc; } - +.content { + display: flex; + flex: 1; + flex-direction: column; + overflow: auto; + height: 100%; +} .tabcontent { display: none; + flex: 1; padding: 6px 12px; border: 1px solid #ccc; border-top: none; - height: 100%; + flex-direction: column; + overflow: auto; } html { @@ -59,12 +80,12 @@ html { } h1{ color: #0F3376; - padding: 2vh; + font-size: 24px } p{ font-size: 1.5rem; } -.button { +.canberemoved_button { display: inline-block; background-color: #008CBA; border: none; @@ -79,3 +100,31 @@ p{ .button2 { background-color: #f44336; } +.save { + background-color: #0F3376; + border: black; + border-width: 1; + color: white; + padding: 8px 30px; + text-align: center; + text-decoration: none; + display: block; + font-size: 14px; + margin: 0 +} +.ctlbtn { + background-color: #ccc; + border: black; + border-width: 1; + color: black; + padding: 4px 30px; + text-align: center; + text-decoration: none; + display: block; + margin: 2; + font-size: 4vh; +} +.update { + margin: 0; + display: block; +} diff --git a/RX_FSK/version.h b/RX_FSK/version.h index e5049d5..e8cc738 100644 --- a/RX_FSK/version.h +++ b/RX_FSK/version.h @@ -1,4 +1,4 @@ const char *version_name = "rdzTTGOsonde"; -const char *version_id = "devel20210209"; +const char *version_id = "devel20210213"; const int SPIFFS_MAJOR=2; -const int SPIFFS_MINOR=9; +const int SPIFFS_MINOR=10; diff --git a/libraries/SondeLib/RS41.cpp b/libraries/SondeLib/RS41.cpp index a71c9ac..6047914 100644 --- a/libraries/SondeLib/RS41.cpp +++ b/libraries/SondeLib/RS41.cpp @@ -357,7 +357,7 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p) sonde.si()->dir = dir; Serial.print(" "); sonde.si()->hs = sqrt((float)(vn*vn+ve*ve)); - Serial.print(sonde.si()->hs); + Serial.print(sonde.si()->hs*3.6); Serial.print("km/h "); Serial.print(dir); Serial.print("deg ");