vendredi 12 février 2021

MysondyGO

Chaque jours des ballons-sondes sont lancés un peu partout dans le monde (en principe à midi/minuit). Ils émettent de la télémétrie que l'on peut capter. Ayant vu une vidéo de Andreas Spiess, je n'ai pas pu résister à l'achat d'un récepteur TTGO pour lequels des radioamateurs italiens ont réalisé un logiciel de réception (mysondyGO). Sur AliExpress, c'était un 'LILYGO® TTGO ESP32-Paxcounter LoRa32 V2.1 1.6 Version 433/868/915MHZ LoRa ESP-32 OLED 0.96 Inch SD Card Bluetooth WIFI Module' pour une petite vingtaine d'euros (si je me souviens bien.

J'ai dû un peu chercher avant de trouver le moyen facile de flasher le logiciel italien. Je ne trouvais que des SDK de développement complexes pour ESP-32 alors que je voulais juste flasher un .bin. Et j'ai finalement trouvé exactement ce qu'il me fallait dans cet article de Anthony Le Cren F4GOH – KB1GOH.
 2024  ls -l ./MySondyGO_1_40/MySondyGO.bin
 2025  ls -l
 2026  git clone https://github.com/espressif/esptool.git
 2027  cd esptool/
 2028  ls -l
 2029  ./esptool.py
 2030  ./esptool.py --port /dev/ttyUSB0 read_flash_status
 2031  ./esptool.py --port /dev/ttyUSB0 erase_flash
 2032  ./esptool.py --port /dev/ttyUSB0 write_flash -z 0x0000 ../MySondyGO_1_40/MySondyGO.bin


Ne restait plus qu'à guêter sur radiosondy.info le passage d'un ballon dans le coin... C'est ennuyeux que le bidule ne semble configurable qu'en BlueTooth avec un smartphone mais bon... Je le configure sur une sonde RS41 sur 403.5 MHz comme celles qui sont lancées vers midi à l'observatoire de Uccle.

Comme l'antenne d'origine, mesurée avec nanoVNA clone, ne me paraîssait pas trop bonne, j'ai bricolé un dipôle coupé pour 403.5 Mhz et ça y est, aujourd'hui, un ballon est passé dans les parages.

Dans une rue est-ouest, j'ai bien capté le ballon du côté sud.

R3420743 a été suivie sur Radiosondy et on peut retrouver la télémétrie captée ici. À l'heure de la photo, elle était à environ 25 kilomètres et 28.002 mètres d'altitude. Elle a été retrouvée du côté de Malmedy.


La prochaine fois, il faudra essayer de la capter avec un RTL-SDR. Éventuellement avec RS ou, essayer d'extraire les messages moi-même (avec ou sans GNUradio), c'est du BFSK; cela ne devrait pas être trop sorcier (?).

Quelqu'un m'a invité à remplacer le lien nanovna.com (qui vendrait des clones de mauvaise qualité) par nanorfe.com qui vend une version V2, utiliserait des composants de meilleure qualité et serait plus 'officiel'. Il existe donc une controverse... Là, j'ai utilisé un clone V1, je ne sais pas d'où il vient ni quelle est sa qualité.

dimanche 13 décembre 2020

TinySA

Un petit 'spectrum analyser' étonant, le tinySA. Une cinquantaine d'euros, il permet de se faire une idée du spectre électromagnétique jusqu'à 960 MHz.

Une première captation autour de 162 MHz, avec l'option 'Max hold', où l'on voit les deux canaux de l'AIS à 161.975 et 162.025 MHz. C'est capté du côté de la Citadelle de Liège et c'est émis par des bateaux, en contrebas sur la Meuse. Il faut noter que le scan sur 100 kHz dure 514 mS et que donc, on loupe des paquets. Ceux-ci, émis à 9600 bauds ne font que 168 ou 440 bits et ne sont donc présent que 20 ou 40 mS. Il arrive aussi que le TinySA ne voie que le début ou la fin du paquet et on n'a donc qu'une partie du spectre. La partie qu'il était en train de scanner pendant sa transmission. D'où l'utilisation de 'Max hold' pour cumuler les spectre.


Spectre UHF, jusqu'à 500 MHz, du côté de la Place Saint-Lambert à Liège. Pas de 'Max hold' ici, pour différencier les émissions continues des émissions par paquets.


En allant jusqu'à 960MHz, on voit des bandes GSM (?).




Bizarre, bizarre, le plan des fréquences de l'IBPT ne semble pas aider beaucoup à l'identification des signaux.

Un bout de la bande FM avec 'Max hold'. Le petit pic à droite du curseur est Bel-RTL sur 103.6.


Sur Linux,
$ sudo minicom -D /dev/ttyACM0 -b 115200

ch> help

Commands: version reset freq dac saveconfig clearconfig data frequencies scan scanraw sweep test touchcal 
touchtest pause resume caloutput save recall trace trigger marker usart capture vbat vbat_offset help info
 color if attenuate level sweeptime leveloffset levelchange modulation rbw mode spur load offset output
 deviceid selftest correction threads
permet de contrôler le TinySA en mode terminal

lundi 1 juin 2020

Simple ad hoc SNMP

SNMP est l'acronyme de 'Simple Network Management Protocol' et, comme tout ce qui est simple, cela fini par devenir compliqué...

Ci-dessous un petit programme en 'C', utilisant la librairie Net-snmp pour extraire quelques valeurs de la dskTable en SNMP version 2c.

Le programme fait une peu plus d'une centaine de lignes. On passe en paramètre la cible, la 'community string' et un index. Une session est ouverte vers la machine cible avec sa 'community string', un PDU est construit pour interroger 4 variables. Les 4 OIDs sont construites à partir de l'OID de base et un index passé en paramètre au programme. Le programme envoie la requête et reçoit la réponse. On suppose que la réponse reflète la requête et que l'on reçoit, dans l'ordre, les chaînes de caractères dskPath, dskDevice et les entiers dskUsed et dskTotal. Il ne reste plus qu'à les imprimer. Tous les problèmes possibles ne sont pas traités en profondeur mais il y a quand même un minimum de 'sanity checks'.

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>

#define DSK_PATH 2
#define DSK_DEVICE 3
#define DSK_TOTAL 6
#define DSK_USED 8
unsigned char oid_str[128];
char *oid_template = ".1.3.6.1.4.1.2021.9.1.%d.%d"; /* What, idx */

void build_part_req(netsnmp_pdu *pdu, int idx, int what)
 {
 oid oid_buf[128];
 size_t oid_len;

 sprintf(oid_str, oid_template, what, idx);
 if (!snmp_parse_oid(oid_str, oid_buf, &oid_len))
  {
  fprintf(stderr, "error snmp_parse_oid()\n");
  exit(-1);
  }
 snmp_add_null_var(pdu, oid_buf, oid_len);
 }
netsnmp_pdu *build_req(int idx)
 {
 netsnmp_pdu *pdu;

 pdu = snmp_pdu_create(SNMP_MSG_GET);

 build_part_req(pdu, idx, DSK_PATH);
 build_part_req(pdu, idx, DSK_DEVICE);
 build_part_req(pdu, idx, DSK_USED);
 build_part_req(pdu, idx, DSK_TOTAL);
 return(pdu);
 }
void use_it(struct snmp_pdu *response)
 {
 char buf[1024];
 struct variable_list *vp;
 char dskPath[1024];
 char dskDevice[1024];
 long dskUsed;
 long dskTotal;

 if (response->errstat == SNMP_ERR_NOERROR)
  { /* vp->type (4=str; 2=int), vp->val.string, *vp->val.integer ; snprint_variable(), snprint_value() */
  vp = response->variables;
  strcpy(dskPath, vp->val.string); /* DSK_PATH */
  vp = vp->next_variable;
  strcpy(dskDevice, vp->val.string); /* DSK_DEVICE */
  vp = vp->next_variable;
  dskUsed = *vp->val.integer;  /* DSK_USED */
  vp = vp->next_variable;
  dskTotal = *vp->val.integer;  /* DSK_TOTAL */
  printf("%ld/%ld for %s on %s\n", dskUsed, dskTotal, dskPath, dskDevice);
  }
 else {
  printf("Uhm... : response->errstat = 0x%lx\n", response->errstat);
  }
 }
struct snmp_session *open_session(char *host, char *community)
 {
 struct snmp_session ss, *sp;

 init_snmp("get_snmp_fs");
 snmp_sess_init(&ss);
 ss.version = SNMP_VERSION_2c;
 ss.peername = strdup(host);
 ss.community = strdup(community);
 ss.community_len = strlen(community);
 if ((sp = snmp_open(&ss)) == NULL)
  {
  snmp_perror("snmp_open");
  exit(-1);
  }
 return(sp);
 }
void main (int argc, char **argv)
 {
 struct snmp_session *sp;
 netsnmp_pdu *req, *resp;
 char  *host, *community;
 int  index, status;

 if (argc != 4)
  {
  fprintf(stderr, "usage is '%s <host> <community> <index>\n", argv[0]);
  exit(-1);
  }
 host = argv[1];
 community = argv[2];
 index = atoi(argv[3]);

 sp = open_session(host, community);
 req = build_req(index);

 if ((status = snmp_synch_response(sp, req, &resp)) == STAT_SUCCESS)
  use_it(resp);
 else fprintf(stderr, "snmp_synch_response() returns %d\n", status);

 snmp_free_pdu(resp);
 snmp_close(sp);

 exit(0);
 }


Le Makefile, lui aussi, réduit à sa plus simple expression est :

CFLAGS=-I. `net-snmp-config --cflags`
BUILDLIBS=`net-snmp-config --libs`

get_snmp_fs: get_snmp_fs.o
 gcc -o get_snmp_fs get_snmp_fs.o $(BUILDLIBS)




Et maintenant, le test...



On regarde d'abord un disque avec df(1). On retrouve son indice en faisant un snmpwalk(1). On peut retrouver plusieurs valeurs avec snmpget(1) et les comparer avec ce que le programme ci-dessus donne.

$ df -h |grep sda1
/dev/sda1       916G  159G  711G  19% /


$ snmpwalk localhost -Os -m /usr/share/snmp/mibs/UCD-SNMP-MIB.txt -v 2c -c public  dskPath
dskPath.1 = STRING: /
dskPath.2 = STRING: /var
dskPath.3 = STRING: /
dskPath.4 = STRING: /run
dskPath.5 = STRING: /dev/shm
dskPath.6 = STRING: /run/lock
dskPath.7 = STRING: /sys/fs/cgroup


$ snmpget localhost -Os -m /usr/share/snmp/mibs/UCD-SNMP-MIB.txt -v 2c -c public  dskPath.1 dskDevice.1 dskUsed.1 dskTotal.1
dskPath.1 = STRING: /
dskDevice.1 = STRING: /dev/sda1
dskUsed.1 = INTEGER: 166654692
dskTotal.1 = INTEGER: 960379920


$ ./get_snmp_fs localhost public 1
166654708/960379920 for / on /dev/sda1


Les machines actuelles sont tellement rapides qu'il est difficile de mesurer la différence entre un programme ne faisant qu'un échange de PDUs et un programme qui doit, en plus, analyser une MIB (sinon plusieurs)... J'avais été motivé par le fait que Nagios utilise souvent des scripts shell lançant des tas de processus pour exécuter des fonctions élémentaires.

vendredi 17 avril 2020

De grand matin...

Activité aéroportuaire, ce jeudi 16 avril 2020, entre 04h13 et 05h13 captée avec un stick RTL-SDR (NewGen/RTL2832SDR alu TCXO .5 ppm) et dump1090 de la Citadelle de Liège... De la Citadelle de Liège, on capte l'ADS-B jusque Nancy.

jeudi 2 janvier 2020

SigInt ISM433

Petite captation sur la bande ISM 433 MHz avec un stick RTL-SDR v3, centrée sur 433.500 MHz avec 2048000 échantillons par seconde.

En regardant avec SDRAngel (ou sdrangelove)
En regardant avec gnuradio, on remarque de courtes trames centrées vers 433.922 MHz.
En important les données dans GNUoctave et en s'intéressant à la magnitude du signal, on observe :
En regardant de plus près, cela ressemble à du codage Manchester modulé en amplitude (ASK/OOK). Ce qui ne semble pas très économe en bande passante(?).
En s'intéressant à quelques bits seulement, on devrait pouvoir déduire le 'baud rate'...
En principe, on a tout pour créer un système pour décoder les trames avec gnuradio-companion...

Avec GnuRadio

(je ne maîtrise pas GnuRadio...) La première chose à faire est d'isoler le signal intéressant avec le 'Frequency Xlating FIR filter bloc'.
En envoyant ça dans 'Complex to Mag', puis dans un fichier qu'on analyse avec Octave, on obtient :


Autre signal, un FSK très soutenu

Si on s'intéresse au signal en FSK un peu à gauche, avec le bloc, 'Quadrature demod', le signal ressemble à :

dimanche 1 décembre 2019

BeSt - XSLT - mySQL

Il est possible de télécharger des listes de rues et de communes belges sur le site Open Data de BoSa (SPF Stratégie et Appui) : best-full-latest.zip (BeSt = Belgian Street).

Ce .zip contient des fichiers XML avec diverses informations concernant les rues et les communes de Belgique. C'est la référence officielle. Ces données sont aussi accessibles via un webservice (REST).

En ne prenant que la Wallonie, on a un peu plus que 61.000 rues et 263 communes. Ce qui va permettre de jouer un peu avec SQL.
J'ignore quelle est la meilleure manière d'injecter ces données XML dans une base de données mySQL. Alors, je le fais à ma manière... J'utilise un fichier de transformation XSL pour extraire les données qui m'intéressent et les mettre sous une forme facilement injectable dans une DB mySQL. Je crée une nouvelle base de données, best_street, Je crée deux tables (et comme je n'y connais rien, je leur donne de mauvais noms... : street_table et municipality_table.
$ mysql -u root -p
CREATE DATABASE best_street;
GRANT select, show view, lock tables, reload, replication client on *.* to 'bibi'@'localhost';
USE best_street;
CREATE TABLE street_table(
          street_id INT,
          street_name VARCHAR(100),
          municipality_id INT,
          PRIMARY KEY (street_id));
CREATE TABLE municipality_table(
          municipality_id INT,
          municipality_name VARCHAR(100),
          PRIMARY KEY (municipality_id));


Je récupère deux fichiers dans le .zip : WalloniaStreetname20191018.xml et WalloniaMunicipality20191018.xml. Ils contiennent des trucs du genre (attention, les fichiers des rues sont très grands, Firefox s'engorge) :

et


Que l'on peut traiter (sous Linux) avec
$ xsltproc x.xls y.xml > z.sql
Les fichiers .xsl étant, pour les rues,
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
 xmlns:tns="http://fsb.belgium.be/mappingservices/FullDownload/v1_00"
 xmlns:com="http://vocab.belgif.be/ns/inspire/"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
INSERT INTO street_table VALUES
      <xsl:for-each select="tns:StreetResponseBySource/tns:Streetname">
   (<xsl:value-of select="com:streetnameCode/com:objectIdentifier"/>,"<xsl:value-of select="com:streetname/com:spelling"/>",<xsl:value-of select="com:isAssignedBy/com:Municipality/com:objectIdentifier"/>),</xsl:for-each>
</xsl:template>

</xsl:stylesheet> 

Et pour les communes,
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
        xmlns:tns="http://fsb.belgium.be/mappingservices/FullDownload/v1_00"
        xmlns:com="http://vocab.belgif.be/ns/inspire/"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
INSERT INTO municipality_table VALUES
      <xsl:for-each select="tns:MunResponseBySource/tns:Municipality">
          (<xsl:value-of select="com:municipalityCode/com:objectIdentifier"/>,"<xsl:value-of select="com:municipalityName/com:spelling"/>"),</xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Le résultat n'est pas parfait et quelques adaptations cosmétiques sont nécessaires pour en faire de bons fichiers SQL. Il y a aussi des 'streetname' qui contiennent des '"' et le .xsl est trop primitif pour en tenir compte. Et, en fait, ce sont des données incorrectes. Cela donne quelque chose comme :
INSERT INTO street_table VALUES
   (7700015,"Chemin des Prés",25005),
   (7700016,"Chemin des Ramiers",25005),
   (7700017,"Chemin des Soeurs",25005),
   (7700018,"Chemin du Grand Champ",25005),
   (7700019,"Chemin du Petit Brou",25005),
...
   (7741188,"Rue Jean-Louis Dumont",64074),
   (7741189,"Rue Joseph Nicolas",64074),
   (7741190,"Rue Joseph Noville",64074),
   (7741254,"Rue des Bâtis",64075),
   (7736185,"Tribomont",63035),
   (7736549,"Route de Manhay",63045);

et
INSERT INTO municipality_table VALUES
   (52074,"Aiseau-Presles"),
   (61003,"Amay"),
   (63001,"Amel"),
   (92003,"Andenne"),
   (56001,"Anderlues"),
   (91005,"Anhée"),
...
   (25110,"Waterloo"),
   (25112,"Wavre"),
   (63084,"Welkenraedt"),
   (84075,"Wellin"),
   (91141,"Yvoir");

Il faut aussi noter qu'ils n'ont pas été fichus de donner des street_id distincts couvrant les trois régions. Il y a des duplicates du côté des 36000 et 37000 (!).
Il suffit maintenant d'injecter ces données dans notre DB :
SOURCE insert_streets.sql;
SOURCE insert_municipality.sql;

Notre DB est prête, on peut commencer à jouer avec...
mysql> SELECT * FROM street_table LIMIT 5;
+-----------+------------------------+-----------------+
| street_id | street_name            | municipality_id |
+-----------+------------------------+-----------------+
|   7700001 | Aux Tiennes            |           25005 |
|   7700002 | Avenue des Bouleaux    |           25005 |
|   7700003 | Avenue des Cerisiers   |           25005 |
|   7700004 | Avenue des Combattants |           25005 |
|   7700005 | Avenue des Pruniers    |           25005 |
+-----------+------------------------+-----------------+
5 rows in set (0.00 sec)

mysql> SELECT * FROM municipality_table ORDER BY municipality_name DESC LIMIT 5;
+-----------------+-------------------+
| municipality_id | municipality_name |
+-----------------+-------------------+
|           91141 | Yvoir             |
|           84075 | Wellin            |
|           63084 | Welkenraedt       |
|           25112 | Wavre             |
|           25110 | Waterloo          |
+-----------------+-------------------+
5 rows in set (0.00 sec)

mysql> 
Par exemple, je peux avoir la liste de commune ayant le moins ou le plus de rues :
mysql> SELECT count(*), m.municipality_name from street_table s LEFT JOIN municipality_table m USING (municipality_id) GROUP BY municipality_id ORDER BY count(*) LIMIT 10;
+----------+-------------------+
| count(*) | municipality_name |
+----------+-------------------+
|       54 | Martelange        |
|       58 | Hélécine          |
|       61 | Stoumont          |
|       62 | Gouvy             |
|       63 | Lincent           |
|       64 | Flobecq           |
|       65 | Verlaine          |
|       65 | Rumes             |
|       66 | Donceel           |
|       66 | Oreye             |
+----------+-------------------+
10 rows in set (0.38 sec)

mysql> SELECT count(*), m.municipality_name from street_table s LEFT JOIN municipality_table m USING (municipality_id) GROUP BY municipality_id ORDER BY count(*) DESC LIMIT 10;
+----------+----------------------------+
| count(*) | municipality_name          |
+----------+----------------------------+
|     2117 | Charleroi                  |
|     1998 | Liège                      |
|     1730 | Namur                      |
|     1280 | Mons                       |
|     1086 | Tournai                    |
|      923 | La Louvière                |
|      812 | Ottignies-Louvain-la-Neuve |
|      738 | Wavre                      |
|      677 | Mouscron                   |
|      649 | Ath                        |
+----------+----------------------------+
10 rows in set (0.10 sec)

mysql> 

Cette dernière requête étant :
SELECT count(*), m.municipality_name from street_table s
            LEFT JOIN municipality_table m USING (municipality_id)
            GROUP BY municipality_id
            ORDER BY count(*)
            DESC LIMIT 10;



lundi 18 novembre 2019

pps

Je me demandais à quel point les signaux PPS de deux modules GPS à quelques euros étaient synchronisé...
Trigger sur le channel-1. Il y a du jitter sur l'autre trace.
La réception n'est pas idéale à l'endroit où j'ai effectué la mesure, sur une terrasse entre les maisons. Trigger sur le channel-2. Idem.
Un autre point à noter est que les modules semblent interférer entre eux. J'ai utilisé deux alimentations différentes et j'ai dû en démarrer un avant l'autre... Une question intéressante serait de savoir si le jitter diminue quand on a une meilleure réception avec plus de satellites visibles en même temps.