Monday, November 26, 2012

C snippet for generating Opera symbols

char* itoa(unsigned long val, int n, char* buf, int radix)
{
  char c[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  int i = 0;
  while (n-i) {
    unsigned long d = (unsigned long)pow(radix, n-i-1);
    buf[i++] = c[val/d];
    val = val%d;
  }
  buf[i] = '\0';
  return buf;
}

int crc16op(char* msg, int n)
{
   int i,j,crc=0; // crc-16 (0x8005 poly, flip in and out, 2 zero bytes in tail) of msg[0..n-1]
   for(i=0;i!=n+2;i++){
     if(i
     for(j=0;j!=8;j++)
       crc = crc&1 ? (crc>>1) ^ 0xa001 : crc>>1;
   }
   // replace, swap and store crc in msg[32..48]
   crc = crc&0xff00 ? ( crc&0x00ff ? crc : 0x001b|(crc&0xff00) ) : 0x2b00|(crc&0x00ff);
   crc = ((crc&0x00ff)<<8 amp="amp" crc="crc" xff00="xff00">>8);
   return crc;
}

int main(int argc, const char* argv[]){
   if(argc != 2){
     printf("usage: opera [callsign]\n");
     return 0;
   }
   const char* call = argv[1];
   char msg[239];
   int i,j;

   char c[]="      "; //align last prefix digit at c[2] and fill gaps with blanks
   int aligned = isdigit(call[2]);
   strncpy(aligned ? c : &c[1], call, aligned ? 6 : 5); 
   strupr(c);

   unsigned long n1=(c[0]>='0'&&c[0]<='9'?c[0]-'0'+27:c[0]==' '?0:c[0]-'A'+1); // packing call e.g. " K1JT ", "AA1AA " into n1
   n1=36*n1+(c[1]>='0'&&c[1]<='9'?c[1]-'0'+26:c[1]-'A');
   n1=10*n1+c[2]-'0';
   n1=27*n1+(c[3]==' '?0:c[3]-'A'+1);
   n1=27*n1+(c[4]==' '?0:c[4]-'A'+1);
   n1=27*n1+(c[5]==' '?0:c[5]-'A'+1);
   itoa(n1, 28, &msg[4], 2); //msg[4..32] = binary representation n1 in ASCII
   itoa(crc16op(&msg[4], 28), 16, &msg[32], 2); //store bin-crc of msg[4..31] in msg[32..47]
   itoa(crc16op(&msg[4], 28+16) & 0x07, 3, &msg[48], 2); //store bin-crc of msg[4..47] in msg[48..51]
   msg[0]=msg[1]=msg[2]=msg[3]='0';  //unused bits msg[0-3]

   const char* prn_vec = "111000010101011111100110110100000001100100010101011";
   for(i=0; i!=51; i++) //scramble
     msg[i] = ((msg[i]-'0') ^ (prn_vec[i]-'0')) +'0';

   const char* wh[] = {"0000000", "1010101", "0110011", "1100110", "0001111", "1011010", "0111100", "1101001"};
   for(i=(51/3)-1; i>=0; i--){  // Walsh-Hadamard encoding to msg[0..118]
     char b = (msg[i*3+0]-'0')*4+(msg[i*3+1]-'0')*2+(msg[i*3+2]-'0')*1;
     for(j=0; j!=7;j++)
       msg[i*7+j] = wh[b][j];
   }

   for(i=0; i!=7; i++)  // interleave 7x17 to msg[121..240]
     for(j=0; j!=17; j++)
       msg[121+j+17*i] = msg[i+7*j];

   for(i=0; i!=119; i++){  // Manchester encoding to msg[2..120]
     msg[2*i+1+2] = msg[121+i];
     msg[2*i+0+2] = (msg[2*i+1+2] == '0') + '0';
   }
   msg[0] = msg[1] ='1'; // head
   msg[239] = '\0'; // tail

   printf("message=%s symbols=%s\n", c, msg);
}

jt65-source code

As it seems the author has lost the source code of the original jt65-hf version 1093, here is a link to the  source code: http://sdrv.ms/PbOz48

Thursday, March 12, 2009

Saturday, February 14, 2009

1st WSPR spot @18MHz

The Si570 10mW WSPR beacon has reached now OH3QN, 1634km away at 17m band. Interesting to see that it received at -7dB below noise in 2.4kHz SSB bandwidth. As the WSPR threshold is -27dB below noise, this means in theory the beacon could still be seen with a power below 100µW, wow!

Also DL2NQ did receive the beacon at 80m. It is special as the antenna is just an inerted V for 40m.

Wednesday, February 11, 2009

10mW WSPR beacon at 40m is born and alive


Just to inform you about the results of the beacon experiments. After some hacking the ATTINY45 firmware of my SoftrockV9 / USB Synthesizer board, I finally succeeded to integrate the WSPR generation code. As an experiment it broadcasts on three different frequencies in the 40m band my call, grid location and TX power. 

This morning, just for fun and as a joke I connected my dipole directly to the output of the si570. A small 3 pole lowpass filter was placed in between to attenuate harmonics, the output must be about 10mW. 

In the picture you see the reception reports. Amazing that this is working. The first three lines below were reception reports by using the Softrock RXTX (1 Watt); the upper lines where the SI570 transmissions; interesting to see that HB9OAB sees a signal strength difference of 20dB.


Friday, January 30, 2009

Simplified WSPR code
















Simplified C snippet for generating WSPR sequences:


#include <stdio.h>

void main(){

#ifndef TEXTMSG
   // pack call in n1
   static char c[6]=" K1JT "; //call
   // all must be in uppercase
   // char at position 3 must be last digit of call prefix
   // left and right idented with space characters
   unsigned long n1;
   n1=(c[0]>='0'&&c[0]<='9'?c[0]-'0':c[0]==' '?36:c[0]-'A'+10);
   n1=36*n1+(c[1]>='0'&&c[1]<='9'?c[1]-'0':c[1]==' '?36:c[1]-'A'+10);
   n1=10*n1+c[2]-'0';
   n1=27*n1+(c[3]==' '?26:c[3]-'A');
   n1=27*n1+(c[4]==' '?26:c[4]-'A');
   n1=27*n1+(c[5]==' '?26:c[5]-'A');

   // pack grid, dbm in n2 
   static int lon = 5;   //degrees longitude east  -74       5
   static int lat  = 51;   //degrees latitude north  40      51

   int ng = 90*(180-lon+(lon<0?1:0) ) + lat;
   static int dbm = 33;    //EIRP dBm  (0<=dbm<=60)
   // must be in set {0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60}
   unsigned long n2=(ng<<7)|(dbm+64);
#else
   unsigned char msg[8] = "HI W0RLD";
   // pack text
   unsigned long long dn=0;
   int y;
   for(y=0;y!=8;y++){
      unsigned char c = msg[y];
      dn = 41*dn + (c>='0'&&c<='9'?c-'0': c>='A'&&c<='Z'?c-'A'+10: 
c=='+'?37: c=='.'?38: c=='/'?39: c=='?'?40: 36);
   }
   unsigned int ng=dn&0x7fff;
   unsigned long n1=dn>>15;
   int ntype=-57;  //plain text
   unsigned long n2=ng<<7|(ntype+64);
#endif

   // pack n1,n2 into 50 bits
   char packed[11] = {n1>>20, n1>>12, n1>>4, (n1&0x0f)<<4|(n2>>18)&0x0f, 
n2>>10, n2>>2, (n2&0x03)<<6, 0, 0, 0, 0};

   // Convolutional encoder for a K=32, r=1/2 code.
   // Layland-Lushbaugh polynomials for a K=32, r=1/2 convolutional code
   int k = 0;
   int i,j,p;
   int nstate = 0;
   unsigned char symbol[176];
   for(j=0;j!=sizeof(packed);j++){
      for(i=7;i>=0;i--){
         unsigned long poly[2] = { 0xf2d05351L, 0xe4613c47L };
         nstate = (nstate<<1) | ((packed[j]>>i)&1);
         for(p=0;p!=2;p++){   //convolve
            unsigned long n = nstate & poly[p];
            // even := parity(n)
            int even = 0;
            while(n){
               even = 1 - even;
               n = n & (n - 1);
            }
            symbol[k] = even;
            k++;
         }
      }
   }

   // Interleave symbols
   unsigned char symbol2[162];
   for(i=0;i!=162;i++){
      // j0 := bit reversed_values_smaller_than_161[i]
      unsigned char j0;
      p=-1;
      for(k=0;p!=i;k++){
         for(j=0;j!=8;j++)   // j0:=bit_reverse(k)
            j0 = ((k>>j)&1) | (j0<<1);
         if(j0<162)
          p++;
      }
      symbol2[j0]=symbol[i]; //interleave
   }

   // Sync vector 162 bits
   const unsigned char npr3[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 };
   for(i=0;i!=162;i++){
      printf("symbol[%u]=%u\n", i, symbol2[i]);
      symbol2[i] = npr3[i] | symbol2[i]<<1;
   }

   // Modulate
   FILE* pf = fopen("a.au", "wb");
   fputc(0x2e,pf); //.au magic number
   fputc(0x73,pf);
   fputc(0x6e,pf);
   fputc(0x64,pf);
   fputc(0x00,pf); //offset
   fputc(0x00,pf);
   fputc(0x00,pf);
   fputc(24,pf);
   fputc(0xff,pf); //size
   fputc(0xff,pf);
   fputc(0xff,pf);
   fputc(0xff,pf);
   fputc(0x00,pf); //encoding
   fputc(0x00,pf);
   fputc(0x00,pf);
   fputc(0x03,pf);
   fputc(0x00,pf); //samplerate
   fputc(0x00,pf);
   fputc(0x2e,pf);
   fputc(0xe0,pf);
   fputc(0x00,pf); //channels
   fputc(0x00,pf);
   fputc(0x00,pf);
   fputc(0x01,pf);

   const int nmax=120*12000;
   double tsymbol=8192.0/12000.0;
   double dt=1.0/12000.0;
   double f0=1500;
   double dfgen=12000.0/8192.0;
   double t=0;
   double dphi;
   double phi=0;
   double dj0=0;
   for(i=0; i!=nmax; i++){
      t=t+dt;
      j=(int)(t/tsymbol)+1;
      if(j!=dj0){
         double f=f0+dfgen*(symbol2[j]-1.5);
         dj0=j;
         dphi=2*3.14*dt*f;
      }
      phi=phi+dphi;
      int wave=32767.0*sin(phi);
      fputc((wave>>8)&0xff,pf);
      fputc(wave&0xff,pf);
   }
   fclose(pf);
}