Le jambon à 50 $: une balise WSPR simple

J’étais en train de bavarder récemment avec quelqu’un et cela m’a surpris qu’elle ait une licence de radio amateur. Je suppose que cela n’aurait pas dû être aussi surprenant; après tout, obtenir une licence de radio amateur est un rite de passage assez courant dans la vie d’un pirate informatique. Je suppose que cela m’a surpris parce qu’elle ne l’avait jamais mentionné dans nos conversations passées, et en en parlant, j’ai appris pourquoi. «J’ai obtenu mon permis parce que je pensais que la radio amateur consistait à construire des radios», a-t-elle déclaré. « Mais ce n’est pas. »

À bien des égards, elle a raison sur l’état de la radio amateur. Il fut un temps où la fabrication de son propre équipement était aussi centrale dans le passe-temps que la diffusion des ondes, et peut-être plus encore. Maintenant, cependant, avec des radios aussi bon marché que 30 $ et le matériel qui peut vous aider à atteindre la planète de manière triviale, la construction de vos propres radios a glissé de quelques crans. Mais le homebrewing est loin d’être un art mort, et comme nous le verrons dans cet épisode de « The $ 50 Ham », une balise WSPR pour les groupes HF est en fait un moyen amusant et simple – et bon marché – pour les amateurs de homebrew. un avant-goût de ce que signifie construire votre propre émetteur.

Une approche minimaliste

Dans le dernier versement de 50 $ de Ham, j’ai expliqué comment le mode de propagation du signal faible, ou WSPR, est utilisé pour explorer les conditions de propagation à travers le monde. Le concept est simple: un émetteur-récepteur connecté à un programme client WSPR, tel que celui intégré à WSJT-X, écoute les signaux modulés FSK qui sont transmis par d’autres stations. Les signaux à faible débit codent un message minimal – l’indicatif de la station émettrice, l’emplacement de la grille Maidenhead et la puissance d’émission – en un signal numérique qui prend près de deux minutes complètes à envoyer. La station de réception signale ensuite le message décodé à une base de données centrale WSPR, qui garde la trace des contacts et peut afficher une carte des chemins entre les stations.

Du côté de la réception, l’essentiel de la magie de WSPR réside dans le logiciel, en particulier dans le traitement du signal numérique qui extrait les données du signal souvent faible et dégradé. Mais le côté transmetteur est une autre histoire; là, le logiciel nécessaire pour encoder le message minimal est assez simple, si simple qu’il ne faut pas beaucoup plus qu’un microcontrôleur pour faire le travail. En réalité, il suffit d’un oscillateur capable de générer un signal à une fréquence fixe et de faire varier cette fréquence sous contrôle logiciel pour coder le message.

Il existe de nombreuses façons de procéder, notamment en utilisant les broches GPIO sur un Raspberry Pi pour générer directement le signal RF. Dans ce cas, cependant, j’ai décidé de suivre l’exemple de beaucoup d’autres jambons et d’utiliser une carte de dérivation de générateur d’horloge Si5351 et un Arduino Nano. La carte du générateur d’horloge comporte un oscillateur à trois canaux contrôlé par PLL qui parle I2C et dispose d’une bibliothèque bien prise en charge, ce qui facilite la mise en œuvre d’un oscillateur simple pour à peu près n’importe quelle bande.

J’ai décidé de fabriquer ma balise WSPR pour la bande de 20 mètres, sans aucune raison autre que j’ai toujours eu de la chance en établissant des contacts WSPR sur cette bande pendant la journée, c’est-à-dire lorsque je passe le plus de temps dans ma cabane. J’ai également décidé que pour au moins mon premier passage à ce projet, je supprimerais toutes les cloches et sifflets qui sont si faciles à ajouter à un projet Arduino. Les transmissions WSPR doivent être soigneusement synchronisées pour démarrer au début de chaque minute paire, de sorte que beaucoup de ces projets incluent des élaborations telles qu’un récepteur GPS ou un client NTP pour s’occuper de la synchronisation. J’ai pensé qu’il serait beaucoup plus rapide et plus facile pour moi de simplement regarder l’horloge et d’appuyer sur un bouton pour démarrer le cycle de transmission WSPR au bon moment.

À cette fin, j’ai recherché des «émetteurs WSPR minimaux» et j’ai trouvé un certain nombre de modèles qui fonctionneraient pour moi, y compris celui de Peter B. Marks. Il a adapté le code des exemples de Jason Milldrum (NT7S) dans son excellente bibliothèque Etherkit pour le Si5351 – nous empruntons tous les uns aux autres. Mon seul ajout au code est la prise en charge d’un bouton pour lancer l’émetteur. Le code prend simplement mon indicatif, le carré de la grille et la puissance de transmission, l’encode dans un message WSPR et dit au Si5351 d’envoyer la séquence de quatre tonalités FSK différentes qui composent le message de 162 symboles.


