diff --git a/lib/wsprd/wsprd_exp.c b/lib/wsprd/wsprd_exp.c index ed7e982d2..51cf07e8d 100644 --- a/lib/wsprd/wsprd_exp.c +++ b/lib/wsprd/wsprd_exp.c @@ -194,9 +194,9 @@ void sync_and_demodulate(double *id, double *qd, long np, float dt=1.0/375.0, df=375.0/256.0, fbest=0.0; int i, j, k; double pi=4.*atan(1.0),twopidt; - float f0=0.0,fp,fplast=-10000.0,ss; + float f0=0.0,fp,ss; int lag; - + static float fplast=-10000.0; double i0[162],q0[162],i1[162],q1[162],i2[162],q2[162],i3[162],q3[162]; double p0,p1,p2,p3,cmet,totp,syncmax,fac; double c0[256],s0[256],c1[256],s1[256],c2[256],s2[256],c3[256],s3[256]; @@ -220,7 +220,7 @@ void sync_and_demodulate(double *id, double *qd, long np, for (i=0; i<162; i++) { fp = f0 + ((float)*drift1/2.0)*((float)i-81.0)/81.0; if( i==0 || (fp != fplast) ) { // only calculate sin/cos if necessary - dphi0=2*pi*(fp-1.5*df)*dt; + dphi0=twopidt*(fp-1.5*df); cdphi0=cos(dphi0); sdphi0=sin(dphi0); @@ -343,16 +343,16 @@ void subtract_signal(double *id, double *qd, long np, for (i=0; i<162; i++) { fp = f0 + ((float)drift0/2.0)*((float)i-81.0)/81.0; - dphi=twopidt*(fp+((float)channel_symbols[i]-1.5)*df); - cdphi=cos(dphi); - sdphi=sin(dphi); + dphi=twopidt*(fp+((float)channel_symbols[i]-1.5)*df); + cdphi=cos(dphi); + sdphi=sin(dphi); - c0[0]=1; s0[0]=0; + c0[0]=1; s0[0]=0; - for (j=1; j<256; j++) { - c0[j]=c0[j-1]*cdphi - s0[j-1]*sdphi; - s0[j]=c0[j-1]*sdphi + s0[j-1]*cdphi; - } + for (j=1; j<256; j++) { + c0[j]=c0[j-1]*cdphi - s0[j-1]*sdphi; + s0[j]=c0[j-1]*sdphi + s0[j-1]*cdphi; + } i0=0.0; q0=0.0; @@ -363,30 +363,20 @@ void subtract_signal(double *id, double *qd, long np, q0=q0 - id[k]*s0[j] + qd[k]*c0[j]; } } - + // subtract the signal here. - - i0=i0/256.0; + + i0=i0/256.0; //will be wrong for partial symbols at the edges... q0=q0/256.0; - double p0=i0*i0+q0*q0; - double is=0, qs=0; for (j=0; j<256; j++) { k=shift0+i*256+j; if( (k>0) & (k511 ) - k=k-512; - ps[j][i]=fftout[k][0]*fftout[k][0]+fftout[k][1]*fftout[k][1]; - } - } - - fftw_free(fftin); - fftw_free(fftout); - - // Compute average spectrum - float psavg[512]; - memset(psavg,0.0, sizeof(float)*512); - for (i=0; ismspec[j-1]) && (smspec[j]>smspec[j+1]) && (npk<200)) { - freq0[npk]=(j-205)*df; - snr0[npk]=10*log10(smspec[j])-snr_scaling_factor; - npk++; - } - } - - // Compute corrected fmin, fmax, accounting for dial frequency error - fmin += dialfreq_error; // dialfreq_error is in units of Hz - fmax += dialfreq_error; - - // Don't waste time on signals outside of the range [fmin,fmax]. - i=0; - for( j=0; j= fmin && freq0[j] <= fmax ) { - freq0[i]=freq0[j]; - snr0[i]=snr0[j]; - i++; - } - } - npk=i; - - t0=clock(); - /* Make coarse estimates of shift (DT), freq, and drift - - * Look for time offsets up to +/- 8 symbols (about +/- 5.4 s) relative - to nominal start time, which is 2 seconds into the file - - * Calculates shift relative to the beginning of the file - - * Negative shifts mean that signal started before start of file - - * The program prints DT = shift-2 s - - * Shifts that cause sync vector to fall off of either end of the data - vector are accommodated by "partial decoding", such that missing - symbols produce a soft-decision symbol value of 128 - - * The frequency drift model is linear, deviation of +/- drift/2 over the - span of 162 symbols, with deviation equal to 0 at the center of the - signal vector. - */ - - int idrift,ifr,if0,ifd,k0; - int kindex; - float smax,ss,pow,p0,p1,p2,p3; - for(j=0; j smax ) { //Save coarse parameters - smax=sync1; - shift0[j]=128*(k0+1); - drift0[j]=idrift; - freq0[j]=(ifr-256)*df; - sync0[j]=sync1; - } - } - } - } - } - tcandidates += (double)(clock()-t0)/CLOCKS_PER_SEC; - - nbits=81; - symbols=malloc(sizeof(char)*nbits*2); - memset(symbols,0,sizeof(char)*nbits*2); - decdata=malloc((nbits+7)/8); - grid=malloc(sizeof(char)*5); - grid6=malloc(sizeof(char)*7); - callsign=malloc(sizeof(char)*13); - call_loc_pow=malloc(sizeof(char)*23); - cdbm=malloc(sizeof(char)*3); - float allfreqs[npk]; - memset(allfreqs,0,sizeof(float)*npk); - char allcalls[npk][13]; - memset(allcalls,0,sizeof(char)*npk*13); - memset(grid,0,sizeof(char)*5); - memset(grid6,0,sizeof(char)*7); - memset(callsign,0,sizeof(char)*13); - memset(call_loc_pow,0,sizeof(char)*23); - memset(cdbm,0,sizeof(char)*3); - char hashtab[32768][13]; - memset(hashtab,0,sizeof(char)*32768*13); - int nh; - if( usehashtable ) { char line[80], hcall[12]; if( (fhash=fopen(hash_fname,"r+")) ) { @@ -835,163 +653,360 @@ int main(int argc, char *argv[]) fclose(fhash); } - int uniques=0, noprint=0; - /* - Refine the estimates of freq, shift using sync as a metric. - Sync is calculated such that it is a float taking values in the range - [0.0,1.0]. - - Function sync_and_demodulate has three modes of operation - mode is the last argument: - - 0 = no frequency or drift search. find best time lag. - 1 = no time lag or drift search. find best frequency. - 2 = no frequency or time lag search. Calculate soft-decision - symbols using passed frequency and shift. - - NB: best possibility for OpenMP may be here: several worker threads - could each work on one candidate at a time. - */ - - for (j=0; j511 ) + k=k-512; + ps[j][i]=fftout[k][0]*fftout[k][0]+fftout[k][1]*fftout[k][1]; + } + } - // Fine search for frequency peak (mode 1) - fstep=0.1; - t0 = clock(); - sync_and_demodulate(idat, qdat, npoints, symbols, &f1, fstep, &shift1, - lagmin, lagmax, lagstep, &drift1, symfac, &sync1, 1); - tsync1 += (double)(clock()-t0)/CLOCKS_PER_SEC; + // Compute average spectrum + memset(psavg,0.0, sizeof(float)*512); + for (i=0; i minsync1 ) { - worth_a_try = 1; + // Smooth with 7-point window and limit spectrum to +/-150 Hz + int window[7]={1,1,1,1,1,1,1}; + float smspec[411]; + for (i=0; i<411; i++) { + smspec[i]=0.0; + for(j=-3; j<=3; j++) { + k=256-205+i+j; + smspec[i]=smspec[i]+window[j+3]*psavg[k]; + } + } + + // Sort spectrum values, then pick off noise level as a percentile + float tmpsort[411]; + for (j=0; j<411; j++) { + tmpsort[j]=smspec[j]; + } + qsort(tmpsort, 411, sizeof(float), floatcomp); + + // Noise level of spectrum is estimated as 123/411= 30'th percentile + float noise_level = tmpsort[122]; + + /* Renormalize spectrum so that (large) peaks represent an estimate of snr. + * We know from experience that threshold snr is near -7dB in wspr bandwidth, + * corresponding to -7-26.3=-33.3dB in 2500 Hz bandwidth. + * The corresponding threshold is -42.3 dB in 2500 Hz bandwidth for WSPR-15. */ + + float min_snr, snr_scaling_factor; + min_snr = pow(10.0,-7.0/10.0); //this is min snr in wspr bw + if( wspr_type == 2 ) { + snr_scaling_factor=26.3; } else { - worth_a_try = 0; + snr_scaling_factor=35.3; + } + for (j=0; j<411; j++) { + smspec[j]=smspec[j]/noise_level - 1.0; + if( smspec[j] < min_snr) smspec[j]=0.1; + continue; } - int idt=0, ii=0, jiggered_shift; - double y,sq,rms; - not_decoded=1; + // Find all local maxima in smoothed spectrum. + for (i=0; i<200; i++) { + freq0[i]=0.0; + snr0[i]=0.0; + drift0[i]=0.0; + shift0[i]=0; + sync0[i]=0.0; + } - while ( worth_a_try && not_decoded && idt<=(128/iifac)) { - ii=(idt+1)/2; - if( idt%2 == 1 ) ii=-ii; - ii=iifac*ii; - jiggered_shift=shift1+ii; + int npk=0; + for(j=1; j<410; j++) { + if((smspec[j]>smspec[j-1]) && (smspec[j]>smspec[j+1]) && (npk<200)) { + freq0[npk]=(j-205)*df; + snr0[npk]=10*log10(smspec[j])-snr_scaling_factor; + npk++; + } + } + + // Compute corrected fmin, fmax, accounting for dial frequency error + fmin += dialfreq_error; // dialfreq_error is in units of Hz + fmax += dialfreq_error; + + // Don't waste time on signals outside of the range [fmin,fmax]. + i=0; + for( j=0; j= fmin && freq0[j] <= fmax ) { + freq0[i]=freq0[j]; + snr0[i]=snr0[j]; + i++; + } + } + npk=i; + + // bubble sort on snr, bringing freq along for the ride + int pass; + float tmp; + for (pass = 1; pass <= npk - 1; pass++) { + for (k = 0; k < npk - pass ; k++) { + if (snr0[k] < snr0[k+1]) { + tmp = snr0[k]; + snr0[k] = snr0[k+1]; + snr0[k+1] = tmp; + tmp = freq0[k]; + freq0[k] = freq0[k+1]; + freq0[k+1] = tmp; + } + } + } + + t0=clock(); + /* Make coarse estimates of shift (DT), freq, and drift + + * Look for time offsets up to +/- 8 symbols (about +/- 5.4 s) relative + to nominal start time, which is 2 seconds into the file + + * Calculates shift relative to the beginning of the file + + * Negative shifts mean that signal started before start of file + + * The program prints DT = shift-2 s + + * Shifts that cause sync vector to fall off of either end of the data + vector are accommodated by "partial decoding", such that missing + symbols produce a soft-decision symbol value of 128 + + * The frequency drift model is linear, deviation of +/- drift/2 over the + span of 162 symbols, with deviation equal to 0 at the center of the + signal vector. + */ + + int idrift,ifr,if0,ifd,k0; + int kindex; + float smax,ss,pow,p0,p1,p2,p3; + for(j=0; j smax ) { //Save coarse parameters + smax=sync1; + shift0[j]=128*(k0+1); + drift0[j]=idrift; + freq0[j]=(ifr-256)*df; + sync0[j]=sync1; + } + } + } + } + } + tcandidates += (double)(clock()-t0)/CLOCKS_PER_SEC; + + /* + Refine the estimates of freq, shift using sync as a metric. + Sync is calculated such that it is a float taking values in the range + [0.0,1.0]. + + Function sync_and_demodulate has three modes of operation + mode is the last argument: + + 0 = no frequency or drift search. find best time lag. + 1 = no time lag or drift search. find best frequency. + 2 = no frequency or time lag search. Calculate soft-decision + symbols using passed frequency and shift. + + NB: best possibility for OpenMP may be here: several worker threads + could each work on one candidate at a time. + */ + + for (j=0; j minsync1 ) { + worth_a_try = 1; + } else { + worth_a_try = 0; } - rms=sqrt(sq/162.0); - if((sync1 > minsync2) && (rms > minrms)) { - deinterleave(symbols); + int idt=0, ii=0, jiggered_shift; + double y,sq,rms; + not_decoded=1; + + while ( worth_a_try && not_decoded && idt<=(128/iifac)) { + ii=(idt+1)/2; + if( idt%2 == 1 ) ii=-ii; + ii=iifac*ii; + jiggered_shift=shift1+ii; + + // Use mode 2 to get soft-decision symbols t0 = clock(); - not_decoded = fano(&metric,&cycles,&maxnp,decdata,symbols,nbits, - mettab,delta,maxcycles); - tfano += (double)(clock()-t0)/CLOCKS_PER_SEC; + sync_and_demodulate(idat, qdat, npoints, symbols, &f1, fstep, + &jiggered_shift, lagmin, lagmax, lagstep, &drift1, symfac, + &sync1, 2); + tsync2 += (double)(clock()-t0)/CLOCKS_PER_SEC; - /* ### Used for timing tests: - if(not_decoded) fprintf(fdiag, - "%6s %4s %4.1f %3.0f %4.1f %10.7f %-18s %2d %5u %4d %6.1f %2d\n", - date,uttime,sync1*10,snr0[j], shift1*dt-2.0, dialfreq+(1500+f1)/1e6, - "@ ", (int)drift1, cycles/81, ii, rms, maxnp); - */ - } - idt++; - if( quickmode ) break; - } - - if( worth_a_try && !not_decoded ) { - for(i=0; i<11; i++) { - if( decdata[i]>127 ) { - message[i]=decdata[i]-256; - } else { - message[i]=decdata[i]; + sq=0.0; + for(i=0; i<162; i++) { + y=(double)symbols[i] - 128.0; + sq += y*y; } + rms=sqrt(sq/162.0); + + if((sync1 > minsync2) && (rms > minrms)) { + deinterleave(symbols); + t0 = clock(); + not_decoded = fano(&metric,&cycles,&maxnp,decdata,symbols,nbits, + mettab,delta,maxcycles); + tfano += (double)(clock()-t0)/CLOCKS_PER_SEC; + + /* ### Used for timing tests: + if(not_decoded) fprintf(fdiag, + "%6s %4s %4.1f %3.0f %4.1f %10.7f %-18s %2d %5u %4d %6.1f %2d\n", + date,uttime,sync1*10,snr0[j], shift1*dt-2.0, dialfreq+(1500+f1)/1e6, + "@ ", (int)drift1, cycles/81, ii, rms, maxnp); + */ + } + idt++; + if( quickmode ) break; } - // Unpack the decoded message, update the hashtable, apply - // sanity checks on grid and power, and return - // call_loc_pow string and also callsign (for de-duping). - noprint=unpk_(message,hashtab,call_loc_pow,callsign); - - unsigned char channel_symbols[162]; - get_wspr_channel_symbols(call_loc_pow, channel_symbols); - - subtract_signal(idat, qdat, npoints, f1, shift1, drift1, channel_symbols); - - // Remove dupes (same callsign and freq within 1 Hz) - int dupe=0; - for (i=0; i127 ) { + message[i]=decdata[i]-256; + } else { + message[i]=decdata[i]; + } + } - printf("%4s %3.0f %4.1f %10.6f %2d %-s \n", - uttime, snr0[j],dt_print,freq_print, - (int)drift1, call_loc_pow); - fprintf(fall_wspr, - "%6s %4s %3.0f %3.0f %4.1f %10.7f %-22s %2d %5u %4d\n", - date,uttime,sync1*10,snr0[j], - dt_print, freq_print, - call_loc_pow, (int)drift1, cycles/81, ii); + // Unpack the decoded message, update the hashtable, apply + // sanity checks on grid and power, and return + // call_loc_pow string and also callsign (for de-duping). + noprint=unpk_(message,hashtab,call_loc_pow,callsign); - fprintf(fwsprd,"%6s %4s %3d %3.0f %4.1f %10.6f %-22s %2d %5u %4d\n", - date,uttime,(int)(sync1*10),snr0[j], - dt_print, freq_print, - call_loc_pow, (int)drift1, cycles/81, ii); + if( subtraction ) { + + unsigned char channel_symbols[162]; + + if( get_wspr_channel_symbols(call_loc_pow, channel_symbols) ) { + subtract_signal(idat, qdat, npoints, f1, shift1, drift1, channel_symbols); + } else { + break; + } + + } - /* For timing tests - - fprintf(fdiag, - "%6s %4s %4.1f %3.0f %4.1f %10.7f %-18s %2d %5u %4d %6.1f\n", - date,uttime,sync1*10,snr0[j], - shift1*dt-2.0, dialfreq+(1500+f1)/1e6, - call_loc_pow, (int)drift1, cycles/81, ii, rms); - */ + // Remove dupes (same callsign and freq within 3 Hz) + int dupe=0; + for (i=0; i\n"); + fftw_free(fftin); + fftw_free(fftout); + if ((fp_fftw_wisdom_file = fopen(wisdom_fname, "w"))) { fftw_export_wisdom_to_file(fp_fftw_wisdom_file); fclose(fp_fftw_wisdom_file); @@ -1032,13 +1047,13 @@ int main(int argc, char *argv[]) fclose(fhash); } - char c2filename[15]; - double carrierfreq=dialfreq; - int wsprtype=2; - strcpy(c2filename,"000000_0001.c2"); - printf("Writing %s\n",c2filename); - writec2file(c2filename, wsprtype, carrierfreq, idat, qdat); - - if(fblank+tblank+writenoise == 999) return -1; //Silence compiler warning +// char c2filename[15]; +// double carrierfreq=dialfreq; +// int wsprtype=2; +// strcpy(c2filename,"000000_0001.c2"); +// printf("Writing %s\n",c2filename); +// writec2file(c2filename, wsprtype, carrierfreq, idat, qdat); + + if(writenoise == 999) return -1; //Silence compiler warning return 0; } diff --git a/lib/wsprd/wsprsim.c b/lib/wsprd/wsprsim.c index 40bb6d67c..54189686c 100644 --- a/lib/wsprd/wsprsim.c +++ b/lib/wsprd/wsprsim.c @@ -124,22 +124,27 @@ int main(int argc, char *argv[]) { extern char *optarg; extern int optind; - int i, c, printchannel=0; + int i, c, printchannel=0, writec2=0; float snr=50.0; char *message, *c2filename; + c2filename=malloc(sizeof(char)*15); + // message length is 22 characters message=malloc(sizeof(char)*23); - c2filename=malloc(sizeof(char)*15); - memset(c2filename,0,sizeof(char)*15); + strcpy(c2filename,"000000_0001.c2"); + printf("%s\n",c2filename); while ( (c = getopt(argc, argv, "cdo:s:")) !=-1 ) { switch (c) { case 'c': printchannel=1; + break; case 'd': printdata=1; + break; case 'o': c2filename = optarg; + writec2=1; break; case 's': snr = (float)atoi(optarg); @@ -187,14 +192,10 @@ int main(int argc, char *argv[]) f0=0.0; t0=1.0; add_signal_vector(f0, t0, snr, channel_symbols, isig, qsig); - - if( strlen(c2filename) >0 ) { + if( writec2) { // write a .c2 file double carrierfreq=10.1387; int wsprtype=2; - if( strlen(c2filename) != 14 ) { - strcpy(c2filename,"000000_0001.c2"); - } printf("Writing %s\n",c2filename); writec2file(c2filename, wsprtype, carrierfreq, isig, qsig); } diff --git a/lib/wsprd/wsprsim_utils.c b/lib/wsprd/wsprsim_utils.c index 19c8de766..211065582 100644 --- a/lib/wsprd/wsprsim_utils.c +++ b/lib/wsprd/wsprsim_utils.c @@ -236,7 +236,6 @@ int get_wspr_channel_symbols(char* rawmessage, unsigned char* symbols) { } grid6[5]=grid[0]; n=pack_call(grid6); - printf("Callsign %s hash %d\n",callsign,ihash); } else if ( i2 < mlen ) { // just looks for a right slash // Type 2: PJ4/K1ABC 37 callsign=strtok(message," "); @@ -251,7 +250,7 @@ int get_wspr_channel_symbols(char* rawmessage, unsigned char* symbols) { m=128*ng+ntype+64; n=n1; } else { - printf("Error: bad message format\n"); +// printf("Error: bad message format\n"); return 0; }