< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Sunday, January 4, 2015

Weak signals and a random piece of wire with ad9850 dds and arduino - testing out WSPR

ADVERTISEMENT
Here is a simple weekend project. WSPR pronounced as whisper is an amazing tool to learn more about weak signal propogation for radio amateurs. This consist of weak signal transmission which carries a station's callsign, Maidenhead grid locator, and transmitter power in dBm. Signals are encoded according to wspr protocol (more about it can be read at wsjt home page here)

Propagation paths from WSPRnet

WSPR is unique that there are several automated receiving stations which reports the signals and shows it on a world map so that you can easily see the propagation patterns (see here). Each WSPR transmission will start at the top on the even minutes in UTC. So exact time synchronization is a must. Secondly the signals are encoded in frequency shifted keying (hence needs a stable oscillator)

Amazed by the potentials of whisper and the lack of space to put up a proper antenna made me to give a try with this protocol. So i decided to test it out with an AD9850 ebay module (dds signal generator) and an arduino uno. To boost up the output from the module a little bit, i hooked up a small power-amp from a frog sound qrp kit (popular on ebay/aliexpress)with a low pass filter ( for testing i simple removed NE602 from the socket and connected the AD9850 output to pin 3 on ic socket & ground to dds ground, also make sure that you use the Q1 9018 and feed 9 volt to keep the power output low and to keep the d882 safe from damage ). Hooked up a random length of wire (i got from a cat 5 cable, about 5 mtr long) to the antenna socket and a similar piece as counterpoise). In this testing , i put the wire on the curtain of the room and the counter poise on the floor! so i had the worst possible antenna and swr )


Simple direct conversion  cw trx
Now what i need is a simple way to synchronize the transmission to UTC ( the best solution is to use a gps module or a dcf clock). To make it simple, i did that manually!! So i hit the tx button at every UTC even minutes( for eg at 2:00, 4:00 and 6:00 minutes).  So basically i pressed after looking at http://time.is/UTC and when it is the first second of an even minute.

After the test i checked on the WSPRnet website and was amazed to see the spots a 1000km away!!. This was done for testing and a solution with gps sync and a proper antenna & swr matching  will do the best for long run. It is also important that the tx should be on the standard whisper frequencies (see here). As my LPF was for 40 mtr , i did my test on 7mhz band. But most popular one for wspr is 30mtr band.

For a constant use , i would recommend the kit from hansummers which costs a lot less for the entire setup (See: http://www.hanssummers.com/ultimate3.html)

Results

Got a couple of spots from dx stations located several miles away. Snr was not so good, but as a proof of concept , the bad antenna and quick set up with manual utc trigger worked!

Spots on 40mtrs

and finally here is my messy setup followed by the arduino code (courtesy to http://www.george-smart.co.uk/wiki/Arduino_WSPR).

wspr testing with ad9850 and arduino uno

  • More on ad 9850 module connection can be read here and datasheet
  • For generating the tone array for the sketch , i used genwspr.exe listed at http://www.g4jnt.com/jtmodesbcns.htm. You get a list of numbers and use search replace function to put a set of commas in between the numbers and paste it to arduino ide. If you need the source/logic for making this by yourself you can get them (as well as the exe) from here
Arduino code (dont forget to insert your call details). A switch is connected to A0 which needs to be connected to +5volt so that A0 reads 1023 to start the transmit cycle)

/* 
 * A simple single freq AD9850 Arduino test script
 * Original AD9851 DDS sketch by Andrew Smallbone at www.rocketnumbernine.com
 * Modified for testing the inexpensive AD9850 ebay DDS modules
 * Pictures and pinouts at nr8o.dhlpilotcentral.com
 * 9850 datasheet at http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf
 * From http://www.george-smart.co.uk/wiki/Arduino_WSPR
 *  modification by http://blog.riyas.org
 * Use freely
 */
 
 #define W_CLK 8       // Pin 8 - connect to AD9850 module word load clock pin (CLK)
 #define FQ_UD 9       // Pin 9 - connect to freq update pin (FQ)
 #define DATA 10       // Pin 10 - connect to serial data load pin (DATA)
 #define RESET 11      // Pin 11 - connect to reset pin (RST).
 #define LED       13 //flashes duty cycle
 #define SWITCH    A0 // connect this to a switch and the other end to vcc via 10k resistor
 #define CLOCKLED  3 //flash every seconds 
 
 #define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }
 
 #define WSPR_TX_B     7.040000e6  // this is the bottom of the band. The station moves about.

 // DDS Offset in Hz
#define DDS_OSET  50  //DDS #2
#define WSPR_DUTY  3 // transmit every N slices.

// WSPR Tone Data - line breaks in no parciular place, just to look pretty. (0-161).
//callsign locator power in dbm
// to bulid this array see:http://www.george-smart.co.uk/wiki/Arduino_WSPR
static byte WSPR_DATA_HOME[] = {1,1,0,0,1,1,0,0,3,0,0,2,3,3,1,2,2,2,1,2,2,1,0,3,3,3,3,0,0,2,0,2,
...................................,2}; 

#define WSPR_DATA WSPR_DATA_HOME

#include <stdint.h>

// Variables
unsigned long  WSPR_TXF = 0;
unsigned long minute =1;
unsigned long seconds=0;
unsigned long tstart=0;


unsigned long startT = 0, stopT = 0;
char sz[32];
int duty=0;
int val=0;
int band=0;
 
// transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
void tfr_byte(byte data)
{
  for (int i=0; i<8; i++, data>>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}

 // frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850
  for (int b=0; b<4; b++, freq>>=8) {
    tfr_byte(freq & 0xFF);
  }
  tfr_byte(0x000);   // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);  // Done!  Should see output
}

void setup() {
 // configure arduino data pins for output
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT);
   
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode - Datasheet page 12 figure 10
  
  pinMode (LED,       OUTPUT);
  pinMode (CLOCKLED,       OUTPUT);
  pinMode(SWITCH, INPUT);
  
  
  Serial.begin(9600);
  
  sprintf(sz, "\nLB7UG Compiled %s %s", __TIME__, __DATE__);
  Serial.println(sz);  
  Serial.print("\n\nDDS Reset   ");
  delay(900);
  delay(100);
  Serial.println("OK");  
  duty = 0;  


}