/*
* Minimal WSPR beacon using Si5351Arduino library
*
* Based on code:
* Copyright (C) 2015 - 2016 Jason Milldrum
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* ;
*/
#include "Arduino.h"
#include "si5351.h"
#include "Wire.h"
#define TONE_SPACING 146          // ~1.46 Hz
#define WSPR_CTC 10672            // CTC value for WSPR
#define SYMBOL_COUNT WSPR_SYMBOL_COUNT
#define CORRECTION 94674          // Determined experimentally -- parts per billion?
#define INPUT_PIN 7               // pushbutton
#define TX_LED_PIN 13

Si5351 si5351;
JTEncode jtencode;

unsigned long freq = 14097100UL;  // Transmit frequency
char call[7] = "N7DPM";           // Callsign
char loc[5] = "DN17";             // Grid square
uint8_t dbm = 10;                 // Transmit power
uint8_t tx_buffer[SYMBOL_COUNT];
int val = 0;

// Global variables used in ISRs
volatile bool proceed = false;

// Timer interrupt vector. This toggles the variable we use to gate
// each column of output to ensure accurate timing. Called whenever
// Timer1 hits the count set below in setup().
ISR(TIMER1_COMPA_vect)
{
    proceed = true;
    // Serial.println("timer fired");
}

// Loop through the string, transmitting one character at a time.
void encode()
{
    uint8_t i;
    Serial.println("encode()");
    jtencode.wspr_encode(call, loc, dbm, tx_buffer);

    // Reset the tone to 0 and turn on the output
    si5351.set_clock_pwr(SI5351_CLK0, 1);
    digitalWrite(TX_LED_PIN, HIGH);

    // Now do the rest of the message
    for (i = 0; i < SYMBOL_COUNT; i++)
    {
        uint64_t frequency = (freq * 100) + (tx_buffer[i] * TONE_SPACING);
        si5351.set_freq(frequency, SI5351_CLK0);
        Serial.print("freq = ");
        Serial.println(tx_buffer[i]);
        proceed = false;
        while (!proceed);
    }
    Serial.println("message done");
    // Turn off the output
    si5351.set_clock_pwr(SI5351_CLK0, 0);
    digitalWrite(TX_LED_PIN, LOW);
}

void setup()
{
    Serial.begin(115200);
    Serial.println("setup");

    // Use the Arduino's on-board LED as a keying indicator.
    pinMode(TX_LED_PIN, OUTPUT);
    digitalWrite(TX_LED_PIN, LOW);

    // Initialize the Si5351
    // Change the 2nd parameter in init if using a ref osc other
    // than 25 MHz
    si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, CORRECTION);

    // Set CLK0 output
    si5351.set_freq(freq * 100, SI5351_CLK0);
    si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Set for max power
    si5351.set_clock_pwr(SI5351_CLK0, 0);                 // Disable the clock initially

    // Set up Timer1 for interrupts every symbol period.
    noInterrupts(); // Turn off interrupts.
    TCCR1A = 0;     // Set entire TCCR1A register to 0; disconnects
    // interrupt output pins, sets normal waveform
    // mode. We're just using Timer1 as a counter.
    TCNT1 = 0;                     // Initialize counter value to 0.
    TCCR1B = (1 << CS12) | // Set CS12 and CS10 bit to set prescale
             (1 << CS10) | // to /1024
             (1 << WGM12); // turn on CTC
    // which gives, 64 us ticks
    TIMSK1 = (1 << OCIE1A); // Enable timer compare interrupt.
    OCR1A = WSPR_CTC;               // Set up interrupt trigger count;
    interrupts();                   // Re-enable interrupts.

    pinMode(INPUT_PIN, INPUT);
}

// wait for button press at the top of any even-numbered minute
void loop()
{
    val = digitalRead(INPUT_PIN);
    if (val == LOW)
    {
        encode(); // transmit once and stop
    }
}

Nettoyage du signal

Comme tout bon jambon, j’ai testé mon petit émetteur avant de le mettre en ondes. La simple charge factice que j’ai construite vers le début de cette série est venue en main pour cela, puisque j’ai pu la brancher directement au connecteur SMA sur la carte de dérivation. J’ai connecté mon oscilloscope à la sortie et j’ai déclenché le code. Le Si5351 est censé générer une onde carrée; il a fini par ressembler plus à une onde en dents de scie, mais de toute façon, le signal était chargé d’harmoniques et aurait besoin d’être nettoyé avant de passer à l’antenne.

Nettoyage des harmoniques du Si5351. La trace jaune est la sortie brute de la carte de développement; la trace verte est émise par le filtre passe-bas.

