Archives par mot-clé : G0XAR

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
};

Nouveau kit QRSS par G0UPL et G0XAR

Schéma G0UPL G0XAR Balise Multimode (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/05/G0UPL-G0XAR-Balise-Multimode NULL.jpg)Hans et Stephen viennent d’annoncer un nouveau kit balise multimode QRSS / QRPp (http://www NULL.hanssummers NULL.com/qrsskitmm). La grande nouveauté de ce kit c’est que le micro-contrôleur est maintenant un ATMega168 ce qui permet de faire beaucoup plus beaucoup plus de choses que le très limité ATTiny13 utilisé précédemment.

Les modes supportés seront :

  • QRSS (télégraphie lente par porteuse ON/OFF)
  • FSK/CW (télégraphie lente par modulation de la fréquence
  • DFCW (télégraphie lente par modulation de fréquence mais traits et points chacun sur une fréquence)
  • WSPR (Weak Signal Propagation Reporter par K1JT)
  • Hellschreiber lent (Hellschreiber lent par modulation de fréquence)
  • Hellschreiber (Hellschreiber vitesse normale et demi-vitesse)
  • CW (télégraphie normale)
  • Motifs FSK personnalisables

Autres fonctionnalités

  • Afficheur LCD 24 caractères LCD et interface utilisateur deux boutons
  • Programmable par l’utilisateur (indicatif, message, vitesse, FSK, mode, etc)
  • Paramètres stockés en EEPROM
  • Interface GPS pour verrouiller la fréquence
  • Génération du message WSPR (pas de PC requis)
  • Le locator de la séquence WSPR peut être généré à partir des informations du GPS
  • Taille de la trame QRSS paramètrable pour une réception QRSS de trames superposées (moyenne étalée dans le temps)
  • Balise CW standard à intervales réglables
  • Produit 150mW en sortie HF, sortie audio possible pour piloter un transceiver
  • Puissance supérieure possible par l’ajout d’un transistor supplémentaire au PA ou par l’utilisation d’une tension d’alimentation supérieure

Les commandes seront ouvertes d’ici une à deux semaines et l’expédition des kits devrait débuter dans trois semaines. Le prix prévu est de l’ordre de 15 à 20£, soit environ 23-31$US ou 18-25€.

Un brouillon du manuel de 26 pages peut-être téléchargé pour avoir plus de détails (http://www NULL.hanssummers NULL.com/images/stories/qrsskitmm/instructions1 NULL.pdf).

Balise WSPR autonome avec Arduino chez W3PM

Hier j’ai laissé tourné WSPR sur 30 mètres pour voir si la propagation était revenue à un niveau correct sur cette bande. Cela m’a aussi permis de valider les performances de l’OCF Dipole sur le 30 mètres où en principe elle ne devrait pas fonctionner…

Réception WSPR 19 mars 2012 sur 30m (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/03/WSPR_19mars2012_30m NULL.jpg)En lisant la partie “blog” du site WSPRnet.org (http://wsprnet NULL.org/) je suis tombé sur un OM qui parlait d’une balise autonome WSPR autour d’un Arduino. Intéressé, j’ai creusé un peu et j’ai trouvé une mine d’information sur le site de W3PM (http://www NULL.knology NULL.net/~gmarcus/). Cet OM a fait un excellent travail autour de WSPR en concevant des émetteur-récepteur mais aussi en utilisant des Arduino Uno et Arduino MEGA pour piloter un DDS. Il a aussi conçu le même style de générateur WSPR pour la platine QRSS de G0UPL/G0XAR (http://xv4y NULL.radioclub NULL.asia/2010/08/16/kit-balise-qrss/) mais sur un micro-contrôleur PIC. C’est cette dernière qui m’intéresse le plus mais je n’ai pas de PIC.

J’avoue que pour l’instant je n’ai pas encore eu l’occasion de regarder en détails comment me servir de toutes ces infos. Je me pencherai sur ce projet courant mai. Générer une séquence WSPR “manuellement” est possible car K1JT fournit le petit programme adéquat. Il faut ensuite moduler correctement la fréquence pour un shift de 1 à 3 Hz (positif et négatif, 4 symboles en tout). La difficulté pour les transmissions de séquences WSPR étant de devoir être calé à la seconde près sur les minutes paires sinon aucune trame n’est décodée… Une horloge asservie à un GPS résout le problème, mais je n’en ai pas. W3PM semble utiliser aussi un récepteur de signaux WWBV (disponible à bas prix) mais ça ne marchera pas très bien hors des USA.

A noter que F4GKA (http://www NULL.itsrainingelephants NULL.com/2012/03/04/fabrication-dun-mept/) s’intéresse aussi à ce problème et utilise lui une échelle de 4 résistance comme DAC pour piloter la varicap (une LED sur la balise de G0UPL).

Dernière chose, voici k6HX qui a construit une balise SSTV à partir d’un Arduino (http://brainwagon NULL.org/2009/07/23/success-robot36-encoder-works/). Bon, en fait il génère un fichier son (WAV) sur PC et l’Arduino en déclenche la lecture sur l’AudioShield… mais il y a de l’idée.

Programmer un micro-contrôleur Atmel ATtiny avec un Arduino (MAJ)

[GTranslate]

Il y a plus d’un an j’avais monté un kit balise QRSS (MEPT QRSS pour être exact) de G0UPL (http://xv4y NULL.radioclub NULL.asia/2010/08/16/kit-balise-qrss/). Hans livre le micro-contrôleur ATTiny13 du kit programmé à l’avance avec l’indicatif que vous lui demandez, or le mien a changé entretemps!

Arduino utilisé comme ISP pour reprogrammer un ATTiny13

J’avais donc en tête de reprogrammer celui-ci avec le bon indicatif. C’est facilement faisable avec une platine Arduino programmée pour agir en tant qu’ISP (In System Programmer). Une condition cependant, que les Fuse-bits ne soient pas programmés car dans ce cas un programmateur à tension élevée est nécessaire pour tout remettre à zéro. Hans et Steve m’ont confirmé que ce n’était pas le cas pour leur micro-contrôleur.

Je ne vais pas vous faire un guide pas-par-pas ici, plusieurs tutoriaux sont disponibles sur internet et mieux documentés. Je vais plutôt vous dire où trouver les informations. A noter que j’utilise un Macintosh, mais les grandes lignes restent les mêmes.

Liste des ingrédients :

  • La dernière version de l’IDE de l’Arduino (http://arduino NULL.cc/en/Main/Software).
  • Le pack de développement CrossPack pour AVR (http://www NULL.obdev NULL.at/products/crosspack/index NULL.html) qui permettra de compiler le code vers un binaire AVR avec XCode et installera aussi AVRDude permettant de communiquer avec le microcontrôleur de chez Atmel.
  • Un Arduino avec ATMega328, une plaque d’essais, des câbles, des jumpers et un condensateur (à mettre entre la pin RESET et la masse de l’Arduino.
Recette :
  • Lancer l’IDE Arduino, ouvrir le sketch Arduino ISP des exemples, le programmer dans l’Arduino.
  • Créer un projet dans XCode, copier le code du keyer QRSS (http://www NULL.hanssummers NULL.com/images/stories/qrsskeyer/beacon NULL.c) et le compiler.
  • Câbler l’Arduino et l’ATTiny de telle façon à raccorder les masses et +5V des deux circuits ensembles. Connecter les broches 10, 11, 12 et 13 de l’Arduino respectivement aux broches 1 (RST), 5 (MOSI), 6 (MISO),7 (SCK) de l’ATTiny13. (Voir photo un peu plus haut)
  • Ouvrir une fenêtre du terminal (shell) et récupérer l’ancien binaire (voir capture ci-dessous,cliquez pour élargir).Récupération du binaire du précédent programme dans la Flash de l'ATTiny13 avec AVRDude (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/01/Download-binaire-ATTiny13-XV4TUJ NULL.jpg)
  • Programmer l’ATTiny avec le fichier .hex généré par XCode (voir capture ci-dessous, cliquez pour élargir).Envoi du binaire du nouveau programme dans la Flash de l'ATTiny13 avec AVRDude (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/01/Upload-binaire-ATTiny13-XV4Y NULL.jpg)

Ca marche, ma balise envoie maintenant le bon indicatif (voir plus bas pour la mise à jour). Je n’ai plus qu’à procéder au réalignement périodique de la fréquence car celle-ci bouge un peu (50Hz c’est beaucoup en QRSS) avec les changements de saison et de température moyenne.

Mon but était de reprogrammer un ATTiny13 (http://futurlec NULL.com/Atmel/ATTiny13V NULL.shtml) avec le code source de G0UPL/G0XAR. Celui-ci est dans le langage C spécifique à ce micro-contrôleur, et avec le template AVR de XCode ont produit un binaire (fichier .hex) en code machine pour celui-ci. La flash sur l’ATTiny13 et de 1Ko, le binaire fait 664o.

Si vous disposez d’ATTiny45 ou ATTiny85, (http://futurlec NULL.com/Atmel/ATTiny85 NULL.shtml) ceux-ci ayant plus de mémoire, une autre possibilité existe si vous souhaitez créer vos propres programmes. En effet, le projet Arduino-Tiny (http://code NULL.google NULL.com/p/arduino-tiny/) a pour but de porter le noyau Arduino sur ATTiny. Ceci permet d’utiliser l’environnement de développement (IDE) d’Arduino et son langage plus évolué. Toutes les commandes ne sont pas utilisables car certaines sont propres au micro-contrôleur ATMega (http://futurlec NULL.com/Atmel/ATMEGA328P-AU NULL.shtml) des Arduino. Toutefois, pour un bon nombre de projets simples (balise (http://xv4y NULL.radioclub NULL.asia/2011/07/01/balise-cw-a-base-de-micro-controleur-arduino/), manipulateur électronique (http://xv4y NULL.radioclub NULL.asia/2011/12/09/arduino-manipulateur-cw-de-numero-de-serie-pour-concours/)…) cela permet de réduire de le coût et de simplifier le circuit.

Mise à jour : Contrairement à ce que je pensais au début même si la programmation du micro-contrôleur se passait bien aucune “manipulation” n’était effectuée par la balise, le signal restait fixe mais variait à cause du changement de température…

Je prends contact avec Stephen et Hans qui me donne deux informations. D’abord la fréquence à indiquer dans le Makefile pour la compilation est 9 600 000 Hz, car les ATTiny ont été programmés avec les fuse-bytes indiquant une horloge de 9,6 Mhz et pas de prescaling par le diviseur-par-8. Ensuite un petit bogue réside dans leur code qui ne compile plus sur les nouvelles versions de WinAVR (qui utilise avr-libc comme Crosspack). Après quelques recherches (je ne suis pas très doué en programmation), le code est corrigé et compile maintenant parfaitement. La version mise à jour du code devrait être disponible chez G0UPL, n’hésitez pas à me contacter pour plus de détails.

Kit balise QRSS

Je viens de recevoir un kit simple et rapide à réaliser. Il a été conçu par QRP Labs, groupe plus connu sous les personnes de Hans G0UPL et Steve G0XAR. Ce kit avait déjà reçu un très grand succès quant il avait été distribué aux salons de Dayton puis Friedrichaven. Plusieurs centaines de kit programmés sur place s’étaient écoulé comme des petits pains. La qualité du kit et le prix très contenu (moins de 15€ port compris) n’y sont pas étrangers.

Composants et notice du kit balise QRSS de G0UPL et G0XAR (http://capheda NULL.files NULL.wordpress NULL.com/2010/08/100_2518 NULL.jpg)Ce kit balise QRSS (QRSS QRP beacon kit (http://www NULL.hanssummers NULL.com/qrsskit NULL.html)) est composé d’une trentaine de composants. C’est un oscillateur à quartz, suivi d’un étage buffer et d’un amplificateur de puissance à FET. Il peut sortir environ 100mW pour travailler dans de bonnes conditions. L’oscillateur est modulé en fréquence par un micro-contrôlleur programmé par les auteurs du kit. Le micro-contrôlleur transmet un signal QRSS DFCW à 5 Hertz avec une longueur de point programmable (de 1 à 20s, ou CW 6 ou 12 mots par minutes). A l’achat, une fréquence sur 3,5 MHz, 7 MHz ou 10,140 MHz peut être choisie. J’ai préféré la bande des 30 mètres (10,100-10,150 MHz) car du point de vue étude de la propagation c’est celle qui offre le plus de curiosités. Elle offre une excellente couverture en tant que bande de nuit mais aussi une propagation de jour pas négligeable.

Je vous en dirai plus quand le kit sera monté et opérationnel…