1 Commits
Author SHA1 Message Date
magnum88 6978f1cccc first commit 2022-08-24 08:34:25 +02:00
25 changed files with 1346 additions and 170 deletions
+206 -42
View File
@@ -1,7 +1,8 @@
RadioSonde Version 0.8.8 RadioSonde Version 0.9.1
============================ ============================
<img src="http://xavier.debert.free.fr/RS/TTGO.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%"> <img src="http://xavier.debert.free.fr/RS/TTGO.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%"> <img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO6.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/TTGO7.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO8.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/Web4.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web.png" width="20%"> <img src="http://xavier.debert.free.fr/RS/Web4.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web3.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web2.png" width="20%"> <img src="http://xavier.debert.free.fr/RS/Web3.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web2.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web5.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web6.png" width="20%"> <img src="http://xavier.debert.free.fr/RS/Web5.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web6.png" width="20%">
@@ -11,66 +12,90 @@
Projet basé sur le travail de DL9RDZ Projet basé sur le travail de DL9RDZ
==================================== ====================================
Pour TTGO LORA 32 esp32 pico D4 Pour TTGO LORA 32 esp32 pico D4 <br>
Décodage de RadioSonde RS41 and DFM06/09 et M10 Décodage de RadioSonde RS41/92 and DFM06/09/17 et M10+/20
Attention à la version de votre TTGO! Attention à la version de votre TTGO! <br>
vous devez modifier dans config.txt, le port de l'écran OLED vous devez modifier dans config.txt, le port de l'écran OLED <br>
- TTGO v1: SDA=4 SCL=15, RST=16 - TTGO v1: SDA=4 SCL=15, RST=16 <br>
- TTGO v2: SDA=21 SCL=22, RST=16 - TTGO v2: SDA=21 SCL=22, RST=16
## Version en production 0.8.5 devel 0.8.7 ## Version en production 0.9.1 devel 0.9.2
## 0.8.0 ## 0.9.1
travail de refonte et réécriture du code Corection RS41 <br>
Correction DFM 06/09 <br>
Add DFM17 <br>
Correction for all trame recived for M10 and M20 1000ms to 1512ms,<br>
Correction formulaire QRG, and end RS no save
## 0.9.0
Add M20
## 0.8.8
Add M10+ <br>
Add Temps restant avant impacte au sol si 99: 0. 0 soit le balon de la sonde n a pas encore éclaté, <br>
ou les informations ne sont pas disponible actuellement <br>
Add Test Buzzer au démarrage "Arche Perdu" Lol pour des chasseurs de sonde!<br>
Compatible Lilygo esp32 GPS inboard pin 34 Rx, 12 Tx
## 0.8.7
correction bug Buzzer Off->On->Off <br>
Add GainLNA RX SX1278FSK on Web config paramètre <br>
Add update OTA Os + DataWeb <br>
correction bugs sondmap.html <br>
correction text upgrade Os et DataWeb <br>
correction texte boussole S et N <br>
correction bugs distance 4928Km si lat et lon =0 erroné <br>
correction bugs fonction Vbat <br>
Add Telemetry width export data.csv <br>
Suppression µSD incompatible avec pin SX1278FSK et SPI <br>
Add transfert Telemetry To µSD on put SD automatic
## 0.8.5
Evolution majeur du système <br>
affichage du pourcentage de la batterie en mode scanning <br>
création d'une fenetre Batterie, Boussole <br>
suppresion lib et code TFT <br>
création Azimute, elevation correction de Bugs majeur , mineur <br>
Ajout fonction Smetre, Buzzer, QTH, Gps on off ... <br>
mise à jour OTA <br>
trop de modification pour toutes les expliciter!
## 0.8.1 ## 0.8.1
modification de la partie Web modification de la partie Web
## 0.8.5 ## 0.8.0
Evolution majeur du système travail de refonte et réécriture du code
affichage du pourcentage de la batterie en mode scanning
création d'une fenetre Batterie, Boussole
suppresion lib et code TFT
création Azimute, elevation correction de Bugs majeur , mineur
Ajout fonction Smetre, Buzzer, QTH, Gps on off ...
mise à jour OTA
trop de modification pour toutes les expliciter!
## 0.8.7 -------------------------------------------------------------------------------
correction bug Buzzer Off->On->Off
Add GainLNA RX SX1278FSK on Web config paramètre
Add update OTA Os + DataWeb
correction bugs sondmap.html
correction text upgrade Os et DataWeb
correction texte boussole S et N
correction bugs distance 4928Km si lat et lon =0 erroné
correction bugs fonction Vbat
Add Telemetry width export data.csv
Suppression µSD incompatible avec pin SX1278FSK et SPI
Add transfert Telemetry To µSD on put SD automatic
## 0.8.8
Add M10+
## Les Boutons optionnel à ajouter(souder) ## Les Boutons optionnel à ajouter(souder)
sur les GPIO 1002 et 1004 sur les GPIO 1002 et 1004 <br>
attention: attention:
+5V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms +3.3V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms
- appuie court <1.5 seconds - appuie court <1.5 seconds <br>
- appuie double court 0.5 seconds - appuie double court 0.5 seconds <br>
- appuie moyen 2-4 seconds - appuie moyen 2-4 seconds <br>
- appuie long >5 seconds - appuie long >5 seconds
## Buzzer optionnel à ajouter(souder)
sur les GPIO 25 ou 12 suivant le modèl
GPIO --[ BUZZER ]---/
## Wifi configuration ## Wifi configuration
Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP<br>
le SSID et mot de passe par défaut est: Radiosonde le SSID et mot de passe par défaut est: <b>Radiosonde</b> <br>
en mode AP, il doit être en 192.168.4.1, en mode AP, il doit être en 192.168.4.1, <br>
mais vous avez aussi la possibilité de mettre http://radiosonde.local dans n'importe quel Wifi mais vous avez aussi la possibilité de mettre http://radiosonde.local dans n'importe quel Wifi
connecté. connecté.
@@ -109,3 +134,142 @@ voir Setup.md pour l'installation!
Xavier Xavier
RadioSonde Version 0.9.1
============================
<img src="http://xavier.debert.free.fr/RS/TTGO.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO2.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/TTGO3.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO4.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO5.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO6.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/TTGO7.jpg" width="50%"><img src="http://xavier.debert.free.fr/RS/TTGO8.jpg" width="50%">
<img src="http://xavier.debert.free.fr/RS/Web4.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web3.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web2.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web5.png" width="20%"><img src="http://xavier.debert.free.fr/RS/Web6.png" width="20%">
<img src="http://xavier.debert.free.fr/RS/Web7.png" width="20%">
Projet basé sur le travail de DL9RDZ
====================================
Pour TTGO LORA 32 esp32 pico D4 <br>
Décodage de RadioSonde RS41/92 and DFM06/09/17 et M10+/20
Attention à la version de votre TTGO! <br>
vous devez modifier dans config.txt, le port de l'écran OLED <br>
- TTGO v1: SDA=4 SCL=15, RST=16 <br>
- TTGO v2: SDA=21 SCL=22, RST=16
## Version en production 0.9.1 devel 0.9.2
## 0.9.1
Corection RS41 <br>
Correction DFM 06/09 <br>
Add DFM17 <br>
Correction for all trame recived for M10 and M20 1000ms to 1512ms,<br>
Correction formulaire QRG, and end RS no save
## 0.9.0
Add M20
## 0.8.8
Add M10+ <br>
Add Temps restant avant impacte au sol si 99: 0. 0 soit le balon de la sonde n a pas encore éclaté, <br>
ou les informations ne sont pas disponible actuellement <br>
Add Test Buzzer au démarrage "Arche Perdu" Lol pour des chasseurs de sonde!<br>
Compatible Lilygo esp32 GPS inboard pin 34 Rx, 12 Tx
## 0.8.7
correction bug Buzzer Off->On->Off <br>
Add GainLNA RX SX1278FSK on Web config paramètre <br>
Add update OTA Os + DataWeb <br>
correction bugs sondmap.html <br>
correction text upgrade Os et DataWeb <br>
correction texte boussole S et N <br>
correction bugs distance 4928Km si lat et lon =0 erroné <br>
correction bugs fonction Vbat <br>
Add Telemetry width export data.csv <br>
Suppression µSD incompatible avec pin SX1278FSK et SPI <br>
Add transfert Telemetry To µSD on put SD automatic
## 0.8.5
Evolution majeur du système <br>
affichage du pourcentage de la batterie en mode scanning <br>
création d'une fenetre Batterie, Boussole <br>
suppresion lib et code TFT <br>
création Azimute, elevation correction de Bugs majeur , mineur <br>
Ajout fonction Smetre, Buzzer, QTH, Gps on off ... <br>
mise à jour OTA <br>
trop de modification pour toutes les expliciter!
## 0.8.1
modification de la partie Web
## 0.8.0
travail de refonte et réécriture du code
-------------------------------------------------------------------------------
## Les Boutons optionnel à ajouter(souder)
sur les GPIO 1002 et 1004 <br>
attention:
+3.3V--[ SW ]---GPIO----[ R1 ]---/ R1=10 ou 12KOhms
- appuie court <1.5 seconds <br>
- appuie double court 0.5 seconds <br>
- appuie moyen 2-4 seconds <br>
- appuie long >5 seconds
## Buzzer optionnel à ajouter(souder)
sur les GPIO 25 ou 12 suivant le modèl
GPIO --[ BUZZER ]---/
## Wifi configuration
Au démarrage, si aucune connexion possible au wifi paramètré, il monte un Wifi AP<br>
le SSID et mot de passe par défaut est: <b>Radiosonde</b> <br>
en mode AP, il doit être en 192.168.4.1, <br>
mais vous avez aussi la possibilité de mettre http://radiosonde.local dans n'importe quel Wifi
connecté.
## Mode Scanne
Le fichier qrg.txt contient la liste par défaut des cannaux.
pour y configurer les noms, fréquences et mode [M=M10, 6=DFM06, 9=DFM09 et 4=RS41]
## Mode Réception
En réception, une seul fréquence est décodé, les infos de la sonde (ID, GPS, RSSI, ...)
seront affichées dans plusieur fenetre à choisir ( 0 à 6) à configurer dans la page Web
rubrique config.
In receiving mode, a single frequency will be decoded, and sonde info (ID, GPS
coordinates, RSSI) will be displayed. The bar above the IP address indicates,
for the last 18 frames, if reception was successfull (|) or failed (.)
A DOUBLE press will switch to scanning mode.
A SHORT press will switch to the next channel in channels.txt
## Mode Analyseur
Le mode analyseur de spectre (400..406 MHz) est affiché (chaque ligne == 50 kHz)
Pour les cartes TTGO sans bouton configurable, il y a un nouveau paramètre dans config.txt:
- spectrum=10 // 0=off / 1-99 nombre de seconds pour afficher l'analyseur
- timer=1 // 0=off / 1= afficher le compte à rebours du spectre dans l'affichage du spectre
- marker=1 // 0=off / 1= afficher la fréquence dans l'affichage du spectre
## Setup
voir Setup.md pour l'installation!
73
Xavier
+90 -46
View File
@@ -5,8 +5,6 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <SPIFFS.h> #include <SPIFFS.h>
//#include <U8x8lib.h>
//#include <U8g2lib.h>
#include <SPI.h> #include <SPI.h>
#include <Update.h> #include <Update.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>
@@ -24,6 +22,7 @@
#include "spiffs_sd.h" #include "spiffs_sd.h"
int e; int e;
enum MainState { ST_DECODER, ST_SPECTRUM, ST_WIFISCAN, ST_UPDATE, ST_TOUCHCALIB }; enum MainState { ST_DECODER, ST_SPECTRUM, ST_WIFISCAN, ST_UPDATE, ST_TOUCHCALIB };
static MainState mainState = ST_WIFISCAN; // ST_WIFISCAN; static MainState mainState = ST_WIFISCAN; // ST_WIFISCAN;
@@ -42,15 +41,7 @@ String updateBinM = "/RS/radiosonde/update.ino.bin";
String updateBinD = "/RS/radiosondedevel/update.ino.bin"; String updateBinD = "/RS/radiosondedevel/update.ino.bin";
String *updateBin = &updateBinM; String *updateBin = &updateBinM;
String updateDataWeb = "/RS/dataweb/index.html.txt"; String updateDataWeb = "/RS/dataweb/data.tar";
String updateDataWeb2 = "/RS/dataweb/sondemap.html.txt";
String updateDataWeb3 = "/RS/dataweb/config.txt";
String updateDataWeb4 = "/RS/dataweb/screens.txt";
String updateDataWeb5 = "/RS/dataweb/style.css.txt";
String updateDataWeb6 = "/RS/dataweb/qrg.txt";
String updateDataWeb7 = "/RS/dataweb/networks.txt";
String updateDataWeb8 = "/RS/dataweb/fontawesome-webfont.eot.txt";
String updateDataWeb9 = "/RS/dataweb/fontawesome-webfont.woff.txt";
#define LOCALUDPPORT 9002 #define LOCALUDPPORT 9002
@@ -164,9 +155,10 @@ void setupChannelList() {
sonde.clearSonde(); sonde.clearSonde();
Serial.println("Reading channel config:"); Serial.println("Reading channel config:");
while (file.available()) { while (file.available()) {
String line = readLine(file); //file.readStringUntil('\n'); //String line = readLine(file); //file.readStringUntil('\n');
String line = file.readStringUntil('\n');
String sitename; String sitename;
if (!file.available()) break; //if (!file.available()) break;
if (line[0] == '#') continue; if (line[0] == '#') continue;
char *space = strchr(line.c_str(), ' '); char *space = strchr(line.c_str(), ' ');
if (!space) continue; if (!space) continue;
@@ -184,9 +176,15 @@ void setupChannelList() {
else if (space[1] == '6') { else if (space[1] == '6') {
type = STYPE_DFM06; type = STYPE_DFM06;
} }
else if (space[1] == 'D') {
type = STYPE_DFM;
}
else if (space[1] == 'M') { else if (space[1] == 'M') {
type = STYPE_M10; type = STYPE_M10;
} }
else if (space[1] == '2') {
type = STYPE_M20;
}
else continue; else continue;
int active = space[3] == '+' ? 1 : 0; int active = space[3] == '+' ? 1 : 0;
if (space[4] == ' ') { if (space[4] == ' ') {
@@ -205,25 +203,55 @@ void setupChannelList() {
const char *createQRGForm() { const char *createQRGForm() {
char *ptr = message; char *ptr = message;
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></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>"); strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">");
strcat(ptr, "<script type=\"text/javascript\">"
"let stypes=new Map();"
"stypes.set('4', 'RS41');"
"stypes.set('R', 'RS92');"
"stypes.set('9', 'DFM9');"
"stypes.set('6', 'DFM6');"
"stypes.set('D', 'DFM All');"
"stypes.set('M', 'M10');"
"stypes.set('2', 'M20');"
"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; "
"</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>");
for (int i = 0; i < sonde.config.maxsonde; i++) { for (int i = 0; i < sonde.config.maxsonde; i++) {
String s = sondeTypeSelect(i >= sonde.nSonde ? 2 : sonde.sondeList[i].type); //String s = sondeTypeSelect(i >= sonde.nSonde ? 2 : sonde.sondeList[i].type);
String site = sonde.sondeList[i].launchsite; String site = sonde.sondeList[i].launchsite;
sprintf(ptr + strlen(ptr), "<tr><td>%d</td><td><input name=\"A%d\" type=\"checkbox\" %s/></td>" 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\" value=\"%3.3f\"></td>"
"<td><input name=\"S%d\" type=\"text\" value=\"%s\"></td>" "<td><input name=\"S%d\" type=\"text\" value=\"%s\"></td>"
"<td><select name=\"T%d\">%s</select></td>", //"<td><select name=\"T%d\">%s</select></td>",
"<td><input class='stype' name='T%d' value='%c'>",
i + 1, i + 1,
i + 1, (i < sonde.nSonde && sonde.sondeList[i].active) ? "checked" : "", i + 1, (i < sonde.nSonde && sonde.sondeList[i].active) ? "checked" : "",
i + 1, i >= sonde.nSonde ? 400.000 : sonde.sondeList[i].freq, i + 1, i >= sonde.nSonde ? 400.000 : sonde.sondeList[i].freq,
i + 1, i >= sonde.nSonde ? " " : sonde.sondeList[i].launchsite, i + 1, i >= sonde.nSonde ? " " : sonde.sondeList[i].launchsite,
i + 1, s.c_str()); 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><input type=\"submit\" value=\"Update\"/></form></body></html>");
Serial.printf("QRG form: size=%d bytes\n", strlen(message)); Serial.printf("QRG form: size=%d bytes\n", strlen(message));
return message; return message;
} }
const char *handleQRGPost(AsyncWebServerRequest *request) { const char *handleQRGPost(AsyncWebServerRequest *request) {
char label[10]; char label[10];
// parameters: a_i, f_1, t_i (active/frequency/type) // parameters: a_i, f_1, t_i (active/frequency/type)
@@ -260,7 +288,7 @@ const char *handleQRGPost(AsyncWebServerRequest *request) {
const char *tstr = tstring.c_str(); const char *tstr = tstring.c_str();
const char *sstr = sstring.c_str(); const char *sstr = sstring.c_str();
Serial.printf("Processing a=%s, f=%s, t=%s, site=%s\n", active ? "YES" : "NO", fstr, tstr, sstr); Serial.printf("Processing a=%s, f=%s, t=%s, site=%s\n", active ? "YES" : "NO", fstr, tstr, sstr);
char typech = (tstr[2] == '4' ? '4' : tstr[2] == '9' ? 'R' : tstr[0] == 'M' ? 'M' : tstr[3]); // a bit ugly char typech = tstr[0];
file.printf("%3.3f %c %c %s\n", atof(fstr), typech, active ? '+' : '-', sstr); file.printf("%3.3f %c %c %s\n", atof(fstr), typech, active ? '+' : '-', sstr);
} }
file.close(); file.close();
@@ -310,7 +338,6 @@ void setupWifiList() {
} }
} }
const char *createWIFIForm() { const char *createWIFIForm() {
char *ptr = message; char *ptr = message;
char tmp[4]; char tmp[4];
@@ -374,7 +401,7 @@ void addSondeStatus(char *ptr, int i)
strcat(ptr, "<table>"); strcat(ptr, "<table>");
sprintf(ptr + strlen(ptr), "<tr><td id=\"sfreq\">%3.3f MHz, Type: %s</td><tr><td>ID: %s", s->freq, sondeTypeStr[s->type], sprintf(ptr + strlen(ptr), "<tr><td id=\"sfreq\">%3.3f MHz, Type: %s</td><tr><td>ID: %s", s->freq, sondeTypeStr[s->type],
s->validID ? s->id : "<?""?>"); s->validID ? s->id : "<?""?>");
if (s->validID && (s->type == STYPE_DFM06 || s->type == STYPE_DFM09 || s->type == STYPE_M10)) { if (s->validID && (s->type == STYPE_DFM || s->type == STYPE_DFM06 || s->type == STYPE_DFM09 || s->type == STYPE_M10 || s->type == STYPE_M20)) {
sprintf(ptr + strlen(ptr), " (ser: %s)", s->ser); sprintf(ptr + strlen(ptr), " (ser: %s)", s->ser);
} }
sprintf(ptr + strlen(ptr), "</td></tr><tr><td>QTHSonde: %.6f,%.6f h=%.0fm</td></tr>\n", s->lat, s->lon, s->alt); sprintf(ptr + strlen(ptr), "</td></tr><tr><td>QTHSonde: %.6f,%.6f h=%.0fm</td></tr>\n", s->lat, s->lon, s->alt);
@@ -387,9 +414,11 @@ void addSondeStatus(char *ptr, int i)
sprintf(ptr + strlen(ptr), "<tr><td>Burst-KT=%d Launch-KT=%d Countdown=%d (vor %ds)</td></tr>\n", 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->burstKT, s->launchKT, s->countKT, ((uint16_t)s->frame - s->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), "<tr><td>");
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.fr/maps/dir//%.6f;%.6f\">G-MAPS</a> - ", s->lat, s->lon);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://tracker.sondehub.org/?sondehub=1#!mt=osm&mz=8&qm=6_hours&mc=%.6f,%.6f&q=RS_%s\">Sondehub</a> - ", s->lat, s->lon, s->id); sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://tracker.sondehub.org/?sondehub=1#!mt=osm&mz=8&qm=6_hours&mc=%.6f,%.6f&q=RS_%s\">Sondehub</a> - ", s->lat, s->lon, s->id);
sprintf(ptr + strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a></td></tr>", s->lat, s->lon); 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);
strcat(ptr, "</table><p/>\n"); strcat(ptr, "</table><p/>\n");
} }
@@ -1103,7 +1132,11 @@ char buffer[85];
MicroNMEA nmea(buffer, sizeof(buffer)); MicroNMEA nmea(buffer, sizeof(buffer));
int lastCourse = 0; int lastCourse = 0;
void unkHandler(const MicroNMEA& nmea) {
/// Arrg. MicroNMEA changes type definition... so lets auto-infer type
template<typename T>
//void unkHandler(const MicroNMEA& nmea) {
void unkHandler(T nmea) {
if (strcmp(nmea.getMessageID(), "VTG") == 0) { if (strcmp(nmea.getMessageID(), "VTG") == 0) {
const char *s = nmea.getSentence(); const char *s = nmea.getSentence();
while (*s && *s != ',') s++; while (*s && *s != ',') s++;
@@ -1113,6 +1146,7 @@ void unkHandler(const MicroNMEA& nmea) {
Serial.printf("Course update: %d\n", lastCourse); Serial.printf("Course update: %d\n", lastCourse);
} }
} }
void gpsTask(void *parameter) { void gpsTask(void *parameter) {
nmea.setUnknownSentenceHandler(unkHandler); nmea.setUnknownSentenceHandler(unkHandler);
@@ -1136,10 +1170,31 @@ void gpsTask(void *parameter) {
} }
} }
#define UBX_SYNCH_1 0xB5
#define UBX_SYNCH_2 0x62
uint8_t ubx_set9k6[]={UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8F};
uint8_t ubx_factorydef[]={UBX_SYNCH_1, UBX_SYNCH_2, 0x06, 0x09, 13, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0x17, 0x8A };
void initGPS() { void initGPS() {
if (sonde.config.gps_rxd < 0) return; // GPS disabled if (sonde.config.gps_rxd < 0) return; // GPS disabled
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd); Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
if (sonde.config.gps_txd >= 0) { // TX enable, thus try setting baud to 9600 and do a factory reset
Serial.println("Trying to reset GPS...");
Serial2.begin(115200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
delay(100);
Serial2.begin(38400, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
delay(100);
Serial2.begin(19200, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
Serial2.write(ubx_set9k6, sizeof(ubx_set9k6));
delay(100);
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
Serial2.write(ubx_factorydef, sizeof(ubx_factorydef));
delay(1000);
} else {
Serial2.begin(9600, SERIAL_8N1, sonde.config.gps_rxd, sonde.config.gps_txd);
}
xTaskCreate( gpsTask, "gpsTask", xTaskCreate( gpsTask, "gpsTask",
5000, /* stack size */ 5000, /* stack size */
NULL, /* paramter */ NULL, /* paramter */
@@ -1457,6 +1512,7 @@ void setup()
return; return;
} }
Serial.println("Reading initial configuration"); Serial.println("Reading initial configuration");
setupConfigData(); // configuration must be read first due to OLED ports!!! setupConfigData(); // configuration must be read first due to OLED ports!!!
@@ -1663,10 +1719,10 @@ const int melodie[][3] = { {4, 2, 2}, {5, 2, 1}, {7, 2, 3}, {0, 3, 6},
setupChannelList(); setupChannelList();
#if 0 #if 0
sonde.clearSonde(); sonde.clearSonde();
sonde.addSonde(402.700, STYPE_RS41); sonde.addSonde(402.800, STYPE_RS41);
sonde.addSonde(405.700, STYPE_RS41); sonde.addSonde(402.000, STYPE_M10);
sonde.addSonde(405.900, STYPE_RS41); sonde.addSonde(404.800, STYPE_RS41);
sonde.addSonde(403.450, STYPE_DFM09); sonde.addSonde(403.000, STYPE_DFM09);
Serial.println("No channel config file, using defaults!"); Serial.println("No channel config file, using defaults!");
Serial.println(); Serial.println();
#endif #endif
@@ -2417,29 +2473,15 @@ void execOTA() {
// execOTA(); // execOTA();
} }
//ICI Update Data
/* /*
//ICI Update Data
// Update DATAWeb!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Update DATAWeb!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//Data1 index.html //Data data.tar
updatedataweb(updateHost,updatePort,updateDataWeb,"index.html"); updatedataweb(updateHost,updatePort,updateDataWeb,"index.html");
//Data2 sondemap.html
updatedataweb(updateHost,updatePort,updateDataWeb2,"sondemap.html");
//Data3 config.txt
updatedataweb(updateHost,updatePort,updateDataWeb3,"config.txt");
//Data4 screens.txt
updatedataweb(updateHost,updatePort,updateDataWeb4,"screens.txt");
//Data5 styles.css
updatedataweb(updateHost,updatePort,updateDataWeb5,"style.css");
//Data6 qrg.txt
updatedataweb(updateHost,updatePort,updateDataWeb6,"qrg.txt");
//Data7 networks.txt
updatedataweb(updateHost,updatePort,updateDataWeb7,"networks.txt");
//Data8 fontawesome-webfont.eot
updatedataweb(updateHost,updatePort,updateDataWeb8,"fontawesome-webfont.eot");
//Data9 fontawesome-webfont.eot
updatedataweb(updateHost,updatePort,updateDataWeb9,"fontawesome-webfont.woff");
*/ */
if (Update.end()) { if (Update.end()) {
Serial.println("OTA done!"); Serial.println("OTA done!");
if (Update.isFinished()) { if (Update.isFinished()) {
@@ -2525,6 +2567,8 @@ void updatedataweb(String host, int port, String dataweb, String fichier){
file.close(); file.close();
} }
client.flush(); client.flush();
//Untar data.tar
} }
void loop() { void loop() {
+4 -4
View File
@@ -91,7 +91,7 @@ dfm.rxbw=10400
# data not sanitized / quality checked, outliers not filtered out # data not sanitized / quality checked, outliers not filtered out
axudp.active=1 axudp.active=1
axudp.host=fra1od.fr.to axudp.host=fra1od.fr.to
axudp.port=14580 axudp.port=20350
axudp.symbol=/O axudp.symbol=/O
axudp.highrate=1 axudp.highrate=1
axudp.idformat=0 axudp.idformat=0
@@ -99,9 +99,9 @@ axudp.idformat=0
# maybe some time in the future # maybe some time in the future
#-------------------------------# #-------------------------------#
# currently simply not implemented, no need to put anything here anyway # currently simply not implemented, no need to put anything here anyway
tcp.active=0 tcp.active=1
tcp.host=radiosondy.info tcp.host=fra1od.fr.to
tcp.port=14590 tcp.port=14580
tcp.symbol=/O tcp.symbol=/O
tcp.highrate=20 tcp.highrate=20
tcp.idformat=0 tcp.idformat=0
+1 -1
View File
@@ -77,7 +77,7 @@
<br> <br>
Autodetect info: %AUTODETECT_INFO%<br> Autodetect info: %AUTODETECT_INFO%<br>
<br> <br>
CopyLeft 2020 Modifier par <a href="http://openpmr.cla.fr">FRS2013</a><br> CopyLeft 2020 Modifier par <a href="http://openpmr.cla.fr">FRS2013</a> & Vigor<br>
Licence GNU GPL <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a> Licence GNU GPL <a href="https://www.gnu.org/licenses/gpl-2.0.txt">https://www.gnu.org/licenses/gpl-2.0.txt</a>
for details for details
</div> </div>
+2 -2
View File
@@ -1,5 +1,5 @@
# Frequency in Mhz (format nnn.nnn) # Frequency in Mhz (format nnn.nnn)
# Type (4=RS41, R=RS92, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09, M=M10) # Type (4=RS41, R=RS92, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09, D=DFM, M=M10, 2=M20)
# #
400.000 9 + Test(FR) 400.000 9 + Test(FR)
401.500 4 - Santander(ES) 401.500 4 - Santander(ES)
@@ -20,5 +20,5 @@
405.900 4 - Bergen_2 405.900 4 - Bergen_2
405.100 4 - Meppen_2 405.100 4 - Meppen_2
405.300 4 - Essen 405.300 4 - Essen
405.500 4 - Essen_2 405.500 2 - Essen_2
# end # end
+1 -3
View File
@@ -1,4 +1,2 @@
const char *version_name = "RadioSonde"; const char *version_name = "RadioSonde";
const char *version_id = "0.8.8"; const char *version_id = "0.9.1";
//const int SPIFFS_MAJOR=3;
//const int SPIFFS_MINOR=5;
+1 -1
View File
@@ -194,7 +194,7 @@ uint8_t SX1278FSK::setFSK()
{ {
uint8_t state = 2; uint8_t state = 2;
byte st0; byte st0;
byte config1; //byte config1;
#if (SX1278FSK_debug_mode > 1) #if (SX1278FSK_debug_mode > 1)
Serial.println(); Serial.println();
+320 -10
View File
@@ -1,4 +1,3 @@
/* DFM decoder functions */ /* DFM decoder functions */
#include "DFM.h" #include "DFM.h"
#include "SX1278FSK.h" #include "SX1278FSK.h"
@@ -12,11 +11,26 @@
#define DFM_DBG(x) #define DFM_DBG(x)
#endif #endif
int DFM::setup(float frequency, int inv) #define DFM_FRAMELEN 33
// single data structure, search restarts after decoder change
static struct st_dfmstat {
int idcnt0;
int idcnt1;
int lastfrid;
int lastfrcnt;
uint8_t start[50];
uint16_t dat[50*2];
uint8_t cnt[50*2];
uint8_t nameregok;
uint8_t nameregtop;
} dfmstate;
int DFM::setup(float frequency, int type)
{ {
inverse = inv; stype = type;
#if DFM_DEBUG #if DFM_DEBUG
Serial.printf("Setup sx1278 for DFM sonde (inv=%d)\n",inv); Serial.printf("Setup sx1278 for DFM sonde (type=%d)\n", stype);
#endif #endif
if(sx1278.ON()!=0) { if(sx1278.ON()!=0) {
DFM_DBG(Serial.println("Setting SX1278 power on FAILED")); DFM_DBG(Serial.println("Setting SX1278 power on FAILED"));
@@ -44,6 +58,10 @@ int DFM::setup(float frequency, int inv)
DFM_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.dfm.rxbw)); DFM_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.dfm.rxbw));
return 1; return 1;
} }
if(type == STYPE_DFM09 || type == STYPE_DFM06) {
// packet mode, old version, misses some frames because chip enables rx too late after
// one frame was recevied. TODO: check if this can be fixed by changing parameters
// Enable auto-AFC, auto-AGC, RX Trigger by preamble // Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) { if(sx1278.setRxConf(0x1E)!=0) {
DFM_DBG(Serial.println("Setting RX Config FAILED")); DFM_DBG(Serial.println("Setting RX Config FAILED"));
@@ -52,12 +70,13 @@ int DFM::setup(float frequency, int inv)
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte // Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010"; //char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
const char *SYNC=inverse?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA"; const char *SYNC=(stype==STYPE_DFM09)?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA";
if(sx1278.setSyncConf(0x53, 4, (const uint8_t *)SYNC)!=0) { if(sx1278.setSyncConf(0x53, 4, (const uint8_t *)SYNC)!=0) {
DFM_DBG(Serial.println("Setting SYNC Config FAILED")); DFM_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1; return 1;
} }
if(sx1278.setPreambleDetect(0xA8)!=0) { //if(sx1278.setPreambleDetect(0xA8)!=0) {
if(sx1278.setPreambleDetect(0xAA)!=0) {
DFM_DBG(Serial.println("Setting PreambleDetect FAILED")); DFM_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1; return 1;
} }
@@ -68,11 +87,41 @@ int DFM::setup(float frequency, int inv)
DFM_DBG(Serial.println("Setting Packet config FAILED")); DFM_DBG(Serial.println("Setting Packet config FAILED"));
return 1; return 1;
} }
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
} else {
// continuous mode
// Enable auto-AFC, auto-AGC, RX Trigger by preamble ????
if(sx1278.setRxConf(0x1E)!=0) {
DFM_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// working with continuous RX
const char *SYNC="\xAA\xAA";
if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) {
DFM_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
if(sx1278.setPreambleDetect(0xA8)!=0) {
//if(sx1278.setPreambleDetect(0x9F)!=0) {
DFM_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
DFM_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
sx1278.setPayloadLength(0); // infinite for now...
}
Serial.print("DFM: setting RX frequency to "); Serial.print("DFM: setting RX frequency to ");
Serial.println(frequency); Serial.println(frequency);
int retval = sx1278.setFrequency(frequency); int retval = sx1278.setFrequency(frequency);
sx1278.clearIRQFlags(); sx1278.clearIRQFlags();
// Do this only once in setup in continous mode
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
memset((void *)&dfmstate, 0, sizeof(dfmstate));
DFM_DBG(Serial.println("Setting SX1278 config for DFM finished\n"); Serial.println()); DFM_DBG(Serial.println("Setting SX1278 config for DFM finished\n"); Serial.println());
return retval; return retval;
} }
@@ -163,8 +212,147 @@ void DFM::printRaw(const char *label, int len, int ret, const uint8_t *data)
Serial.print(" "); Serial.print(" ");
} }
const char* typestr[16]={
"", "", "", "", "", "", // 00..05
"DFM6", // 06 => DFM6
"PS15", // 07 => PS15 (untested)
"", "",
"DFM9", // 0A => DFM9
"DF17", // 0B => DFM17?
"DF9P", // 0C => DFM9P or DFM17 test
"DF17", // 0D => DFM17
"", ""
};
#define DFMIDTHRESHOLD 2
/* inspired by oe5dxl's finddnmae in sondeudp.c of dxlaprs */
void DFM::finddfname(uint8_t *b)
{
uint8_t st;
uint32_t thres;
uint32_t i;
uint8_t ix;
uint16_t d;
st = b[0]; /* frame start byte */
ix = b[3]; /* hi/lo part of ser; (LSB due to our bitsToBytes...) */
d = (b[1]<<8) + b[2]; /* data byte */
/* find highest channel number single frame serial,
(2 frame serial will make a single serial too) */
if(dfmstate.idcnt0 < DFMIDTHRESHOLD && dfmstate.idcnt1 < DFMIDTHRESHOLD) {
uint32_t v = (st<<20) | (d<<4) | ix;
if ( st > (dfmstate.lastfrid>>20) ) {
dfmstate.lastfrid = v;
Serial.print(" MAXCH:"); Serial.print(st);
dfmstate.lastfrcnt = 0;
} else if ( st == (dfmstate.lastfrid>>20) ) {
/* same id found */
if (v == dfmstate.lastfrid) {
++dfmstate.lastfrcnt;
thres = DFMIDTHRESHOLD * 2;
/* may be a 2 frame serial so increase safety level */
if (ix <= 1) thres *= 2;
/* may be not a dfm6 so increase safety level */
if ( (st>>4) != 6) thres *= 2;
if (dfmstate.lastfrcnt >= thres) {
/* id found */
if (dfmstate.lastfrcnt == thres) {
uint32_t id = ((st&0x0F)<<20) | (d<<4) | ix;
uint32_t chkid = id;
int i;
/* check validity */
for(i=0; i<6; i++) {
if((chkid&0x0f)>9) { break; /* not ok */ }
chkid >>= 4;
}
if(i==6) {
snprintf(sonde.si()->id, 10, "D%x ", id);
sonde.si()->validID = true;
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
return;
}
dfmstate.lastfrcnt = 0;
Serial.print(" NOT NUMERIC SERIAL");
}
//anonym->idtime = osic_time();
} else {
Serial.print(" MAXCHCNT/SECLVL:");
Serial.print(dfmstate.lastfrcnt);
Serial.print("/");
Serial.print(thres);
}
} else {
dfmstate.lastfrid = v; /* not stable ser */
dfmstate.lastfrcnt = 0UL;
}
}
} /*find highest channel number single frame serial */
i = 0;
while (i<dfmstate.nameregtop && dfmstate.start[i]!=st) i++;
Serial.printf(" %02x:i=%d,top=%d", st, i, dfmstate.nameregtop);
if (i<dfmstate.nameregtop) {
if (ix<=1UL && (dfmstate.cnt[2*i+ix]==0 || dfmstate.dat[2*i+ix]==d)) {
dfmstate.dat[2*i+ix] = d;
if(dfmstate.cnt[2*i+ix] < 255) dfmstate.cnt[2*i+ix]++;
Serial.print(" ID:");
Serial.print(st, HEX);
Serial.print("[");
Serial.print(ix);
Serial.print("] CNT:");
Serial.print(dfmstate.cnt[2*i]);
Serial.print(",");
Serial.print(dfmstate.cnt[2*i+1]);
Serial.print(",st=");
Serial.print(st);
Serial.print(",lastfrid=");
Serial.print(dfmstate.lastfrid>>20);
if( (dfmstate.cnt[2*i]>DFMIDTHRESHOLD && dfmstate.cnt[2*i+1]>DFMIDTHRESHOLD) ||
(dfmstate.cnt[2*i]>0 && dfmstate.cnt[2*i+1]>0 && st == (dfmstate.lastfrid>>20) && (st>>4)>6) ) {
if(dfmstate.idcnt0 <= 1) {
dfmstate.idcnt0 = dfmstate.cnt[2*i];
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);
Serial.print("\nNEW AUTOID:");
Serial.println(sonde.si()->id);
sonde.si()->validID = true;
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
}
if(dfmstate.nameregok==i) {
Serial.print(" ID OK");
// idtime = .... /* TODO */
}
}
} else {
/* data changed so not ser */
dfmstate.cnt[2*i] = 0;
dfmstate.cnt[2*i+1] = 0;
if(dfmstate.nameregok == i) { /* found id wrong */
dfmstate.idcnt0 = 0;
dfmstate.idcnt1 = 0;
}
}
} else if (ix<=1) { /* add new entry for possible ID */
dfmstate.start[dfmstate.nameregtop] = st;
dfmstate.cnt[2*dfmstate.nameregtop] = 0;
dfmstate.cnt[2*dfmstate.nameregtop+1] = 0;
dfmstate.cnt[2*dfmstate.nameregtop+ix] = 1;
dfmstate.dat[2*dfmstate.nameregtop+ix] = d;
if(dfmstate.nameregtop<49) dfmstate.nameregtop++;
}
}
void DFM::decodeCFG(uint8_t *cfg) void DFM::decodeCFG(uint8_t *cfg)
{ {
#if 1
// 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);
#else
// old ID
static int lowid, highid, idgood=0, type=0; static int lowid, highid, idgood=0, type=0;
if((cfg[0]>>4)==0x06 && type==0) { // DFM-6 ID if((cfg[0]>>4)==0x06 && type==0) { // DFM-6 ID
lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f); lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f);
@@ -190,6 +378,7 @@ void DFM::decodeCFG(uint8_t *cfg)
sonde.si()->validID = true; sonde.si()->validID = true;
} }
} }
#endif
} }
static int bitCount(int x) { static int bitCount(int x) {
@@ -288,20 +477,140 @@ void DFM::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len)
bytes[(i-1)/8] &= 0x0F; bytes[(i-1)/8] &= 0x0F;
} }
static int haveNewFrame = 0;
int DFM::processDFMdata(uint8_t dt) {
static uint8_t data[1024];
static uint32_t rxdata = 0;
static uint8_t rxbitc = 0;
static uint8_t rxbyte = 0;
static uint8_t rxsearching = 1;
static uint8_t rxp;
static int rssi=0, fei=0, afc=0;
static uint8_t invers = 0;
for(int i=0; i<8; i++) {
uint8_t d = (dt&0x80)?1:0;
dt <<= 1;
rxdata = (rxdata<<1) | d;
if( (rxbitc&1)==0 ) {
// "bit1"
rxbyte = (rxbyte<<1) | d;
} else {
// "bit2" ==> 01 or 10 => 1, otherweise => 0
// not here: (10=>1, 01=>0)!!! rxbyte = rxbyte ^ d;
}
//
if(rxsearching) {
if( rxdata == 0x6566A5AA || rxdata == 0x9A995A55 ) {
rxsearching = false;
rxbitc = 0;
rxp = 0;
rxbyte = 0;
rssi=sx1278.getRSSI();
fei=sx1278.getFEI();
afc=sx1278.getAFC();
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
invers = (rxdata == 0x6566A5AA)?1:0;
}
} else {
rxbitc = (rxbitc+1)%16; // 16;
if(rxbitc == 0) { // got 8 data bit
if(invers) rxbyte=~rxbyte;
data[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff;
if(rxp>=DFM_FRAMELEN) {
rxsearching = true;
//Serial.println("Got a DFM frame!");
Serial.print("[RSSI="); Serial.print(rssi);
Serial.print(" FEI="); Serial.print(fei);
Serial.print(" AFC="); Serial.print(afc); Serial.print("] ");
decodeFrameDFM(data);
haveNewFrame = 1;
}
}
}
}
return 0;
}
int DFM::receive() { int DFM::receive() {
if( stype == STYPE_DFM ) {
return receiveNew();
} else {
return receiveOld();
}
}
int DFM::receiveNew() {
int rxframes = 4;
// tentative continuous RX version...
unsigned long t0 = millis();
while( ( millis() - t0 ) < 1000 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) {
Serial.println("FIFO full");
}
if ( bitRead(value, 4) ) {
Serial.println("FIFO overflow");
// new: (maybe clear only overflow??) TODO
sx1278.clearIRQFlags();
}
if ( bitRead(value, 2) == 1 ) {
Serial.println("FIFO: payload ready()");
// does not make much sence? (from m10): TODO
// ??????? sx1278.clearIRQFlags();
}
if(bitRead(value, 6) == 0) { // while FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
processDFMdata(data);
value = sx1278.readRegister(REG_IRQ_FLAGS2);
} else {
#if 0
if(headerDetected) {
t0 = millis(); // restart timer... don't time out if header detected...
headerDetected = 0;
}
#endif
if(haveNewFrame) {
//Serial.printf("DFM::receive(): new frame complete after %ldms\n", millis()-t0);
haveNewFrame = 0;
rxframes--;
if(rxframes==0) return RX_OK;
}
delay(2);
}
}
return RX_TIMEOUT;
}
int DFM::receiveOld() {
byte data[1000]; // pending data from previous mode may write more than 33 bytes. TODO. byte data[1000]; // pending data from previous mode may write more than 33 bytes. TODO.
for(int i=0; i<2; i++) { for(int i=0; i<2; i++) {
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes) sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
//int t = millis();
int e = sx1278.receivePacketTimeout(1000, data); int e = sx1278.receivePacketTimeout(1000, data);
//Serial.printf("rxPTO done after %d ms", (int)(millis()-t));
if(e) { return RX_TIMEOUT; } //if timeout... return 1 if(e) { return RX_TIMEOUT; } //if timeout... return 1
Serial.printf("inverse is %d\b", inverse); if(!(stype==STYPE_DFM09)) { for(int i=0; i<33; i++) { data[i]^=0xFF; } }
if(!inverse) { for(int i=0; i<33; i++) { data[i]^=0xFF; } } decodeFrameDFM(data);
}
return RX_OK;
}
int DFM::decodeFrameDFM(uint8_t *data) {
deinterleave(data, 7, hamming_conf); deinterleave(data, 7, hamming_conf);
deinterleave(data+7, 13, hamming_dat1); deinterleave(data+7, 13, hamming_dat1);
deinterleave(data+20, 13, hamming_dat2); deinterleave(data+20, 13, hamming_dat2);
#if 0
Serial.print("RAWCFG:");
for(int i=0; i<7*8; i++) {
Serial.print(hamming_conf[i]?"1":"0");
}
#endif
int ret0 = hamming(hamming_conf, 7, block_conf); int ret0 = hamming(hamming_conf, 7, block_conf);
int ret1 = hamming(hamming_dat1, 13, block_dat1); int ret1 = hamming(hamming_dat1, 13, block_dat1);
@@ -318,7 +627,7 @@ int DFM::receive() {
decodeCFG(byte_conf); decodeCFG(byte_conf);
decodeDAT(byte_dat1); decodeDAT(byte_dat1);
decodeDAT(byte_dat2); decodeDAT(byte_dat2);
} Serial.println("");
return RX_OK; return RX_OK;
} }
@@ -345,3 +654,4 @@ int DFM::waitRXcomplete() {
} }
DFM dfm = DFM(); DFM dfm = DFM();
+9 -2
View File
@@ -23,16 +23,23 @@
class DFM class DFM
{ {
private: private:
int inverse=0; int stype;
char *stypename=NULL;
void deinterleave(uint8_t *str, int L, uint8_t *block); void deinterleave(uint8_t *str, int L, uint8_t *block);
uint32_t bits2val(const uint8_t *bits, int len); uint32_t bits2val(const uint8_t *bits, int len);
int check(uint8_t code[8]); int check(uint8_t code[8]);
int hamming(uint8_t *ham, int L, uint8_t *sym); int hamming(uint8_t *ham, int L, uint8_t *sym);
void printRaw(const char *prefix, int len, int ret, const uint8_t* data); void printRaw(const char *prefix, int len, int ret, const uint8_t* data);
void finddfname(uint8_t *cfg);
void decodeCFG(uint8_t *cfg); void decodeCFG(uint8_t *cfg);
void decodeDAT(uint8_t *dat); void decodeDAT(uint8_t *dat);
void bitsToBytes(uint8_t *bits, uint8_t *bytes, int len); void bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
int processDFMdata(uint8_t dt);
int decodeFrameDFM(uint8_t *data);
int receiveOld();
int receiveNew();
#define B 8 #define B 8
#define S 4 #define S 4
@@ -55,7 +62,7 @@ private:
public: public:
DFM(); DFM();
// main decoder API // main decoder API
int setup(float frequency, int inverse); int setup(float frequency, int type);
int receive(); int receive();
int waitRXcomplete(); int waitRXcomplete();
+14 -13
View File
@@ -13,11 +13,11 @@ int readLine(Stream &stream, char *buffer, int maxlen);
extern const char *version_name; extern const char *version_name;
extern const char *version_id; extern const char *version_id;
#include <../fonts/FreeMono9pt7b.h> #include <fonts/FreeMono9pt7b.h>
#include <../fonts/FreeMono12pt7b.h> #include <fonts/FreeMono12pt7b.h>
#include <../fonts/FreeSans9pt7b.h> #include <fonts/FreeSans9pt7b.h>
#include <../fonts/FreeSans12pt7b.h> #include <fonts/FreeSans12pt7b.h>
#include <../fonts/Picopixel.h> #include <fonts/Picopixel.h>
extern Sonde sonde; extern Sonde sonde;
@@ -30,7 +30,8 @@ extern SemaphoreHandle_t axpSemaphore;
SPIClass spiDisp(HSPI); SPIClass spiDisp(HSPI);
const char *sondeTypeStr[NSondeTypes] = { "DFM6", "DFM9", "RS41", "RS92", "M10 " }; const char *sondeTypeStr[NSondeTypes] = { "DFM6", "DFM9", "RS41", "RS92", "M10 ", "M20 ", "DFM " };
const char sondeTypeChar[NSondeTypes] = { '6', '9', '4', 'R', 'M', '2', 'D' };
byte myIP_tiles[8*11]; byte myIP_tiles[8*11];
static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00}; static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00};
@@ -268,12 +269,12 @@ void U8x8Display::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y
void U8x8Display::welcome() { void U8x8Display::welcome() {
u8x8->clear(); u8x8->clear();
setFont(FONT_LARGE); setFont(FONT_LARGE);
drawString(8 - strlen(version_name) / 2, 1, version_name); drawString(0, 1, version_name);
//drawString(8 - strlen(version_id) / 2, 2, version_id); drawString(11, 1, version_id);
setFont(FONT_SMALL); setFont(FONT_SMALL);
drawString(0, 4, "RS41/92,DFM,M10"); drawString(0, 4, "RS41/92,DFM,M1&2");
drawString(0, 6, "by FRS2013 "); drawString(0, 6, "by Vigor & Xav ");
drawString(11, 6, version_id); //drawString(11, 6, version_id);
} }
static String previp; static String previp;
@@ -858,7 +859,7 @@ void Display::drawHS(DispEntry *de) {
} }
boolean is_ms = (de->extra && de->extra[0]=='m')?true:false; // m/s or km/h 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()->hs;
if(is_ms) hs = hs / 3.6; if(is_ms) hs = hs * 3.6;
boolean has_extra = (de->extra && de->extra[1]!=0)? true: false; boolean has_extra = (de->extra && de->extra[1]!=0)? true: false;
snprintf(buf, 16, hs>99?" %3.0f":" %2.1f", hs); snprintf(buf, 16, hs>99?" %3.0f":" %2.1f", hs);
if(has_extra) { strcat(buf, de->extra+1); } if(has_extra) { strcat(buf, de->extra+1); }
@@ -1214,7 +1215,7 @@ void Display::calcDurVol() {
} }
} else { } else {
heure=99; heure=0;
} }
sonde.si()->durvolheure=heure; sonde.si()->durvolheure=heure;
sonde.si()->durvolminute=minute; sonde.si()->durvolminute=minute;
+7 -6
View File
@@ -48,7 +48,7 @@ int M10::setup(float frequency)
M10_DBG(Serial.println("Setting FSJ mode FAILED")); M10_DBG(Serial.println("Setting FSJ mode FAILED"));
return 1; return 1;
} }
if(sx1278.setBitrate(9600)!=0) { if(sx1278.setBitrate(9616)!=0) {
M10_DBG(Serial.println("Setting bitrate 9600bit/s FAILED")); M10_DBG(Serial.println("Setting bitrate 9600bit/s FAILED"));
return 1; return 1;
} }
@@ -80,12 +80,13 @@ int M10::setup(float frequency)
//const char *SYNC="\x99\x9A"; //const char *SYNC="\x99\x9A";
#if 1 #if 1
// version 1, working with continuous RX // version 1, working with continuous RX
const char *SYNC="\x66\x65"; //const char *SYNC="\x66\x65";
if(sx1278.setSyncConf(0x70, 2, (const uint8_t *)SYNC)!=0) {
const char *SYNC="\x99\x99\x4C\x99";
if(sx1278.setSyncConf(0x70, 4, (const uint8_t *)SYNC)!=0) {
M10_DBG(Serial.println("Setting SYNC Config FAILED")); M10_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1; return 1;
} }
//if(sx1278.setPreambleDetect(0xA8)!=0) {
if((sx1278.setPreambleDetect(0x9F)!=0)&&((sx1278.setPreambleDetect(0xAF)!=0))) { if((sx1278.setPreambleDetect(0x9F)!=0)&&((sx1278.setPreambleDetect(0xAF)!=0))) {
M10_DBG(Serial.println("Setting PreambleDetect FAILED")); M10_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1; return 1;
@@ -258,7 +259,7 @@ int M10::decodeframeM10(uint8_t *data) {
} }
} }
Serial.println(crcok?"CRC OK":"CRC NOT OK"); Serial.println(crcok?"CRC OK":"CRC NOT OK");
//M10 0x9F M10Plus 0xAF M20 0x20 //M10 0x9F M10Plus 0xAF
if((data[1]==0x9F && data[2]==0x20) || (data[1]==0xAF && data[2]==0x20)) { if((data[1]==0x9F && data[2]==0x20) || (data[1]==0xAF && data[2]==0x20)) {
Serial.println("Decoding..."); Serial.println("Decoding...");
// Its a M10 // Its a M10
@@ -381,7 +382,7 @@ void M10::processM10data(uint8_t dt)
int M10::receive() { int M10::receive() {
unsigned long t0 = millis(); unsigned long t0 = millis();
Serial.printf("M10::receive() start at %ld\n",t0); Serial.printf("M10::receive() start at %ld\n",t0);
while( millis() - t0 < 1000 ) { while( millis() - t0 < 1512 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2); uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) { if ( bitRead(value, 7) ) {
Serial.println("FIFO full"); Serial.println("FIFO full");
+535
View File
@@ -0,0 +1,535 @@
/* M20 decoder functions */
#include "M20.h"
#include "SX1278FSK.h"
#include "rsc.h"
#include "Sonde.h"
#include <SPIFFS.h>
// well...
//#include "rs92gps.h"
#define M20_DEBUG 1
#if M20_DEBUG
#define M20_DBG(x) x
#else
#define M20_DBG(x)
#endif
static byte data1[512];
static byte *dataptr=data1;
static uint8_t rxbitc;
static uint16_t rxbyte;
static int rxp=0;
static int haveNewFrame = 0;
static int lastFrame = 0;
static int headerDetected = 0;
int M20::setup(float frequency)
{
#if M20_DEBUG
Serial.println("Setup sx1278 for M20 sonde");
#endif
//if(!initialized) {
//Gencrctab();
//initrsc();
// not here for now.... get_eph("/brdc.19n");
// initialized = true;
//}
if(sx1278.ON()!=0) {
M20_DBG(Serial.println("Setting SX1278 power on FAILED"));
return 1;
}
if(sx1278.setFSK()!=0) {
M20_DBG(Serial.println("Setting FSJ mode FAILED"));
return 1;
}
if(sx1278.setBitrate(9600)!=0) {
M20_DBG(Serial.println("Setting bitrate 9600bit/s FAILED"));
return 1;
}
#if M20_DEBUG
float br = sx1278.getBitrate();
Serial.print("Exact bitrate is ");
Serial.println(br);
#endif
if(sx1278.setAFCBandwidth(sonde.config.rs92.rxbw)!=0) {
M20_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.rs92.rxbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.rs92.rxbw)!=0) {
M20_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.rs92.rxbw));
return 1;
}
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) {
M20_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
//const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8";
//const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F";
// was 0x57
//const char *SYNC="\x99\x9A";
#if 1
// version 1, working with continuous RX
//const char *SYNC="\x66\x65";
const char *SYNC="\x99\x99\x4C\x99";
if(sx1278.setSyncConf(0x70, 4, (const uint8_t *)SYNC)!=0) {
M20_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
//if(sx1278.setPreambleDetect(0xA8)!=0) {
if(sx1278.setPreambleDetect(0x20)!=0) {
M20_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
#endif
#if 0
// version 2, with per-packet rx start, untested
// header is 2a 10 65, i.e. with lsb first
// 0 0101 0100 1 0 0000 1000 1 0 1010 0110 1
// 10 10011001 10011010 01 10 10101010 01101010 01 10 01100110 10010110 01
// preamble 0x6A 0x66 0x6A
// i.e. preamble detector on (0x80), preamble detector size 1 (0x00), preample chip errors??? (0x0A)
// after 2a2a2a2a2a1065
if(sx1278.setPreambleDetect(0xA8)!=0) {
M20_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// sync config: ato restart (01), preamble polarity AA (0), sync on (1), resevered (0), syncsize 2+1 (010) => 0x52
const char *SYNC="\x6A\x66\x69";
if(sx1278.setSyncConf(0x52, 3, (const uint8_t *)SYNC)!=0) {
M20_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
// payload length is ((240 - 7)*10 +6)/8 = 292
#endif
// Packet config 1: fixed len, no mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
M20_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
Serial.print("M20: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
//sx1278.setPayloadLength(292);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
#if M20_DEBUG
M20_DBG(Serial.println("Setting SX1278 config for M20 finished\n"); Serial.println());
#endif
return res;
}
#if 0
int M20::setFrequency(float frequency) {
Serial.print("M20: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
return res;
}
#endif
#if 0
uint32_t M20::bits2val(const uint8_t *bits, int len) {
uint32_t val = 0;
for (int j = 0; j < len; j++) {
val |= (bits[j] << (len-1-j));
}
return val;
}
#endif
M20::M20() {
}
#define M20_FRAMELEN 70
#define M20_CRCPOS 68
void M20::printRaw(uint8_t *data, int len)
{
char buf[3];
int i;
for(i=0; i<len; i++) {
snprintf(buf, 3, "%02X ", data[i]);
Serial.print(buf);
}
Serial.println();
}
static int update_checkM20(int c, uint8_t b) {
int c0, c1, t, t6, t7, s;
c1 = c & 0xFF;
// B
b = (b >> 1) | ((b & 1) << 7);
b ^= (b >> 2) & 0xFF;
// A1
t6 = ( c & 1) ^ ((c >> 2) & 1) ^ ((c >> 4) & 1);
t7 = ((c >> 1) & 1) ^ ((c >> 3) & 1) ^ ((c >> 5) & 1);
t = (c & 0x3F) | (t6 << 6) | (t7 << 7);
// A2
s = (c >> 7) & 0xFF;
s ^= (s >> 2) & 0xFF;
c0 = b ^ t ^ s;
return ((c1 << 8) | c0) & 0xFFFF;
}
static bool checkM20crc(uint8_t *msg) {
int i, cs, cs1;
cs = 0;
for (i = 0; i < M20_CRCPOS; i++) {
cs = update_checkM20(cs, msg[i]);
}
cs = cs & 0xFFFF;
cs1 = (msg[M20_CRCPOS] << 8) | msg[M20_CRCPOS+1];
return (cs1 == cs);
}
typedef uint32_t SET256[8];
static SET256 sondeudp_VARSET = {0x03BBBBF0UL,0x80600000UL,0x06A001A0UL,
0x0000001CUL,0x00000000UL,0x00000000UL,0x00000000UL,
0x00000000UL};
// VARSET=SET256{4..9,11..13,15..17,19..21,23..25,53..54,63,69,71,72,85,87,89,90,98..100};
static uint8_t fixcnt[M20_FRAMELEN];
static uint8_t fixbytes[M20_FRAMELEN];
static int32_t getint32(uint8_t *data) {
return (int32_t)( data[3]|(data[2]<<8)|(data[1]<<16)|(data[0]<<24) );
}
static int32_t getint24(uint8_t *data) {
return (int32_t)(data[2]|(data[1]<<8)|(data[0]<<16) );
}
static int16_t getint16(uint8_t *data) {
return (int16_t)(data[1]|((uint16_t)data[0]<<8));
}
static int16_t getint16_r(uint8_t *data) {
return (int16_t)(((uint16_t)data[1]<<8) |data[0]);
}
static char dez(uint8_t nr) {
nr = nr%10;
return '0'+nr;
}
static char hex(uint8_t nr) {
nr = nr&0x0f;
if(nr<10) return '0'+nr;
else return 'A'+nr-10;
}
const static float DEGMUL = 1.0/0xB60B60;
#define VMUL 0.005
#define VMUL_M20 0.01
#ifndef PI
#define PI (3.1415926535897932384626433832795)
#endif
#define RAD (PI/180)
// ret: 1=frame ok; 2=frame with errors; 0=ignored frame (m20dop-alternativ)
int M20::decodeframeM20(uint8_t *data) {
int repairstep = 16;
int repl = 0;
bool crcok;
// error correction, inspired by oe5dxl's sondeudp
do {
crcok = checkM20crc(data);
if(crcok) break;
repl = 0;
for(int i=0; i<M20_CRCPOS; i++) {
if( ((sondeudp_VARSET[i/32]&(1<<(i%32))) != 1) && (fixcnt[i]>=repairstep) ) {
repl++;
data[i] = fixbytes[i];
}
}
repairstep >>= 1;
} while(repairstep>0);
if(crcok) {
for(int i=0; i<M20_CRCPOS; i++) {
if(fixbytes[i]==data[i] &&fixcnt[i]<255) fixcnt[i]++;
else { fixcnt[i]=0; fixbytes[i]=data[i]; }
}
}
Serial.println(crcok?"CRC OK":"CRC NOT OK");
//M20 0x20
if(data[1]==0x20) {
Serial.println("Decoding...");
// Its a M20
// getid...
char ids[11]={'M','E','0','0','0','0','0','0','0','0','0'};
ids[0] = 'M';
ids[1] = 'E';
uint32_t id = getint16(data+18);
ids[2] = hex(id/16);
ids[3] = hex(id);
//
id = getint16_r(data+19)/4;
ids[4] = (char)((id/10000)%10+48);
ids[5] = (char)((id/1000)%10+48);
ids[6] = (char)((id/100)%10+48);
ids[7] = (char)((id/10)%10+48);
ids[8] = (char)(id%10+48);
strncpy(sonde.si()->ser, ids, 10);
sonde.si()->validID = true;
//Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
// ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal
// position data
// 0x1C 4 byte
sonde.si()->lat = getint32(data+28) * 1e-6;
//0x20 4 byte
sonde.si()->lon = getint32(data+32) * 1e-6;
//0x08 3 byte
sonde.si()->alt = getint24(data+8) * VMUL_M20;
//0x0B 2 byte
//VMUL_M20 specific
float ve = getint16(data+11)*VMUL_M20;
//0x0D 2 byte
float vn = getint16(data+13)*VMUL_M20;
//0x18 2 byte
sonde.si()->vs = getint16(data+24) * VMUL_M20;
sonde.si()->hs = sqrt(ve*ve+vn*vn);
float dir = atan2(vn, ve)*(1.0/RAD);
if(dir<0) dir+=360;
sonde.si()->dir = dir;
sonde.si()->validPos = 0x3f;
//0x0F 3 byte
uint32_t tow = getint24(data+15);
uint16_t week = getint16(data+26);
sonde.si()->time = (tow+week*604800+315964800)-18;
sonde.si()->validTime = true;
} else {
Serial.printf("data is %02x %02x %02x\n", data[0], data[1], data[2]);
return 0;
}
return crcok?1:2;
}
static uint32_t rxdata;
static bool rxsearching=true;
// search for
// //101001100110011010011010011001100110100110101010100110101001
// //1010011001100110100110100110 0110.0110 1001.1010 1010.1001 1010.1001 => 0x669AA9A9
void M20::processM20data(uint8_t dt)
{
for(int i=0; i<8; i++) {
uint8_t d = (dt&0x80)?1:0;
dt <<= 1;
rxdata = (rxdata<<1) | d;
//uint8_t value = ((rxdata>>1)^rxdata)&0x01;
//if((rxbitc&1)==1) { rxbyte = (rxbyte>>1) + ((value)<<8); } // mancester decoded data
//rxbyte = (rxbyte>>1) | (d<<8);
if( (rxbitc&1)==0 ) {
// "bit1"
rxbyte = (rxbyte<<1) | d;
} else {
// "bit2" ==> 01 or 10 => 1, otherweise => 0
rxbyte = rxbyte ^ d;
}
//
if(rxsearching) {
if( rxdata == 0xcccca64c || rxdata == 0x333359b3 ) {
rxsearching = false;
rxbitc = 0;
rxp = 0;
#if 1
int rssi=sx1278.getRSSI();
int fei=sx1278.getFEI();
int afc=sx1278.getAFC();
Serial.print("Test: RSSI="); Serial.print(rssi);
Serial.print(" FEI="); Serial.print(fei);
Serial.print(" AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
#endif
}
} else {
rxbitc = (rxbitc+1)%16; // 16;
if(rxbitc == 0) { // got 8 data bit
//Serial.printf("%03x ",rxbyte);
dataptr[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff;
#if 0
if(rxp==7 && dataptr[6] != 0x65) {
Serial.printf("wrong start: %02x\n",dataptr[6]);
rxsearching = true;
}
#endif
if(rxp>=M20_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeM20(dataptr);
}
}
}
}
}
int M20::receive() {
unsigned long t0 = millis();
Serial.printf("M20::receive() start at %ld\n",t0);
while( millis() - t0 < 1512 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) {
Serial.println("FIFO full");
}
if ( bitRead(value, 4) ) {
Serial.println("FIFO overflow");
}
if ( bitRead(value, 2) == 1 ) {
Serial.println("FIFO: ready()");
sx1278.clearIRQFlags();
}
if(bitRead(value, 6) == 0) { // while FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
//Serial.printf("%02x",data);
processM20data(data);
value = sx1278.readRegister(REG_IRQ_FLAGS2);
} else {
if(headerDetected) {
t0 = millis(); // restart timer... don't time out if header detected...
headerDetected = 0;
}
if(haveNewFrame) {
Serial.printf("M20::receive(): new frame complete after %ldms\n", millis()-t0);
printRaw(dataptr, M20_FRAMELEN);
int retval = haveNewFrame==1 ? RX_OK: RX_ERROR;
haveNewFrame = 0;
return retval;
}
delay(2);
}
}
Serial.printf("M20::receive() timed out\n");
return RX_TIMEOUT; // TODO RX_OK;
}
#define M20MAXLEN (240)
int M20::waitRXcomplete() {
// called after complete...
#if 0
Serial.printf("decoding frame %d\n", lastFrame);
print_frame(lastFrame==1?data1:data2, 240);
SondeInfo *si = sonde.sondeList+rxtask.receiveSonde;
si->lat = gpx.lat;
si->lon = gpx.lon;
si->alt = gpx.alt;
si->vs = gpx.vU;
si->hs = gpx.vH;
si->dir = gpx.vD;
si->validPos = 0x3f;
memcpy(si->id, gpx.id, 9);
si->validID = true;
int res=0;
uint32_t t0 = millis();
while( rxtask.receiveResult == 0xFFFF && millis()-t0 < 2000) { delay(20); }
if( rxtask.receiveResult<0 || rxtask.receiveResult==RX_TIMEOUT) {
res = RX_TIMEOUT;
} else if ( rxtask.receiveResult==0) {
res = RX_OK;
} else {
res = RX_ERROR;
}
rxtask.receiveResult = 0xFFFF;
Serial.printf("M20::waitRXcomplete returning %d (%s)\n", res, RXstr[res]);
return res;
#endif
return 0;
}
#if 0
int oldwaitRXcomplete() {
Serial.println("M20: receive frame...\n");
sx1278receiveData = true;
delay(6000); // done in other task....
//sx1278receiveData = false;
#if 0
//sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header)
//sx1278.setPayloadLength(0); // infinite for now...
////// test code for continuous reception
// sx1278.receive(); /// active FSK RX mode -- already done above...
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
unsigned long previous = millis();
byte ready=0;
uint32_t wait = 8000;
// while not yet done or FIFO not yet empty
// bit 6: FIFO Empty
// bit 2 payload ready
int by=0;
while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) )
{
if( bitRead(value, 7) ) { Serial.println("FIFO full"); }
if( bitRead(value, 4) ) { Serial.println("FIFO overflow"); }
if( bitRead(value,2)==1 ) ready=1;
if( bitRead(value, 6) == 0 ) { // FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
process8N1data(data);
by++;
#if 0
if(di==1) {
int rssi=getRSSI();
int fei=getFEI();
int afc=getAFC();
Serial.print("Test: RSSI="); Serial.println(rssi);
Serial.print("Test: FEI="); Serial.println(fei);
Serial.print("Test: AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
}
if(di>520) {
// TODO
Serial.println("TOO MUCH DATA");
break;
}
previous = millis(); // reset timeout after receiving data
#endif
}
value = sx1278.readRegister(REG_IRQ_FLAGS2);
}
Serial.printf("processed %d bytes before end/timeout\n", by);
#endif
/////
#if 0
int e = sx1278.receivePacketTimeout(1000, data+8);
if(e) { Serial.println("TIMEOUT"); return RX_TIMEOUT; } //if timeout... return 1
printRaw(data, M20MAXLEN);
//for(int i=0; i<M20MAXLEN; i++) { data[i] = reverse(data[i]); }
//printRaw(data, MAXLEN);
//for(int i=0; i<M20MAXLEN; i++) { data[i] = data[i] ^ scramble[i&0x3F]; }
//printRaw(data, MAXLEN);
//int res = decode41(data, M20MAXLEN);
#endif
int res=0;
return res==0 ? RX_OK : RX_ERROR;
}
#endif
M20 m20 = M20();
+96
View File
@@ -0,0 +1,96 @@
/*
* M20.h
* Functions for decoding Meteomodem M20 sondes with SX127x chips
* Copyright (C) 2019 Hansi Reiser, dl9rdz
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef M20_h
#define M20_h
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
#if 0
struct CONTEXTR9 {
char calibdata[512];
uint32_t calibok;
char mesok;
char posok;
char framesent;
double lat;
double lon;
double heig;
double speed;
double dir;
double climb;
double lastlat;
double laslong;
double lastalt;
double lastspeed;
double lastdir;
double lastclb;
float hrmsc;
float vrmsc;
double hp;
double hyg;
double temp;
double ozontemp;
double ozon;
uint32_t goodsats;
uint32_t timems;
uint32_t framenum;
};
#endif
/* Main class */
class M20
{
private:
void printRaw(uint8_t *data, int len);
void processM20data(uint8_t data);
int decodeframeM20(uint8_t *data);
#if 0
void stobyte92(uint8_t byte);
void dogps(const uint8_t *sf, int sf_len,
struct CONTEXTR9 * cont, uint32_t * timems,
uint32_t * gpstime);
uint32_t bits2val(const uint8_t *bits, int len);
int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len);
int decode92(byte *data, int maxlen);
uint8_t hamming_conf[ 7*8]; // 7*8=56
uint8_t hamming_dat1[13*8]; // 13*8=104
uint8_t hamming_dat2[13*8];
uint8_t block_conf[ 7*4]; // 7*4=28
uint8_t block_dat1[13*4]; // 13*4=52
uint8_t block_dat2[13*4];
uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix
{{ 0, 1, 1, 1, 1, 0, 0, 0},
{ 1, 0, 1, 1, 0, 1, 0, 0},
{ 1, 1, 0, 1, 0, 0, 1, 0},
{ 1, 1, 1, 0, 0, 0, 0, 1}};
uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
// 1-bit-error-Syndrome
boolean initialized = false;
#endif
public:
M20();
int setup(float frequency);
int receive();
int waitRXcomplete();
//int use_ecc = 1;
};
extern M20 m20;
#endif
+2 -1
View File
@@ -356,7 +356,8 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
if (dir<0.0) dir = 360.0+dir; if (dir<0.0) dir = 360.0+dir;
sonde.si()->dir = dir; sonde.si()->dir = dir;
Serial.print(" "); Serial.print(" ");
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve))*3.6f; //sonde.si()->hs = sqrt((float)(vn*vn+ve*ve))*3.6f;
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve));
Serial.print(sonde.si()->hs); Serial.print(sonde.si()->hs);
Serial.print("km/h "); Serial.print("km/h ");
Serial.print(dir); Serial.print(dir);
+13
View File
@@ -6,6 +6,7 @@
#include "RS92.h" #include "RS92.h"
#include "DFM.h" #include "DFM.h"
#include "M10.h" #include "M10.h"
#include "M20.h"
#include "SX1278FSK.h" #include "SX1278FSK.h"
#include "Display.h" #include "Display.h"
#include <Wire.h> #include <Wire.h>
@@ -428,6 +429,7 @@ void Sonde::setup() {
break; break;
case STYPE_DFM06: case STYPE_DFM06:
case STYPE_DFM09: case STYPE_DFM09:
case STYPE_DFM:
dfm.setup( sondeList[rxtask.currentSonde].freq * 1000000, sondeList[rxtask.currentSonde].type==STYPE_DFM06?0:1 ); dfm.setup( sondeList[rxtask.currentSonde].freq * 1000000, sondeList[rxtask.currentSonde].type==STYPE_DFM06?0:1 );
break; break;
case STYPE_RS92: case STYPE_RS92:
@@ -436,6 +438,9 @@ void Sonde::setup() {
case STYPE_M10: case STYPE_M10:
m10.setup( sondeList[rxtask.currentSonde].freq * 1000000); m10.setup( sondeList[rxtask.currentSonde].freq * 1000000);
break; break;
case STYPE_M20:
m20.setup( sondeList[rxtask.currentSonde].freq * 1000000);
break;
} }
// debug // debug
float afcbw = sx1278.getAFCBandwidth(); float afcbw = sx1278.getAFCBandwidth();
@@ -458,6 +463,10 @@ void Sonde::receive() {
case STYPE_M10: case STYPE_M10:
res = m10.receive(); res = m10.receive();
break; break;
case STYPE_M20:
res = m20.receive();
break;
case STYPE_DFM:
case STYPE_DFM06: case STYPE_DFM06:
case STYPE_DFM09: case STYPE_DFM09:
res = dfm.receive(); res = dfm.receive();
@@ -550,6 +559,10 @@ rxloop:
case STYPE_M10: case STYPE_M10:
m10.waitRXcomplete(); m10.waitRXcomplete();
break; break;
case STYPE_M20:
m20.waitRXcomplete();
break;
case STYPE_DFM:
case STYPE_DFM06: case STYPE_DFM06:
case STYPE_DFM09: case STYPE_DFM09:
dfm.waitRXcomplete(); dfm.waitRXcomplete();
+6 -4
View File
@@ -46,9 +46,10 @@ extern const char *RXstr[];
// 01000000 => goto sonde -1 // 01000000 => goto sonde -1
// 01000001 => goto sonde +1 // 01000001 => goto sonde +1
#define NSondeTypes 5 #define NSondeTypes 7
enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41, STYPE_RS92, STYPE_M10 }; enum SondeType { STYPE_DFM06, STYPE_DFM09, STYPE_RS41, STYPE_RS92, STYPE_M10, STYPE_M20, STYPE_DFM };
extern const char *sondeTypeStr[NSondeTypes]; extern const char *sondeTypeStr[NSondeTypes];
extern const char sondeTypeChar[NSondeTypes];
typedef struct st_sondeinfo { typedef struct st_sondeinfo {
// receiver configuration // receiver configuration
@@ -56,6 +57,7 @@ typedef struct st_sondeinfo {
SondeType type; SondeType type;
float freq; float freq;
// decoded ID // decoded ID
char typestr[5]; // decoded type (use type if *typestr==0)
char id[10]; char id[10];
char ser[12]; char ser[12];
bool validID; bool validID;
@@ -69,8 +71,8 @@ typedef struct st_sondeinfo {
int durvolminute; // duree vol minute int durvolminute; // duree vol minute
int durvolseconde; // duree vol seconde int durvolseconde; // duree vol seconde
float alt; // altitude float alt; // altitude
float vs; // vertical speed float vs; // vertical speed in m/s
float hs; // horizontal speed float hs; // horizontal speed in m/s
float dir; // 0..360 float dir; // 0..360
uint8_t sats; // number of sats uint8_t sats; // number of sats
uint8_t validPos; // bit pattern for validity of above 7 fields; 0x80: position is old uint8_t validPos; // bit pattern for validity of above 7 fields; 0x80: position is old
+1 -1
View File
@@ -317,7 +317,7 @@ char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
char comm[100]; 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, s->freq, sondeTypeStr[s->type]);
strcat(b, comm); strcat(b, comm);
if(s->type==STYPE_M10||s->type==STYPE_DFM06||s->type==STYPE_DFM09) { if(s->type==STYPE_M20||s->type==STYPE_M10||s->type==STYPE_DFM||s->type==STYPE_DFM06||s->type==STYPE_DFM09) {
snprintf(comm, 100, " ser=%s", s->ser); snprintf(comm, 100, " ser=%s", s->ser);
strcat(b, comm); strcat(b, comm);
} }
+5 -1
View File
@@ -136,11 +136,15 @@ void geteph() {
int len=0; int len=0;
while(dclient.connected()) { while(dclient.connected()) {
while(dclient.available()) { while(dclient.available()) {
char c = dclient.read(); int c = dclient.read();
if(c==-1) {
Serial.println("dclient.read() returned -1 inspite of available() being true?!");
} else {
fh.write(c); fh.write(c);
len++; len++;
} }
} }
}
Serial.printf("fetched %d bytes\n", len); Serial.printf("fetched %d bytes\n", len);
fh.close(); fh.close();
snprintf(buf, 16, "Fetched %d B ",len); snprintf(buf, 16, "Fetched %d B ",len);
BIN
View File
Binary file not shown.