mercredi 4 décembre 2013

Signal bi-horaire

Le basculement heures pleines/heures creuses des compteurs électriques bi-horaire se fait par des commandes envoyées par courants porteurs en ligne (CPL). Le gestionnaire du réseau envoie des 'messages', sur le réseau, entre 22 et 23 heures et entre 6 et 7 heures du matin. Ce n'est pas simplement une impulsion, ce sont de véritables messages. Pour éviter les à-coups, toutes les installations ne basculent pas en même temps et d'autres messages circulent sur le réseau. En France, c'est assez bien documenté (par exemple, ce document.pdf donne les spécifications d'un récepteur 175 Hz à 40 ordres de télécommande). On peut donc trouver des montages à micro-contrôleur comme celui-ci qui détecte le signal Pulsadis prévenant d'un jour 'rouge' du plan tarifaire EJP (Effacement Jour de Pointe). En Belgique, cela ne semble pas être le cas (voir, par exemple, ici). Tout au plus, apprend-on sur rundsteuerung.de qu'à Liège, les messages passent sur 283.3 Hz. Il serait amusant d'adapter le montage pour détecter le 283.3 Hz local... Mais bon, c'est un peu compliqué, il y a de l'électronique, il faut programmer un micro-contrôleur (utiliser un Arduino, par exemple), ça touche au 220, c'est difficile à tester,... Aussi, quelqu'un m'a suggéré d'utiliser la carte son du PC pour simplifier le problème. Et, tant qu'à faire, pourquoi ne pas capter le 50 Hz ambiant plutôt que de mettre ses doigts dans la prise... Un peu d'électronique pour amplifier le signal, un petit programme en 'C' et le tour est joué. En fait, c'est beaucoup plus simple que ça. Il suffit de raccorder un simple fil volant sur la prise micro du PC et d'utiliser un utilitaire comme 'arecord(1)' sous Linux pour se retrouver avec un fichier binaire contenant le signal brut en 16 bits à une fréquence d'échantillonnage quelconque. Du coup, cela devient très facile à tester. Il suffit d'enregistrer pendant l'heure à laquelle les signaux sont censés passer et on peut analyser les données à loisir. Comme on se propose de faire une détection synchrone, il semble logique de prendre un multiple de 283.3 Hz comme fréquence d'échantillonnage. Pourquoi pas 28330 échantillons par seconde?... Ce qui se traduit par la commande :
$ arecord -f S16_LE -t raw -r 28330 > enregistrement.raw
Un petit programme (sdump.c) de quelques lignes transforme le binaire en texte pour pouvoir utiliser Gnuplot et voir à quoi cela ressemble.
#include <stdio.h<
main()  /* sdump.c */
    {
    short    buf[1024];   
    int    i, len;

    while ((len = read(0, buf, sizeof(buf))) > 0)
        {
        len /= 2;
        for (i = 0; i < len; i++)
            printf("%d\n", buf[i]);
        }
    }
