rdz_ttgo_sonde/RX_FSK/src/ShFreqImport.cpp

248 wiersze
8.7 KiB
C++

#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <Arduino.h>
#include "ShFreqImport.h"
#include "Sonde.h"
static int ppos;
static int quotes;
static char id[20];
static int idpos;
static float lat, lon, freq;
static char type[20];
static uint8_t inuse[1+99/8]; // MAXSONDE is 99
static char keyword[40];
static int keywordpos;
static char value[40];
static int valuepos;
static int importState;
static float homelat, homelon;
// Map SondeHub type string to Stype. -1 for not supported types.
int ShFreqImport::stringToStype(const char *type) {
if(type[2]=='4') return STYPE_RS41;
if(type[2]=='9') return STYPE_RS92;
if(type[1]=='1') return STYPE_M10M20;
if(type[1]=='2') return STYPE_M10M20;
if(type[0]=='D') return STYPE_DFM;
if(type[2]=='3') return STYPE_MP3H; // TODO: check if '3' is correct
return -1; // iMet is not supported
}
// in Display.cpp
extern float calcLatLonDist(float lat1, float lon1, float lat2, float lon2);
void ShFreqImport::setLabel(int idx, char *id, float lat, float lon) {
snprintf(sonde.sondeList[idx].launchsite, 18, "@%s/%d", id, (int)(calcLatLonDist(homelat, homelon, lat, lon)/1000));
sonde.sondeList[idx].launchsite[17] = 0;
}
void ShFreqImport::usekeyvalue() {
if(strcmp(keyword,"lat")==0) lat = atof(value);
if(strcmp(keyword,"lon")==0) lon = atof(value);
if(strcmp(keyword,"frequency")==0) { if(isnan(freq)) freq = atof(value); } // prefer tx_frequency if available
if(strcmp(keyword,"tx_frequency")==0) freq = atof(value);
if(strcmp(keyword,"type")==0) strcpy(type, value);
}
/* populate qrg.txt with frequency of near sonde */
void ShFreqImport::populate(char *id, float lat, float lon, float freq, const char *type)
{
//printf(" ID %s: %.5f, %.5f f=%.3f, type=%s \n", id, lat, lon, freq, type);
// Skip if freq already exists
int stype = stringToStype(type);
if(stype<0) return; // unsupported type
// check if frequency exists already
// don't do anything if its a static entry
// 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.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;
if( *l=='@' || *l==' ' || *l==0 ) {
setLabel(i, id, lat, lon);
inuse[i/8] |= (1<<(i&7));
}
sonde.sondeList[i].active = 1;
return;
}
}
}
// find slot
// slots with empty launchsite are considered available for automated entries
while(ppos < sonde.config.maxsonde) {
if( *sonde.sondeList[ppos].launchsite==' ' || *sonde.sondeList[ppos].launchsite== 0 ) break;
ppos++;
}
if(ppos >= sonde.config.maxsonde) {
Serial.println("populate: out of free slots");
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;
setLabel(ppos, id, lat, lon);
inuse[ppos/8] |= (1<<(ppos&7));
ppos++;
}
// clears all remaining automatically filled slots (no longer in SH data)
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;
sonde.sondeList[i].freq = 400;
}
}
}
#define BUFLEN 128
#define VALLEN 20
int ShFreqImport::handleChar(char c) {
Serial.print(c);
switch(importState) {
case START:
// wait for initial '{'
if(c=='{') {
Serial.println("{ found");
importState++;
}
break;
case BEFOREID:
// what for first '"' in { "A1234567" : { ... } }; or detect end
if(c=='"') { idpos = 0; lat = NAN; lon = NAN; freq = NAN; *type = 0; importState++; }
if(c=='}') {
importState = ENDREACHED;
cleanup();
return 1;
}
break;
case COPYID:
// copy ID "A1234567" until second '"' is earched
if(c=='"') { id[idpos] = 0; importState++; }
else id[idpos++] = c;
break;
case AFTERID:
// wait for '{' in '"A1234567": { ...'
if(c=='{') importState++;
break;
case BEFOREKEY:
if(c=='"') { keywordpos = 0; importState++; }
break;
case COPYKEY:
if(c=='"') { importState++; keyword[keywordpos] = 0; /* printf("Key: >%s<\n", keyword);*/ }
else keyword[keywordpos++] = c;
break;
case AFTERKEY:
if(c==':') {
valuepos = 0;
quotes = 0;
if(strcmp(keyword,"lat")==0 || strcmp(keyword, "lon")==0 || strcmp(keyword, "frequency")==0 || strcmp(keyword, "tx_frequency")==0)
importState = BEFORENUMVAL;
else {
if (strcmp(keyword, "type")==0)
importState = BEFORESTRINGVAL;
else
importState = SKIPVAL;
}
}
break;
case BEFORENUMVAL:
if( (c>='0'&&c<='9') || c=='-') { value[0] = c; valuepos=1; importState++; }
break;
case COPYNUMVAL:
if( !(c>='0'&&c<='9') && c!='-' && c!='.' ) {
value[valuepos]=0; importState=SKIPVAL; usekeyvalue();
if(c!=',' && c!='}') break;
}
else { value[valuepos++] = c; break; }
// intenionall fall-through
case SKIPVAL:
// This is rather fragile, we *should* handle more escaping and so on but do not do so so far, only simple quotes
if(c=='"') quotes = !quotes;
if(quotes) break;
if(c==',') importState = BEFOREKEY;
if(c=='}') {
// we have an ID and all key/value pairs, check if its good....
if( !isnan(lat) && !isnan(lon) && !isnan(freq) && type[0] ) {
printf("SondeHub import: populate %s %f %f %f %s\n", id, lat, lon, freq, type);
populate(id, lat, lon, freq, type);
} else {
printf("Skipping incomplete %s\n", id);
}
importState = ENDORNEXT;
}
break;
case BEFORESTRINGVAL:
if(c=='"') importState++;
break;
case COPYSTRINGVAL:
if(c=='"') { importState=SKIPVAL; value[valuepos]=0; usekeyvalue(); }
else value[valuepos++] = c;
break;
case ENDORNEXT:
// next we have to see either a final "}', or a comma before the next id
if(c==',') importState = BEFOREID;
else if (c=='}') { importState = ENDREACHED; cleanup(); return 1; }
break;
case ENDREACHED:
Serial.println("REPLY: END REACHED");
return 1;
}
return 0;
}
// lat lon in deg, dist in km, time in minutes
int ShFreqImport::shImportSendRequest(WiFiClient *client, float lat, float lon, int dist, int time) {
if(!client->connected()) {
if(!client->connect(sonde.config.sondehub.host, 80)) {
Serial.println("Connection FAILED");
return 1;
}
}
Serial.println("Sending SondeHub import request");
char req[300];
snprintf(req, 200, "GET /sondes?lat=%f&lon=%f&distance=%d&last=%d HTTP/1.1\r\n"
"Host: %s\r\n"
"Accept: application/json\r\n"
"Cache-Control: no-cache\r\n\r\n",
lat, lon, dist*1000, time*60, sonde.config.sondehub.host);
client->print(req);
Serial.print(req);
importState = START;
homelat = lat;
homelon = lon;
memset(inuse, 0, sizeof(inuse));
ppos = 0;
return 0;
}
// return 0 if more data should be read (later), 1 if finished (close connection...)
int ShFreqImport::shImportHandleReply(WiFiClient *client) {
if(!client->connected()) return 1;
while(client->available()) {
int res = handleChar(client->read());
if(res) return res;
}
return 0;
}