|
发表于 2011-5-4 09:35:00
|
显示全部楼层
/*
* WSPR beacon for weak signal radio transmission
* encodes a WSPR message from call locator and power to a symbol/tone
* and generates sinewaves with a PWM and software DDS
* Thanks to Andy Talbot for his article The WSPR Coding Process
* generates from a Callsign, Locator, and Power Level a symbol Table
* due to the WSPR protocol
* rund on a Arduino Diecimila or Duemilanove board / Arduino 17
*
* ptt connected to pin10
* vfo for tone modulation connected to pin 11
* tone output PWM connected to pin 3
* DCF-time receiver connected to pin 7
* soft DDS with ATMEGS 168
* Timer2 set to 31373 KHz for interrupt and DDS timebase
*
*
*
* KHM 2009 / Martin Nawrath
* Kunsthochschule fuer Medien Koeln
* Academy of Media Arts Cologne
*/
#include <avr/pgmspace.h>
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
//! Macro that clears all Timer/Counter1 interrupt flags.
#define CLEAR_ALL_TIMER1_INT_FLAGS (TIFR1 = TIFR1)
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
};
// table of 256 sine values / one sine period / stored in flash memory
PROGMEM prog_uchar sine256[] = {
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124
};
const unsigned long mt[4] = {
800974,801757,802540,803323 }; // DDS freq table for 1497,8 1499,3 1500,7 1502,2 WSPR tones
int ledPin = 13; // LED pin 13
int led2Pin = 8; // LED pin 13
int t1Pin = 4; // test pin interrupt
int t2Pin = 5; // test pin main
int pttPin=10;
int dcfPin=7;
int state=0;
// byte sine[258]; // sinewave Memory Array 8-Bit
byte c[11]; // encoded message
byte sym[170]; // symbol table 162
byte symt[170]; // symbol table temp
char call[] = "DH3JO "; // default values
char locator[] = "JO30";
byte power = 20;
byte dcfPin_a;
int dcfcnt;
int dcfmin;
int dcfhour;
byte n;
byte f_led;
unsigned long n1; // encoded callsign
unsigned long m1; // encodes locator
char cnt1;
unsigned char cc1;
int ii,bb;
// interrupt service variables
volatile boolean f_tone; // set 1,46 Hz tonespacing time flag
volatile unsigned int tcnt; // tonespacing timer
volatile unsigned long cnt16u; // 16us timer
volatile byte icnt;
volatile byte icnt2;
volatile unsigned long phaccu; // soft DDS phase accu
volatile unsigned long mm; // soft DDS frequency word
volatile unsigned int sycnt;
volatile unsigned int cpin;
void setup()
{
pinMode(ledPin, OUTPUT); // sets the digital pin as output
pinMode(led2Pin, OUTPUT);
pinMode(t1Pin, OUTPUT);
pinMode(t2Pin, OUTPUT);
pinMode(pttPin, OUTPUT);
pinMode(dcfPin, INPUT);
pinMode(11, OUTPUT); // PWM VFO Control
pinMode(3, OUTPUT); // PWM WSPR tone output
Serial.begin(115200); // connect to the serial port
Serial.println("WSPR beacon");
Setup_timer2();
//cli(); // disable interrupts to avoid distortion
// cbi (TIMSK0,TOIE0); // disable Timer0 !!! arduino delay function is off now
sbi (TIMSK2,TOIE2); // enable Timer2 Interrupt
OCR2B=64;
OCR2A=64;
mm=402063;
encode_call();
Serial.print("call: ");
Serial.print(call);
Serial.print(" ");
Serial.print(n1,HEX);
Serial.println(" ");
encode_locator();
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("");
encode_conv();
Serial.println("");
for (bb=0;bb<162 ;bb++){
Serial.print(symt[bb],DEC);
Serial.print(".");
if ( (bb+1) %32 == 0) Serial.println("");
}
Serial.println("");
interleave_sync();
for (bb=0;bb<162 ;bb++){
Serial.print(sym[bb],DEC);
Serial.print(".");
if ( (bb+1) %32 == 0) Serial.println("");
}
Serial.println("");
tcnt=0;
mm=0;
dcfmin=-1;
}
void loop()
{
sycnt =0;
state=10;
while(1) {
ii=dcf_minute();
if ((ii >= 0) && ( ii % 10 ==0) || (ii-2) % 10 ==0 ){ // every 10 minutes for 4 minutes send
Serial.println("Wspr Start");
digitalWrite(pttPin,1);
tcnt=0;
sycnt =0;
state=5;
}
if ((ii >= 0) && (ii-4) % 10 ==0 ){ // every 10 minutes 6 for 1 minute without ptt
Serial.println("Wspr Start without ptt");
tcnt=0;
sycnt =0;
state=5;
}
if (f_tone==1) {
f_tone=0;
switch (state){
case 0:
break;
case 5: // Wspr Start with some delay
if (sycnt==6) {
sycnt=0;
state=10;
}
break;
case 10: // Wspr Run
bb=sym[sycnt];
mm=mt[bb];
OCR2A=240-(40*bb); // output symbol as a control voltage to vco
/*
Serial.print(sycnt);
Serial.print(" ");
Serial.print(bb);
*/
Serial.print("*");
if (sycnt >= 162) state = 11;
break;
case 11: // Wspr Stop
digitalWrite(pttPin,0);
break;
}
}
}
} // loop
//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM, 16000000/510 = 31372.55 Hz clock
void Setup_timer2() {
// Timer2 Clock Prescaler to : 1
sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
cbi (TCCR2B, CS22);
// Timer2 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0); // clear Compare Match
sbi (TCCR2A, COM2A1);
cbi (TCCR2A, COM2B0); // clear Compare Match
sbi (TCCR2A, COM2B1);
sbi (TCCR2A, WGM20); // Mode 3 / fast PWM
sbi (TCCR2A, WGM21);
cbi (TCCR2B, WGM22);
}
//******************************************************************
int dcf_minute() {
int bb;
int bb2,bb3;
int rr=-1;
bb= digitalRead(dcfPin);
if (bb == 1 && dcfPin_a==0) {
cpin=0;
digitalWrite(ledPin,f_led);
digitalWrite(led2Pin,f_led);
f_led= !f_led;
}
if (bb == 0 && dcfPin_a==1) {
bb2=0;
dcfcnt++;
if (cpin > 30) bb2=1;
if ( dcfcnt > 15 && dcfcnt < 29) {
/*
Serial.print(" S:");
Serial.print(dcfcnt);
Serial.print(" ");
Serial.print(bb2);
*/
Serial.print(".");
}
if ((dcfcnt==22) && (bb2 == 1)) dcfmin +=1;
if ((dcfcnt==23) && (bb2 == 1)) dcfmin +=2;
if ((dcfcnt==24) && (bb2 == 1)) dcfmin +=4;
if ((dcfcnt==25) && (bb2 == 1)) dcfmin +=8;
if ((dcfcnt==26) && (bb2 == 1)) dcfmin +=10;
if ((dcfcnt==27) && (bb2 == 1)) dcfmin +=20;
if ((dcfcnt==28) && (bb2 == 1)) dcfmin +=40;
if ((dcfcnt==30) && (bb2 == 1)) dcfhour +=1;
if ((dcfcnt==31) && (bb2 == 1)) dcfhour +=2;
if ((dcfcnt==32) && (bb2 == 1)) dcfhour +=4;
if ((dcfcnt==33) && (bb2 == 1)) dcfhour +=8;
if ((dcfcnt==34) && (bb2 == 1)) dcfhour +=10;
if ((dcfcnt==35) && (bb2 == 1)) dcfhour +=20;
}
if (cpin > 300) {
dcfcnt=0;
cpin=0;
rr=dcfmin;
Serial.print("DCF Time:");
Serial.print(dcfhour);
Serial.print(":");
Serial.println(dcfmin);
dcfmin=0;
dcfhour=0;
}
dcfPin_a=bb;
return(rr);
}
//******************************************************************
void encode() {
encode_call();
encode_locator();
encode_conv();
interleave_sync();
};
//******************************************************************
// 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++;
}
}
}
//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {
sbi(PORTD,4); // set PORTD,4 high to observe timing with a oscope
// cnt16u++;
if (tcnt++ >= 21417) {
tcnt=0; // set tone spacing flag at 1,4648 Hz
f_tone=1;
sycnt++;
}
phaccu=phaccu+mm; // soft DDS phase accu with 24 bits
icnt=phaccu >> 16; // upper 8 bits for pwm modulator
OCR2B=sine256[icnt]; // send phase state to PWM
if(icnt2++ == 0) cpin++;
cbi(PORTD,4); // set PORTD,4
} |
|