Archives par mot-clé : G0UPL

Essai comparatif MS5351M et Si5351A

Même s’il a encore environ 1000 pièces disponibles de Si5351A pour inclure dans les divers kits qu’il produit. Hans Summers G0UPL (http://qrp-labs NULL.com) est bien conscient du risque que pose la pénurie de composants électronique sur la disponibilité du très populaire Si5351A.
Beaucoup d’OM cite le MS5351M comme une alternative, mais sans avoir de preuve concrète de ses aptitudes. Hans a voulu en avoir le coeur net et a conduit une série de test comparatifs en terme de fonctionalité et de performance.
Les résultats sont disponibles en ligne sur le site de QRP Labs (http://qrp-labs NULL.com/synth/ms5351m NULL.html), mais la conclusion est que oui, le MS5351M peut constitute une alternative très acceptable comme circuit générateur de signal à PLL dans vos kits et constructions personnelles HF.

(http://qrp-labs NULL.com/synth/ms5351m NULL.html)

Nouveau Kit QRP par G0UPL: Le QCX-mini

C’est un chouette cadeau de Noël que Hans G0UPL vient de faire à la communauté amateur avec l’annonce de la disponibilité de son nouveau kit QRP le QCX-mini. Il est basé sur le QCX avec des caractéristiques similaires mais sans le côté ouvert et expérimental : monobande (au choix entre 80m et 17m, possible jusque 6m), 5W en transmission CW environ, un filtre 200Hz et une construction autour du Si5351A. Des modes balise et WSPR sont disponibles, une interface GPS disponible, le contrôle par CAT est possible et un kit amplificateur de puissance 50W existe pour 29.5$US (+20$US pour le boîtier).

La construction se veut simple avec un câblage réduit: tous les contacts et connecteurs sont soudés directement sur le circuit imprimé. Idéal pour le portable QRP et la randonnée radio: le poids est extrêmement faible à 202 grammes, la consommation de 58mA en réception et l’étage émetteur final est en classe E. Le prix est très abordable à 55 $US. Ceci ne comprend pas le boîtier en aluminium anodisé noir du plus bel effet qui est en option à 20$US.

Les commandes peuvent se faire sur cette page (http://shop NULL.qrp-labs NULL.com/qcxmini).

The QCX: A QRP transceiver kit by Hans G0UPL

Hans G0UPL a récemment publié une vidéo du QCX. C’est un kit facile à monter qui vous permet d’avoir un transceiver 5W mono-bande pour 49USD.

Ce transceiver permet de communiquer en telegraphie (CW) sur une bande au choix entre les bandes 80, 60, 40, 30, 20 or 17 mètres. Il utilise un Si5351A pour générer la fréquence du VFO et dispose de filtre 7 pôles avec une bande passante de 200Hz. La réception se fait par détection par quadrature (QSD). Le manipulateur électronique, les mémoires et les fonctions habituelles de transceiver modernes sont disponibles grâce à un micro-contrôleur ATMega328.

Plus de détails sur le site de QRP Labs. (http://qrp-labs NULL.com/qcx NULL.html)

Vidéo du QCX par G0UPL

Merci à K4SWL pour l’information. (http://qrper NULL.com/2017/10/videos-featuring-the-qcx-transceiver-kit/)

Nouveaux kits récepteurs SDR par Hans G0UPL

Récepteur SDR I/Q QRP Labs G0UPL (http://blog NULL.qscope NULL.org/wp-content/uploads/2016/08/rx NULL.jpg)Hans vient d’annoncer deux nouveaux kits originaux dans la lignée de ses célèbres balises QRSS ou WSPR Ultimate (http://blog NULL.qscope NULL.org/2014/09/18/nouveautes-chez-qrp-labs-g0upl/) qui ont fait retrouver à des milliers de radioamateurs le goût de la construction de kits.

Le premier est un kit récepteur SDR de type I/Q (détection QSD) qui peut servir à la réception des signaux faibles WSPR ou QRSS, mais pas seulement. Il est proposé à 25 USD avec un module filtre de bande aux choix entre 160m et 10m.

Module démodulateur polyphase QRP Labs G0UPL (http://blog NULL.qscope NULL.org/wp-content/uploads/2016/08/pic23 NULL.jpg)Le deuxième est un module optionnel de démodulateur polyphase pour le récepteur. Il permet de se passer d’une carte son stéréo pour la démodulation des signaux I/Q et produit un signal BLU en sortie.

Comme toujours les kits produits par Hans sont un excellent compromis prix / qualité et surtout proposent des solutions techniques originales. Ils sont ouverts à l’expérimentation et laissent la place pour la personnalisation.

Sproutie : La balise QRSS HiFer de Dave AA7EE

AA7EE carte ATTiny programmateur pour Arduino (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2015/11/AA7EE_programmer_ATTiny NULL.jpg)Il y a quelques temps, Dave AA7EE m’a contacté pour avoir ma version corrigée du code la balise QRSS de G0UPL/G0XAR (http://xv4y NULL.radioclub NULL.asia/2012/01/11/programmer-un-micro-controleur-atmel-attiny-avec-un-arduino/). Il l’a utilisé pour modifier sa propre balise et la placer sur la bande dite HiFer 13553-13567 KHz  autorisée aux USA sans licence. Lisez sur son blog le détail de son projet. Personellement moi j’adore la construction soignée de son programmateur de micro-contrôleur ATTiny.

Nouveautés chez QRP-Labs (G0UPL)

Synthetiseur Si5351 à OCXO QRP Labs G0UPL (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/09/Synthetiseur_OCXO_G0UPL NULL.jpg)L’annonce de ces nouveautés datent d’en fait il y a près d’une semaine mais je n’ai pas eu le temps d’en parler. Hans G0UPL va ajouter quelques produits à sa boutique dans les jours qui viennent.

  • Tout d’abord un boîtier en aluminium anodisé noir, pré-découpé pour le kit complet balise Ultimate III (http://www NULL.qrp-labs NULL.com/index NULL.php?route=product/product&product_id=77), avec la place pour toutes les options (filtre de bande, GPS…). Le prix de 20$ (plus port de 10$) est très correct.
  • Un module enfichable pour Arduino Uno qui reprend toutes les fonctionnalités du kit Ultimate III mais sans LCD et boutons (que vous pouvez ajouter vous-même). Cela permettra à ceux qui le veulent de développer eux-même leur logiciel de balise ou de générateur de fréquence en bénéficiant d’une plateforme matérielle performante et à prix raisonnable.
  • Prototype Synthetiseur OCXO G0UPL (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/09/Proto_Synthetiseur_OCXO_G0UPL NULL.jpg)Le plus important, c’est un kit synthétiseur de fréquence avec Si5351A et référence 10MHz à OCXO (http://www NULL.hanssummers NULL.com/ocxosynth NULL.html). Le but de ce kit est de remplacer avantageusement les modules chinois avec AD9850. Ceux-ci sont devenus impossibles à approvisionner et présentaient des limites en terme de performance et d’utilisation. Le nouveau kit devrait couvrir de 8 KHz à 160 MHz avec une stabilité de l’ordre de 0,1ppm à chaud, ce qui est excellent!

Le dernier kit est vraiment intéressant et pourra prendre la place dans les kits QRSS Ultimate III existant après une mise à jour du firmware. Avec le module Arduino, cela ouvre la possibilité de construire des générateurs de fréquence très stable jusque 160 MHz.

Un quatrième projet et quant à lui retardé, c’est celui d’un récepteur QRSS comme compagnon au kit balise. Hans cherchait une solution technique innovante mais ses tentatives autour de la démodulation méthode Weaver se sont avérées décevante.

Nouveau site QRP Labs par G0UPL

Kist balise QRP Labs Ultimate 3 (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/05/G0UPL_Utlimate3 NULL.jpg)Hans de G0UPL vient de lancer un nouveau site pour son activité de kits radio QRP (http://qrp-labs NULL.com/). Cette activité prenait de plus en plus de place sur son site personnel et il était judicieux de la rendre indépendante.

En pratique rien de nouveau avec les mêmes kits à succès pour réaliser vos balises QRSS ou WSPR synchronisées par GPS. La présentation est plus claire et la boutique optimisée.

Ma pomme dans CQ Magazine de Décembre – Kits balise WSPR

Sommaire CQ Décembre 2013 (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2013/11/2013_12_cq_toc NULL.jpg)Le numéro “Spéciale technique” de décembre de CQ Magazine (http://www NULL.cq-amateur-radio NULL.com/) contiendra un article que j’ai écrit à propos des micro-contrôleurs MSP430 et du LaunchPad de Texas Instruments. En fait j’ai proposé cet article il y a plus d’un an mais ils ne trouvaient pas la place adéquate dans leurs colonnes.

Pour continuer sur ma lancée, je leur ai aussi proposé un article détaillant comment fabriquer sa propre balise WSPR ou QRSS avec un DDS AD9850 et un LaunchPad MSP430. C’est en fait la balise que je proposais en kit (http://xv4y NULL.radioclub NULL.asia/2013/03/21/la-balise-wspr-de-g4jvf/) dont je vais publier les schémas et code source. En principe l’article est accepté pour une publication au premier semestre 2014, j’attends leurs demandes de corrections.

Il y a quelques jours Hans G0UPL a lancé son kit Ultimate QRSS beacon 3 (U3) (http://hanssummers NULL.com/ultimate3 NULL.html)qui a maintenant atteint un bon niveau de fonctionnalité et surtout de stabilité (les premières versions ont essuyées pas mal de bogues). Il propose en option les kits module GPS et platine de filtre de bande à commutation par relais. C’est aujourd’hui l’offre la plus intéressante car il en produit des volumes de plusieurs centaines d’exemplaires. Son seul inconvénient c’est que Hans refuse d’en ouvrir le code source et qu’il est donc impossible de faire vos propres adaptations…

Au passage je suis content de voir que mon travail a porté ses fruits et que Hans a intégré dans son codes des idées issues du mien comme la mise en veille du DDS par le bit W34 et le mode WSPR-15 (pour les VLF). Je suis sûr que le côté “reprogrammable” de mes kits l’a aussi poussé à diffuser les binaires de ses firmwares en ligne pour que les utilisateurs puissent faire les mises à jour, ce qu’il ne voulait pas faire au début.

Balise QRSS synchrone avec le LaunchPad/MSP430

Modifications de la balise QRSS QRP Labs pour WSPR - XV4Y (http://xv4y NULL.radioclub NULL.asia/2012/10/18/balise-wspr-autonome-avec-msp430-le-code-source/kit-wspr-mod-balise/)

[GTranslate]

Voici le code que j’ai écrit et qui permet de modifier l‘émetteur original de QRP Labs (http://xv4y NULL.radioclub NULL.asia/2010/08/16/kit-balise-qrss/) pour en faire une balise transmettant régulièrement toutes les minutes paires. Cela est nécessaire si vous voulez que votre transmission soit “superposable” dans les captures du grabber. En effet, de plus en plus d’OM effectuent des compositages de captures sur une base de 10 minutes. En effectuant un traitement de moyenne ou d’interpolation on peut ainsi faire sortir du bruit un signal qui n’était pas visible autrement. Ce traitement est similaire à celui fait en astronomie planétaire avec Lynkeos (http://lynkeos NULL.sourceforge NULL.net/french/index NULL.html) par exemple.

Un kit préparé avec votre indicatif permettant de modifier le circuit de la balise pour fonctionne avec ce programme (ou la version WSPR) est disponible dans la boutique (http://xv4y NULL.radioclub NULL.asia/boutique/?slug=product_info NULL.php&cPath=22&products_id=30).

Ce programme se compile avec Energia (http://energia NULL.nu/) pour un LaunchPad avec MSP430G2553. Il nécessite ma librairie sRTC (http://xv4y NULL.radioclub NULL.asia/2012/12/19/bibliotheque-rtc-pour-energia-version-1-02/).

/* QRSS beacon with 10 minutes frame for MSP430G2553
 * Code for Energia 008

 * By Yannick DEVOS - XV4Y - May-October 2012
    http://xv4y.radioclub.asia/

    Copyright 2012 Yannick DEVOS under GPL 3.0 license
    Any commercial use or inclusion in a kit is subject to author approval

====
 * Ouput on 2 bits PinA and PinB
 * PTT_key output allows to turn on/off the TX PA while sending
 * Mirror on LED1 and LED2 du LaunchPad for testing
 * Output to Serial for testing
 * Using an R-2R ladder it makes the G0UPL/G0XAR beacon frequency shift

====
Revision history :
v1.00    2012-10-26
         First release
v1.01    2012-12-01
         Cleaning and commenting
v1.02    2012-12-19
         Changes to support version 1.02 of RTC library
         Correction for better time accuracy with Serial output enabled

====
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 can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/>
*/

// Here under don't touch anything unless you know what you do

#include <legacymsp430.h>
#include <sRTC.h> // Library for RTC clock with MSP430

// Alphabet binary coding by G0UPL
// 0 = dot, 1 = dash
const int _A	=	0b11111001;
const int _B	=	0b11101000;
const int _C	=	0b11101010;
const int _D	=	0b11110100;
const int _E	=	0b11111100;
const int _F	=	0b11100010;
const int _G	=	0b11110110;
const int _H	=	0b11100000;
const int _I	=	0b11111000;
const int _J	=	0b11100111;
const int _K	=	0b11110101;
const int _L	=	0b11100100;
const int _M	=	0b11111011;
const int _N	=	0b11111010;
const int _O	=	0b11110111;
const int _P	=	0b11100110;
const int _Q	=	0b11101101;
const int _R	=	0b11110010;
const int _S	=	0b11110000;
const int _T	=	0b11111101;
const int _U	=	0b11110001;
const int _V	=	0b11100001;
const int _W	=	0b11110011;
const int _X	=	0b11101001;
const int _Y	=	0b11101011;
const int _Z	=	0b11101100;
const int _SPC	=       0b11101111;
const int _0	=	0b11011111;
const int _1	=	0b11001111;
const int _2	=	0b11000111;
const int _3	=	0b11000011;
const int _4	=	0b11000001;
const int _5	=	0b11000000;
const int _6	=	0b11010000;
const int _7	=	0b11011000;
const int _8	=	0b11011100;
const int _9	=	0b11011110;
const int _BRK	 =      0b11010010;
const int _WAIT  =	0b10000000;

byte begin_sec;
int begin_chunk;

// Déclaration et initilisation des variables
byte msgIndex = 1;
byte inc_bit = 8;
byte character = _SPC;
boolean start = false;

byte key = 0;
byte etat = 0;

// *** PARAMETERS
// Here modify to your taste or needs

#define PTT_key 8
#define wsprPinA 10
#define wsprPinB 9
#define LEDPinA 14
#define LEDPinB 2
#define StartBtn 5

const int msg[] = {5, _X, _V, _4, _Y, _WAIT};  // Format, is lenght in decimal and then the character constants including the _WAIT for the end of the sequence

int vitesse = 6;    // Number of seconds per dot (QRSS6 = 6)

// Be carefull, this is only for debugging.
// If you enable this in production, the clock will drift to much (5 seconds in 10 minutes)
#define SERIAL_OUT_INFO

// This is in case you use a MSP430 that does not have Hardwart UART. TimerSerial works great but bring a lot of clock drift. Replace each "Serial" object call in the code by mySerial.
/*
#ifdef SERIAL_OUT_INFO
#include <TimerSerial.h> 
TimerSerial mySerial;
#endif
*/

// *** END OF PARAMETERS

#ifdef SERIAL_OUT_INFO
#endif
RealTimeClock myClock;

//******************************************************************
// Defining pins mode
// Encoding the WSPR sequence

void setup() {
  pinMode(PTT_key, OUTPUT);
  pinMode(wsprPinA, OUTPUT);
  pinMode(wsprPinB, OUTPUT);
  pinMode(LEDPinA, OUTPUT);
  pinMode(LEDPinB, OUTPUT);
  pinMode(StartBtn, INPUT_PULLUP);

  myClock.begin();

  #ifdef SERIAL_OUT_INFO
  Serial.begin(9600);
  Serial.println("\n* MSP430 QRSS beacon"); 
  #endif

  digitalWrite( PTT_key, LOW );
  digitalWrite( wsprPinA, LOW );
  digitalWrite( wsprPinB, LOW );

  digitalWrite( LEDPinA, HIGH );
  digitalWrite( LEDPinB, HIGH );

  #ifdef SERIAL_OUT_INFO
  Serial.println("* Wait clock is set. Sending test sequence.");
  #endif
  begin_chunk = myClock.RTC_chunk;
  begin_sec = myClock.RTC_sec;
  while(digitalRead(StartBtn)) {
    // This is for blinking LED fast
    if ((myClock.RTC_chunk-(begin_chunk+15))%60==0)
      digitalWrite( LEDPinA, HIGH );
    if ((myClock.RTC_chunk-(begin_chunk+30))%60==0)
      digitalWrite( LEDPinB, HIGH );
    if ((myClock.RTC_chunk-(begin_chunk+45))%60==0)
      digitalWrite( LEDPinA, LOW );
    if ((myClock.RTC_chunk-(begin_chunk+60))%60==0)
      digitalWrite( LEDPinB, LOW );

    // We also send a test sequence to help calibrate the transmitter
    if ((myClock.RTC_chunk-(begin_chunk+0))%240==0) 
      send_test(begin_sec);

   };
  //randomSeed(myClock.RTC_chunk);
  myClock.Set_Time(0,0,0);
};

//******************************************************************
// Here starts the actual sequence sending

void loop() {
  #ifdef SERIAL_OUT_INFO
  Serial.print("Time:");
  Serial.print(myClock.RTC_min, DEC);
  Serial.print(":");
  Serial.print(myClock.RTC_sec, DEC);
  Serial.print(".");
  Serial.println(myClock.RTC_chunk, DEC);
  #endif

  digitalWrite( PTT_key, LOW );

  digitalWrite( wsprPinB, LOW );
  digitalWrite( LEDPinB, LOW );

  digitalWrite( wsprPinA, LOW );
  digitalWrite( LEDPinA, LOW );

  begin_chunk = myClock.RTC_chunk;

  #ifdef SERIAL_OUT_INFO
  Serial.println("* Waiting 1st sec of every 2 mins.");
  #endif

  while (!(myClock.RTC_sec==1 && (myClock.RTC_min%2)==0)) {  // We start each first second of even minutes
    if ((myClock.RTC_chunk-(begin_chunk+10))%20==0) digitalWrite( LEDPinA, HIGH );
    if ((myClock.RTC_chunk-(begin_chunk+20))%20==0) digitalWrite( LEDPinA, LOW );
  };

  #ifdef SERIAL_OUT_INFO
  Serial.println("\n* Sequence begin");
  #endif
  digitalWrite( PTT_key, HIGH );
  send_sequence();
  #ifdef SERIAL_OUT_INFO
  Serial.println("\n* End");
  #endif
};

//******************************************************************
// Function to send the test sequence
void send_test(char begin_sec) {
    if ((myClock.RTC_sec-(begin_sec+0))%30==0) {
      digitalWrite( PTT_key, LOW );
      digitalWrite( wsprPinA, LOW );
      digitalWrite( wsprPinB, LOW );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX Low, Send 00 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+6))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, LOW );
      digitalWrite( wsprPinB, LOW );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 00 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+12))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, HIGH );
      digitalWrite( wsprPinB, LOW );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 01 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+18))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, LOW );
      digitalWrite( wsprPinB, HIGH );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 10 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+24))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, HIGH );
      digitalWrite( wsprPinB, HIGH );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 11 *");
      #endif
    }

};

//******************************************************************
// Function to send the sequence
void send_sequence() {

  while (character != _WAIT) {
    msgIndex = 1;

    while (msgIndex < msg[0]+1) {
      // We read each character in the array
      character = msg[msgIndex];

      inc_bit = 8;

      // Special characters
      if (character == _SPC) {  // For inter-words spacing, we need 5 dots, since we already have 1+2, we need 2 more
         begin_sec = myClock.RTC_sec;
         #ifdef SERIAL_OUT_INFO
         Serial.println("\n* Inter word Space *");
         #endif
         while ( ((60+myClock.RTC_sec-begin_sec)%60)<(2*vitesse) ) {
            delay(0);
         };
         inc_bit = 0;
      };
      if (character == _WAIT) {  // For WAIT or end of sequence, we just skip sending
         #ifdef SERIAL_OUT_INFO
         Serial.println("\n* Enf of sequence *");
         #endif
         inc_bit = 0;
      };

      while (inc_bit) {

        // We read each bit of the character
        etat = bitRead(character,inc_bit-1);

        if (start) {
          if (etat) {
            key=3; // If the bit is 1 then it is a dash
            #ifdef SERIAL_OUT_INFO
            Serial.print("-");
            #endif
          } else {
            key=1; // If the bit is 0 then it is a dot
            #ifdef SERIAL_OUT_INFO
            Serial.print(".");
            #endif
          }

          while (key) {

            digitalWrite( wsprPinB, HIGH );
            digitalWrite( LEDPinB, HIGH );
            digitalWrite( wsprPinA, HIGH );
            digitalWrite( LEDPinA, HIGH );

            // We wait 1 second x VITESSE parameter
            begin_sec = myClock.RTC_sec;
            while ( ((60+myClock.RTC_sec-begin_sec)%60)<(1*vitesse) ) {
              delay(0);
            };

            key--;
          }

          digitalWrite( wsprPinB, LOW );
          digitalWrite( LEDPinB, LOW );
          digitalWrite( wsprPinA, LOW );
          digitalWrite( LEDPinA, LOW );
            // We wait 1 second x VITESSE parameter
          begin_sec = myClock.RTC_sec;
          while ( ((60+myClock.RTC_sec-begin_sec)%60)<(1*vitesse) ) {
            delay(0);
          };
        }

        if (!etat && !start) start=true;

        inc_bit--;

      }
      // We add one space between each characteur (lenght is 3 dots, including the one in the here above loop)
      begin_sec = myClock.RTC_sec;
      #ifdef SERIAL_OUT_INFO
      Serial.print(" ");
      #endif
      while ( ((60+myClock.RTC_sec-begin_sec)%60)<(2*vitesse) ) {
        delay(0);
      };

      start = false;
      msgIndex++;
    };
   };
   character = _SPC;
};

//******************************************************************
// Interruption for the RTC clock
interrupt(TIMER1_A0_VECTOR) Tic_Tac(void) {
  myClock.Inc_chunk();		      // Update chunks
};

Balise WSPR autonome avec MSP430 – le code source

[GTranslate]

Un kit pour ce circuit de générateur de balise WSPR sera bientôt disponible (http://xv4y NULL.radioclub NULL.asia/boutique/?slug=product_info NULL.php&products_id=30).

Ca m’a pris plus de temps que prévu pour publier le code source de mon générateur WSPR à MSP430 pour la balise QRSS de QRP Labs. Le programme “en production” est stable et fonctionne plusieurs jours d’affilée sans problèmes avec de bons reports (spots) sur WSPR. Le problème vient d’un bogue dans le mode debugging justement, les sorties sur port série sensées aidées à la mise au point ne fonctionne pas bien. Ce bogue résulte à la fois d’un problème de l’IDE Energia (au niveau du compilateur certainement) et d’une utilisation trop importante de la SRAM quand je compile le programme en incluant la librairie Serial. Difficile à résoudre, j’ai passé plusieurs jours à essayer d’optimiser les variables et l’utilisation de la RAM mais sans succès. Je vous livre donc le code “tel que” en sachant que le bogue n’est pas gênant en production.

Pour modifier votre balise QRSS en balise autonome WSPR rien de bien compliqué. Il faut construire un petit circuit selon le schéma ci-dessous. En utilisant un LaunchPad les circuits des LED, contacteurs et quartz sont déjà présents. Il faut y ajouter de quoi :

  • alimenter le MSP430G2553 du LaunchPad en 3,3V
  • créer une tension analogique à 4 niveaux grâce à une échelle R-2R à partir des sorties P2.1 et P2.2
  • alimenter en +5V l’étage PA de la balise uniquement quand c’est nécessaire grâce à un transistor BC548 (ou équivalent) connecté sur la sortie P2.0

Schéma générateur balise autonome WSPR à MSP430 (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/09/WSPR_balise_schéma NULL.png)

Sur le circuit de la balise par elle-même, rien de compliqué. Les modifications sont résumées sur le schéma suivant et consiste à :

  • Retirer le micro-contrôleur ATTiny8
  • Récupérer le +5V, la masse et le contrôle de la LED “varicap” sur le connecteur DIL8
  • Dessouder la patte +5V du potentiomètre de polarisation (bias) du transistor FET du PA pour la connecter sur le transistor BC548
  • Procéder à un nouvel alignement de la puissance de sortie du PA,
  • Procéder à un nouvel alignement de l’excursion en fréquence (shift) pour qu’elle soit au total de 6Hz
  • Procéder à un nouveau réglage de la fréquence pour être dans la bande WSPR (autour de 10 140 200 Hz sur 30 mètres)

Modifications de la balise QRSS QRP Labs pour WSPR - XV4Y (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/10/Kit-WSPR-Mod-balise NULL.jpg)

Pour compiler le code il vous faut bien entendu un LaunchPad avec un MSP430G2253 (http://xv4y NULL.radioclub NULL.asia/2012/04/27/les-micro-controleurs-msp430-de-texas-instruments/). L’environnement de développement Energia correctement installé sur votre micro-ordinateur. La bibliothèque RTC que j’ai développée (http://xv4y NULL.radioclub NULL.asia/2012/05/22/bibliotheque-rtc-pour-le-msp430/) doit aussi être installée dans le répertoire de vos documents Energia. Il vous faut ensuite modifier les paramètre d’indicatif (callsign), de carré Locator et de puissance. Ni la compilation ni le téléchargement ne doivent produire d’erreurs. Une fois le LaunchPad programmé, je vous conseille de placer en position ouverte tous les cavaliers (jumpers) reliant le circuit de programmation-émulation au micro-contrôleur.

L’utilisation du programme par lui même est simple. Vous le mettez sous tension, les deux LEDs rouges et jaunes du LaunchPad vont clignoter alternativement assez rapidement. Le générateur est mode “attente de synchro” et transmet une séquence de test durant au total 30 secondes permettant de vérifier les différentes fréquences symboles et l’extinction du PA.

Ensuite, en utilisant une horloge précise (celle de votre GPS, horloge DCF-77 ou ordinateur synchronisé par NTP), vous appuyez sur le bouton S2 du LaunchPad juste quand l’horloge passe à la seconde zéro d’une minute paire.

Le générateur se met alors en mode “balise” et va transmettre tout de suite une première séquence WSPR. La séquence finie la LED jaune se met à clignoter très rapidement. Si vous appuyez sur le bouton S2 à ce moment, la balise passera à nouveau en émission à la prochaine minute paire. Sinon, elle passera en émission aléatoirement suivant le paramètre que vous avez donné, par défaut le taux est de 20% (tx_rate = 5).

Chez moi, l’horloge reste bonne pendant au moins 72 heures. Après cela elle prend environ une seconde de retard, ce qui suffit à faire que les stations les plus lointaines ne décodent plus la séquence WSPR. La remettre à l’heure ne prend que quelques minutes. L’expérience du GPS montre que sauf à partir dans des solutions complexes et coûteuses (http://www NULL.oe1ifm NULL.at/index NULL.php?option=com_content&view=article&id=49&Itemid=56), atteindre une meilleure stabilité effective reste difficile.

/* WSPR Static Generator for MSP430G2553
 * Code for Energia 008
 
 * By Yannick DEVOS - XV4Y - May-October 2012
    http://xv4y.radioclub.asia/

    Copyright 2012 Yannick DEVOS under GPL 3.0 license
    Any commercial use or inclusion in a kit is subject to author approval
 
 * Based on code by DH3JO Martin Nawrath for WSPR sequence encoding

====
 * Ouput on 2 bits PinA and PinB
 * PTT_key output allows to turn on/off the TX PA while sending
 * Mirror on LED1 and LED2 du LaunchPad for testing
 * Output to Serial for testing
 * Using an R-2R ladder it makes the G0UPL/G0XAR beacon frequency shift

====
Revision history :
v1.00    2012-10-18
         First release

====
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 can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/>
*/

// Here modify to your taste or needs

#define PTT_key 8
#define wsprPinA 10
#define wsprPinB 9
#define LEDPinA 14
#define LEDPinB 2
#define StartBtn 5

const char call[] = "XV4Y ";
const char locator[] = "OK20";
const byte power = 20;
const char tx_rate = 5;

// Be carefull, this is only for debugging.
// If you enable this in production, the clock will drift to much (5 seconds in 10 minutes)
//#define SERIAL_OUT_INFO

// This is in case you use a MSP430 that does not have Hardwart UART. TimerSerial works great but bring a lot of clock drift. Replace each "Serial" object call in the code by mySerial.
/*
#ifdef SERIAL_OUT_INFO
#include <TimerSerial.h> 
TimerSerial mySerial;
#endif
*/

// Here under don't touch anything unless you know what you do

#include <legacymsp430.h>
#include <sRTC.h> // Library for RTC clock with MSP430

const char SyncVec[162] = {
  1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0,
  1,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0,
  0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,
  0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0,0,0
};

boolean force_send = 1;  // The first time the beacon is turned on it will send a sequence
byte symbol, bb, begin_sec;
int begin_chunk;

byte c[11];                // encoded message

byte sym[170];             // symbol table 162
byte symt[170];            // symbol table temp
unsigned long n1;          // encoded callsign
unsigned long m1;          // encodes locator

#ifdef SERIAL_OUT_INFO
#endif
RealTimeClock myClock;

//******************************************************************
// Defining pins mode
// Encoding the WSPR sequence

void setup() {
  pinMode(PTT_key, OUTPUT);
  pinMode(wsprPinA, OUTPUT);
  pinMode(wsprPinB, OUTPUT);
  pinMode(LEDPinA, OUTPUT);
  pinMode(LEDPinB, OUTPUT);
  pinMode(StartBtn, INPUT_PULLUP);

  #ifdef SERIAL_OUT_INFO
  Serial.begin(9600);
  Serial.println("\n* MSP430 WPSR beacon"); 
  #endif

  digitalWrite( PTT_key, LOW );
  digitalWrite( wsprPinA, LOW );
  digitalWrite( wsprPinB, LOW );

  digitalWrite( LEDPinA, HIGH );
  digitalWrite( LEDPinB, HIGH );

  prepare_sequence();

  #ifdef SERIAL_OUT_INFO
  Serial.println("* Wait clock is set. Sending test sequence.");
  #endif
  begin_chunk = myClock.RTC_chunk;
  begin_sec = myClock.RTC_sec;
  while(digitalRead(StartBtn)) {
    // This is for blinking LED fast
    if ((myClock.RTC_chunk-(begin_chunk+15))%60==0)
      digitalWrite( LEDPinA, HIGH );
    if ((myClock.RTC_chunk-(begin_chunk+30))%60==0)
      digitalWrite( LEDPinB, HIGH );
    if ((myClock.RTC_chunk-(begin_chunk+45))%60==0)
      digitalWrite( LEDPinA, LOW );
    if ((myClock.RTC_chunk-(begin_chunk+60))%60==0)
      digitalWrite( LEDPinB, LOW );
    
    // We also send a test sequence to help calibrate the transmitter
    if ((myClock.RTC_chunk-(begin_chunk+0))%240==0) 
      send_test(begin_sec);

   };
  randomSeed(myClock.RTC_chunk);
  myClock.Set_Time(0,0,0);
};

//******************************************************************
// Here starts the actual sequence sending

void loop() {
  #ifdef SERIAL_OUT_INFO
  Serial.print("Time:");
  Serial.print(myClock.RTC_min, DEC);
  Serial.print(":");
  Serial.print(myClock.RTC_sec, DEC);
  Serial.print(".");
  Serial.println(myClock.RTC_chunk, DEC);
  #endif

  digitalWrite( PTT_key, LOW );

  digitalWrite( wsprPinB, LOW );
  digitalWrite( LEDPinB, LOW );

  digitalWrite( wsprPinA, LOW );
  digitalWrite( LEDPinA, LOW );

  begin_chunk = myClock.RTC_chunk;
  
  #ifdef SERIAL_OUT_INFO
  Serial.println("* Waiting 1st sec of even mins.");
  #endif

  while (!(myClock.RTC_sec==1 && (myClock.RTC_min%2)==0)) {  // We start each first second of even minutes
    if ((myClock.RTC_chunk-(begin_chunk+10))%20==0) digitalWrite( LEDPinA, HIGH );
    if ((myClock.RTC_chunk-(begin_chunk+20))%20==0) digitalWrite( LEDPinA, LOW );
    if (!digitalRead(StartBtn)) force_send = 1;  // Pressing the button while in wait mode force the sequence to start next time
  };
  
  if (random(tx_rate+1)==tx_rate || force_send) {  // In order to minimize band occupation, randomly start the transmission depending on tx_rate
    force_send = 0;
    digitalWrite( PTT_key, HIGH );
    send_sequence();
    #ifdef SERIAL_OUT_INFO
    Serial.println("\n* End");
    #endif
  } else {
    // If not, we wait around 1 sec so we don't go through this too early...
    digitalWrite( PTT_key, LOW );
    begin_chunk = myClock.RTC_chunk;
    while ( ((256+myClock.RTC_chunk-begin_chunk)%256)<220 ) {
      delay(0);
    };
  };
};

/* Functions declaration
 * This code by DH3JO
 * KHM 2009 /  Martin Nawrath
 * Kunsthochschule fuer Medien Koeln
 * Academy of Media Arts Cologne
*/


//******************************************************************
// normalize characters 0..9 A..Z Space in order 0..36
char chr_normf(char bc ) {
  char cc=36;
  if (bc >= '0' && bc <= '9') cc=bc-'0';
  if (bc >= 'A' && bc <= 'Z') cc=bc-'A'+10;
  if (bc == ' ' ) cc=36;

  return(cc);
}

//******************************************************************
void encode_call(){
  unsigned long t1;

  // coding of callsign
  n1=chr_normf(call[0]);
  n1=n1*36+chr_normf(call[1]);
  n1=n1*10+chr_normf(call[2]);
  n1=n1*27+chr_normf(call[3])-10;
  n1=n1*27+chr_normf(call[4])-10;
  n1=n1*27+chr_normf(call[5])-10;

  // merge coded callsign into message array c[]
  t1=n1;
  c[0]= t1 >> 20;
//  t1=n1;
  c[1]= t1 >> 12;
//  t1=n1;
  c[2]= t1 >> 4;
//  t1=n1;
  c[3]= t1 << 4;
}

//******************************************************************
void encode_locator(){

  unsigned long t1;
  // coding of locator
  m1=179-10*(chr_normf(locator[0])-10)-chr_normf(locator[2]);
  m1=m1*180+10*(chr_normf(locator[1])-10)+chr_normf(locator[3]);
  m1=m1*128+power+64;

  // merge coded locator and power into message array c[]
  t1=m1;
  c[3]= c[3] + ( 0x0f & t1 >> 18);
//  t1=m1;
  c[4]= t1 >> 10;
//  t1=m1;
  c[5]= t1 >> 2;
//  t1=m1;
  c[6]= t1 << 6;

}
//******************************************************************
// convolutional encoding of message array c[] into a 162 bit stream
void encode_conv(){
  int bc=0;
  int cnt=0;
  int cc;
  unsigned long sh1=0;

  cc=c[0];

  for (int i=0; i < 81;i++) {
    if (i % 8 == 0 ) {
      cc=c[bc];
      bc++;
    }
    if (cc & 0x80) sh1=sh1 | 1;

    symt[cnt++]=parity(sh1 & 0xF2D05351);
    symt[cnt++]=parity(sh1 & 0xE4613C47);

    cc=cc << 1;
    sh1=sh1 << 1;
  }

}

//******************************************************************
byte parity(unsigned long li)
{
  byte po = 0;
  while(li != 0)
  {
    po++;
    li&= (li-1);
  }
  return (po & 1);
}

//******************************************************************
// interleave reorder the 162 data bits and and merge table with the sync vector
void interleave_sync(){
  int ii,ij,b2,bis,ip;
  ip=0;

  for (ii=0;ii<=255;ii++) {
    bis=1;
    ij=0;
    for (b2=0;b2 < 8 ;b2++) {
      if (ii & bis) ij= ij | (0x80 >> b2);
      bis=bis << 1;
    }
    if (ij < 162 ) {
      sym[ij]= SyncVec[ij] +2*symt[ip];
      ip++;
    }
  }
}

// This code by Yannick DEVOS XV4Y 2012

//******************************************************************
// Compute and initialize the sequence
void prepare_sequence() {

  encode_call();
  #ifdef SERIAL_OUT_INFO
  Serial.print("call: ");
  Serial.print(call);
  Serial.print(" ");
  Serial.print(n1,HEX);
  Serial.println(" ");
  #endif

  encode_locator();
  #ifdef SERIAL_OUT_INFO
  Serial.print("locator: ");
  Serial.print(locator);
  Serial.print(" ");
  Serial.print(m1 << 2,HEX);
  Serial.println(" ");
  for (bb=0;bb<=10;bb++){
    Serial.print(c[bb],HEX);
    Serial.print(",");
  }
  Serial.println("");
  #endif

  encode_conv();
  #ifdef SERIAL_OUT_INFO
  Serial.println("");
  for (bb=0;bb<162 ;bb++){
    Serial.print(symt[bb],DEC);
    Serial.print(".");
    if ( (bb+1) %32 == 0) Serial.println("");
  }
  Serial.println("");
  #endif

  interleave_sync();
  #ifdef SERIAL_OUT_INFO
  Serial.println("Channel symbols :");
  for (bb=0;bb<162 ;bb++){
    Serial.print(sym[bb],DEC);
    Serial.print(".");
    if ( (bb+1) %32 == 0) Serial.println("");
  }
  Serial.println("\nEnd of init.");
  #endif

};

//******************************************************************
// Function to send the test sequence
void send_test(char begin_sec) {
    if ((myClock.RTC_sec-(begin_sec+0))%30==0) {
      digitalWrite( PTT_key, LOW );
      digitalWrite( wsprPinA, LOW );
      digitalWrite( wsprPinB, LOW );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX Low, Send 00 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+6))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, LOW );
      digitalWrite( wsprPinB, LOW );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 00 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+12))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, HIGH );
      digitalWrite( wsprPinB, LOW );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 01 *");
      #endif
    }

    if ((myClock.RTC_sec-(begin_sec+18))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, LOW );
      digitalWrite( wsprPinB, HIGH );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 10 *");
      #endif
    }
    
    if ((myClock.RTC_sec-(begin_sec+24))%30==0) {
      digitalWrite( PTT_key, HIGH );
      digitalWrite( wsprPinA, HIGH );
      digitalWrite( wsprPinB, HIGH );
      #ifdef SERIAL_OUT_INFO
      Serial.println("* TX High, Send 11 *");
      #endif
    }

};

  
//******************************************************************
// Function to send the sequence
void send_sequence() {
  for (bb = 0; bb < 162; bb++) {
    symbol = sym[bb];

    #ifdef SERIAL_OUT_INFO
    Serial.print(" ");
    Serial.print(symbol%10, DEC);
    #endif

    digitalWrite( wsprPinB, (symbol >> 1) );
    digitalWrite( LEDPinB, (symbol >> 1) );

    digitalWrite( wsprPinA, (symbol & 1) );
    digitalWrite( LEDPinA, (symbol & 1) );
    begin_chunk = myClock.RTC_chunk;
    while ( ((256+myClock.RTC_chunk-begin_chunk)%256)<175 ) {
      delay(0);
    };
  };
};


//******************************************************************
// Interruption for the RTC clock
interrupt(TIMER1_A0_VECTOR) Tic_Tac(void) {
  myClock.Inc_chunk();		      // Update chunks
};