#include "globdef.h" #include "uidef.h" #include "sigdef.h" #include "screendef.h" #include "fft1def.h" #include "options.h" #define MIN_DASHNO 8 #define CW_CORRELATE_GOODLEN 25 #define MIN_CORRELATE_SEP 70 #define MAX_CORRSUM 10 #define OVFL_PROTECT 0.00000000000000001 #define ZZ 0.000001 // name data length // CW_DASH |---_| 4 // CW_DOT |-_| 2 // CW_SPACE |__| 2 // CW_WORDSEP |____| 4 void check_cw(int num,int type); void find_close_parts(void); void first_morse_decode(void); void store_pattern(void); void continued_morse_decode(void); void show_cw(char *caller); // ********************************************************* // ********************************************************* // These defines allow a lot of information to be written to dmp // define DUMPFILE 1 in main.c to use this. #if DUMPFILE == TRUE #define PR00 0 #define PR01 1 #define PR02 0 #define PR03 0 #define PR04 0 #define PR05 0 #define PR06 0 #define PR07 0 #define PR08 0 #define PR09 0 #define PR10 1 #else #define PR00 0 #define PR01 0 #define PR02 0 #define PR03 0 #define PR04 0 #define PR05 0 #define PR06 0 #define PR07 0 #define PR08 0 #define PR09 0 #define PR10 0 #endif #define PRT00 if(PR00 != 0)DEB #define PRT01 if(PR01 != 0)DEB #define PRT02 if(PR02 != 0)DEB #define PRT03 if(PR03 != 0)DEB #define PRT04 if(PR04 != 0)DEB #define PRT05 if(PR05 != 0)DEB #define PRT06 if(PR06 != 0)DEB #define PRT07 if(PR07 != 0)DEB #define PRT08 if(PR08 != 0)DEB #define PRT09 if(PR09 != 0)DEB #define PRT10 if(PR10 != 0)DEB // ********************************************************* // ********************************************************* // float[2] baseb_out=data to send to loudspeaker // float[2] baseb=complex amplitude of first level coherent data. // float[2] baseb_raw=complex amplitude of baseband signal. Raw data, pol. adapted. // float[2] baseb_raw_orthog=complex amplitude of pol. orthog signal. Raw data. // float[2] baseb_carrier=phase of carrier. Complex data, cosines and sines. // float[1] baseb_carrier_ampl=amplitude of carrier // float[1] baseb_totpwr=total power of baseb_raw // float[2] baseb_envelope=complex amplitude from fitted dots and dashes. // float[1] baseb_upthreshold=forward peak detector for power // float[1] baseb_threshold=combined forward and backward peak detector at -6dB // float[2] baseb_fit=fitted dots and dashes. // float[2] baseb_tmp=array for intermediate data in complex format // float[2] baseb_sho1=array for showing intermediate data in complex format // float[2] baseb_sho2=array for showing intermediate data in complex format // float[1] baseb_agc_level=used only when AGC is enabled. // short int[1] baseb_ramp=indicator for time of power above/below threshold. // short_int[1] baseb_clock=CW code clock // float[2] baseb_tmp=for debug during development // baseb_pa The starting point of the latest mix2_size/2 points. // baseb_pb The point up to which thresholds exist. // baseb_pc The point up to which cw speed statistics and ramp is collected. // baseb_pd A key up region close before baseb_pc // baseb_pe The point up to which we have run first detect. // baseb_pf // baseb_px The oldest point that contains valid data. int best_corr_ia; int best_corr_ib; float best_corr_ampl; int corr_len; int no_of_corr; typedef struct { int pos; int sep; float fpos; float val; } CORR_INFO; CORR_INFO corr[MAX_CORRSUM]; CORR_INFO tmpcorr; void make_baseb_sho1(void) { int i,k; k=2*baseband_size; for(i=0; i baseband_sizhalf)goto outside; ia=(ia-corr_len+baseband_size)&baseband_mask; if(ia < baseband_sizhalf)goto outside; if(pa < 0)pa=cw_ptr; pb=cw_ptr; switch (cw[cw_ptr].type) { case CW_DASH: sprintf(s,"dash "); break; case CW_DOT: sprintf(s,"dot "); break; case CW_SPACE: sprintf(s,"space "); break; case CW_WORDSEP: sprintf(s,"wordsep"); break; default: sprintf(s,"{%3d} %c",cw[cw_ptr].type,cw[cw_ptr].type); break; } DEB"\n%s %5d [%8.3f]%8.3f %2d %3d (%f %f)", s,cw_ptr,cw[cw_ptr].midpoint,cw[cw_ptr].sep,cw[cw_ptr].len,cw[cw_ptr].unkn, ZZ*cw[cw_ptr].re,ZZ*cw[cw_ptr].im); outside:; } } int find_best_region(void) { int i, j, k, m; int ia, ib; float t1,da; // Locate the best region by use of the amplitudes stored in cw[].tmp j=0; t1=0; for(i=0; i t1) { j=i; t1=cw[i].tmp; } } if(best_corr_ampl==0) { best_corr_ampl=t1; } else { if(t1 < 0.33*best_corr_ampl) { PRT10"\n----------- exit -------------"); return FALSE; } } // Decide what length to use for correlation. ia=j; ib=j; k=no_of_cwdat-1; t1*=.25; m=0; get_corrlen:; da=cw[ib].midpoint-cw[ia].midpoint; if(da < 0)da+=baseband_size; if(da < MIN_CORRELATE_SEP*cwbit_pts) { if(ia > 0) { if(ib < k) { if(cw[ia-1].tmp > cw[ib+1].tmp) { goto iadec; } else { goto ibinc; } } else { goto iadec; } } else { if(ib < k) { goto ibinc; } } iadec:; if(cw[ia-1].tmp > t1) { ia--; m=-1; goto get_corrlen; } goto corrlen_x; ibinc:; if(cw[ib+1].tmp > t1) { m=1; ib++; goto get_corrlen; } } corrlen_x:; best_corr_ia=((int)(cw[ia].midpoint-3*cwbit_pts)+baseband_size)&baseband_mask; best_corr_ib=((int)(cw[ib].midpoint+3*cwbit_pts))&baseband_mask; i=(best_corr_ib-best_corr_ia+baseband_size)&baseband_mask; i-=MIN_CORRELATE_SEP*cwbit_pts; if(i > 0) { if(m>0) { ib--; best_corr_ib=(best_corr_ib-i+baseband_size)&baseband_mask; } else { ia++; best_corr_ia=(best_corr_ia+i)&baseband_mask; } } corr_len=(best_corr_ib-best_corr_ia+baseband_size)&baseband_mask; // best_corr_ia and best_corr_ib are pointers in the baseband. // Clear cw[].tmp for points within the region ia to ib so we // will not try to correlate the same region again. ib++; while(ia != ib) { cw[ia].tmp=0; ia++; } return TRUE; } void compute_region_quality(void) { int i, ia, ib; float amp, da, db, t1, trilen; // // Good regions contain several dashes and or dots within a time // frame corresponding to a morse character. // Store a triangle of width +/- CW_CORRELATE_GOODLEN norse code units // around each part. // Make the height A or 3*A for dots and dashes respectively // where A is the signal amplitude. // Good regions are characterised by sucessful detection of the // carrier so we use cw[].re for the amplitude A. trilen=CW_CORRELATE_GOODLEN*cwbit_pts; for(i=0; i=0 && da0) { cw[ia].tmp+=t1*amp; } ia--; } while(ib < no_of_cwdat && db0) { cw[ib].tmp+=t1*amp; } ib++; } keyup:; } } void find_good_regions(void) { int i, j, k, m, ia, ib, ja, jb, corr_step, clrlen; int first_corr, last_corr; float t1, t2, maxval; double dt2; // Our data does not change very rapidly. The shortest signal // duration of interest is cwbit_pts/2, the approximate time for // the separation between the 3 dB points of a Morse code dot. // Search for correlations using a step size of cwbit_pts/2; corr_step=0.5*(cwbit_pts+1); ja=baseb_px/corr_step; baseb_tmp[ja]=0; ia=ja*corr_step; corr[1].val=0; corr[1].pos=-1; getcorr_ia:; ia+=corr_step; ja++; if(ia > baseband_mask) { ia=0; ja=0; } k=(best_corr_ia-ia-corr_len+baseband_size)&baseband_mask; if(k m) { ja=0; } } ib=jb*corr_step; getcorr_ib:; k=(baseb_pc-ib-corr_len+baseband_size)&baseband_mask; if(k baseband_mask) { ib=0; jb=0; } goto getcorr_ib; } baseb_tmp[jb]=0; // If all the data is zero, return here. if(corr[1].pos ==-1)return; // Make sure that the very first point does not create a maximum // from either end. jb=jb-1; if(jb<0)jb=m; k=jb-1; if(k<0)k=m; while(baseb_tmp[k] !=0 && baseb_tmp[k] <= baseb_tmp[jb]) { baseb_tmp[jb]=0; jb=k; k--; if(k<0)k=m; } last_corr=jb; ja=baseb_px/corr_step; ja++; if(ja>0)ja=0; k=ja+1; if(k>m)k=0; while(baseb_tmp[k] !=0 && baseb_tmp[k] <= baseb_tmp[ja]) { baseb_tmp[ja]=0; ja=k; k++; if(k>m)k=0; } first_corr=ja; clrlen=1+corr_len/(4*corr_step); // Clear baseb_tmp in the surroundings of the current maximum. // Then search for the largest value in baseb_tmp. // This way corr[].pos and corr_val[] will contain correlation // maxima in descending order by corr_val. no_of_corr=1; while( no_of_corr < MAX_CORRSUM-1 && corr[no_of_corr].pos != -1) { // First just clear 2*clrlen points. i=corr[no_of_corr].pos-clrlen; if(i<0)i=m; k=corr[no_of_corr].pos+clrlen; if(k>m)k=0; j=i; while(i != k) { baseb_tmp[i]=0; i++; if(i>m)i=0; } // Clear points upwards to avoid that the first point becomes a maximum. i=k+1; if(i>m)i=0; while(baseb_tmp[i] !=0 && baseb_tmp[i] <= baseb_tmp[k]) { baseb_tmp[k]=0; k=i; i++; if(i>m)i=0; } j--; if(j<0)j=m; k=j-1; if(k<0)k=m; while(baseb_tmp[k] !=0 && baseb_tmp[k] <= baseb_tmp[j]) { baseb_tmp[j]=0; j=k; k--; if(k<0)k=m; } no_of_corr++; corr[no_of_corr].val=0; corr[no_of_corr].pos=-1; ia=first_corr; while(ia != last_corr) { if(baseb_tmp[ia] > corr[no_of_corr].val) { corr[no_of_corr].val=baseb_tmp[ia]; corr[no_of_corr].pos=ia; } ia++; if(ia>m)ia=0; } if(corr[no_of_corr].val < 0.5*corr[1].val)corr[no_of_corr].pos=-1; } while(no_of_corr>1))/corr_step; no_of_corr=0; corr[0].sep=0; maxval=0; accumulate_corr:; ia=((corr[no_of_corr].pos-1)*corr_step+baseband_mask)&baseband_mask; ib=((corr[no_of_corr].pos+1)*corr_step+2)&baseband_mask; corr[no_of_corr].val=0; while(ia!=ib) { dt2=0; i=best_corr_ia; k=ia; // Correlate to the summed function in baseb_tmp while(i != best_corr_ib) { dt2+=baseb_tmp[2*i]*baseb[2*k]+baseb_tmp[2*i+1]*baseb[2*k+1]; i=(i+1)&baseband_mask; k=(k+1)&baseband_mask; } t2=dt2*OVFL_PROTECT; if(corr[no_of_corr].val < t2) { corr[no_of_corr].pos=ia; corr[no_of_corr].val=t2; } ia=(ia+1)&baseband_mask; } if(no_of_corr==0) { PRT10"\nCorr no:%d pos:%d val %f maxval %f", no_of_corr,corr[no_of_corr].pos,corr[no_of_corr].val,maxval); goto noadd; } if(no_of_corr==1) { PRT10"\nCorr no:%d pos:%d val %f maxval %f", no_of_corr,corr[no_of_corr].pos,corr[no_of_corr].val,maxval); corr[1].sep=(corr[1].pos-best_corr_ia+baseband_size)&baseband_mask; if(corr[1].sep>(baseband_size>>1))corr[1].sep= (baseband_size-corr[1].sep)&baseband_mask; PRT10" sep%d",corr[1].sep); } // Check if this point has an integer relation to previously esteblished // repeat separations. if(no_of_corr > 1) { corr[no_of_corr].val*=corr[0].val/maxval; PRT10"\nCorr no:%d pos:%d val %f maxval %f", no_of_corr,corr[no_of_corr].pos,corr[no_of_corr].val,maxval); corr[no_of_corr].sep=(corr[no_of_corr].pos-best_corr_ia +baseband_size)&baseband_mask; if(corr[no_of_corr].sep>(baseband_size>>1))corr[no_of_corr].sep= (baseband_size-corr[no_of_corr].sep)&baseband_mask; PRT10" sep%d", corr[no_of_corr].sep); t1=(float)(corr[no_of_corr].sep)/corr[1].sep; PRT10"\nt1=%f",t1); if(corr[no_of_corr].val < 0.4*corr[0].val) { PRT10" skip for small corr_value."); goto skip_corr; } if(!(fabs(t1-1.0) < 0.015 || fabs(t1-2.0) < 0.02 || fabs(t1-3.0) < 0.03 || fabs(t1-0.5) < 0.01 || fabs(t1-1.5) < 0.015 || fabs(t1-0.33333333) < 0.01 || fabs(t1-0.66666666) < 0.015 || fabs(t1-1.33333333) < 0.015 || fabs(t1-1.66666666) < 0.015)) { PRT10" skip for non-integer ratio."); skip_corr:; i=no_of_corr+1; while(i>1; mix2_mask=mix2_size-1; // The standard procedure for detect has failed but there is // probably a signal present because we have decoded many dots // and dashes although not well enough to identify a single character. // First find a few particularly good regions, then compute // the auto-correlation function to see if the pattern repeats. compute_region_quality(); best_corr_ampl=0; DEB"\n----------- start correlate undecoded -------------"); goto zzb; retry_correlation:; DEB"\n---------- retry correlate undecoded -------------"); zzb:; if(find_best_region() == FALSE)return; find_good_regions(); if(no_of_corr==0)goto retry_correlation; // The average waveform we now have in baseb_tmp would be correct // and it would have S/N better by a factor of no_of_corr compared to an // individual waveform if AND ONLY IF the phase of the carrier // were correct and the signal level were constant. // // Real signals typically suffer from multipath propagation and // as a result there is fading, QSB. When the amplitude goes // through a minimum, the phase may twist rapidly and the // carrier phase is very uncertain at such times. Whether a // 180 degree phase shift goes over 90 or 270 degrees may depend // on the noise only and the very weak signal close to the // minimum might come out from our coherent detection procedure // with the wrong phase. Actually with a random phase. // // Coherent averaging improves S/N for reasonably good signals, // good enough to give good S/N for the carrier, but the price // to pay is that poor signals average out to nothing. // // Here we have some good coherently averaged signals in baseb_tmp. // We will use them to improve the carrier phase. // // Compute the optimum position with decimals for each sequence // from the correlation with the summed curve. Store in corr_val[] j=0; while(j t2 && t3 > t1) { // If the middle point is a local minimum, something is very wrong. // Then skip this waveform. skipit:; i=j; while(i < no_of_corr-1) { corr[i].pos=corr[i+1].pos; i++; } no_of_corr--; goto shiftfind_x; } if(t1 > t2) { t3=t2; t2=t1; ia=corr[j].pos; ia=(ia+baseband_mask)&baseband_mask; corr[j].pos=ia; dt1=0; i=best_corr_ia; k1=(ia+baseband_mask)&baseband_mask; while(i != best_corr_ib) { dt1+=baseb_tmp[2*i]*baseb[2*k1]+baseb_tmp[2*i+1]*baseb[2*k1+1]; i=(i+1)&baseband_mask; k1=(k1+1)&baseband_mask; } t1=dt1*OVFL_PROTECT; if(t1>t2)goto skipit; } if(t3 > t2) { t1=t2; t2=t3; ia=corr[j].pos; ia=(ia+1)&baseband_mask; corr[j].pos=ia; dt3=0; i=best_corr_ia; k3=(ia+1)&baseband_mask; while(i != best_corr_ib) { dt3+=baseb_tmp[2*i]*baseb[2*k3]+baseb_tmp[2*i+1]*baseb[2*k3+1]; i=(i+1)&baseband_mask; k3=(k3+1)&baseband_mask; } t3=dt3*OVFL_PROTECT; if(t3>t2)goto skipit; } parabolic_fit(&,&corr[j].fpos,t1*t1,t2*t2,t3*t3); corr[j].fpos+=ia; j++; shiftfind_x:; } if(no_of_corr < 2)goto retry_correlation; // Shift our pos references for corr[0].pos to become equal to best_corr_ia. // Then increase the separation between corr[].fpos and corr[].pos // by a factor no_of_corr/(no_of_corr-1) to compensate to some extent // for the fact that correlation contains a component of the // signal to itself. t1=corr[0].fpos-best_corr_ia; t3=(float)(no_of_corr)/(no_of_corr-1); for(i=0; i baseband_size)t2-=baseband_size; t2=corr[i].pos-t2; t2*=t3; corr[i].fpos=corr[i].pos-t2; if(corr[i].fpos < 0)corr[i].fpos+=baseband_size; if(corr[i].fpos > baseband_size)corr[i].fpos-=baseband_size; corr[i].pos=((int)(corr[i].fpos+0.5))&baseband_mask; corr[i].sep=(corr[i].pos-baseb_px+baseband_size)&baseband_mask; } // Reorder the regions from ascending order of .val (quality) // to .sep (now time = distance to baseb_px) for(i=0; i corr[j].sep) { tmpcorr=corr[i]; corr[i]=corr[j]; corr[j]=tmpcorr; } } } // Make corr[].sep the distance between consecutive regions. corr[0].sep=0; for(i=1; i corr[i].sep) { k=corr[i].sep; } } // k is now the smallest repeat separation. // Divide k by 2 or 3 (or 6) in case it is required to make // the longer separations multiples of k. i=0; while(icorr_len)k=corr_len; // Shift the start pointers by k downwards. // make sure we do not go below baseb_px. ia=0; for(i=0; i baseband_sizhalf) { j=baseband_size-j; if(j>ia)ia=j; } } for(i=0; i= baseband_size)corr[i].fpos-=baseband_size; corr[i].pos=corr[i].fpos+0.5; corr[i].pos&=baseband_mask; } k=corr_len+2*k-ia; // Make sure the last point is not above baseb_pc ib=0; for(i=0; i baseband_sizhalf) { j=baseband_size-j; if(j>ib)ib=j; } } corr_len=k-ib; for(i=0; icorr_len) { region++; if(region > no_of_corr)goto gate_x; ia=corr[region].pos; goto gate_part; } for(j=0; j0.5*bg.coh_factor)t2=0.5*bg.coh_factor; i=1+t2*carrfilter_pts; k=mix2_size-i-1; k2=i+1; k1=k; ia=2*k3-1; ib=(k2-k1-ia+mix2_size)&mix2_mask; while(i>=0) { t2=mix2_tmp[2*i ]*mix2_tmp[2*i ]+ mix2_tmp[2*i+1]*mix2_tmp[2*i+1]; mix2_pwr[i]=t2; if(t2 > t1) { j=i; t1=t2; } t2=mix2_tmp[2*k ]*mix2_tmp[2*k ]+ mix2_tmp[2*k+1]*mix2_tmp[2*k+1]; mix2_pwr[k]=t2; if(t2 > t1) { j=k; t1=t2; } i--; k++; } i=j; if(i>sizhalf)i-=mix2_size; if(abs(i) > k3)goto fail; i=k1; r1=0; r2=0; while(i!=k2) { k=(i-j+mix2_size)&mix2_mask; if(k>sizhalf)k=mix2_size-k; if(k0; i--) { t2=0; k1=(ia+mix2_mask)&mix2_mask; k2=ib; j=i; while(j!=0) { t2+=mix2_pwr[k1]-mix2_pwr[k2]; k1=(k1+mix2_mask)&mix2_mask; k2=(k2+mix2_mask)&mix2_mask; j--; } t3=0; k1=ia; k2=(ib+1)&mix2_mask; j=i; while(j!=0) { t3+=mix2_pwr[k2]-mix2_pwr[k1]; k1=(k1+1)&mix2_mask; k2=(k2+1)&mix2_mask; j--; } if(t2 > t3) { if(t2 > 0) { ia=(ia-i+mix2_size)&mix2_mask; ib=(ib-i+mix2_size)&mix2_mask; } } else { if(t3 > 0) { ia=(ia+i)&mix2_mask; ib=(ib+i)&mix2_mask; } } } // Now that we have the optimum position for the passband, // see if there is a good reason to increase the bandwidth slightly. // The noise floor is r2 and the max signal is t1. // Look at both sides for a signal that is 9 dB or more above the noise. // The signal must also be less than 15 dB below the main signal. t3=r2*4; if(t3 < t1/32)t3=t1/32; for(i=1; it3) { ia=k1; } if(mix2_pwr[k2]>t3) { ib=k2; } } // Now, use a 3 dB lower threshold and see if we can make the // filter narrower. t3/=2; while(mix2_pwr[ia] < t3)ia=(ia+1)&mix2_mask; while(mix2_pwr[ib] < t3)ib=(ib+mix2_mask)&mix2_mask; } // Now that we decided to use the frequencies from ia to ib, clear all // other frequencies in our buffer. ib=(ib+1)&mix2_mask; while(ib != ia) { mix2_tmp[2*ib ]=0; mix2_tmp[2*ib+1]=0; ib=(ib+1)&mix2_mask; } // Go back to the time domain to get the carrier. fftback(mix2_size, mix2_n, mix2_tmp, mix2_table, mix2_permute); if(region_flag < 3) { ia=(ka+sizhalf)&baseband_mask; for(i=sizhalf; i 2) { cw_detect_flag=CWDETECT_DEBUG_STOP;//öö xz("STOP!!"); } return;//öööööööööööööööööööööööööööööööööööööööööööööööööööö // Use the carrier now present in baseb_tmp[] to compute a new // and hopefully more accurate coherently detected signal in baseb_fit[] // Accumulate the average waveform in baseb_envelope[]. // !!!!!!!!!!!!!!!!!!!!! for debug purposes !!!!!!!!!!!!!!! // ö ö ö for(i=0;i40 if sucessful } void show_cw(char *caller) { int i; char s[80]; DEB"\n**** show_cw: (%s) *****",caller); DEB"\n N [mid] sep l unk tmp"); for(i=0; i cw_ptr) { cw[i]=cw[i-1]; i--; } if(cw_ptr != no_of_cwdat) { r2=cw[cw_ptr].midpoint-cg_wave_midpoint; if(r2 < 0)r2+=baseband_size; cw[cw_ptr+1].sep=r2; } cw[cw_ptr].midpoint=cg_wave_midpoint; cw[cw_ptr].type=type; cw[cw_ptr].re=cg_wave_re; cw[cw_ptr].im=cg_wave_im; cw[cw_ptr].len=cw_item_len[255-type]; cw[cw_ptr].unkn=-1; //DEB"\ninsert[%f] type %s len %d sep[i] %.0f sep[i+1] %.0f", // cg_wave_midpoint,cw_item_text[255-type],cw_item_len[255-type],r1,r2); cw[cw_ptr].sep=r1; no_of_cwdat++; if(no_of_cwdat > max_cwdat)lirerr(1189); } void second_detect(void) { int sizhalf; int k, ia; int ity, item; float dotpos1_nom, dotpos2_nom, dotpos3_nom, dotpos4_nom; float dashpos1_nom, dashpos2_nom; float dot1_chk, dot2_chk, dot3_chk, dot4_chk, dash1_chk, dash2_chk; float dashpos1, dashpos2, dotpos1, dotpos2, dotpos3, dotpos4; float dashpos1_err, dashpos2_err; float dotpos1_err, dotpos2_err, dotpos3_err, dotpos4_err; float dash_fit1, dash_fit2, dot_fit1, dot_fit2, dot_fit3, dot_fit4; float r1, t1, t2, t3; float dash1_re, dash1_im, dash2_re, dash2_im; float dot1_re, dot1_im, dot2_re, dot2_im, dot4_re, dot4_im; sizhalf=baseband_size>>1; // Initiate with stupid values. This avoids compiler complaints. dot1_chk=-BIG; dotpos1=BIG; dotpos1_err=BIG; dot1_re=BIG; dot1_im=BIG; dot_fit1=-BIG; dot2_chk=-BIG; dotpos2=BIG; dotpos2_err=BIG; dot2_re=BIG; dot2_im=BIG; dot_fit2=-BIG; dot_fit3=-BIG; dotpos1_nom=BIG; dotpos2_nom=BIG; dash_fit2=-BIG; dot_fit4=-BIG; // ***************************************************** // ***************************************************** // ***** Fit key up or key down patterns to short ***** // ***** regions that are surrounded by dashes ***** // ***************************************************** // ***************************************************** // Look for the following cases: // -_-_ 2 // ---_- 3 // -_--- 3 // -___- 4 // ---___- 5 // -___--- 5 // -_____- 7 // // Code Sep // ---_--- 4 // ---_-_--- 6 // ---___--- 6 // ---_-_-_--- 8 // ---_---_--- 8 // ---___-_--- 8 // ---_-___--- 8 // ---_____--- 8 // ---_---_?_--- 10 // ---_?_---_--- 10 // ---_---_????.... default // ....????_---_--- default // Look for the following cases: // Code Unkn // |__| 2 // |-_| 2 // |____| 4 // |-___| 4 // |__-_| 4 // |-_-_| 4 // |---_| 4 // |______| 6 // |-_____| 6 // |__-___| 6 //show_cw("second_detect enter"); PRT02"\nsecond_detect: CWBIT PTS %f x",cwbit_pts); // Store the most probable signal in each case. // name data length // CW_DASH |---_| 4 // CW_DOT |-_| 2 // CW_SPACE |__| 2 // CW_WORDSEP |____| 4 // The item starts (length/2 + 0.5) below cw[].midpoint because // midpoint is the center of a key down period while the item // midpoint is 0.5 above that because one space is always // included in an item. PRT02"\n0 PRT02(2)> [%7.1f] ", cw[0].midpoint); cw_ptr=1; while(cw_ptr < no_of_cwdat && cw[cw_ptr].type==CW_DASH && cw[cw_ptr-1].type==CW_DASH) { r1=cw[cw_ptr].sep/cwbit_pts; if( (cw[cw_ptr].type==CW_DASH && cw[cw_ptr-1].type==CW_DASH)|| (cw[cw_ptr].type==CW_DOT && cw[cw_ptr-1].type==CW_DOT) ) { k=(int)(r1)&0xfffffffe; if(r1-k > 1)k+=2; } else { if( (cw[cw_ptr].type==CW_DASH && cw[cw_ptr-1].type==CW_DOT)|| (cw[cw_ptr].type==CW_DOT && cw[cw_ptr-1].type==CW_DASH) ) { k=(int)(r1+1)&0xfffffffe; if(r1+1-k > 1)k+=2; k--; goto skip; // !!!!! REMOVE !!!!! } else { goto skip; } } PRT02"\n%d PRT02(2)>k=%3d [%7.1f][%7.1f]",cw_ptr,k,cw[cw_ptr-1].midpoint, cw[cw_ptr].midpoint); switch (k) { case 2: case 4: // We have two close spaced dashes. // There can be nothing in between. break; case 6: // ---_-_--- 6 // ---___--- 6 // Base the decision on the probability that there is a dot // right between the two dashes. set_region_envelope(); dotpos1_nom=cw[cw_ptr].midpoint-0.5*cw[cw_ptr].sep; if(dotpos1_nom < 0)dotpos1_nom+=baseband_size; dot1_chk=check_dot(dotpos1_nom); PRT03"\n{6}CHK DOT %f (%f)",dot1_chk,dotpos1_nom); // If dot1_chk in negative, the fit is better for a space than for a dot. // Be careful and check if a dot would fit even for dot1_chk a little below zero. if(dot1_chk > -0.25) { cg_wave_start=dotpos1_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); PRT03" [%f]", cg_wave_midpoint); dotpos1_err=dotpos1_nom-cg_wave_midpoint; if(dotpos1_err < -sizhalf)dotpos1_err+=baseband_size; PRT03" poserr %f fit %f",dotpos1_err/cwbit_pts,cg_wave_fit); t3=-1; // Use t3 as an indicator. // t3 < 0 => store space. // t3 > 0 => store dot using t3 as the weight on fit_dot data. if(cg_wave_fit > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.25*cwbit_pts && dot1_chk > 0) { if(dot1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } if(cg_wave_fit > DOT_MISFIT_LIMIT && fabs(dotpos1_err) < 0.5*cwbit_pts && dot1_chk > 0.5)t3=0.5; if(t3 >= 0) { t2=1-t3; cg_wave_midpoint+=t2*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*cg_wave_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*cg_wave_im+t2*baseb_envelope[2*ia+1]; clear_region(); PRT03"\nstore dot %f",cg_wave_midpoint); item=CW_DOT; } else { if( dot1_chk > 0) { item=0; } else { cg_wave_midpoint=dotpos1_nom; clear_region(); PRT03"\nstore space %f",cg_wave_midpoint); item=CW_SPACE; } } } else { cg_wave_midpoint=dotpos1_nom; clear_region(); PRT03"\nstore space %f",cg_wave_midpoint); item=CW_SPACE; } if(item!=0)insert_item(item); if(kill_all_flag)return; break; case 8: // ---_-_-_--- 8 ity=4 // ---_---_--- 8 ity=1 // ---___-_--- 8 ity=3 // ---_-___--- 8 ity=2 // ---_____--- 8 ity=5 set_region_envelope(); ity=0; // See if a dash will fit well. dashpos1_nom=cw[cw_ptr].midpoint-0.5*cw[cw_ptr].sep; if(dashpos1_nom < 0)dashpos1_nom+=baseband_size; dash1_chk=check_dash(dashpos1_nom); cg_wave_start=dashpos1_nom-(dash_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dash(); dash1_re=cg_wave_re; dash1_im=cg_wave_im; dashpos1=cg_wave_midpoint; dashpos1_err=dashpos1_nom-cg_wave_midpoint; if(dashpos1_err < -sizhalf)dashpos1_err+=baseband_size; dash_fit1=cg_wave_fit; if( dash_fit1 > DASH_DETECT_LIMIT && dash1_chk > 0.5 && fabs(dashpos1_err) < 0.5*cwbit_pts) { ity=1; PRT04"\n{8}GOOD DASH %f fit= %f (%f) [%f]", dash1_chk,cg_wave_fit,dashpos1_nom,cg_wave_midpoint); goto store8; } PRT04"\n{8}CHK DASH %f fit= %f (%f) [%f]", dash1_chk,cg_wave_fit,dashpos1_nom,cg_wave_midpoint); dotpos1_nom=cw[cw_ptr].midpoint-0.625*cw[cw_ptr].sep; if(dotpos1_nom < 0)dotpos1_nom+=baseband_size; dot1_chk=check_dot(dotpos1_nom); dotpos2_nom=cw[cw_ptr].midpoint-0.375*cw[cw_ptr].sep; if(dotpos2_nom < 0)dotpos2_nom+=baseband_size; dot2_chk=check_dot(dotpos2_nom); if(dash1_chk < -0.25 && dot1_chk < -0.25 && dot2_chk < -0.25) { PRT04"\nALL chk NEG (< -0.25)"); ity=5; goto store8; } cg_wave_start=dotpos1_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dot1_re=cg_wave_re; dot1_im=cg_wave_im; dotpos1=cg_wave_midpoint; dotpos1_err=dotpos1_nom-cg_wave_midpoint; if(dotpos1_err < -sizhalf)dotpos1_err+=baseband_size; dot_fit1=cg_wave_fit; PRT04"\n{8}CHK DOT1 %f fit=%f (%f) [%f]", dot1_chk,cg_wave_fit,dotpos1_nom,cg_wave_midpoint); cg_wave_start=dotpos2_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dot2_re=cg_wave_re; dot2_im=cg_wave_im; dotpos2=cg_wave_midpoint; dotpos2_err=dotpos2_nom-cg_wave_midpoint; if(dotpos2_err < -sizhalf)dotpos2_err+=baseband_size; dot_fit2=cg_wave_fit; PRT04"\n{8}CHK DOT2 %f fit=%f (%f) [%f]", dot2_chk,cg_wave_fit,dotpos2_nom,cg_wave_midpoint); if( dash_fit1 < DASH_MISFIT_LIMIT || ( (dash1_chk < -0.25 && fabs(dashpos1_err) < 0.5*cwbit_pts) || ( dash1_chk < 0.25 && fabs(dashpos1_err) < cwbit_pts ) ) ) { // The fit to a dash is very poor. // Set ity to 5 (word separator), then check whether dot or space will fit. ity=5; PRT04"\nset ity=5"); PRT04"\ndot1 fit %f chk %f poserr %f ",dot_fit1,dot1_chk,dotpos1_err); if( dot_fit1 > DOT_MISFIT_LIMIT && dot1_chk > 0.25 && fabs(dotpos1_err) < 0.5*cwbit_pts ) { ity=2; PRT04"\nset ity=2"); } PRT04"\ndot2 fit %f chk %f poserr %f ",dot_fit2,dot2_chk,dotpos2_err); if( dot_fit2 > DOT_MISFIT_LIMIT && dot2_chk > 0.25 && fabs(dotpos2_err) < 0.5*cwbit_pts ) { if(ity == 2) { ity=4; PRT04"\nset ity=4"); } else { ity=3; PRT04"\nset ity=3"); } } goto store8; } PRT04"\nUnclear"); // The situation is unclear. Nothing fits well........... if(dash1_chk < 0 && dot1_chk < 0 && dot2_chk < 0) { ity=5; PRT04"\nset ity=5 (all neg)"); goto store8; } t1=dot1_chk*dot2_chk; if( fabs(t1) > dash1_chk*fabs(dash1_chk) ) { // The fit to a dash is worse than dots and/or spaces. PRT04"\nset ity=5 (dash worse than dots)"); ity=5; if( dot_fit1 > DOT_MISFIT_LIMIT && dot1_chk > 0 && fabs(dotpos1_err) < 0.7*cwbit_pts ) { ity=2; PRT04"\nset ity=2"); } if( dot_fit2 > DOT_MISFIT_LIMIT && dot2_chk > 0 && fabs(dotpos2_err) < 0.7*cwbit_pts ) { if(ity == 2) { ity=4; PRT04"\nset ity=4"); } else { ity=3; PRT04"\nset ity=3"); } } goto store8; } if( dash_fit1 > DASH_MISFIT_LIMIT && (dash1_chk > 0 || fabs(dashpos1_err) < cwbit_pts ) ) { ity=1; PRT04"\nset ity=1"); } store8:; clear_region(); PRT04"\nity=%d",ity); switch (ity) { case 1: // Store a dash // ---_---_--- 8 ity=1 t3=0; if(dash_fit1 > DASH_DETECT_LIMIT && fabs(dashpos1_err) < 0.25*cwbit_pts) { if(dash1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dashpos1+t2*dashpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dash1_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dash1_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore_dash %f",cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; cw_ptr++; break; case 2: // Store dot1 and space // ---_-___--- 8 ity=2 // ---___-_--- 8 ity=3 // ---_____--- 8 ity=5 // ---_-_-_--- 8 ity=4 t3=0; if(dot_fit1 > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.25*cwbit_pts) { if(dot1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos1+t2*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot1_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot1_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; cg_wave_midpoint=dotpos2_nom; PRT04"\nstore_space %f",cg_wave_midpoint); insert_item(CW_SPACE); if(kill_all_flag)return; cw_ptr++; break; case 3: // Store space and dot2 // ---___-_--- 8 ity=3 cg_wave_midpoint=dotpos1_nom; PRT04"\nstore_space %f",cg_wave_midpoint); insert_item(CW_SPACE); if(kill_all_flag)return; cw_ptr++; t3=0; if(dot_fit2 > DOT_DETECT_LIMIT && fabs(dotpos2_err) < 0.25*cwbit_pts) { if(dot2_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos2+t2*dotpos2_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot2_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot2_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; break; case 4: // Store dot1 and dot2 // ---_-_-_--- 8 ity=4 t3=0; if(dot_fit1 > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.25*cwbit_pts) { if(dot1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos1+t2*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot1_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot1_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; t3=0; if(dot_fit2 > DOT_DETECT_LIMIT && fabs(dotpos2_err) < 0.25*cwbit_pts) { if(dot2_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos2+t2*dotpos2_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot2_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot2_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; break; case 5: // Store word separator // ---_____--- 8 ity=5 cg_wave_midpoint=dashpos1_nom; PRT04"\nstore word sep %f",cg_wave_midpoint); insert_item(CW_WORDSEP); if(kill_all_flag)return; cw_ptr++; break; } break; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // ööAug 2004 // default: if(k==10) { set_region_envelope(); } else { set_long_region_envelope(); } // See if a dash will fit in the first position. dashpos1_nom=cw[cw_ptr-1].midpoint+4*cwbit_pts; if(dashpos1_nom >= baseband_size)dashpos1_nom-=baseband_size; dash1_chk=check_dash(dashpos1_nom); cg_wave_start=dashpos1_nom-(dash_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dash(); dash1_re=cg_wave_re; dash1_im=cg_wave_im; dashpos1=cg_wave_midpoint; dashpos1_err=dashpos1_nom-cg_wave_midpoint; if(dashpos1_err < -sizhalf)dashpos1_err+=baseband_size; dash_fit1=cg_wave_fit; PRT05"\n{def.l(2)}CHK DASH %f fit= %f (%f) [%f]", dash1_chk,dash_fit1,dashpos1_nom,cg_wave_midpoint); // See if a dot will fit in the first position dotpos1_nom=dashpos1_nom-cwbit_pts; if(dotpos1_nom < 0)dotpos1_nom+=baseband_size; dot1_chk=check_dot(dotpos1_nom); cg_wave_start=dotpos1_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dot1_re=cg_wave_re; dot1_im=cg_wave_im; dotpos1=cg_wave_midpoint; dotpos1_err=dotpos1_nom-cg_wave_midpoint; if(dotpos1_err < -sizhalf)dotpos1_err+=baseband_size; dot_fit1=cg_wave_fit; PRT05"\n{def.l(2)}CHK DOT1 %f fit=%f (%f) [%f]", dot1_chk,dot_fit1,dotpos1_nom,cg_wave_midpoint); ity=0; if( (dash_fit1 > DASH_DETECT_LIMIT && fabs(dashpos1_err) < 0.7*cwbit_pts && dash1_chk > 0) || (dash_fit1 > DASH_MISFIT_LIMIT && fabs(dashpos1_err) < 0.5*cwbit_pts && dash1_chk > 0.5) ) { // A dash fits reasonably well in the lower position. ity=1; // Check how well dots would fit at the beginning or end of the dash position. if(fabs(dotpos1_err) < 0.7 * cwbit_pts && dash_fit1 < dot_fit1+0.05 && dot1_chk > 0) { PRT05"\ndot1.l good(2). Skip dash for now."); ity=0; if( dot_fit1 > DOT_DETECT_LIMIT || (dot_fit1 > DOT_MISFIT_LIMIT && fabs(dotpos1_err) < 0.5*cwbit_pts && dash_fit1 < dot_fit1 && dot1_chk > 0.5) ) { goto store_dot1; } } else { dotpos2_nom=dashpos1_nom+cwbit_pts; if(dotpos2_nom >= baseband_size)dotpos2_nom-=baseband_size; dot2_chk=check_dot(dotpos2_nom); cg_wave_start=dotpos2_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dotpos2=cg_wave_midpoint; dotpos2_err=dotpos2_nom-cg_wave_midpoint; if(dotpos2_err < -sizhalf)dotpos2_err+=baseband_size; dot_fit2=cg_wave_fit; PRT05"\n{def.l}CHK DOT2 %f fit=%f (%f) [%f]", dot2_chk,cg_wave_fit,dotpos2_nom,cg_wave_midpoint); if(fabs(dotpos2_err) < 0.5 * cwbit_pts && dash_fit1 < dot_fit2+0.05 && dot2_chk > 0.25) { PRT05"\ndot2.h good, skip dash for now."); ity=0; } } } else { // A dash did not fit in the first position. // See if a dot does. if( (dot_fit1 > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.7*cwbit_pts && dot1_chk > 0) || (dot_fit1 > DOT_MISFIT_LIMIT && fabs(dotpos1_err) < 0.5*cwbit_pts && dot1_chk > 0.5) ) { PRT05"\ndash1 bad, dot1 good."); store_dot1:; cg_wave_midpoint=dotpos1+0.5*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=0.5*dot1_re+0.5*baseb_envelope[2*ia ]; cg_wave_im=0.5*dot1_im+0.5*baseb_envelope[2*ia+1]; PRT05"\nstore dot1.l %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; } } // See if a dash will fit in the upper position. dashpos2_nom=cw[cw_ptr].midpoint-4*cwbit_pts; if(dashpos2_nom < 0)dashpos2_nom+=baseband_size; dash2_chk=check_dash(dashpos2_nom); cg_wave_start=dashpos2_nom-(dash_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dash(); dash2_re=cg_wave_re; dash2_im=cg_wave_im; dashpos2=cg_wave_midpoint; dashpos2_err=dashpos2_nom-cg_wave_midpoint; if(dashpos2_err < -sizhalf)dashpos2_err+=baseband_size; dash_fit2=cg_wave_fit; PRT05"\n{def.h(2)}CHK DASH %f fit= %f (%f) [%f]", dash2_chk,dash_fit2,dashpos2_nom,cg_wave_midpoint); dotpos4_nom=dashpos2_nom+cwbit_pts; if(dotpos4_nom >= baseband_size)dotpos4_nom-=baseband_size; dot4_chk=check_dot(dotpos4_nom); cg_wave_start=dotpos4_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dot4_re=cg_wave_re; dot4_im=cg_wave_im; dotpos4=cg_wave_midpoint; dotpos4_err=dotpos4_nom-cg_wave_midpoint; if(dotpos4_err < -sizhalf)dotpos4_err+=baseband_size; dot_fit4=cg_wave_fit; PRT05"\n{def.h(2)}CHK DOT4 %f fit=%f (%f) [%f]", dot4_chk,dot_fit4,dotpos4_nom,cg_wave_midpoint); if( (dash_fit2 > DASH_DETECT_LIMIT && fabs(dashpos2_err) < 0.7*cwbit_pts && dash2_chk > 0) || (dash_fit2 > DASH_MISFIT_LIMIT && fabs(dashpos2_err) < 0.5*cwbit_pts && dash2_chk > 0.5) ) { // A dash fits reasonably well in the upper position. ity|=2; // Check how well dots would fit at the beginning or end of the dash position. if( fabs(dotpos4_err) < 0.7 * cwbit_pts && dash_fit2 < dot_fit4+0.05 && dot4_chk > 0) { PRT05"\ndot4.h good(2), skip dash for now."); ity&=1; if( dot_fit4 > DOT_DETECT_LIMIT || (dot_fit4 > DOT_MISFIT_LIMIT && fabs(dotpos4_err) < 0.5*cwbit_pts && dash_fit2 < dot_fit4 && dot4_chk > 0.5) ) { goto store_dot4; } } else { dotpos3_nom=dashpos2_nom-cwbit_pts; if(dotpos3_nom < 0)dotpos3_nom+=baseband_size; dot3_chk=check_dot(dotpos3_nom); cg_wave_start=dotpos3_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dotpos3=cg_wave_midpoint; dotpos3_err=dotpos3_nom-cg_wave_midpoint; if(dotpos3_err < -sizhalf)dotpos3_err+=baseband_size; dot_fit3=cg_wave_fit; PRT05"\n{def.h}CHK DOT3 %f fit=%f (%f) [%f]", dot3_chk,cg_wave_fit,dotpos3_nom,cg_wave_midpoint); if(fabs(dotpos3_err) < 0.5 * cwbit_pts && dash_fit2 < dot_fit3+0.05 && dot3_chk > 0.25) { PRT05"\ndot3.h good, skip dash for now."); ity&=1; } } if( (ity & 3) == 3 && k < 12) { // We can not have one dash at each side in a short region. // choose the best one. if( fabs(dash_fit1-dash_fit2) > 0.05) { if(dash_fit1 > dash_fit2) { ity=1; } else { ity=2; } } else { t1=2*dash_fit1-dot_fit1-dot_fit2-2*dash_fit2+dot_fit3+dot_fit4; if(fabs(t1) < 0.05) { ity=0; } else { if(t1>0)ity=1; else ity=2; } } } } else { // A dash did not fit in the upper position. // See if a dot does. if( (dot_fit4 > DOT_DETECT_LIMIT && fabs(dotpos4_err) < 0.7*cwbit_pts && dot4_chk > 0) || (dot_fit4 > DOT_MISFIT_LIMIT && fabs(dotpos4_err) < 0.5*cwbit_pts && dot4_chk > 0.5) ) { PRT05"\ndash2 bad, dot4 good."); store_dot4:; ity|=4; } } if( (ity & 3) == 3 && k < 12) { // We can not have one dash at each side in a short region. // choose the best one. if( fabs(dash_fit1-dash_fit2) > 0.05) { if(dash_fit1 > dash_fit2) { ity=1; } else { ity=2; } } else { t1=2*dash_fit1-dot_fit1-dot_fit2-2*dash_fit2+dot_fit3+dot_fit4; if(fabs(t1) < 0.05) { ity=0; } else { if(t1>0)ity=1; else ity=2; } } } if( (ity&1) != 0) { cg_wave_midpoint=dashpos1+0.5*dashpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=0.5*dash1_re+0.5*baseb_envelope[2*ia ]; cg_wave_im=0.5*dash1_im+0.5*baseb_envelope[2*ia+1]; PRT05"\nstore_dash.l %f",cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; cw_ptr++; } if( (ity&2) != 0) { cg_wave_midpoint=dashpos2+0.5*dashpos2_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=0.5*dash2_re+0.5*baseb_envelope[2*ia ]; cg_wave_im=0.5*dash2_im+0.5*baseb_envelope[2*ia+1]; PRT05"\nstore_dash.h %f",cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; cw_ptr++; } if( (ity&4) != 0) { cg_wave_midpoint=dotpos4+0.5*dotpos4_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=0.5*dot4_re+0.5*baseb_envelope[2*ia ]; cg_wave_im=0.5*dot4_im+0.5*baseb_envelope[2*ia+1]; PRT05"\nstore dot4.h(2) %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; } break; } skip:; cw_ptr++; } //show_cw("second_detect exit"); } void get_old_dashes(void) { int i, j, k; int lmin, lmax; int ia, ib; float t1,t2,t3; MORSE_DECODE_DATA *tmpcw; tmpcw=(void *)fftn_tmp; // Speed and wave forms are known. // Look for new dashes, in the range cw[0].midpoint // to baseb_px. ia=baseb_px; while(baseb_ramp[ia] < 0)ia=(ia+1)&baseband_mask; while(baseb_ramp[ia] > 0)ia=(ia+1)&baseband_mask; while(baseb_ramp[ia] < 0)ia=(ia+1)&baseband_mask; ia=(ia+baseband_mask)&baseband_mask; ib=cw[0].midpoint; ib=(ib-abs(baseb_ramp[ib])+baseband_size)&baseband_mask; if(baseb_ramp[ib] > 0)ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; if(no_of_cwdat < 1) { i=0; } else { i=((int)(cw[no_of_cwdat-1].midpoint)-ib+baseband_size)&baseband_mask; } if( i+((ib-ia+baseband_size)&baseband_mask) > baseband_neg)return; lmax=4.5*cwbit_pts; lmin=1.5*cwbit_pts; check_cw(30010,0); if(kill_all_flag)return; if(PR02 == 1)show_cw("get old dashes 1"); k=-1; while( ib != ia) { // Step downwards by the (negative) ramp length. // We should always be on a negative ramp here. ib=(ib+baseb_ramp[ib]+baseband_size)&baseband_mask; if( ((ib-baseb_pe+baseband_size)&baseband_mask) > baseband_neg)goto good_dash_x; // Now we must be on a positive ramp. if(baseb_ramp[ib] >= lmin && baseb_ramp[ib] <= lmax) { cg_wave_start= (ib-((baseb_ramp[ib]+dot_pts)>>1)+baseband_size)&baseband_mask; fit_dot(); t2=cg_wave_fit; cg_wave_start= (ib-((baseb_ramp[ib]+dash_pts)>>1)+baseband_size)&baseband_mask; fit_dash(); PRT00"\n%f %f [%f]",cg_wave_fit, t2, cg_wave_midpoint); t3=fabs(cw[0].midpoint-cg_wave_midpoint); if(t3 > baseband_size/2)t3-=baseband_size; if(cg_wave_fit >DASH_DETECT_LIMIT && cg_wave_fit-t2 > 0.05 && fabs(t3) > 3.3*cwbit_pts) { accurate_store_dash(); if(cg_wave_dat > 5*cg_wave_err) { k++; PRT00"\n store dash %d %f (get_old_dashes) S/N=%f ", k,cg_wave_midpoint,cg_wave_err/cg_wave_dat); tmpcw[k].type=CW_DASH; tmpcw[k].midpoint=cg_wave_midpoint; tmpcw[k].re=cg_wave_re; tmpcw[k].im=cg_wave_im; if(k+no_of_cwdat > max_cwdat) { lirerr(1190); return; } } } } ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; } good_dash_x:; // Reorder cw data. if(k != -1) { i=no_of_cwdat-1; j=i+k+1; while(i >= 0) { cw[j]=cw[i]; //fprintf(dmp,"\n%d %f",j,cw[j].midpoint); j--; i--; } while(k >= 0) { cw[j]=tmpcw[k]; //fprintf(dmp,"\n=>%d %f",j,cw[j].midpoint); t1=cw[j+1].midpoint-cw[j].midpoint; if(t1 < 0)t1+=baseband_size; cw[j+1].sep=t1; j--; k--; no_of_cwdat++; } t1=cw[0].midpoint-baseb_px; if(t1 < 0)t1+=baseband_size; cw[0].sep=t1; } if(PR02 == 1)show_cw("get old dashes 4"); check_cw(31001,0); } void get_recent_dashes(void) { char s[80]; int i, j; int lmin, lmax, old_no_of_dashes; int ia, ib; float t1,t2; MORSE_DECODE_DATA *tmpcw; tmpcw=(void *)fftn_tmp; // Speed and wave forms are known. // Look for new dashes, in the range cw[no_of_cwdat-1].midpoint // to baseb_pc. ia=cw[no_of_cwdat-1].midpoint; while(baseb_ramp[ia] > 0)ia=(ia+1)&baseband_mask; while(baseb_ramp[ia] < 0)ia=(ia+1)&baseband_mask; ia=(ia+baseband_mask)&baseband_mask; // ia now points to the last point of the first negative ramp // after the most recent cw[].midpoint. ib=baseb_pc; if(baseb_ramp[ib] > 0)ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; if( ((ib-ia+baseband_size)&baseband_mask) > baseband_neg)return; old_no_of_dashes=no_of_cwdat; lmax=4.5*cwbit_pts; lmin=1.5*cwbit_pts; check_cw(30032,0); if(kill_all_flag)return; while( ib != ia) { // Step downwards by the (negative) ramp length. // We should always be on a negative ramp here. ib=(ib+baseb_ramp[ib]+baseband_size)&baseband_mask; if( ((ib-baseb_pe+baseband_size)&baseband_mask) > baseband_neg)goto good_dash_x; // Now we must be on a positive ramp. if(baseb_ramp[ib] >= lmin && baseb_ramp[ib] <= lmax) { cg_wave_start= (ib-((baseb_ramp[ib]+dot_pts)>>1)+baseband_size)&baseband_mask; fit_dot(); t2=cg_wave_fit; cg_wave_start= (ib-((baseb_ramp[ib]+dash_pts)>>1)+baseband_size)&baseband_mask; fit_dash(); PRT00"\n%f %f [%f]",cg_wave_fit, t2, cg_wave_midpoint); if(cg_wave_fit >DASH_DETECT_LIMIT && cg_wave_fit-t2 > 0.05 ) { check_cw(3002,0); if(kill_all_flag)return; PRT00"\nstore dash %f (get_recent_dashes)", cg_wave_midpoint); accurate_store_dash(); cw_ptr=no_of_cwdat; insert_item(CW_DASH); check_cw(3003,0); if(kill_all_flag)return; } } ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; } good_dash_x:; check_cw(30031,0); if(kill_all_flag)return; sprintf(s,"%4d ",no_of_cwdat); settextcolor(15); lir_pixwrite(MAX_CG_OSCW+2,0,s); settextcolor(7); // We stepped backwards using the ramps when collecting dash midpoints. // Reorder dash midpoints if(no_of_cwdat - old_no_of_dashes > 1) { i=no_of_cwdat-1; j=old_no_of_dashes; while(j < i) { tmpcw[0]=cw[i]; cw[i]=cw[j]; cw[j]=tmpcw[0]; j++; i--; } } // Collect dash separations for(i=old_no_of_dashes; i>1; // Initiate with stupid values. This avoids compiler complaints. dot1_chk=-BIG; dotpos1=BIG; dotpos1_err=BIG; dot1_re=BIG; dot1_im=BIG; dot_fit1=-BIG; dot2_chk=-BIG; dotpos2=BIG; dotpos2_err=BIG; dot2_re=BIG; dot2_im=BIG; dot_fit2=-BIG; dotpos1_nom=BIG; dotpos2_nom=BIG; dash_fit2=-BIG; dot_fit4=-BIG; // ***************************************************** // ***************************************************** // ***** Fit key up or key down patterns to short ***** // ***** regions that are surrounded by dashes ***** // ***************************************************** // ***************************************************** // Look for the following cases: // Code Sep // ---_--- 4 // ---_-_--- 6 // ---___--- 6 // ---_-_-_--- 8 // ---_---_--- 8 // ---___-_--- 8 // ---_-___--- 8 // ---_____--- 8 // ---_---_?_--- 10 // ---_?_---_--- 10 // ---_---_????.... default // ....????_---_--- default #if PR02 == 1 show_cw("first_detect enter"); PRT02"\nfirst_detect: CWBIT PTS %f",cwbit_pts); #endif // Store the most probable signal in each case. // name data length // CW_DASH |---_| 4 // CW_DOT |-_| 2 // CW_SPACE |__| 2 // CW_WORDSEP |____| 4 // The item starts (length/2 + 0.5) below cw[].midpoint because // midpoint is the center of a key down period while the item // midpoint is 0.5 above that because one space is always // included in an item. cw_ptr=first_item; while(cw_ptr < no_of_cwdat) { r1=cw[cw_ptr].sep/cwbit_pts; k=(int)(r1)&0xfffffffe; if(r1-k > 1)k+=2; PRT02"\n%d PRT02(1)> k=%d %f [%f][%f]",cw_ptr,k, r1,cw[cw_ptr-1].midpoint,cw[cw_ptr].midpoint); check_cw(467501,1); if(kill_all_flag)return; switch (k) { case 2: case 4: // We have two close spaced dashes. // There can be nothing in between. break; case 6: // ---_-_--- 6 // ---___--- 6 // Base the decision on the probability that there is a dot // right between the two dashes. set_region_envelope(); dotpos1_nom=cw[cw_ptr].midpoint-0.5*cw[cw_ptr].sep; if(dotpos1_nom < 0)dotpos1_nom+=baseband_size; dot1_chk=check_dot(dotpos1_nom); PRT03"\n{6}CHK DOT %f (%f)",dot1_chk,dotpos1_nom); // If dot1_chk in negative, the fit is better for a space than for a dot. // Be careful and check if a dot would fit even for dot1_chk a little below zero. if(dot1_chk > -0.25) { cg_wave_start=dotpos1_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); PRT03" [%f]", cg_wave_midpoint); dotpos1_err=dotpos1_nom-cg_wave_midpoint; if(dotpos1_err < -sizhalf)dotpos1_err+=baseband_size; PRT03" poserr %f fit %f",dotpos1_err/cwbit_pts,cg_wave_fit); t3=-1; // Use t3 as an indicator. // t3 < 0 => store space. // t3 > 0 => store dot using t3 as the weight on fit_dot data. if(cg_wave_fit > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.25*cwbit_pts) { if(dot1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } if(cg_wave_fit > DOT_MISFIT_LIMIT && fabs(dotpos1_err) < 0.5*cwbit_pts && dot1_chk > 0.5)t3=0.5; if(t3 >= 0) { t2=1-t3; cg_wave_midpoint+=t2*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*cg_wave_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*cg_wave_im+t2*baseb_envelope[2*ia+1]; clear_region(); PRT03"\nstore dot %f",cg_wave_midpoint); item=CW_DOT; } else { if( dot1_chk > 0) { item=0; } else { cg_wave_midpoint=dotpos1_nom; clear_region(); PRT03"\nstore space %f",cg_wave_midpoint); item=CW_SPACE; } } } else { cg_wave_midpoint=dotpos1_nom; clear_region(); PRT03"\nstore space %f",cg_wave_midpoint); item=CW_SPACE; } if(item!=0)insert_item(item); if(kill_all_flag)return; break; case 8: // ---_-_-_--- 8 ity=4 // ---_---_--- 8 ity=1 // ---___-_--- 8 ity=3 // ---_-___--- 8 ity=2 // ---_____--- 8 ity=5 set_region_envelope(); ity=0; // See if a dash will fit well. dashpos1_nom=cw[cw_ptr].midpoint-0.5*cw[cw_ptr].sep; if(dashpos1_nom < 0)dashpos1_nom+=baseband_size; dash1_chk=check_dash(dashpos1_nom); cg_wave_start=dashpos1_nom-(dash_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dash(); dash1_re=cg_wave_re; dash1_im=cg_wave_im; dashpos1=cg_wave_midpoint; dashpos1_err=dashpos1_nom-cg_wave_midpoint; if(dashpos1_err < -sizhalf)dashpos1_err+=baseband_size; dash_fit1=cg_wave_fit; if( dash_fit1 > DASH_DETECT_LIMIT && dash1_chk > 0.5 && fabs(dashpos1_err) < 0.5*cwbit_pts) { ity=1; PRT04"\n{8}GOOD DASH %f fit= %f (%f) [%f]", dash1_chk,cg_wave_fit,dashpos1_nom,cg_wave_midpoint); goto store8; } PRT04"\n{8}CHK DASH %f fit= %f (%f) [%f]", dash1_chk,cg_wave_fit,dashpos1_nom,cg_wave_midpoint); dotpos1_nom=cw[cw_ptr].midpoint-0.625*cw[cw_ptr].sep; if(dotpos1_nom < 0)dotpos1_nom+=baseband_size; dot1_chk=check_dot(dotpos1_nom); dotpos2_nom=cw[cw_ptr].midpoint-0.375*cw[cw_ptr].sep; if(dotpos2_nom < 0)dotpos2_nom+=baseband_size; dot2_chk=check_dot(dotpos2_nom); if(dash1_chk < -0.25 && dot1_chk < -0.25 && dot2_chk < -0.25) { PRT04"\nALL chk NEG (< -0.25)"); ity=5; goto store8; } cg_wave_start=dotpos1_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dot1_re=cg_wave_re; dot1_im=cg_wave_im; dotpos1=cg_wave_midpoint; dotpos1_err=dotpos1_nom-cg_wave_midpoint; if(dotpos1_err < -sizhalf)dotpos1_err+=baseband_size; dot_fit1=cg_wave_fit; PRT04"\n{8}CHK DOT1 %f fit=%f (%f) [%f]", dot1_chk,cg_wave_fit,dotpos1_nom,cg_wave_midpoint); cg_wave_start=dotpos2_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dot2_re=cg_wave_re; dot2_im=cg_wave_im; dotpos2=cg_wave_midpoint; dotpos2_err=dotpos2_nom-cg_wave_midpoint; if(dotpos2_err < -sizhalf)dotpos2_err+=baseband_size; dot_fit2=cg_wave_fit; PRT04"\n{8}CHK DOT2 %f fit=%f (%f) [%f]", dot2_chk,cg_wave_fit,dotpos2_nom,cg_wave_midpoint); if( dash_fit1 < DASH_MISFIT_LIMIT || ( (dash1_chk < -0.25 && fabs(dashpos1_err) < 0.5*cwbit_pts) || ( dash1_chk < 0.25 && fabs(dashpos1_err) < cwbit_pts ) ) ) { // The fit to a dash is very poor. // Set ity to 5 (word separator), then check whether dot or space will fit. ity=5; PRT04"\nset ity=5"); PRT04"\ndot1 fit %f chk %f poserr %f ",dot_fit1,dot1_chk,dotpos1_err); if( dot_fit1 > DOT_MISFIT_LIMIT && dot1_chk > 0.25 && fabs(dotpos1_err) < 0.5*cwbit_pts ) { ity=2; PRT04"\nset ity=2"); } PRT04"\ndot2 fit %f chk %f poserr %f ",dot_fit2,dot2_chk,dotpos2_err); if( dot_fit2 > DOT_MISFIT_LIMIT && dot2_chk > 0.25 && fabs(dotpos2_err) < 0.5*cwbit_pts ) { if(ity == 2) { ity=4; PRT04"\nset ity=4"); } else { ity=3; PRT04"\nset ity=3"); } } goto store8; } PRT04"\nUnclear"); // The situation is unclear. Nothing fits well........... if(dash1_chk < 0 && dot1_chk < 0 && dot2_chk < 0) { ity=5; PRT04"\nset ity=5 (all neg)"); goto store8; } t1=dot1_chk*dot2_chk; if( fabs(t1) > dash1_chk*fabs(dash1_chk) ) { // The fit to a dash is worse than dots and/or spaces. PRT04"\nset ity=5 (dash worse than dots)"); ity=5; if( dot_fit1 > DOT_MISFIT_LIMIT && dot1_chk > 0 && fabs(dotpos1_err) < 0.7*cwbit_pts ) { ity=2; PRT04"\nset ity=2"); } if( dot_fit2 > DOT_MISFIT_LIMIT && dot2_chk > 0 && fabs(dotpos2_err) < 0.7*cwbit_pts ) { if(ity == 2) { ity=4; PRT04"\nset ity=4"); } else { ity=3; PRT04"\nset ity=3"); } } goto store8; } if( dash_fit1 > DASH_MISFIT_LIMIT && (dash1_chk > 0 || fabs(dashpos1_err) < cwbit_pts ) ) { ity=1; PRT04"\nset ity=1"); } store8:; clear_region(); PRT04"\nity=%d",ity); switch (ity) { case 1: // Store a dash // ---_---_--- 8 ity=1 t3=0; if(dash_fit1 > DASH_DETECT_LIMIT && fabs(dashpos1_err) < 0.25*cwbit_pts) { if(dash1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dashpos1+t2*dashpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dash1_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dash1_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore_dash %f",cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; cw_ptr++; break; case 2: // Store dot1 and space // ---_-___--- 8 ity=2 // ---___-_--- 8 ity=3 // ---_____--- 8 ity=5 // ---_-_-_--- 8 ity=4 t3=0; if(dot_fit1 > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.25*cwbit_pts) { if(dot1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos1+t2*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot1_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot1_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; cg_wave_midpoint=dotpos2_nom; PRT04"\nstore_space %f",cg_wave_midpoint); insert_item(CW_SPACE); if(kill_all_flag)return; cw_ptr++; break; case 3: // Store space and dot2 // ---___-_--- 8 ity=3 cg_wave_midpoint=dotpos1_nom; PRT04"\nstore_space %f",cg_wave_midpoint); insert_item(CW_SPACE); if(kill_all_flag)return; cw_ptr++; t3=0; if(dot_fit2 > DOT_DETECT_LIMIT && fabs(dotpos2_err) < 0.25*cwbit_pts) { if(dot2_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos2+t2*dotpos2_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot2_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot2_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; break; case 4: // Store dot1 and dot2 // ---_-_-_--- 8 ity=4 t3=0; if(dot_fit1 > DOT_DETECT_LIMIT && fabs(dotpos1_err) < 0.25*cwbit_pts) { if(dot1_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos1+t2*dotpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot1_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot1_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; t3=0; if(dot_fit2 > DOT_DETECT_LIMIT && fabs(dotpos2_err) < 0.25*cwbit_pts) { if(dot2_chk < 0.25) { t3=0.75; } else { t3=0.5; } } t2=1-t3; cg_wave_midpoint=dotpos2+t2*dotpos2_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=t3*dot2_re+t2*baseb_envelope[2*ia ]; cg_wave_im=t3*dot2_im+t2*baseb_envelope[2*ia+1]; PRT04"\nstore dot %f",cg_wave_midpoint); insert_item(CW_DOT); if(kill_all_flag)return; cw_ptr++; break; case 5: // Store word separator // ---_____--- 8 ity=5 cg_wave_midpoint=dashpos1_nom; PRT04"\nstore word sep %f",cg_wave_midpoint); insert_item(CW_WORDSEP); if(kill_all_flag)return; cw_ptr++; break; } break; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! default: if(k==10) { set_region_envelope(); } else { set_long_region_envelope(); } // See if a dash will fit in the first position. dashpos1_nom=cw[cw_ptr-1].midpoint+4*cwbit_pts; if(dashpos1_nom >= baseband_size)dashpos1_nom-=baseband_size; dash1_chk=check_dash(dashpos1_nom); cg_wave_start=dashpos1_nom-(dash_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dash(); dash1_re=cg_wave_re; dash1_im=cg_wave_im; dashpos1=cg_wave_midpoint; dashpos1_err=dashpos1_nom-cg_wave_midpoint; if(dashpos1_err < -sizhalf)dashpos1_err+=baseband_size; dash_fit1=cg_wave_fit; PRT05"\n{def.l}CHK DASH %f fit= %f (%f) [%f]", dash1_chk,cg_wave_fit,dashpos1_nom,cg_wave_midpoint); ity=0; if( (dash_fit1 > DASH_DETECT_LIMIT && fabs(dashpos1_err) < 0.7*cwbit_pts && dash1_chk > 0) || (dash_fit1 > DASH_MISFIT_LIMIT && fabs(dashpos1_err) < 0.5*cwbit_pts && dash1_chk > 0.5) ) { // A dash fits reasonably well in the lower position. ity=1; // Check how well dots would fit at the beginning or end of the dash position. dotpos1_nom=dashpos1_nom-cwbit_pts; if(dotpos1_nom < 0)dotpos1_nom+=baseband_size; dot1_chk=check_dot(dotpos1_nom); cg_wave_start=dotpos1_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dotpos1=cg_wave_midpoint; dotpos1_err=dotpos1_nom-cg_wave_midpoint; if(dotpos1_err < -sizhalf)dotpos1_err+=baseband_size; dot_fit1=cg_wave_fit; PRT05"\n{def.l}CHK DOT1 %f fit=%f (%f) [%f]", dot1_chk,cg_wave_fit,dotpos1_nom,cg_wave_midpoint); if(fabs(dotpos1_err) < 0.5 * cwbit_pts && dash_fit1 < dot_fit1+0.05 && dot1_chk > 0.25) { PRT05"\ndot1.l good, skip dash for now."); ity=0; } else { dotpos2_nom=dashpos1_nom+cwbit_pts; if(dotpos2_nom >= baseband_size)dotpos2_nom-=baseband_size; dot2_chk=check_dot(dotpos2_nom); cg_wave_start=dotpos2_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dotpos2=cg_wave_midpoint; dotpos2_err=dotpos2_nom-cg_wave_midpoint; if(dotpos2_err < -sizhalf)dotpos2_err+=baseband_size; dot_fit2=cg_wave_fit; PRT05"\n{def.l}CHK DOT2 %f fit=%f (%f) [%f]", dot2_chk,cg_wave_fit,dotpos2_nom,cg_wave_midpoint); if(fabs(dotpos2_err) < 0.5 * cwbit_pts && dash_fit1 < dot_fit2+0.05 && dot2_chk > 0.25) { PRT05"\ndot2.h good, skip dash for now."); ity=0; } } } // See if a dash will fit in the upper position. dashpos2_nom=cw[cw_ptr].midpoint-4*cwbit_pts; if(dashpos2_nom < 0)dashpos2_nom+=baseband_size; dash2_chk=check_dash(dashpos2_nom); cg_wave_start=dashpos2_nom-(dash_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dash(); dash2_re=cg_wave_re; dash2_im=cg_wave_im; dashpos2=cg_wave_midpoint; dashpos2_err=dashpos2_nom-cg_wave_midpoint; if(dashpos2_err < -sizhalf)dashpos2_err+=baseband_size; dash_fit2=cg_wave_fit; PRT05"\n{def.u}CHK DASH %f fit= %f (%f) [%f]", dash2_chk,cg_wave_fit,dashpos2_nom,cg_wave_midpoint); if( (dash_fit2 > DASH_DETECT_LIMIT && fabs(dashpos2_err) < 0.7*cwbit_pts && dash2_chk > 0) || (dash_fit2 > DASH_MISFIT_LIMIT && fabs(dashpos2_err) < 0.5*cwbit_pts && dash2_chk > 0.5) ) { // A dash fits reasonably well in the upper position. ity|=2; // Check how well dots would fit at the beginning or end of the dash position. dotpos3_nom=dashpos2_nom-cwbit_pts; if(dotpos3_nom < 0)dotpos3_nom+=baseband_size; dot3_chk=check_dot(dotpos3_nom); cg_wave_start=dotpos3_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dotpos3=cg_wave_midpoint; dotpos3_err=dotpos3_nom-cg_wave_midpoint; if(dotpos3_err < -sizhalf)dotpos3_err+=baseband_size; dot_fit3=cg_wave_fit; PRT05"\n{def.h}CHK DOT3 %f fit=%f (%f) [%f]", dot3_chk,cg_wave_fit,dotpos3_nom,cg_wave_midpoint); if(fabs(dotpos3_err) < 0.5 * cwbit_pts && dash_fit2 < dot_fit3+0.05 && dot3_chk > 0.25) { PRT05"\ndot3.h good, skip dash for now."); ity&=1; } else { dotpos4_nom=dashpos2_nom+cwbit_pts; if(dotpos4_nom >= baseband_size)dotpos4_nom-=baseband_size; dot4_chk=check_dot(dotpos4_nom); cg_wave_start=dotpos4_nom-(dot_pts>>1)+baseband_size; cg_wave_start&=baseband_mask; fit_dot(); dotpos4=cg_wave_midpoint; dotpos4_err=dotpos4_nom-cg_wave_midpoint; if(dotpos4_err < -sizhalf)dotpos4_err+=baseband_size; dot_fit4=cg_wave_fit; PRT05"\n{def.h}CHK DOT4 %f fit=%f (%f) [%f]", dot4_chk,cg_wave_fit,dotpos4_nom,cg_wave_midpoint); if(fabs(dotpos4_err) < 0.5 * cwbit_pts && dash_fit2 < dot_fit4+0.05 && dot4_chk > 0.25) { PRT05"\ndot2.h good, skip dash for now."); ity&=1; } } if( (ity & 3) == 3 && k < 12) { // We can not have one dash at each side in a short region. // choose the best one. if( fabs(dash_fit1-dash_fit2) > 0.05) { if(dash_fit1 > dash_fit2) { ity=1; } else { ity=2; } } else { t1=2*dash_fit1-dot_fit1-dot_fit2-2*dash_fit2+dot_fit3+dot_fit4; if(fabs(t1) < 0.05) { ity=0; } else { if(t1>0)ity=1; else ity=2; } } } } if( (ity&1) != 0) { cg_wave_midpoint=dashpos1+0.5*dashpos1_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=0.5*dash1_re+0.5*baseb_envelope[2*ia ]; cg_wave_im=0.5*dash1_im+0.5*baseb_envelope[2*ia+1]; PRT05"\nstore_dash.l %f",cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; cw_ptr++; } if( (ity&2) != 0) { cg_wave_midpoint=dashpos2+0.5*dashpos2_err; if(cg_wave_midpoint < 0)cg_wave_midpoint+=baseband_size; if(cg_wave_midpoint >= baseband_size)cg_wave_midpoint-=baseband_size; ia=cg_wave_midpoint; cg_wave_re=0.5*dash2_re+0.5*baseb_envelope[2*ia ]; cg_wave_im=0.5*dash2_im+0.5*baseb_envelope[2*ia+1]; PRT05"\nstore_dash.h %f",cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; cw_ptr++; } break; } cw_ptr++; } if(PR05 != 0)show_cw("first_detect exit"); } typedef struct{ float pos; float s; float n; float ston; float s_avg; float n_avg; int cwptr; }STONDATA; void improve_cwspeed(void) { int i, k, m, n, dashno; int repeat_flag; float r1, r3, t1, t2, t3; double s_sum, n_sum; STONDATA *stondt; stondt=(void*)(fftn_tmp); // Collect the average waveform again. // We probably have some more dashes now and their position is better // known as compared to the previous speed determination. check_cw(362003,1); if(kill_all_flag)return; repeat_flag=0; repeat:; check_cw(342003,1); if(kill_all_flag)return; seldash_cwspeed(0, no_of_cwdat); cw_ptr=0; check_cw(2003,1); if(kill_all_flag)return; dashno=0; t3=0; s_sum=0; n_sum=0; check_cw(22001,1); if(kill_all_flag)return; while(cw_ptr 0) { cw[cw_ptr].sep=cg_wave_midpoint-cw[cw_ptr-1].midpoint; } if(cw_ptr+1 < no_of_cwdat) { cw[cw_ptr+1].sep=cw[cw_ptr+1].midpoint-cg_wave_midpoint; } s_sum+=cg_wave_dat; n_sum+=cg_wave_err; stondt[dashno].s=cg_wave_dat; stondt[dashno].n=cg_wave_err; stondt[dashno].cwptr=cw_ptr; stondt[dashno].pos=t3; dashno++; check_cw(22003,1); if(kill_all_flag)return; } } cw_ptr++; } check_cw(22009,1); if(kill_all_flag)return; if(dashno <= MIN_DASHNO)return; stondt[dashno].pos=t3; check_cw(200591,1); if(kill_all_flag)return; // Use the data in stondt and to decide if the new data // has worsened the S/N ratio significantly. // If it has, the cw speed has changed or the // signal has degraded or disappeared. // Average S and N over 3 points. t1=stondt[0].s; r1=stondt[0].n; for(i=1; i<3; i++) { t1+=stondt[i].s; r1+=stondt[i].n; } stondt[0].s_avg=t1/3; stondt[0].n_avg=r1/3; stondt[0].ston=t1/r1; k=dashno-2; for(i=1; istondt[i+1].ston)r1=stondt[i+1].ston; t3+=(t1-t2)*r1*(t1+t2); r3+=(t1-t2)*r1; } t3/=r3; // t3 is now twice the center of gravity for the S/N function. // If it is much smaller than the total interval, the signal // has degraded significantly. if(t3 < 0.8*stondt[k].pos) { // The signal seems to have degraded. // Get the average S/N over the interval from 0 to t3 (weighted by S) PRT01"\nSignal seems to have degraded. %f %f",t3,0.8*stondt[k].pos); i=0; t1=0; t2=0; while(stondt[i].pos 0)baseb_pc =(baseb_pc+1)&baseband_mask; while(baseb_ramp[baseb_pc] < 0)baseb_pc =(baseb_pc+1)&baseband_mask; baseb_pc =(baseb_pc+baseband_mask)&baseband_mask; baseb_pe=baseb_pc; baseb_pd=baseb_pc; cw_detect_flag=CWDETECT_LIMITS_FOUND; PRT01"\n*** SET cw_detect_flag=CWDETECT_LIMITS_FOUND ***"); } PRT01"\ncw_stoninv %f old=%f",n_sum/s_sum,cw_stoninv); cw_stoninv=n_sum/s_sum; for(i=0; i>1; // When we arrive here the keying speed and the waveforms are well known. // We have detected no_of_cwdat waveforms that fit to a dash, the center // of the dashes are pointed to by cw[].midpoint and cw[].sep contains // the separation between detected dashes. // First look for recent dashes. check_cw(547203,1); if(kill_all_flag)return; if(PR02 == 1)show_cw("first_find_parts 1"); get_recent_dashes(); if(kill_all_flag)return; if(PR02 == 1)show_cw("first_find_parts 2"); // Then collect old ones (if there are any) check_cw(552002,1); if(kill_all_flag)return; get_old_dashes(); if(kill_all_flag)return; if(PR02 == 1)show_cw("first_find_parts 3"); // Check S/N and place a pointer at the end of the current // transmission in case S/N is severely degraded. check_cw(552001,1); if(kill_all_flag)return; improve_cwspeed(); if(kill_all_flag)return; old_no=no_of_cwdat; // ********************************************************** // ********************************************************** // ****** Use the (hopefully) improved dash waveform ******* // **** and search the entire region for good matches **** // ********************************************************** // ********************************************************** cw_ptr=1; lmax=4.5*cwbit_pts; lmin=1.5*cwbit_pts; while(cw_ptr < no_of_cwdat) { r1=cw[cw_ptr].sep/cwbit_pts; k=(int)(r1)&0xfffffffe; if(r1-k > 1)k+=2; PRT07"\nuu %d second search> %d %f [%f] (%d)", cw_ptr,k,r1,cw[cw_ptr].midpoint,no_of_cwdat); if(k > 8) { // The gap between the current code part and the previous one // is larger than 8 code units. // Use the ramps to step downwards within the current gap // and see if we can fit any dashes now that the waveform // is (hopefully) more accurate than before. ib=cw[cw_ptr].midpoint-2.5*cwbit_pts; ia=cw[cw_ptr-1].midpoint+2.5*cwbit_pts; if(ib < 0)ib+=baseband_size; if(ia >= baseband_size)ia-=baseband_size; while(baseb_ramp[ia] > 0)ia=(ia+1)&baseband_mask; while(baseb_ramp[ia] < 0)ia=(ia+1)&baseband_mask; ia=(ia+baseband_mask)&baseband_mask; if(baseb_ramp[ib] > 0)ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; while( ib != ia) { // Step downwards by the (negative) ramp length. // We should always be on a negative ramp here. ib=(ib+baseb_ramp[ib]+baseband_size)&baseband_mask; if( ((ib-baseb_pe+baseband_size)&baseband_mask) > baseband_neg)goto second_dashfind_x; // Now we must be on a positive ramp. if(baseb_ramp[ib] >= lmin && baseb_ramp[ib] <= lmax) { cg_wave_start= (ib-((baseb_ramp[ib]+dot_pts)>>1)+baseband_size)&baseband_mask; fit_dot(); t2=cg_wave_fit; cg_wave_start= (ib-((baseb_ramp[ib]+dash_pts)>>1)+baseband_size)&baseband_mask; fit_dash(); PRT07"\nPRT07 ib=%d dash:%f dot:%f [%f]", ib,cg_wave_fit, t2, cg_wave_midpoint); t1=cg_wave_midpoint-cw[cw_ptr-1].midpoint; if(t1 < 0)t1+=baseband_size; t1/=cwbit_pts; if(t1 > 2.5 && t1 < baseband_neg) { t1=cw[cw_ptr].midpoint-cg_wave_midpoint; if(t1 < 0)t1+=baseband_size; t1/=cwbit_pts; if(t1 > 2.5 && t1 < baseband_neg) { if(cg_wave_fit >DASH_DETECT_LIMIT && cg_wave_fit-t2 > 0.05 ) { PRT07"\nstore dash %f ", cg_wave_midpoint); insert_item(CW_DASH); if(kill_all_flag)return; } } } else { goto second_dashfind_x; } } ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; } second_dashfind_x:; } cw_ptr++; } check_cw(2010,1); if(kill_all_flag)return; if(PR02 == 1)show_cw("first_find_parts 4"); first_detect(1); if(PR02 == 1)show_cw("first_find_parts 5"); if(kill_all_flag)return; if(cw_detect_flag >= CWDETECT_LIMITS_FOUND) { second_detect(); if(kill_all_flag)return; store_pattern(); if(PR08 != 0) { char s[80]; sprintf(s,"first_find_parts (flag=%d)",cw_detect_flag); show_cw(s); } return; } check_cw(547303,1); if(kill_all_flag)return; if(old_no != no_of_cwdat) { PRT08"\n Dashes added(a):%d %d", old_no,no_of_cwdat); improve_cwspeed(); if(kill_all_flag)return; old_no=no_of_cwdat; } // At this point we have fitted dashes and maybe a few dots and // spaces. store_pattern(); cw_detect_flag=CWDETECT_SOME_PARTS_FITTED; PRT08"\n*** SET cw_detect_flag=CWDETECT_SOME_PARTS_FITTED ***"); if(PR08 != 0)show_cw("first_find_parts (flag=CWDETECT_SOME_PARTS_FITTED)"); baseb_pe=baseb_pc; baseb_pd=baseb_pc; } void set_baseb_pc(void) { int ia,ib; // Make a first decision key up/key down based on signal power. // This is the conventional approach, a matched filter (the operator // has to select the optimum bandwidth) followed by a squarelaw detector. // The threshold is conventionally kept at a fixed level but here it is // very close to the noise when no signal is present. while it is at // half the peak amplitude for strong signals. // Store the result in baseb_ramp as ramps. // Positive values indicate key down while negative values // indicate key up (or signal too weak) ia=baseb_pc; ib=(ia+baseband_mask)&baseband_mask; while( ia != baseb_pb ) { if(baseb_totpwr[ia] > baseb_threshold[ia]) { if(baseb_ramp[ib]>0) { baseb_ramp[ia]=baseb_ramp[ib]+1; } else { baseb_ramp[ia]=1; } } else { if(baseb_ramp[ib]<0) { baseb_ramp[ia]=baseb_ramp[ib]-1; } else { baseb_ramp[ia]=-1; } } ib=ia; ia=(ia+1)&baseband_mask; } if(baseb_ramp[ib]>0)ib=(ib-baseb_ramp[ib]+baseband_size)&baseband_mask; baseb_pc=ib; } void second_find_parts(void) { int sizhalf, old_no_of_dashes; sizhalf=baseband_size>>1; // When we arrive here the keying speed and the waveforms are well known. // We have detected no_of_cwdat waveforms, most of them dashes, but // maybe also some dots and spaces. // First look for recent dashes. old_no_of_dashes=no_of_cwdat; set_baseb_pc(); check_cw(252501,1); if(kill_all_flag)return; get_recent_dashes(); if(kill_all_flag)return; // Fit dashes and dots in short gaps and next to // dashes in the new region. check_cw(251502,1); if(kill_all_flag)return; first_detect(old_no_of_dashes-1); if(kill_all_flag)return; if(old_no_of_dashes != no_of_cwdat) { improve_cwspeed(); if(kill_all_flag)return; if(cw_detect_flag >= CWDETECT_LIMITS_FOUND) { second_detect(); store_pattern(); if(PR08 != 0) { char s[80]; sprintf(s,"second_find_parts (flag=%d)",cw_detect_flag); show_cw(s); } return; } old_no_of_dashes=no_of_cwdat; } // Try to fit more dots and dashes in the whole region from 0 to no_of_cwdat. second_detect(); store_pattern(); if(PR08 != 0)show_cw("second_find_parts (flag unchanged)"); baseb_pe=baseb_pc; baseb_pd=baseb_pc; } void cw_decode(void) { /* xz("------ cw_decode ---------"); if(PR09 != 0)show_cw("cw_decode A"); second_find_parts(); if(PR09 != 0)show_cw("cw_decode B"); find_close_parts(); if(PR09 != 0)show_cw("cw_decode C"); continued_morse_decode(); */ } void cw_decode_region(void) { xz("cw_decode_region(do nothing)"); } void init_cw_decode(void) { // We decode because S/N is already good enough to perhaps allow it. // Some more information may have arrived. // Process it first and return if flag says the signal disappeared. second_find_parts(); if(cw_detect_flag != CWDETECT_REGION_WAVEFORM_OK) { xz("\nEND OF REGION!!!"); return; } find_close_parts(); check_cw(256702,1); if(kill_all_flag)return; // Try to find complete characters if(PR09 != 0)show_cw("init_cw_decode 1"); first_morse_decode(); if(kill_all_flag)return; if(PR09 != 0)show_cw("init_cw_decode 2"); check_cw(256703,1); if(kill_all_flag)return; if(cw_decoded_chars != 0) { cw_detect_flag=CWDETECT_SOME_ASCII_FITTED; xz("\nOKOKOKOKOKOKOK"); //show_cw("Set detect_flag to CWDETECT_SOME_ASCII_FITTED"); } else { fprintf(dmp,"\nno_of_cwdat %d no_of_cwdat-correlate_no_of_cwdat %d", no_of_cwdat, no_of_cwdat-correlate_no_of_cwdat); fprintf(dmp,"\n pa %d pb %d pc %d pd %d pe %d", baseb_pa, baseb_pb, baseb_pc, baseb_pd, baseb_pe); if(no_of_cwdat > 25 && no_of_cwdat-correlate_no_of_cwdat > 2) { correlate_undecoded_baseband(); correlate_no_of_cwdat=no_of_cwdat; // ö update baseb_px and other pointers as well as cw[] and no_of_cwdat // if correlate failed and data is becoming too old. } } } void init_cw_decode_region(void) { find_close_parts(); first_morse_decode(); cw_detect_flag=CWDETECT_REGION_INITIATED; }