void loop() {


   //let us make a poor mans clock which needs a click on SWITCH pin to high at an even minute
        
         val=analogRead(SWITCH);
         //Serial.println(val);
         if (val == 1023) { 
          //reset the clock here
     tstart = millis()/1000UL;
         wsprTX(); 
         Serial.println(tstart);
     }

     //WSPR_TXF = (WSPR_TX_B+DDS_OSET) + random(10, 190); // always choose a frequency,  a little with the pRNG.
     WSPR_TXF = (WSPR_TX_B+DDS_OSET); // always choose a frequency,  a little with the pRNG.
          //wsprTX();
         // UPDATING TIME DO NOT INSERT COMMANDS BELOW!!
     seconds=(millis()/1000UL)-tstart;
     minute= seconds/60UL; 
         //Serial.println(minute);

      if ( (minute % 2 == 0) && (minute > 1) && (seconds >= 1) && (seconds <= 4) ) {  // start transmission 
        //if (duty % WSPR_DUTY == 0) {
          if (1) {
          //Serial.print("Beginning WSPR Transmission on ");
          //Serial.print(WSPR_TXF-DDS_OSET);
          //Serial.println(" Hz.");
          wsprTX();
          duty++;
          band++;
          //Serial.println(" Transmission Finished.");    
        } else {
          duty++;
          digitalWrite (LED, LOW);          
          flash_led(WSPR_DUTY, LED); // flash the WSPR duty.
        }
      }
     //update time again 
     seconds=(millis()/1000UL)-tstart; 

     if ((seconds % 2 ==0) && (tstart!=0)){  // oru bhangikku vendi
     digitalWrite(CLOCKLED, HIGH);
     }
     else
     {
     digitalWrite(CLOCKLED, LOW);
     }
      
 }


void flash_led(unsigned int t, int l) {
  unsigned int i = 0;
  if (t > 25) {
    digitalWrite(l, HIGH);
    delay(2000);
    digitalWrite(l, LOW);
  } else {
    for (i=0;i<t;i++) {
      digitalWrite(l, HIGH);
      delay(250);
      digitalWrite(l, LOW);
      delay(250);
    }
  }
}
  
void wsprTX() {
  int i = 0;

  digitalWrite(LED, HIGH);
  for (i=0;i<162;i++) {
    wsprTXtone( WSPR_DATA[i] );
    delay(682);
  }
  sendFrequency(0);
  digitalWrite(LED, LOW);
}
    
void wsprTXtone(int t) {
  if ((t >= 0) && (t <= 3) ) {
     //Serial.print((WSPR_TXF + (t * 2)));
    sendFrequency((WSPR_TXF + (t * 2))); // should really be 1.4648 Hz not 2.
  } else {
    Serial.print("Tone #");
    Serial.print(t);
    Serial.println(" is not valid.  (0 <= t <= 3).");
  }
}

end

1 comment:

  1. I've been thinking along the same lines, only reason its not happening yet is time! CT7AOX, ex G8CYE. 73

    ReplyDelete