Heureusement, les filtres passe-bas qui prennent en charge cet aspect important de l’hygiène spectrale sont assez simples. Vous pouvez les acheter, mais il s’agit de homebrewing, alors j’ai créé une vidéo de Charlie Morris (ZL2CTM) sur la conception du filtre, parcouru ses calculs et proposé des valeurs pour les condensateurs et les inducteurs nécessaires pour un filtre qui coupe tout. au-dessus d’environ 14,2 MHz. J’ai utilisé cette calculatrice toroïdale pour comprendre comment enrouler les bobines, tout soudé sur un morceau de PCB dans lequel des tampons avaient été coupés à l’aide d’un embout de coupe-fiche bon marché de Harbor Freight, et l’ai testé à l’aide de mon analyseur de spectre NanoSA.

N’ayant jamais construit un filtre comme celui-ci auparavant, j’ai été surpris de voir à quel point il nettoyait les harmoniques. La forme d’onde sur l’oscilloscope était une belle onde sinusoïdale douce, et l’analyseur de spectre a montré une diminution marquée des harmoniques. La deuxième harmonique, qui à 42 MHz se situe bien dans la bande VHF, a été atténuée de 35 dBm. C’est exactement le genre de faux qu’un jambon responsable ne voudrait pas vomir, alors je suis content d’avoir construit le filtre.

Sur les ondes – en quelque sorte

Cela ne ressemble pas beaucoup à un émetteur, mais je suis en ondes.

Une fois que j’étais sûr que mon petit émetteur émettait un signal propre, j’ai vérifié pour m’assurer qu’il émettait un signal à la fois sur la fréquence et correctement codé. La carte de développement Si5351 n’est pas exactement une source de signal de qualité laboratoire – bien qu’elle conserve assez bien la fréquence définie, elle peut ou non émettre la fréquence programmée. La carte doit donc être calibrée, ce qui est normalement une simple question de peaufiner un facteur de correction dans le code tout en surveillant la sortie sur un compteur de fréquence. Malheureusement, il n’y a pas encore de «NanoFrequencyCounter» dans ma petite suite de tests, alors j’ai dû faire preuve de créativité.

Mon approche consistait à régler mon appareil HF sur la fréquence souhaitée de l’émetteur WSPR – 14,097100 MHz – et à ajuster lentement la fréquence de l’émetteur tout en transmettant dans une charge fictive. Cela produit une fréquence de battement audible qui disparaît pratiquement lorsque les deux fréquences correspondent. Je n’ai pas pu éliminer complètement la fréquence de battement, mais je l’ai descendue à quelques Hertz, ce que j’ai considéré comme assez proche.

J’ai ensuite vérifié un signal décodable en lançant WSJT-X et en «diffusant» sur mon appareil HF. Même avec la charge factice connectée, j’obtenais un signal très fort sur l’écran de la cascade et je pouvais clairement voir le signal modulé FSK. Et j’ai été très heureux de voir que WSJT-X décodait proprement mon message.

Décoder mon propre signal, pour m’assurer que tout fonctionne. La portée n’était que de quelques mètres et la puissance n’était que de 13 mW, mais cela fonctionnait!

Plus de chance la prochaine fois

Encouragé par ces succès, et sachant que beaucoup de gens ont établi des contacts transcontinentaux WSPR avec moins de puissance que les 13 mW que ma petite balise émettait, j’ai essayé de passer à l’antenne pour de vrai. J’ai accroché la balise à mon antenne demi-onde alimentée par l’extrémité et j’ai appuyé sur le bouton d’envoi à l’heure convenue. Malheureusement, je n’ai jamais pu obtenir aucune autre station pour décoder mon signal. J’ai essayé des dizaines, peut-être des centaines de fois au cours de la dernière semaine, mais je ne semble pas m’en sortir.

Je sais que mon signal est correctement codé, et je sais que je suis sur la fréquence, donc je suis à peu près sûr que le problème vient de mon antenne ou de mon signal de faible puissance. Compte tenu de la nature de cette série, je suis plus enclin à aborder cette dernière avec une simple construction d’amplificateur de puissance. J’ai quelques conceptions en tête pour cela et j’ai commandé des pièces.Nous allons donc les examiner dans le prochain épisode et voir si je peux débloquer cette réalisation en particulier.

François Zipponi
Je suis François Zipponi, éditorialiste pour le site 10-raisons.fr. J'ai commencé ma carrière de journaliste en 2004, et j'ai travaillé pour plusieurs médias français, dont le Monde et Libération. En 2016, j'ai rejoint 10-raisons.fr, un site innovant proposant des articles sous la forme « 10 raisons de... ». En tant qu'éditorialiste, je me suis engagé à fournir un contenu original et pertinent, abordant des sujets variés tels que la politique, l'économie, les sciences, l'histoire, etc. Je m'efforce de toujours traiter les sujets de façon objective et impartiale. Mes articles sont régulièrement partagés sur les réseaux sociaux et j'interviens dans des conférences et des tables rondes autour des thèmes abordés sur 10-raisons.fr.