pull/71/head
Eben van Ellewee 2021-02-15 10:11:35 +01:00
commit 4ed3bafb86
10 zmienionych plików z 202 dodań i 82 usunięć

Wyświetl plik

@ -214,9 +214,24 @@ void setupChannelList() {
file.close();
}
const char *HTMLHEAD="<html><head> <meta charset=\"UTF-8\"> <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">";
void HTMLBODY(char *ptr, const char *which) {
strcat(ptr, "<body><form class=\"wrapper\" action=\"");
strcat(ptr, which);
strcat(ptr, "\" method=\"post\"><div class=\"content\">");
}
void HTMLBODYEND(char *ptr) {
strcat(ptr, "</div></form></body></html>");
}
void HTMLSAVEBUTTON(char *ptr) {
strcat(ptr, "</div><div class=\"footer\"><input type=\"submit\" class=\"save\" value=\"Save changes\"/>");
}
const char *createQRGForm() {
char *ptr = message;
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">");
strcpy(ptr, HTMLHEAD);
strcat(ptr, "<script src=\"rdz.js\"/> <script> window.onload = prep; </script></head>");
/*
strcat(ptr, "<script type=\"text/javascript\">"
"let stypes=new Map();"
"stypes.set('4', 'RS41');"
@ -241,13 +256,15 @@ const char *createQRGForm() {
" } txt.replaceWith(sel); } } "
" window.onload = prep; "
"</script>");
strcat(ptr, "</head><body><form action=\"qrg.html\" method=\"post\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
*/
HTMLBODY(ptr, "qrg.html");
//strcat(ptr, "<body><form class=\"wrapper\" action=\"qrg.html\" method=\"post\"><div class=\"content\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
strcat(ptr, "<table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Launchsite</th><th>Mode</th></tr>");
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), "<tr><td>%d</td><td><input name=\"A%d\" type=\"checkbox\" %s/></td>"
"<td><input name=\"F%d\" type=\"text\" value=\"%3.3f\"></td>"
"<td><input name=\"F%d\" type=\"text\" width=12 value=\"%3.3f\"></td>"
"<td><input name=\"S%d\" type=\"text\" value=\"%s\"></td>"
//"<td><select name=\"T%d\">%s</select></td>",
"<td><input class='stype' name='T%d' value='%c'>",
@ -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, "</table><input type=\"submit\" value=\"Update\"/></form></body></html>");
strcat(ptr, "</table>");
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
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, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"wifi.html\" method=\"post\"><table><tr><th>Nr</th><th>SSID</th><th>Password</th></tr>");
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
HTMLBODY(ptr, "wifi.html");
strcat(ptr, "<table><tr><th>Nr</th><th>SSID</th><th>Password</th></tr>");
for (int i = 0; i < MAX_WIFI; i++) {
sprintf(tmp, "%d", i);
sprintf(ptr + strlen(ptr), "<tr><td>%s</td><td><input name=\"S%d\" type=\"text\" value=\"%s\"/></td>"
@ -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, "</table><input type=\"submit\" value=\"Update\"></input></form></body></html>");
strcat(ptr, "</table>");
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
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, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"><meta http-equiv=\"refresh\" content=\"5\"></head><body>");
strcpy(ptr, HTMLHEAD);
strcat(ptr, "<meta http-equiv=\"refresh\" content=\"5\"></head><body>");
for (int i = 0; i < sonde.nSonde; i++) {
int snum = (i + sonde.currentSonde) % sonde.nSonde;
@ -498,6 +524,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},
@ -606,7 +633,9 @@ void addConfigInt8List(char *ptr, int idx, const char *label, int8_t *list) {
const char *createConfigForm() {
char *ptr = message;
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"config.html\" method=\"post\"><table><tr><th>Option</th><th>Value</th></tr>");
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
HTMLBODY(ptr, "config.html");
strcat(ptr, "<table><tr><th>Option</th><th>Value</th></tr>");
for (int i = 0; i < N_CONFIG; i++) {
switch (config_list[i].type) {
case -5: // Heading
@ -635,7 +664,10 @@ const char *createConfigForm() {
break;
}
}
strcat(ptr, "</table><input type=\"submit\" value=\"Update\"></input></form></body></html>");
strcat(ptr, "</table>");
//</div><div class=\"footer\"><input type=\"submit\" class=\"update\" value=\"Update\"/>");
HTMLSAVEBUTTON(ptr);
HTMLBODYEND(ptr);
Serial.printf("Config form: size=%d bytes\n", strlen(message));
return message;
}
@ -694,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, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"control.html\" method=\"post\">");
strcpy(ptr, HTMLHEAD); strcat(ptr, "</head>");
HTMLBODY(ptr, "control.html");
for (int i = 0; i < 8; i++) {
strcat(ptr, "<input type=\"submit\" name=\"");
strcat(ptr, "<input class=\"ctlbtn\" type=\"submit\" name=\"");
strcat(ptr, ctrlid[i]);
strcat(ptr, "\" value=\"");
strcat(ptr, ctrllabel[i]);
strcat(ptr, "\"></input><br>");
strcat(ptr, "\"></input>");
if(i==3) { strcat(ptr, "<p></p>"); }
}
strcat(ptr, "</form></body></html>");
HTMLBODYEND(ptr);
Serial.printf("Control form: size=%d bytes\n", strlen(message));
return message;
}
@ -1140,7 +1174,7 @@ void unkHandler(T nmea) {
}
}
#define DEBUG_GPS 1
//#define DEBUG_GPS
static bool gpsCourseOld;
static int lastCourse;
void gpsTask(void *parameter) {
@ -1986,8 +2020,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) {

Wyświetl plik

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

Wyświetl plik

@ -1,64 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<title>RDZSonde Server</title>
<title>rdzTTGOsonde Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h2>RDZSonde Server</h2>
<!--
<p>GPIO state: <strong> %STATE%</strong></p>
<p><a href="/on"><button class="button">ON</button></a></p>
<p><a href="/off"><button class="button button2">OFF</button></a></p>
-->
<div class="wrapper"><div class="header">
<h1>RDZSonde Server</h1>
<div class="tab">
<button class="tablinks" onclick="selTab(event,'QRG')" id="defaultTab">QRG</button>
<button class="tablinks" onclick="selTab(event,'WiFi')">WiFi</button>
<button class="tablinks" onclick="selTab(event,'Data')">Data</button>
<!-- <button class="tablinks" onclick="selTab(event,'SondeMap')">SondeMap</button> -->
<button class="tablinks" onclick="selTab(event,'Config')">Config</button>
<button class="tablinks" onclick="selTab(event,'Control')">Control</button>
<button class="tablinks" onclick="selTab(event,'About')">About</button>
</div>
</div>
<div id="QRG" class="tabcontent" data-src="qrg.html">
<h3> QRG - Setup</h3>
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
<iframe class="tci" src="" ></iframe>
</div>
<div id="WiFi" class="tabcontent" data-src="wifi.html">
<h3> WiFi - Settings</h3>
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
<iframe class="tci" src="" ></iframe>
</div>
<div id="Data" class="tabcontent" data-src="status.html">
<h3>Data</h3>
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
<iframe class="tci" src="" ></iframe>
</div>
<!--
<div id="SondeMap" class="tabcontent" data-src="https://wetterson.de/karte/">
<iframe src="" style="border:none;" width="98%%" height="98%%"></iframe>
</div>
-->
<div id="Config" class="tabcontent" data-src="config.html">
<h3>Configuration</h3>
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
<iframe class="tci" src="" ></iframe>
</div>
<div id="Control" class="tabcontent" data-src="control.html">
<h3>Control</h3>
<iframe src="" style="border:none;" width="100%%" height="100%%"></iframe>
<iframe class="tci" src="" ></iframe>
</div>
<div id="About" class="tabcontent">
<h3>About</h3>
<div class="tci">
%VERSION_NAME%<br>
Copyright &copy; 2019-2020 by Hansi Reiser, DL9RDZ<br>
Copyright &copy; 2019-2021 by Hansi Reiser, DL9RDZ<br>
(version %VERSION_ID%)<br><br>
with contributions by Vigor and Xavier (M20 support), <a href="https://www.dl2mf.de/" target="_blank">Meinhard Guenther, DL2MF</a>,
<a href="https://github.com/bazjo">Johannes</a>, <a href="http://www.p1337.synology.me/dokuwiki/doku.php?id=public:wettersonden">Robert Stefanowicz</a>,
@ -68,14 +54,16 @@
<br>
Autodetect info: %AUTODETECT_INFO%<br>
<br>
This program is free software; you can redistribute it and/or<br>
modify it under the terms of the GNU General Public License as<br>
published by the Free Software Foundation; either version 2 of<br>
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.<br>
see <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
for details
See <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
for details.
</div>
</div>
</div>
<script>
function selTab(evt, id) {
var i, tabcontent, tablinks;
@ -93,7 +81,7 @@ function selTab(evt, id) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
var act = document.getElementById(id);
act.style.display = "block";
act.style.display = "flex";
evt.currentTarget.className += " active";
var link = act.dataset.src;
@ -104,6 +92,7 @@ function selTab(evt, id) {
}
document.getElementById("defaultTab").click();
</script>
</body>
</html>

28
RX_FSK/data/rdz.js 100644
Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -1,4 +1,4 @@
const char *version_name = "rdzTTGOsonde";
const char *version_id = "devel20210126";
const char *version_id = "devel20210213";
const int SPIFFS_MAJOR=2;
const int SPIFFS_MINOR=8;
const int SPIFFS_MINOR=10;

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -6,11 +6,11 @@
#include <inttypes.h>
#include <WiFi.h>
#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);