#include "globdef.h" #include "uidef.h" #include "fft1def.h" #include "fft2def.h" #include "screendef.h" #include "caldef.h" #include "thrdef.h" #include "keyboard_def.h" #define TOPLINE 0 #define WAITMENU_LINE TOPLINE+4 #define ERRLINE (WAITMENU_LINE+5) #define MAX_PULSE_ERRORS 5 #define SKIP_BEFORE 0.15 #define SKIP_AFTER 0.25 void final_filtercorr_init(void) { int i, j, k, mm, line; int ja, jb; int ka, kb; int siz_n, siz; float t1, t2, t3, renorm; double dt1, dt2; double *dbuf; double pwrinteg[17]; char s[80]; double dc[4]; mm=twice_rxchan; // In case the native size of the calibration data is smaller // than fft1_size, change fft1_size and remember old values in fft2_size. fft2_size=fft1_size; fft2_n=fft1_n; if(cal_fft1_size < fft1_size) { fft1_size=cal_fft1_size; fft1_n=cal_fft1_n; init_fft1_filtercorr(); } init_fft(0,fft1_n, fft1_size, fft2_tab, fft2_permute); // The correction function fft1_filtercorr is present in the same // number of points as we currently have for fft1. // Since the Q of the hardware is limited there is no need to use // too many points in the filter function. // This routine will reduce the number of points used for the filtercorr // function while removing erronous points and reducing noise. // ****************************************************************** // First get the magnitude of the second derivative into cal_buf. // Large values over narrow frequency regions indicate the presence // of discontinuities in the calibration function due to narrowband // signals (spurs) // Also store the amplitude and the second derivative of the phase // in cal_buf2. // We can expect large second derivatives at the passband ends. // Set ka and kb to point to the interesting region. ka=5; kb=fft1_size-6; while(fft1_desired[ka]<0.5)ka++; while(fft1_desired[kb]<0.5)kb--; for(j=0; j t1)t1=t2; } for(i=0; i20)jb++; if(jbka && cal_buf[ja] > 20)ja--; k=4+(jb-ja)/4; while(ja>ka+k && cal_buf[ja]/cal_buf[ja-k]>4)ja--; while(jb4)jb++; // Make the amplitude constant across the bad region. // The amplitude is set to the average amplitude from both sides. for(j=0; j 0.0001)siz_n++; if(siz_n == k)siz_n--; if(k-siz_n < 3 && siz_n > 4) { settextcolor(14); lir_text(5,line,"The calibration function seems noisy."); line++; lir_text(5,line,"It could also need all the points."); line++; lir_text(5,line,"Make your own judgement!"); line++; settextcolor(7); } line+=2; siz_n+=2; siz=1<= fft1_n) { lir_text(5,line+2,"Filter response needs current number of data points"); lir_text(5,line+3,"Data will be used without change"); goto skip; } resize_filtercorr_td_to_fd( FALSE, fft1_size, cal_buf, siz_n, siz, cal_buf); write_filcorr(siz); if(kill_all_flag) return; sprintf(s,"Filter correction function saved in %d points",siz); lir_text(5,line+2,s); sprintf(s,"File size reduced from %d to %d bytes", (1+twice_rxchan)*fft1_size*sizeof(float)+20*sizeof(int), (1+twice_rxchan)*siz*sizeof(float)+20*sizeof(int)); lir_text(5,line+3,s); lir_text(5,line+5,press_any_key); await_keyboard(); // Restore filtercorr and fft1 desired. if(fft2_size != fft1_size) { fft1_size=fft2_size; fft1_n=fft2_n; init_fft1_filtercorr(); } } void cal_filtercorr(void) { int j,jj,totbytes; char s[80]; int i,k,m,n,width,old_pb; int ia,ib,ic,ja,jb,max_pulpos; int ka, kb; float collect_noiselevel[MAX_ADCHAN/2]; float collect_powerlevel[MAX_ADCHAN/2]; float collect_powermax[MAX_ADCHAN/2]; float old_trig_power,trig_power,dc[2*MAX_ADCHAN],ampmax; float avg_power,summed_power; float t1,t2,t3; int pulse_error[MAX_PULSE_ERRORS]; int siz128, mm, collected_pulses; int pulpos; int mask; int show_flag; double redraw_time; double sum[4],isum[4]; double dsum1, dsum2; double dt1,dt2,dt3,dt4,dt5; fft2_size=2*cal_interval*(1-SKIP_BEFORE); fft2_n=make_power_of_two(&fft2_size); init_fft(0,fft2_n, fft2_size, fft2_tab, fft2_permute); cal_ygain=1; cal_xgain=1; cal_domain=0; mm=twice_rxchan; siz128=fft2_size/128; // ********************************************************************** // To get the pulses on one format independently of the hardware and // fft implementation (some could contain filters) we use the standard // routine for the first fft to get fourier transforms of the input data. // By back transformation the input data is converted to floating point // complex format regardless of the input format. // ********************************************************************** i=0; lir_sleep(10000); fft1_px=fft1_pb; while(i < 2 && thread_command_flag[THREAD_CAL_FILTERCORR]==THRFLAG_ACTIVE) { while(fft1_pb==fft1_px) { lir_sem_wait(SEM_FFT1); } fft1_px=fft1_pb; i++; } clr_restart:; summed_power=0; for(i=0; i Change scale"); lir_text(0,WAITMENU_LINE+1, "U => Compute new corrections in RAM. Do not save on disk."); lir_text(0,WAITMENU_LINE+2,"S => Save to disk from RAM"); lir_text(0,WAITMENU_LINE+3,"T => Toggle time/frequency domain"); lir_text(0,WAITMENU_LINE+4,"A => Clear RAM"); lir_refresh_screen(); thread_status_flag[THREAD_CAL_FILTERCORR]=THRFLAG_ACTIVE; show_flag=TRUE; old_pb=fft1_pb; while(thread_command_flag[THREAD_CAL_FILTERCORR] == THRFLAG_ACTIVE) { while(old_pb==fft1_pb) { lir_sem_wait(SEM_FFT1); if(thread_command_flag[THREAD_CAL_FILTERCORR] != THRFLAG_ACTIVE) goto check_command_flag; fft1_px=(fft1_pb-fft1_block+fft1_mask+1)&fft1_mask; } // Skip old data in case the cpu is a bit slow. while( ((fft1_pb-fft1_px+fft1_mask+1)&fft1_mask) > fft1_block) { fft1_px=(fft1_px+fft1_block)&fft1_mask; } old_pb=fft1_pb; for(j=0; j fft1_mask) { lirerr(879456); goto filcorr_error_exit; } cal_tmp[2*i ]=fft1_float[fft1_px+mm*i+j ]; cal_tmp[2*i+1]=fft1_float[fft1_px+mm*i+j+1]; } if( (ui.rx_input_mode&IQ_DATA) != 0)fft_iqshift(fft1_size, cal_tmp); fftback(fft1_size, fft1_n, cal_tmp, cal_table, cal_permute); for(i=0; i INIT_PULSENUM*cal_interval) { totbytes=INIT_PULSENUM*cal_interval; // Set trig power at -15dB (30 times) // Pulses must be separated well enough for the power level to // have dropped well below -15dB after 70% of the interval time. trig_power=old_trig_power/30; // Look for a pulse, at least interval/2 points into our data. pulpos=cal_interval/2+fft1_size/4; max_pulpos=3*fft1_size/4-cal_interval; while( cal_buf7[pulpos] > trig_power/4 && pulpos= max_pulpos) { lir_text(1,TOPLINE+1,"Noise level too high"); lir_refresh_screen(); } else { sprintf(s,"Search range %d to %d",pulpos,max_pulpos); lir_text(1,TOPLINE+1,s); lir_refresh_screen(); while( cal_buf7[pulpos] < trig_power && pulpos= max_pulpos)goto go_get_data; dt1=current_time(); if(dt1 - redraw_time > 0.1) { redraw_time=dt1; show_flag=TRUE; } if(show_flag == TRUE) { sprintf(s, "Accepted %4d Wide %4d Weak %4d S/N %4d Spur %4d Gain y=%.2f x=%.2f", pulse_error[0],pulse_error[1],pulse_error[2], pulse_error[3],pulse_error[4],cal_ygain,1/cal_xgain); lir_text(0,TOPLINE+2,s); lir_refresh_screen(); } ia=pulpos-cal_interval/2; ib=pulpos+cal_interval/2; if(ib < max_pulpos) { ampmax=0; for(i=ia; i<=ib; i++) { if(cal_buf7[i] > ampmax) { ampmax=cal_buf7[i]; pulpos=i; } } // Copy data into cal_buf2 so the pulse becomes placed at location 0 // Also copy the power function to fft1_sumsq with the pulse at location 0 k=pulpos; pulpos+=cal_interval; for(i=0; i3*fft1_size/4 )fft1_sumsq[i]=-1; k=(k+1)&(fft1_size-1); } // Find the width of the current pulse. // First find the maximum power. if( ampmax < old_trig_power/20) { pulse_error[2]++; goto find_pulse; } // Now step from the peak position until we reach power/8 ampmax/=8; ja=0; jb=fft1_size-1; while( fft1_sumsq[ja] > ampmax && ja ampmax && jb>ja)jb--; width=ja+fft1_size-jb; if(width > cal_interval/4) { pulse_error[1]++; goto find_pulse; } // Filter the power function to reduce noise. Place in fft1_slowsum. width/=2; if(width < cal_interval/64)width=cal_interval/64; if(width < 4)width=4; ja=fft1_size-width; jb=width; width=2*width+1; dsum1=0; for(i=0; i<=jb; i++)dsum1+=fft1_sumsq[i]; for(i=ja; i= fft1_size)jb-=fft1_size; fft1_slowsum[i]=dsum1/width; dsum1+=fft1_sumsq[jb]-fft1_sumsq[ja]; ja=(ja+1)&(fft1_size-1); } // Locate the surrounding pulses. ja=0.5*cal_interval; jb=1.5*cal_interval; t1=0; for(i=ja; i t1) { t1=fft1_slowsum[i]; ib=i; } } ja=fft1_size-1.5*cal_interval; jb=fft1_size-0.5*cal_interval; t1=0; ic=ja; for(i=ja; i t1) { t1=fft1_slowsum[i]; ic=i; } } // ib and ic point to the peaks of surrounding pulses. // skip a range after the previous pulse and before the next one. ib=(1.0-SKIP_BEFORE)*ib; ic=fft1_size-(fft1_size-ic)*(1.0-SKIP_AFTER); // When we pick the range ic to ib we may create a discontinuity. // Remove it by making the first derivative zero. // Differentiate the pulse response starting at ic: mask=fft1_size-1; ja=ic; jb=ic+1; while(ja != ib) { for(j=0; jcal_ymax)t2=cal_ymax; if(j > 1)t2-=0.32; cal_graph[screen_width*j+i]=screen_height*(cal_yzer-t2); lir_setpixel(i, cal_graph[screen_width*j+i], 13); lir_setpixel(i, cal_graph[screen_width*(j+1)+i], 0); t2=0.00001*cal_ygain*cal_buf2[mm*k+j+1]; if(t2 <-cal_ymax)t2=-cal_ymax; if(t2 >cal_ymax)t2=cal_ymax; if(j > 1)t2-=0.32; cal_graph[screen_width*(j+1)+i]=screen_height*(cal_yzer-t2); lir_setpixel(i, cal_graph[screen_width*(j+1)+i], 10); } } } for(j=0; jia; i--) { cal_tmp[2*k ]=cal_buf2[mm*i+j ]; cal_tmp[2*k+1]=cal_buf2[mm*i+j+1]; k--; } while(k >= ib) { cal_tmp[2*k ]=0; cal_tmp[2*k+1]=0; k--; } fftforward(fft2_size, fft2_n, cal_tmp, fft2_tab, fft2_permute); if( (ui.rx_input_mode&IQ_DATA) != 0)fft_iqshift(fft2_size, cal_tmp); for(i=0; icollect_powermax[jj]) { collect_powermax[jj]=cal_buf6[mm*i+j ]; } dsum1+=cal_tmp[2*(i+siz128) ]-cal_tmp[2*(i-siz128) ]; dsum2+=cal_tmp[2*(i+siz128)+1]-cal_tmp[2*(i-siz128)+1]; } } // ****************************************************** // In direct conversion mode there may be a discontinuity at fft2_size/2 // because the A/D converters are AC coupled. else { jb=fft2_size/2-siz128-1; for(i=siz128+2; icollect_powermax[jj]) { collect_powermax[jj]=cal_buf6[mm*i+j ]; } dsum1+=cal_tmp[2*(i+siz128)]-cal_tmp[2*(i-siz128)]; dsum2+=cal_tmp[2*(i+siz128)+1]-cal_tmp[2*(i-siz128)+1]; } for(i=jb; icollect_powermax[jj]) collect_powermax[jj]=cal_buf6[mm*i+j ]; dsum1+=cal_tmp[2*(i+siz128)]-cal_tmp[2*(i-siz128)]; dsum2+=cal_tmp[2*(i+siz128)+1]-cal_tmp[2*(i-siz128)+1]; } cal_buf3[ui.rx_rf_channels*fft2_size+j ]= (cal_buf3[ui.rx_rf_channels*(fft2_size+2)+j ]+ cal_buf3[ui.rx_rf_channels*(fft2_size-2)+j ])/2; cal_buf3[ui.rx_rf_channels*fft2_size+j+1]= (cal_buf3[ui.rx_rf_channels*(fft2_size+2)+j+1]+ cal_buf3[ui.rx_rf_channels*(fft2_size-2)+j+1])/2; cal_buf6[ui.rx_rf_channels*fft2_size+j]= (cal_buf6[ui.rx_rf_channels*(fft2_size+2)+j]+ cal_buf6[ui.rx_rf_channels*(fft2_size-2)+j])/2; } for(i=ja; i Ignore (=try anyway)"); lir_text(5,ERRLINE+5,"S => Save (bad?) correction on hard disk."); lir_text(5,ERRLINE+6,"A => Clear old correction function."); lir_refresh_screen(); goto errkbd; } } for(j=0; j 0.001) { cal_buf2[mm*i+j+1]=atan2(cal_buf2[mm*i+j+1],cal_buf2[mm*i+j ]); } else { cal_buf2[mm*i+j+1]=0; } cal_buf2[mm*i+j ]=t1; } } // Get the first derivative of the phase function for(j=0; j PI_L)cal_buf2[mm*i+j+1] -= 2*PI_L; if(cal_buf2[mm*i+j+1] < -PI_L)cal_buf2[mm*i+j+1] += 2*PI_L; } } // Get the second derivative of the phase function for(j=0; j PI_L)cal_buf2[mm*i+j+1] -= 2*PI_L; if(cal_buf2[mm*i+j+1] < -PI_L)cal_buf2[mm*i+j+1] += 2*PI_L; } } // ****************************************************** //Valid spectra: //cal_buf5 = accumulated power and d2phase/df2 (not normalised) //cal_buf4 = accumulated power and phase (normalised) //cal_buf2 = current pulse: power and d2phase/df2 //cal_buf3 = current pulse: smoothed complex amplitude //cal_buf6 = current pulse: smoothed power and noise power // ****************************************************** // Step through the data and look for points with excessive noise. // Make sure the integral is smaller than PI over the bad interval. // Collect 10 times the average noise power weighted by the // smoothed power in dc[j] // Also save 0.1 times the largest smoothed power in dc[j+1] so we // easily can see if a point is outside the passband. k=siz128/2; for(j=0; j0.75*PI_L)k=1; if(fabs(cal_buf2[mm*ja+j+1]-cal_buf2[mm*(ja-1)+j+1])>0.75*PI_L)k=1; } } if(ja>=fft2_size-siz128)goto spurrem_x; // ja is first bad point jb=ja; while(jb < fft2_size-siz128 && k==1) { k=0; jb++; for(j=0; j0.75*PI_L)k=1; if(fabs(cal_buf2[mm*jb+j+1]-cal_buf2[mm*(jb-1)+j+1])>0.75*PI_L)k=1; } // jb-1 is last bad point. // Check that we are within the passband. k=0; for(j=0; j dc[j+1] || cal_buf6[mm*jb+j] > dc[j+1])k=1; } if(k==1) { n+=jb-ja; if(jb-ja-1>fft2_size/128 || n > fft2_size/32) { spurerr:; pulse_error[4]++; goto find_pulse; } } ja-=fft2_size/256; if(ja<0)ja=0; jb+=fft2_size/256; if(jb>=fft2_size)jb=fft2_size-1; for(j=0; jt3) { t3=cal_buf2[mm*i+j+1]; kb=i; } } m++; // If abs of the integral is larger than PI, change the largest point. if(m < 5) { if(t1 < -PI_L) { cal_buf2[mm*ka+j+1]+=2*PI_L; goto rem_tupi; } if(t1 > PI_L) { cal_buf2[mm*kb+j+1]-=2*PI_L; goto rem_tupi; } } if(fabs(t1) > PI_L/4 && cal_buf6[mm*ja+j] > dc[j+1] && cal_buf6[mm*jb+j] > dc[j+1]) { goto spurerr; } } ja=jb-fft2_size/256+1; if(ja < fft2_size-1-fft2_size/256)goto spur_remove; spurrem_x: // ******************************************************************* //The pulse seems ok. //Add the new power spectrum to the powers already collected in cal_buf5. avg_power=0; for(i=0; i0; i--)cal_buf4[mm*(i-1)+j]+=cal_buf4[mm*i+j]; } // Remove the integration constant. // Choose it for (d1phase weighted by power) = 0. // Exclude points near the spectrum ends. k=fft2_size/16; if( (ui.rx_input_mode&IQ_DATA) == 0) { for(j=0; j0; i--) { cal_buf4[mm*(i-1)+j]+=cal_buf4[mm*i+j]; } } // Remove the integration constant. // Choose it for (phase weighted by power) = 0. if( (ui.rx_input_mode&IQ_DATA) == 0) { for(j=0; j 0.5)t1-=1; cal_buf4[mm*i+j+1]=t1*2*PI_L; } } if(show_flag == TRUE) { if(cal_domain == 0) { for(j=0; jfft2_size)k-=fft2_size; while(k<0)k+=fft2_size; lir_setpixel(i, cal_graph[screen_width*j+i], 0); t2=0.1*cal_ygain*cal_buf4[mm*k+j]; if(t2 <-cal_ymax)t2=-cal_ymax; if(t2 >cal_ymax)t2=cal_ymax; if(j > 1)t2-=0.32; cal_graph[screen_width*j+i]=screen_height*(cal_yzer-t2); lir_setpixel(i, cal_graph[screen_width*j+i], 13); lir_setpixel(i, cal_graph[screen_width*(j+1)+i], 0); t2=0.03*cal_ygain*cal_buf4[mm*k+j+1]; if(t2 <-cal_ymax)t2=-cal_ymax; if(t2 >cal_ymax)t2=cal_ymax; if(j > 1)t2-=0.32; cal_graph[screen_width*(j+1)+i]=screen_height*(0.5-t2); lir_setpixel(i, cal_graph[screen_width*(j+1)+i], 10); } } } lir_refresh_screen(); show_flag=FALSE; } goto find_pulse; } } } go_get_data:; } check_command_flag:; if(thread_command_flag[THREAD_CAL_FILTERCORR]==THRFLAG_IDLE) { goto idle; errkbd:; thread_command_flag[THREAD_CAL_FILTERCORR]=THRFLAG_IDLE; idle:; while(thread_command_flag[THREAD_CAL_FILTERCORR]==THRFLAG_ACTIVE) { lir_sleep(3000); } thread_status_flag[THREAD_CAL_FILTERCORR]=THRFLAG_IDLE; while(thread_command_flag[THREAD_CAL_FILTERCORR]==THRFLAG_IDLE) { lir_sem_wait(SEM_FFT1); old_pb=fft1_pb; } clear_lines(ERRLINE,ERRLINE+5); if(thread_command_flag[THREAD_CAL_FILTERCORR]==THRFLAG_ACTIVE)goto restart; if(thread_command_flag[THREAD_CAL_FILTERCORR]==THRFLAG_RESET) { thread_command_flag[THREAD_CAL_FILTERCORR]=THRFLAG_ACTIVE; goto clr_restart; } } filcorr_error_exit:; thread_status_flag[THREAD_CAL_FILTERCORR]=THRFLAG_RETURNED; while(thread_command_flag[THREAD_CAL_FILTERCORR] != THRFLAG_NOT_ACTIVE) { lir_sleep(1000); } }