Utilisons aussi 'dd(1)' pour aller chercher, disons, un dixième de seconde de mesure :
dd if=enregistrement.raw bs=2 count=2833 skip=10 | ./sdump > sig.txt
Ce n'est pas vraiment une belle sinusoïde mais il y a une bonne dynamique (le volume a été réglé via l'application 'enregistreur de sons'). On notera que le zéro n'est pas à zéro. C'est dû au fait que le fil est flottant... Il faudra faire avec. Va-t-on pouvoir détecter du 283.3 Hz là-dedans? Le montage français utilise une détection synchrone, bonne idée! Faisons de même. Le principe est d'échantillonner au double de la fréquence à analyser, les échantillons sont en opposition, on en fait la différence. On fait la somme de ces différences sur quelques mesures et, en principe, le signal recherché s'accumule tandis que le reste (le 'bruit') s'annule. En fait, on va s'arranger pour que cela s'annule en intégrant sur un nombre de périodes entières de 50 Hz. Dans le cas présent, il y a 283.3/50 = 5.666 périodes de 283.3 Hz pendant une période de 50 Hz, intégrons sur 56 périodes (5600 échantillons). Comme il est possible que l'on échantillonne le 283.3 Hz aux mauvais endroits (aux nœuds plutôt qu'aux extrêmes de la sinusoïde), on va aussi faire la somme des différences un 'quart de tour plus loin' et faire la somme des deux. En 'C', cela donne :
#include <stdio.h>

main()
    {
    short buf[5600];
    int    i, j, s1, s2;

    while (read(0, buf, sizeof(buf)) == sizeof(buf))
        {
        s1 = s2 = 0;
        for (i = 0; i < 56; i++)
            {
            j = i*100;
            s1 += buf[j];
            s1 -= buf[j+50];
            s2 += buf[j+25];
            s2 -= buf[j+75];
            }
        if (s1 < 0) s1 = -s1;
        if (s2 < 0) s2 = -s2;

        printf("%d\n", s1 + s2);
        }
    }
Pas très compliqué et pourtant, déjà efficace. ./dsync < enregistrement.raw > detected.txt, sur 85 minutes, donne :
En faisant une analyse spectrale avec GNU Octave (plotspec() dans une zone où on détecte de 283.3 Hz avec Ts=1/28330), on retrouve bien du 283.3 Hz. C'est assez fable mais bien présent. L'étalement est dû à la modulation du 283.3 Hz par le message binaire. On remarquera également que les 'crasses' dans la sinusoïde sont essentiellement des harmoniques du 50 Hz. La transformée de Fourier est vraiment magique : je lui passe une masse de mesures sorties tout droit de la carte son en précisant que j'ai pris une mesure toutes les 1/28330-ièmes de seconde et paf!, elle me dit que le signal dominant est à 50 Hz avec des pics tous les multiples de 50 (et aussi une importante composante continue).
Reste maintenant à affiner la détection pour capter les messages. Une idée qui vient à l'esprit est de synchroniser la détection du 283.3 Hz avec le 50 Hz : voir, en quelque sorte, si le signal est présent ou non pour chaque période de 50 Hz. Dans un premier temps, sans tenir compte de la phase; juste s'assurer de la périodicité de la détection. Le programme se complique un peu, mais pas beaucoup (j'en ai profité pour faire plus de sommes d'oppositions...). Quelque chose comme :
#include <stdio.h>

main()
    {
    short buf[1133];
    int    i, j, k, s[10], sum;
    int    bias = 0;

    while (read(0, buf, sizeof(buf)) == sizeof(buf))
        {
        for (k=0; k < 10; k++)
            s[k] = 0;
        for (i = 0; i < 11; i++) /* was 5 */
            {
            for (k=0; k < 10; k++)
                {
                j = i*100 + k*5;
                s[k] += buf[j]; 
                s[k] -= buf[j+50];
                }
            }
        sum = 0;
        for (k=0; k < 10; k++)
            {
            if (s[k] < 0) sum -= s[k];
            else sum += s[k];
            }
        printf("%d\n", sum);
        bias += 2;    /* was 6 */
        if (bias >= 10)
            {  /* 50 Hz sync  28330 sps -> 566.6 samples/period;  */
            bias -= 10;
            read(0, buf, 2);
            }
        }
    }
Comme on n'a pas de PGCD entre 28330 et 50, il faut additionner les parties fractionnaires et 'rattraper' quand on passe une unité. Les messages apparaissent déjà beaucoup plus distinctement. Avant, quand je zoomais, il devenait difficile de distinguer les différentes impulsions. Là, au moins, 'à vue', cela devient plus clair.
On y voit deux messages d'environ 16 secondes commençant par une impulsion d'une seconde et suivie par une série d'impulsions plus courtes. Reste à en améliorer la précision, analyser plusieurs messages pour voir quel est l'encodage. Cela n'a pas l'air d'être simplement oui/non toutes les secondes. Dans ce cas-ci, la détection est effectuée sur deux périodes de 50 Hz (environ 1132 échantillons). Dans le graphe en tête d'article, une seule période est utilisée. La vague provient d'une espèce de battement entre le 50 Hz et le 282.3 Hz quand les calculs ne sont pas faits précisément.

À suivre... (se synchroniser sur une phase précise du 50 Hz (?) et intégrer sur une période complète du 50 Hz)

L''antenne' qui capte le 50 Hz ambiant est un simple bout de fil raccordé au jack 'micro' :
Là, il est enroulé autour d'un câble 220 mais je ne pense pas que ce soit même nécessaire...