1 // Copyright (c) <2012> <Leif Asbrink>
2 //
3 // Permission is hereby granted, free of charge, to any person
4 // obtaining a copy of this software and associated documentation
5 // files (the "Software"), to deal in the Software without restriction,
6 // including without limitation the rights to use, copy, modify,
7 // merge, publish, distribute, sublicense, and/or sell copies of
8 // the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
18 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
21 // OR OTHER DEALINGS IN THE SOFTWARE.
22 
23 #include <string.h>
24 #include "globdef.h"
25 #include "uidef.h"
26 #include "fft1def.h"
27 #include "fft2def.h"
28 #include "llsqdef.h"
29 #include "caldef.h"
30 #include "keyboard_def.h"
31 
32 #define IQ_FIT_MINFREQ 500
33 #define CORR_MINLEVEL 0.001
34 #define CORR_MAXLEVEL 20.
35 #define YSH 0.29
36 #define ERF_START -2.2
37 #define ERF_END 2.8
38 
39 #define SYMFIT_ERROR -1
40 #define SYMFIT_ACCEPT -2
41 #define SYMFIT_CHANGE_LIMITS -3
42 
draw_fitline(void)43 void draw_fitline(void)
44 {
45 int i;
46 i=screen_width*(-1+4*(float)(cal_midlim)/fft1_size);
47 lir_line(i,screen_height*(cal_yzer+YSH),i,screen_height*(cal_yzer-YSH),2);
48 }
49 
50 
make_symfit(int kk,int even,float * buf,float offset)51 void make_symfit(int kk, int even, float *buf, float offset)
52 {
53 int i, k, m, mm;
54 float t1, t2, t3, fft1_hsize;
55 mm=twice_rxchan;
56 fft1_hsize=0.5*(fft1_size-1);
57 k=0;
58 for(i=cal_lowedge; i<cal_midlim; i++)
59   {
60   t1=cal_buf5[i];
61   t2=1-i/fft1_hsize;
62   if(kk == 1)llsq_derivatives[k]=t1;
63   if(even == 1)
64     {
65     t2=t2*t2;
66     t3=t1*t2;
67     }
68   else
69     {
70     t3=t1*t2;
71     t2=t2*t2;
72     }
73   for(m=kk; m<llsq_npar; m++)
74     {
75     llsq_derivatives[m*llsq_neq+k]=t3;
76     t3*=t2;
77     }
78   llsq_errors[2*k]=t1*(buf[mm*i  ]-offset);
79   if(ui.rx_rf_channels == 2)
80     {
81     llsq_errors[2*k+1]=t1*(buf[mm*i+2]-offset);
82     }
83   else
84     {
85     llsq_errors[2*k+1]=0;
86     }
87   k++;
88   }
89 if(llsq2() != 0)
90   {
91   lirerr(1050);
92   return;
93   }
94 // Store the fitted curve in cal_buf
95 for(i=0; i<=fft1_size/2; i++)
96   {
97   t2=1-i/fft1_hsize;
98   cal_buf[mm*i]=offset+kk*llsq_steps[0];
99   if(ui.rx_rf_channels == 2)cal_buf[mm*i+2]=offset+kk*llsq_steps[1];
100   if(even == 1)
101     {
102     t2=t2*t2;
103     t3=t2;
104     }
105   else
106     {
107     t3=t2;
108     t2=t2*t2;
109     }
110   for(m=kk; m<llsq_npar; m++)
111     {
112     cal_buf[mm*i  ]+=llsq_steps[2*m]*t3;
113     if(ui.rx_rf_channels == 2)
114       {
115       cal_buf[mm*i+2]+=llsq_steps[2*m+1]*t3;
116       }
117     t3*=t2;
118     }
119   }
120 }
121 
symmetry_fit_decide(float * buf,float offset,char * txt)122 int symmetry_fit_decide(float *buf, float offset, char *txt)
123 {
124 char s[80];
125 float t2;
126 int i,j,k,mm;
127 float center_region_ia;
128 int center_region_pix;
129 center_region_ia=0.4;
130 symfit_begin:;
131 cal_type=CAL_TYPE_FIX_CENTER_SYMFIT;
132 cal_initscreen();
133 mm=twice_rxchan;
134 lir_text(0,1,"Curve fitting to response of direct conversion receiver.");
135 lir_text(0,2,txt);
136 for(j=0; j<ui.rx_rf_channels; j++)
137   {
138   sprintf(s,"Ch%d",j);
139   lir_text(0,j+3,s);
140   for(k=0; k<llsq_npar; k++)
141     {
142     if(llsq_steps[j+2*k] < 1000)
143       {
144       sprintf(s,"%.4f",llsq_steps[j+2*k]);
145       }
146     else
147       {
148       sprintf(s,"%.0f",llsq_steps[j+2*k]);
149       }
150     lir_text(4+11*k,j+3,s);
151     }
152   }
153 lir_text(2,6, " +- => Change Y-scale");
154 lir_text(2,7, "  0 => Use raw data without curve fit");
155 lir_text(2,8, "1-9 => Fit with new number of terms");
156 lir_text(2,9, "  C => Enter a larger number than 9 and fit");
157 lir_text(50,6,"  A => Use curve fitted with current no of terms");
158 lir_text(50,8,"  B => Back, change freq limits");
159 lir_text(50,9,"L,R => Border left, right");
160 settextcolor(14);
161 lir_text(0,10,"Raw data.");
162 settextcolor(12);
163 lir_text(16,10,"Difference raw to fitted.");
164 settextcolor(7);
165 redraw:;
166 center_region_pix=screen_width*(-1+4*center_region_ia);
167 lir_line(center_region_pix,screen_height*(cal_yzer+YSH),
168         center_region_pix,screen_height*(cal_yzer-YSH),1);
169 if(kill_all_flag) return SYMFIT_ERROR;
170 draw_fitline();
171 if(kill_all_flag) return SYMFIT_ERROR;
172 sprintf(s,"Y-gain = %f     No of terms = %d    ",cal_ygain,llsq_npar);
173 lir_text(0,5,s);
174 lir_hline(0,screen_height*cal_yzer,screen_last_xpixel,1);
175 if(kill_all_flag) return SYMFIT_ERROR;
176 lir_hline(0,screen_height*(cal_yzer+YSH),screen_last_xpixel,1);
177 if(kill_all_flag) return SYMFIT_ERROR;
178 for(i=0; i<screen_width; i++)
179   {
180   k=i*fft1_size/(4*screen_width)+fft1_size/4;
181   for(j=0; j<mm; j+=2)
182     {
183     lir_setpixel(i, cal_graph[screen_width*(2*j  )+i], 0);
184     t2=0.1*cal_ygain*(buf[mm*k+j]-offset);
185     if(t2 <-cal_ymax)t2=-cal_ymax;
186     if(t2 >cal_ymax)t2=cal_ymax;
187     if(j > 0)t2-=YSH;
188     cal_graph[screen_width*(2*j  )+i]=screen_height*(cal_yzer-t2);
189     lir_setpixel(i, cal_graph[screen_width*(2*j  )+i], 14);
190     lir_setpixel(i, cal_graph[screen_width*(2*j+1)+i], 0);
191     t2=0.1*cal_ygain*(cal_buf[mm*k+j]-buf[mm*k+j]);
192     if(t2 <-cal_ymax)t2=-cal_ymax;
193     if(t2 >cal_ymax)t2=cal_ymax;
194     if(j > 0)t2-=YSH;
195     cal_graph[screen_width*(2*j+1)+i]=screen_height*(cal_yzer-t2);
196     lir_setpixel(i, cal_graph[screen_width*(2*j+1)+i], 12);
197     }
198   }
199 await_processed_keyboard();
200 if(kill_all_flag) return SYMFIT_ERROR;
201 if(lir_inkey == 'X')return SYMFIT_ERROR;
202 if(lir_inkey == F1_KEY || lir_inkey == '!')
203   {
204   help_message(300);
205   if(kill_all_flag) return SYMFIT_ERROR;
206   goto symfit_begin;
207   }
208 if(lir_inkey == 'C')
209   {
210   sprintf(s,"Enter number of terms (10 to %d)  =>",LLSQ_MAXPAR);
211   lir_text(10,12,s);
212   i=lir_get_integer(11+strlen(s),12,3,10,LLSQ_MAXPAR);
213   clear_lines(12,12);
214   return i;
215   }
216 if(lir_inkey == 'B')return SYMFIT_CHANGE_LIMITS;
217 if(lir_inkey >= '0' && lir_inkey <= '9')return lir_inkey-'0';
218 if(lir_inkey == 'A')
219   {
220   for(i=center_region_ia*fft1_size; i<=fft1_size/2; i++)
221     {
222     for(j=0; j<mm; j+=2)
223       {
224       buf[mm*i+j]=cal_buf[mm*i+j];
225       }
226     }
227   return SYMFIT_ACCEPT;
228   }
229 if(lir_inkey == '+')cal_ygain*=2;
230 if(lir_inkey == '-')cal_ygain/=2;
231 if(lir_inkey == 'L')center_region_ia-=0.003;
232 if(center_region_ia < 0.3)center_region_ia=0.3;
233 if(lir_inkey == 'R')center_region_ia+=0.0025;
234 if(center_region_ia > 0.4975)center_region_ia=0.4975;
235 lir_line(center_region_pix,screen_height*(cal_yzer+YSH),
236         center_region_pix,screen_height*(cal_yzer-YSH),0);
237 if(kill_all_flag) return SYMFIT_ERROR;
238 goto redraw;
239 }
240 
make_cal_fft1_filtercorr(void)241 void make_cal_fft1_filtercorr(void)
242 {
243 int i;
244 // We do calibration in fft2_size points (before Linrad-02.28 it
245 // was done in the much larger size fft1_size)
246 // Start by reducing the sizes of fft1_desired and fft1_filtercorr.
247 resize_fft1_desired(fft1_size, fft1_desired, fft2_size, cal_fft1_desired);
248 for(i=0; i<twice_rxchan*fft1_size; i++)cal_fft1_filtercorr[i]=fft1_filtercorr[i];
249 convert_filtercorr_fd_to_td(fft1_n, fft1_size, cal_fft1_filtercorr);
250 resize_filtercorr_td_to_fd(TRUE, fft1_size, cal_fft1_filtercorr,
251                                       fft2_n, fft2_size, cal_fft1_filtercorr);
252 }
253 
cal_update_ram(void)254 int cal_update_ram(void)
255 {
256 int lowp, midp, higp, siz128;
257 int cal_hw_lowp;
258 int cal_hw_higp;
259 char s[80];
260 int mm, falloff_length, step;
261 int ia, ib;
262 int i, j, k, m, ja, jb, ka, kb;
263 float t1,t2,t3;
264 float ygain;
265 float z[MAX_ADCHAN];
266 int desired_shape;
267 char *shape_texts[2]={"erfc(x)","Parabolic"};
268 desired_shape=0;
269 siz128=fft2_size/128;
270 step=10;
271 ygain=1;
272 mm=twice_rxchan;
273 //clear_screen();
274 settextcolor(14);
275 // ******************************************************
276 // Valid spectra:
277 // cal_buf4 = accumulated power and phase (normalised in fft2_size points)
278 // The spectrum in cal_buf4 is what we have while fft1_desired contains
279 // what we have asked for.
280 // To get the desired spectrum we have to add one more filter
281 // to our filter chain.
282 // The new filter should be fft1_desired/cal_buf4.
283 // ******************************************************
284 make_cal_fft1_filtercorr();
285 for(i=0; i<2*MAX_ADCHAN*screen_width; i++)cal_graph[i]=0;
286 for(j=0; j<mm; j++)z[j]=0;
287 for(i=0;i<mm*fft2_size; i++)cal_buf2[i]=0;
288 // Since there may be serious errors in the spectrum of the average
289 // pulse at the spectrum ends, start by locating the passband center.
290 // for this purpose, skip fft2_size/128 points at each end as well
291 // as around the center if we run in I/Q mode.
292 if(  (ui.rx_input_mode&IQ_DATA) == 0)
293   {
294   for(i=siz128; i<fft2_size-siz128; i++)
295     {
296     if(cal_fft1_desired[i]>0)
297       {
298       for(j=0; j<mm; j+=2)
299         {
300         t1=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
301         if(t1 > 0 )
302           {
303           t1=cal_buf4[mm*i+j]/sqrt(t1);
304           z[j]+=t1*i;
305           z[j+1]+=t1;
306           cal_buf2[mm*i+j]=t1;
307           }
308         }
309       }
310     }
311   }
312 else
313   {
314   for(i=siz128; i<fft2_size/2-siz128; i++)
315     {
316     if(cal_fft1_desired[i]>0)
317       {
318       for(j=0; j<mm; j+=2)
319         {
320         t1=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+
321            pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
322         if(t1 > 0 )
323           {
324           t1=cal_buf4[mm*i+j]/sqrt(t1);
325           z[j]+=t1*i;
326           z[j+1]+=t1;
327           cal_buf2[mm*i+j]=t1;
328           }
329         }
330       }
331     }
332   for(i=fft2_size/2+siz128; i<fft2_size-siz128; i++)
333     {
334     if(cal_fft1_desired[i]>0)
335       {
336       for(j=0; j<mm; j+=2)
337         {
338         t1=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
339         if(t1 > 0 )
340           {
341           t1=cal_buf4[mm*i+j]/sqrt(t1);
342           z[j]+=t1*i;
343           z[j+1]+=t1;
344           cal_buf2[mm*i+j]=t1;
345           }
346         }
347       }
348     }
349   }
350 midp=0;
351 for(j=0; j<mm; j+=2)
352   {
353   if(z[j+1]==0)
354     {
355     lirerr(1162);
356     return 0;
357     }
358   midp+=z[j]/z[j+1];
359   }
360 midp/=mm>>1;
361 ia=midp-fft2_size/16;
362 ib=midp+fft2_size/16;
363 // Locate the maximum near the midpoint.
364 for(j=0; j<mm; j+=2)
365   {
366   z[j]=0;
367   if(  (ui.rx_input_mode&IQ_DATA) == 0)
368     {
369     for(i=ia; i<ib; i++)
370       {
371       if(z[j]<cal_buf2[mm*i+j])z[j]=cal_buf2[mm*i+j];
372       }
373     }
374   else
375     {
376     for(i=ia; i<fft2_size/2-siz128; i++)
377       {
378       if(z[j]<cal_buf2[mm*i+j])z[j]=cal_buf2[mm*i+j];
379       }
380     for(i=fft2_size/2+siz128; i<ib; i++)
381       {
382       if(z[j]<cal_buf2[mm*i+j])z[j]=cal_buf2[mm*i+j];
383       }
384     }
385   z[j]*=.01;
386   z[j+1]=0;
387   }
388 // Locate where the level is -20 dB at the sides relative to the level
389 // at the center.
390 lowp=0;
391 higp=fft2_size-1;
392 k=0;
393 while( lowp < midp && k == 0)
394   {
395   k=1;
396   for(j=0; j<mm; j+=2)
397     {
398     if( z[j]>cal_buf2[mm*lowp+j]) k=0;
399     }
400   lowp++;
401   }
402 m=0;
403 while(higp > midp && m==0)
404   {
405   m=1;
406   for(j=0; j<mm; j+=2)
407     {
408     if( z[j]>cal_buf2[mm*higp+j])m=0;
409     }
410   higp--;
411   }
412 // Get a minimum level for filtercorr power to not divide by zero
413 for(j=0; j<mm; j+=2)
414   {
415   k=higp-lowp-1;
416   if(  (ui.rx_input_mode&IQ_DATA) == 0)
417     {
418     for(i=lowp+1; i<higp; i++)
419       {
420       z[j+1]+=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+
421                                   pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
422       }
423     }
424   else
425     {
426     for(i=lowp+1; i<fft2_size/2-siz128; i++)
427       {
428       z[j+1]+=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+
429                                   pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
430       }
431     for(i=fft2_size/2+siz128; i<higp; i++)
432       {
433       z[j+1]+=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+
434                                   pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
435       }
436     k-=2*siz128;
437     }
438 // Set level at -30dB
439   z[j+1]=0.001*z[j+1]/k;
440   }
441 // Store the hardware spectrum in cal_buf2 as amplitude and phase
442 // Get it as the shape before the correction in filcorr was applied i.e.
443 // as cal_buf4/fft1_filtercorr
444 for(i=0; i<fft2_size; i++)
445   {
446   for(j=0; j<mm; j+=2)
447     {
448     t1=pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+pow(cal_fft1_filtercorr[mm*i+j+1],2.0);
449     if(t1 > z[j+1] && cal_fft1_desired[i] > 0)
450       {
451       cal_buf2[mm*i+j]=cal_buf4[mm*i+j]/t1;
452       cal_buf2[mm*i+j+1]=cal_buf4[mm*i+j+1]-atan2(cal_fft1_filtercorr[mm*i+j+1],
453                                           cal_fft1_filtercorr[mm*i+j  ]);
454       }
455     else
456       {
457       cal_buf2[mm*i+j  ]=0;
458       cal_buf2[mm*i+j+1]=0;
459       }
460     }
461   }
462 for(j=0; j<mm; j+=2)
463   {
464   z[j]=0;
465   if(  (ui.rx_input_mode&IQ_DATA) == 0)
466     {
467     for(i=lowp+1; i<fft2_size; i++)
468       {
469       if(z[j] < cal_buf2[mm*i+j])z[j]=cal_buf2[mm*i+j];
470       }
471     }
472   else
473     {
474     for(i=lowp+1; i<fft2_size/2-siz128; i++)
475       {
476       if(z[j] < cal_buf2[mm*i+j])z[j]=cal_buf2[mm*i+j];
477       }
478     for(i=fft2_size/2+siz128; i<higp; i++)
479       {
480       if(z[j] < cal_buf2[mm*i+j])z[j]=cal_buf2[mm*i+j];
481       }
482     }
483   }
484 for(i=0; i<fft2_size; i++)
485   {
486   for(j=0; j<mm; j+=2)
487     {
488     cal_buf2[mm*i+j]/=z[j];
489     }
490   }
491 // ******************************************************
492 // Valid spectra:
493 // cal_buf4 = accumulated pulse spectrum, power and phase (normalised)
494 // cal_buf2 = hardware spectrum, power and phase (normalised)
495 // ******************************************************
496 // Locate the MIN_GAIN points at the low and high frequency sides of cal_buf2
497 #define MIN_GAIN 0.1     //10dB
498 cal_hw_lowp=lowp;
499 cal_hw_higp=higp;
500 k=0;
501 while(k != ui.rx_rf_channels && cal_hw_lowp < cal_hw_higp)
502   {
503   k=0;
504   cal_hw_lowp++;
505   for(j=0; j<mm; j+=2)
506     {
507     if(cal_buf2[mm*cal_hw_lowp+j] > MIN_GAIN)k++;
508     }
509   }
510 k=0;
511 while(k != ui.rx_rf_channels && cal_hw_lowp<=cal_hw_higp)
512   {
513   k=0;
514   cal_hw_higp--;
515   for(j=0; j<mm; j+=2)
516     {
517     if(cal_buf2[mm*cal_hw_higp+j] > MIN_GAIN)k++;
518     }
519   }
520 screen:;
521 cal_type=CAL_TYPE_SET_FILTERSHAPE;
522 cal_initscreen();
523 t1=(0.001*fft1_size)/fft2_size;
524 sprintf(s,"%.1f dB points:   %fkHz     %fkHz",20*log10(MIN_GAIN),
525        t1*fft1_hz_per_point*cal_hw_lowp,t1*fft1_hz_per_point*cal_hw_higp);
526 lir_text(0,1,s);
527 if( (fft1_calibrate_flag&CALAMP) != CALAMP)
528   {
529 // This is the first time.
530 // Place the 75% point of the corrected spectrum at the MIN_GAIN point of
531 // the hardware. (75% amplitude = -4.4dB)
532 // Make the slope a parabola, start and stop equally many points away
533 // from the 75% point.
534 // Start with a parabola that ends halfway to the farthest end point.
535 // Store the desired filter with parabolic fall offs in cal_buf5.
536   falloff_length=cal_hw_lowp-1;
537   if(fft2_size-cal_hw_higp-2 > falloff_length)
538                     falloff_length=fft2_size-cal_hw_higp-2;
539   if(desired_shape ==1)
540     {
541     lowp=cal_hw_lowp;
542     higp=cal_hw_higp;
543     }
544   else
545     {
546     if(fft2_size/(falloff_length+1) > 20)
547       {
548       falloff_length=fft2_size/20;
549       if(cal_hw_lowp <= falloff_length)cal_hw_lowp=falloff_length+1;
550       if(cal_hw_higp > fft2_size-falloff_length-2)
551                                     cal_hw_higp=fft2_size-falloff_length-2;
552       }
553     t1=(ERF_END-ERF_START)/(ERF_END+0.5);
554     i=falloff_length;
555     falloff_length*=t1;
556     i=falloff_length-i;
557     lowp=cal_hw_lowp+i;
558     higp=cal_hw_higp-i;
559     }
560   }
561 else
562   {
563   i=0;
564   while(cal_fft1_desired[i] == 0 && i<fft2_size)i++;
565   if(i==fft2_size)
566     {
567     lirerr(1054);
568     return 0;
569     }
570   k=fft2_size-1;
571   while(cal_fft1_desired[k] == 0 && k>=i)k--;
572   j=i;
573   while(cal_fft1_desired[j] < 0.99999 && j<fft2_size)j++;
574   falloff_length=(j-i)/2;
575   if(falloff_length >= (k-i)/4)falloff_length=(k-i)/4-1;
576   lowp=i+falloff_length;
577   higp=k-falloff_length;
578   }
579 get_ideal_ampl:;
580 if(falloff_length >=lowp)
581   {
582   i=1+(falloff_length-lowp)/2;
583   lowp+=i;
584   falloff_length-=i;
585   }
586 if(falloff_length >=fft2_size-1-higp)
587   {
588   i=1+(falloff_length-(fft2_size-higp-1))/2;
589   higp-=i;
590   falloff_length-=i;
591   }
592 for(i=0; i<lowp; i++)cal_buf5[i]=0;
593 for(i=higp; i<fft2_size; i++)cal_buf5[i]=0;
594 for(i=lowp+1; i<higp; i++)cal_buf5[i]=1;
595 if(desired_shape ==1)
596   {
597   t1=sqrt(.25)/falloff_length;
598   t2=1;
599   t3=t1;
600   t2=1-t3*t3;
601   t3+=t1;
602   ja=lowp+falloff_length;
603   jb=higp-falloff_length;
604   while(t2 > 0)
605     {
606     cal_buf5[ja]=t2;
607     cal_buf5[jb]=t2;
608     t2=1-t3*t3;
609     t3+=t1;
610     ja--;
611     jb++;
612     }
613   }
614 else
615   {
616 adjust:;
617   ja=lowp+1;
618   jb=higp-1;
619   if(jb-ja < falloff_length/2)
620     {
621     lirerr(9845621);
622     return 0;
623     }
624   t1=(ERF_END-ERF_START)/falloff_length;
625   t2=ERF_START;
626   while(t2 < ERF_END)
627     {
628     t3=0.5*erfc(t2);
629     cal_buf5[ja]=t3;
630     cal_buf5[jb]=t3;
631     t2+=t1;
632     ja--;
633     jb++;
634     if(ja < 0)
635       {
636       lowp++;
637       goto adjust;
638       }
639     if(jb >= fft2_size)
640       {
641       higp--;
642       goto adjust;
643       }
644     }
645   }
646 // ******************************************************
647 // Valid spectra:
648 // cal_buf4 = accumulated pulse spectrum, power and phase (normalised)
649 // cal_buf2 = hardware spectrum, power and phase (normalised)
650 // cal_buf5 = desired spectrum (amplitude only)
651 // ******************************************************
652 // Divide cal_buf5 by sqrt(cal_buf2) to get the total correction function we need
653 // to get the desired frequency response.
654 for(i=0; i<fft2_size; i++)
655   {
656   for(j=0; j<mm; j+=2)
657     {
658     if(cal_buf2[mm*i+j]>0)
659       {
660       cal_buf3[mm*i+j]=cal_buf5[i]/sqrt(cal_buf2[mm*i+j]);
661       }
662     else
663       {
664       cal_buf3[mm*i+j]=0;
665       }
666     }
667   }
668 // Find max of the correction function from both sides.
669 ja=0;
670 jb=fft2_size-1;
671 ka=siz128;
672 kb=siz128;
673 for(j=0; j<mm; j++)z[j]=0;
674 for(i=0; i<fft2_size; i++)
675   {
676   for(j=0; j<mm; j+=2)
677     {
678     if(z[j  ]<cal_buf3[mm*ja+j])
679       {
680       z[j  ]=cal_buf3[mm*ja+j];
681       ka=fft2_size/128;
682       }
683     if(z[j+1]<cal_buf3[mm*jb+j])
684       {
685       z[j+1]=cal_buf3[mm*jb+j];
686       kb=fft2_size/128;
687       }
688     if(z[j  ]==0)ka=fft2_size/128;
689     if(z[j+1]==0)kb=fft2_size/128;
690     }
691   if(ka>0)
692     {
693     ja++;
694     ka--;
695     }
696   if(kb>0)
697     {
698     jb--;
699     kb--;
700     }
701   }
702 for(j=0; j<ui.rx_rf_channels; j++)
703   {
704   sprintf(s,"Max correction Ch %d    low=%.2fdB   high=%.2fdB     ",
705                         j,20*log10(z[2*j]), 20*log10(z[2*j+1]));
706   lir_text(0,2+j,s);
707   }
708 clear_lines(5,13);
709 lir_text(0, 5,"| inc | dec |      Item     |");
710 lir_text(0, 6,"|  A  |  B  |  Flat region  |");
711 lir_text(0, 7,"|  C  |  D  |  Center freq  |");
712 lir_text(0, 8,"|  E  |  F  |  Steepness    |");
713 sprintf(s,    "|  I  |  K  |  Step  %4d   |",step);
714 lir_text(0, 9,s);
715 lir_text(0,10,"|  +  |  -  |   Y-Gain      |");
716 lir_text(5,13,"Y=>Accept   X=Skip");
717 settextcolor(15);
718 lir_text(40,5,"DESIRED AMPLITUDE");
719 settextcolor(14);
720 lir_text(40,6,"CORR. AMPLITUDE");
721 settextcolor(10);
722 lir_text(40,7,"CORR. PHASE");
723 settextcolor(7);
724 sprintf(s,"|  M  |  Shape  %s",shape_texts[desired_shape]);
725 lir_text(40,9,s);
726 sprintf(s,"|     |");
727 lir_text(40,8,s);
728 lir_text(40,10,s);
729 for(i=0; i<screen_width; i++)
730   {
731   k=i*fft2_size/screen_last_xpixel;
732   if(k>fft2_size-1)k=fft2_size-1;
733 //k=fft2_size/2-screen_width/2+i;
734   for(j=0; j<mm; j+=2)
735     {
736     lir_setpixel(i, cal_graph[screen_width*(2*j  )+i], 0);
737     t2=0.1*ygain*cal_buf3[mm*k+j];
738     if(t2 <-cal_ymax)t2=-cal_ymax;
739     if(t2 >cal_ymax)t2=cal_ymax;
740     if(j > 0)t2-=YSH;
741     cal_graph[screen_width*(2*j  )+i]=screen_height*(cal_yzer-t2);
742     lir_setpixel(i, cal_graph[screen_width*(2*j  )+i], 14);
743     lir_setpixel(i, cal_graph[screen_width*(2*j+1)+i], 0);
744     t2=0.1*ygain*cal_buf5[k];
745     if(t2 <-cal_ymax)t2=-cal_ymax;
746     if(t2 >cal_ymax)t2=cal_ymax;
747     if(j > 0)t2-=YSH;
748     cal_graph[screen_width*(2*j+1)+i]=screen_height*(cal_yzer-t2);
749     lir_setpixel(i, cal_graph[screen_width*(2*j+1)+i], 15);
750     lir_setpixel(i, cal_graph[screen_width*(2*j+2)+i], 0);
751     t2=0.03*ygain*cal_buf2[mm*k+j+1];
752     if(t2 <-cal_ymax)t2=-cal_ymax;
753     if(t2 >cal_ymax)t2=cal_ymax;
754     if(j > 0)t2-=YSH;
755     cal_graph[screen_width*(2*j+2)+i]=screen_height*(cal_yzer-t2);
756     lir_setpixel(i, cal_graph[screen_width*(2*j+2)+i], 10);
757     }
758   if(k==fft2_size-1)goto grx;
759   }
760 grx:;
761 if(ui.rx_rf_channels >1)
762   {
763   if(z[0] > z[2])
764     {
765     t1=z[2]/z[0];
766     }
767   else
768     {
769     t1=z[0]/z[2];
770     }
771   sprintf(s,"Channel amplitudes  %f  %f   Diff %f dB",
772                                   z[0],z[2],-10*log10(t1));
773   if(t1 < 0.5)
774     {
775     clear_lines(14,15);
776     lir_text(0,14,"Channel unbalance too large.");
777     lir_text(0,15,"Clear old data or adjust hardware!");
778     }
779   }
780 await_processed_keyboard();
781 if(kill_all_flag) return 0;
782 switch (lir_inkey)
783   {
784   case 'X':
785   return 0;
786 
787   case '+':
788   ygain*=2;
789   break;
790 
791   case '-':
792   ygain/=2;
793   break;
794 
795   case 'A':
796   lowp-=step;
797   if(lowp < 2)lowp=2;
798   higp+=step;
799   if(higp > fft2_size-3)higp=fft2_size-3;
800   break;
801 
802   case 'B':
803   lowp+=step;
804   higp-=step;
805   if(lowp>higp-2*falloff_length)
806     {
807     higp=(lowp+higp)/2;
808     lowp=higp-falloff_length;
809     higp+=falloff_length;
810     }
811   break;
812 
813   case 'C':
814   lowp+=step;
815   higp+=step;
816   if(higp > fft2_size-3)higp=fft2_size-3;
817   break;
818 
819   case 'D':
820   lowp-=step;
821   higp-=step;
822   if(lowp<2)lowp=2;
823   break;
824 
825   case 'E':
826   falloff_length-=step;
827   if(falloff_length<1)falloff_length=1;
828   break;
829 
830   case 'F':
831   falloff_length+=step;
832   if(falloff_length>fft2_size/2.5)falloff_length=fft2_size/2.5;
833   break;
834 
835   case 'I':
836   step++;
837   if(step > siz128)step=siz128;
838   break;
839 
840   case 'K':
841   step--;
842   if(step < 1)step=1;
843   break;
844 
845   case 'M':
846   desired_shape^=1;
847   break;
848 
849   case F1_KEY:
850   case '!':
851   help_message(307);
852   goto screen;
853 
854   case 'Y':
855   goto update_ram;
856   }
857 goto get_ideal_ampl;
858 update_ram:;
859 // ******************************************************
860 // Valid spectra:
861 // cal_buf4 = accumulated pulse spectrum, power and phase (normalised)
862 // cal_buf2 = hardware spectrum, power and phase (normalised)
863 // cal_buf5 = desired spectrum (amplitude only)
864 // ******************************************************
865 // Renormalise cal_buf4 so the average becomes 1 within the hw limits
866 for(j=0; j<mm; j+=2)z[j]=0;
867 for(i=cal_hw_lowp+1; i<cal_hw_higp; i++)
868   {
869   for(j=0; j<mm; j+=2)
870     {
871     z[j]+=cal_buf4[mm*i+j  ];
872     }
873   }
874 for(j=0; j<mm; j+=2)z[j]/=(cal_hw_higp-cal_hw_lowp-1);
875 for(i=0; i<fft2_size; i++)
876   {
877   for(j=0; j<mm; j+=2)
878     {
879     cal_buf4[mm*i+j  ]/=z[j];
880     }
881   }
882 // We get the new filter function as cal_fft1_desired/cal_buf4
883 // Get the new correction by multiplication onto the old.
884 fft1_calibrate_flag|=CALAMP;
885 for(i=0; i<fft2_size; i++)
886   {
887   cal_fft1_desired[i]=cal_buf5[i];
888     {
889     for(j=0; j<mm; j+=2)
890       {
891       if(cal_buf4[mm*i+j]>0)
892         {
893         t1=sqrt(pow(cal_fft1_filtercorr[mm*i+j  ],2.0)+
894                 pow(cal_fft1_filtercorr[mm*i+j+1],2.0));
895         t2=atan2(cal_fft1_filtercorr[mm*i+j+1],
896                 cal_fft1_filtercorr[mm*i+j  ]);
897         t1*=cal_fft1_desired[i]/sqrt(cal_buf4[mm*i+j]);
898         t2-=cal_buf4[mm*i+j+1];
899         cal_fft1_filtercorr[mm*i+j  ]=t1;
900         cal_fft1_filtercorr[mm*i+j+1]=t2;
901         }
902       else
903         {
904         cal_fft1_filtercorr[mm*i+j  ]=0;
905         cal_fft1_filtercorr[mm*i+j+1]=0;
906         }
907       }
908     }
909   }
910 for(i=0; i<fft2_size; i++)
911   {
912   if(cal_fft1_desired[i] > 0)
913     {
914     for(j=0; j<mm; j+=2)
915       {
916       t1=cal_fft1_filtercorr[mm*i+j  ];
917       t2=cal_fft1_filtercorr[mm*i+j+1];
918       cal_fft1_filtercorr[mm*i+j  ]=cos(t2)*t1;
919       cal_fft1_filtercorr[mm*i+j+1]=sin(t2)*t1;
920       }
921     }
922   else
923     {
924     for(j=0; j<mm; j+=2)
925       {
926       cal_fft1_filtercorr[mm*i+j  ]=0;
927       cal_fft1_filtercorr[mm*i+j+1]=0;
928       }
929     }
930   }
931 for(i=0; i<mm*fft2_size; i++)cal_tmp[i]=cal_fft1_filtercorr[i];
932 convert_filtercorr_fd_to_td(fft2_n, fft2_size, cal_tmp);
933 resize_filtercorr_td_to_fd(TRUE, fft2_size, cal_tmp,
934                    fft1_n, fft1_size, fft1_filtercorr);
935 resize_fft1_desired(fft2_size, cal_fft1_desired, fft1_size, fft1_desired);
936 i=0;
937 while(fft1_desired[i] == 0)
938   {
939   for(j=0; j<mm; j++)
940     {
941     fft1_filtercorr[mm*i+j]=0;
942     }
943   i++;
944   if(i==fft1_size)lirerr(936183);
945   }
946 i=fft1_size-1;
947 while(fft1_desired[i] == 0)
948   {
949   for(j=0; j<mm; j++)
950     {
951     fft1_filtercorr[mm*i+j]=0;
952     }
953   i--;
954   }
955 settextcolor(7);
956 clear_screen();
957 return 1;
958 }
959 
show_missing_cal_info(void)960 void show_missing_cal_info(void)
961 {
962 clear_screen();
963 lir_text(5,5,"There is no calibration function.");
964 lir_text(5,7,"Run B first for amplitude and phase calibration");
965 lir_text(8,9,press_any_key);
966 await_processed_keyboard();
967 }
968 
969 
remove_iq_notch(void)970 int remove_iq_notch(void)
971 {
972 char s[80];
973 int i, j, k, m, n, mm;
974 float t1,t2,t3;
975 int old_end, cal_lowedge_min;
976 float xgain, xmid, ampmax;
977 // In case the native size of the calibration data is smaller
978 // than fft1_size, change fft1_size and remember old values in fft2_size.
979 if(cal_fft1_size == 0)
980   {
981   show_missing_cal_info();
982   return 1;
983   }
984 fft2_size=fft1_size;
985 fft2_n=fft1_n;
986 if(cal_fft1_size < fft1_size)
987   {
988   fft1_size=cal_fft1_size;
989   fft1_n=cal_fft1_n;
990   init_fft1_filtercorr();
991   fft1_hz_per_point*=(float)(fft2_size)/fft1_size;
992   }
993 mm=twice_rxchan;
994 old_end=0;
995 llsq_derivatives=malloc(LLSQ_MAXPAR*fft1_size/2*sizeof(float));
996 if(llsq_derivatives == NULL)
997   {
998   lirerr(1048);
999   return 0;
1000   }
1001 llsq_errors=malloc(fft1_size*sizeof(float));
1002 if(llsq_errors == NULL)
1003   {
1004   lirerr(1048);
1005 err_ex1:;
1006   free(llsq_derivatives);
1007   return 0;
1008   }
1009 xgain=(float)(screen_width)/fft1_size;
1010 xmid=fft1_size/2;
1011 // Do not fit curves closer than IQ_FIT_MINFREQ Hz from the center.
1012 // We assume that the phase and amplitude is entirely unaffected
1013 // by the AC coupling at this relatively high frequency.
1014 // The user may change this limit if he so wishes.
1015 cal_midlim=fft1_size/2-1-IQ_FIT_MINFREQ/fft1_hz_per_point;
1016 // Convert fft1_filtercorr to amplitude and phase.
1017 // Split it into a symmetric and an antisymmetric part.
1018 for(i=0; i<fft1_size/2; i++)
1019   {
1020   k=fft1_size-1-i;
1021   t1=0;
1022   for(j=0; j<mm; j+=2)
1023     {
1024     t2=sqrt(pow(fft1_filtercorr[mm*i+j  ],2.0)+
1025             pow(fft1_filtercorr[mm*i+j+1],2.0));
1026     t3=sqrt(pow(fft1_filtercorr[mm*k+j  ],2.0)+
1027             pow(fft1_filtercorr[mm*k+j+1],2.0));
1028     if(t2 > 0 && t3 > 0)
1029       {
1030       cal_buf6[mm*i+j  ]=t2/t3;
1031       cal_buf3[mm*i+j  ]=sqrt(t2*t3);
1032       if( cal_buf3[mm*i+j  ] > t1)t1=cal_buf3[mm*i+j  ];
1033       t2=atan2(fft1_filtercorr[mm*i+j+1],fft1_filtercorr[mm*i+j  ]);
1034       t3=atan2(fft1_filtercorr[mm*k+j+1],fft1_filtercorr[mm*k+j  ]);
1035       cal_buf6[mm*i+j+1]=(t2-t3)/2;
1036       cal_buf3[mm*i+j+1]=(t2+t3)/2;
1037       }
1038     else
1039       {
1040       if(t3 > 0)
1041         {
1042         cal_buf6[mm*i+j  ]=t2/t3;
1043         cal_buf3[mm*i+j  ]=sqrt(t3);
1044         t3=atan2(fft1_filtercorr[mm*k+j+1],fft1_filtercorr[mm*k+j  ]);
1045         cal_buf6[mm*i+j+1]=-t3/2;
1046         cal_buf3[mm*i+j+1]=t3/2;
1047         }
1048       else
1049         {
1050         if(t2 > 0)
1051           {
1052           t3=t2*0.00001;
1053           cal_buf6[mm*i+j  ]=t2/t3;
1054           cal_buf3[mm*i+j  ]=sqrt(t2*t3);
1055           t2=atan2(fft1_filtercorr[mm*i+j+1],fft1_filtercorr[mm*i+j  ]);
1056           cal_buf6[mm*i+j+1]=t2/2;
1057           cal_buf3[mm*i+j+1]=t2/2;
1058           }
1059         else
1060           {
1061           cal_buf6[mm*i+j  ]=0;
1062           cal_buf6[mm*i+j+1]=0;
1063           cal_buf3[mm*i+j  ]=0;
1064           cal_buf3[mm*i+j+1]=0;
1065           }
1066         }
1067       }
1068     }
1069   if(t1 > 0)
1070     {
1071     cal_buf5[i]=1/t1;
1072     }
1073   else
1074     {
1075     cal_buf5[i]=0;
1076     }
1077   }
1078 // cal_buf5 is for use as weight factor in curve fitting.
1079 // We may have incorrect data outside the passband so
1080 // fitting is limited by cal_lowedge
1081 cal_lowedge=0;
1082 while(fft1_desired[cal_lowedge] != 1 &&
1083                             cal_lowedge < fft1_size/2)cal_lowedge++;
1084 if(cal_lowedge < fft1_size/4)cal_lowedge=fft1_size/4;
1085 cal_lowedge_min=cal_lowedge;
1086 restart:;
1087 cal_type=CAL_TYPE_FIX_CENTER_SETUP;
1088 cal_initscreen();
1089 lir_text(5,5,"Remove center discontinuity");
1090 i=fft1_hz_per_point*(fft1_size/2-cal_midlim-1);
1091 sprintf(s,"%d Hz is excluded from polynomial fit on both",i);
1092 lir_text(5,6,s);
1093 lir_text(5,7,"sides of the center as indicated by the green line.");
1094 i=fft1_hz_per_point*(fft1_size/2-cal_lowedge-1);
1095 sprintf(s,"%d Hz away from the center is included in the polynomial fit",i);
1096 lir_text(5,8,s);
1097 lir_text(5,10,"L=Change center limit");
1098 lir_text(5,11,"R=Change range to fit");
1099 lir_text(5,13,"C=Continue with current value");
1100 draw_fitline();
1101 to_upper_await_keyboard();
1102 if(kill_all_flag)
1103   {
1104 err_ex2:;
1105   free(llsq_errors);
1106   goto err_ex1;
1107   }
1108 switch (lir_inkey)
1109   {
1110   case 'X':
1111   goto err_ex2;
1112 
1113   case 'L':
1114   lir_text(5,15,"New limit (Hz):");
1115   i=lir_get_integer(22,15,6,0,99999);
1116   if(kill_all_flag)goto err_ex2;
1117   if(i != 0)
1118     {
1119     cal_midlim=fft1_size/2-1-i/fft1_hz_per_point;
1120     }
1121   if(cal_midlim < 0.3*fft1_size)
1122     {
1123     cal_midlim=0.31*fft1_size;
1124     }
1125   goto restart;
1126 
1127   case 'R':
1128   lir_text(5,15,"New limit (Hz):");
1129   i=lir_get_integer(22,15,7,0,999999);
1130   if(kill_all_flag)goto err_ex2;
1131   if(i != 0)
1132     {
1133     cal_lowedge=fft1_size/2-1-i/fft1_hz_per_point;
1134     }
1135   if(cal_lowedge < cal_lowedge_min)cal_lowedge=cal_lowedge_min;
1136   if(cal_midlim-cal_lowedge < fft1_size/32)
1137     {
1138     cal_lowedge = 1+fft1_size/32-cal_midlim;
1139     }
1140   goto restart;
1141 
1142   case F1_KEY:
1143   case '!':
1144   help_message(308);
1145   goto restart;
1146 
1147   case 'C':
1148   break;
1149 
1150   default:
1151   goto restart;
1152   }
1153 
1154 if(kill_all_flag)goto err_ex2;
1155 llsq_neq=cal_midlim-cal_lowedge;
1156 if(llsq_neq < fft1_size/32)
1157   {
1158   lirerr(1137);
1159   goto err_ex2;
1160   }
1161 ampmax=0;
1162 for(i=cal_lowedge; i<cal_midlim; i++)
1163   {
1164   if(ampmax<cal_buf5[i])ampmax=cal_buf5[i];
1165   }
1166 for(i=0; i<fft1_size/2; i++)
1167   {
1168   for(j=0; j<mm; j+=2)
1169     {
1170     cal_buf3[mm*i+j  ]*=ampmax;
1171     }
1172   }
1173 // ***********************************************************
1174 // The antisymmetric power component has to be 1.0 at (fft1_size-1)/2
1175 // Fit it to y=c1*x + c2*x*x + c3*x*x*x ........
1176 llsq_npar=3;
1177 cal_ygain=1;
1178 fit_asym_power:;
1179 make_symfit(0, 0, cal_buf6, 1.0);
1180 llsq_npar=symmetry_fit_decide(cal_buf6, 1., "Antisymmetric power (RF)");
1181 if(llsq_npar == SYMFIT_CHANGE_LIMITS)goto restart;
1182 if(llsq_npar == -1)goto err_ex2;
1183 if(llsq_npar > 0)goto fit_asym_power;
1184 // ***********************************************************
1185 // The antisymmetric phase may have a dicontinuity at (fft1_size-1)/2.
1186 // Fit it to y=c1 + c2*x + c3*x*x ........
1187 llsq_npar=5;
1188 fit_asym_phase:;
1189 make_symfit(1,0,&cal_buf6[1],0.);
1190 llsq_npar=symmetry_fit_decide(&cal_buf6[1], 0.,"Antisymmetric phase (AF)");
1191 if(llsq_npar == SYMFIT_CHANGE_LIMITS)goto restart;
1192 if(llsq_npar == -1)goto err_ex2;
1193 if(llsq_npar > 0)goto fit_asym_phase;
1194 // Make the antisymmetric phase cross zero at (fft1_size-1)/2 in
1195 // case the user decides to use the fitted values.
1196 for(i=0; i<=fft1_size/2; i++)
1197   {
1198   for(j=0;j<ui.rx_rf_channels;j++)
1199     {
1200     cal_buf6[mm*i+2*j+1]-=llsq_steps[j];
1201     }
1202   }
1203 // ***********************************************************
1204 // The symmetric power part must be a symmetric function (even).
1205 // Fit it to y=c1 + c2*x*x + c3*x*x*x*x ........
1206 llsq_npar=6;
1207 fit_sym_power:;
1208 make_symfit(1,1,cal_buf3,0.);
1209 llsq_npar=symmetry_fit_decide(cal_buf3, 0.,"Symmetric power (AF)");
1210 if(llsq_npar == SYMFIT_CHANGE_LIMITS)goto restart;
1211 if(llsq_npar == -1)goto err_ex2;
1212 if(llsq_npar > 0)goto fit_sym_power;
1213 // ***********************************************************
1214 // The symmetric phase part must be a symmetric function (even).
1215 // Fit it to y=c1 + c2*x*x + c3*x*x*x*x ........
1216 llsq_npar=3;
1217 fit_sym_phase:;
1218 make_symfit(1,1,&cal_buf3[1],0.);
1219 llsq_npar=symmetry_fit_decide(&cal_buf3[1], 0.,"Symmetric phase (RF)");
1220 if(llsq_npar == SYMFIT_CHANGE_LIMITS)goto restart;
1221 if(llsq_npar == -1)goto err_ex2;
1222 if(llsq_npar > 0)goto fit_sym_phase;
1223 // Combine the symmetric and antisymmetric functions to give back
1224 // the total filter response.
1225 for(i=1; i<fft1_size/2; i++)
1226   {
1227   k=fft1_size-1-i;
1228   for(j=0; j<mm; j+=2)
1229     {
1230     t1=cal_buf3[mm*i+j];
1231     t2=sqrt(cal_buf6[mm*i+j]);
1232     if(t1>0 && t2>0)
1233       {
1234       cal_buf3[mm*k+j]=t1/t2;
1235       cal_buf3[mm*i+j]=t1*t2;
1236       t1=cal_buf3[mm*i+j+1]-cal_buf6[mm*i+j+1];
1237       t2=cal_buf3[mm*i+j+1]+cal_buf6[mm*i+j+1];
1238       cal_buf3[mm*k+j+1]=t1;
1239       cal_buf3[mm*i+j+1]=t2;
1240       }
1241     else
1242       {
1243       cal_buf3[mm*k+j]=0;
1244       cal_buf3[mm*i+j]=0;
1245       cal_buf3[mm*k+j+1]=0;
1246       cal_buf3[mm*i+j+1]=0;
1247       }
1248     }
1249   }
1250 cal_ygain=1;
1251 cal_type=CAL_TYPE_FIX_CENTER_SAVE;
1252 cal_initscreen();
1253 lir_text(4,2,"+/- => Y-scale");
1254 lir_text(4,3,"E,C => Expand,Contract x-scale");
1255 lir_text(4,4,"R,L => Right/Left x-scale");
1256 lir_text(4,5,"X  => Exit without saving");
1257 lir_text(4,6,"S  => Update RAM and save to disk");
1258 show:;
1259 for(j=0; j<mm; j++)
1260   {
1261   m=screen_width*j;
1262   n=screen_width*mm;
1263   for(i=1; i<=old_end; i++)
1264     {
1265     lir_line(cal_graph[n+i-1], cal_graph[m+i-1],
1266             cal_graph[n+i  ], cal_graph[m+i  ],0);
1267     if(kill_all_flag)goto err_ex2;
1268     }
1269   }
1270 old_end=-1;
1271 t1=0;
1272 i=-1;
1273 show1:
1274 t1+=xgain;
1275 if(t1<i+1)goto show1;
1276 i=t1;
1277 if(i>screen_last_xpixel)goto showx;
1278 k=(t1-screen_width/2)/xgain+xmid;
1279 while(k<0)k+=fft1_size;
1280 while(k>=fft1_size)k-=fft1_size;
1281 old_end++;
1282 cal_graph[screen_width*mm+old_end]=i;
1283 for(j=0; j<mm; j+=2)
1284   {
1285   t2=0.1*cal_ygain*cal_buf3[mm*k+j  ];
1286   if(t2 <-cal_ymax)t2=-cal_ymax;
1287   if(t2 >cal_ymax)t2=cal_ymax;
1288   if(j > 0)t2-=YSH;
1289   cal_graph[screen_width*j+old_end]=screen_height*(cal_yzer-t2);
1290   t2=0.03*cal_ygain*cal_buf3[mm*k+j+1];
1291   if(t2 <-cal_ymax)t2=-cal_ymax;
1292   if(t2 >cal_ymax)t2=cal_ymax;
1293   if(j > 0)t2-=YSH;
1294   cal_graph[screen_width*(j+1)+old_end]=screen_height*(cal_yzer-t2);
1295   if(old_end>0)
1296     {
1297     lir_line(cal_graph[screen_width*mm+old_end-1],
1298             cal_graph[screen_width*j+old_end-1],
1299           i,cal_graph[screen_width*j+old_end],13);
1300     if(kill_all_flag) goto err_ex2;
1301     lir_line(cal_graph[screen_width*mm+old_end-1],
1302          cal_graph[screen_width*(j+1)+old_end-1],
1303        i,cal_graph[screen_width*(j+1)+old_end],10);
1304     if(kill_all_flag) goto err_ex2;
1305     }
1306   }
1307 goto show1;
1308 showx:;
1309 await_processed_keyboard();
1310 if(kill_all_flag) goto err_ex2;
1311 switch (lir_inkey)
1312   {
1313   case 'S':
1314   for(i=0; i<fft1_size; i++)
1315     {
1316     for(j=0; j<mm; j+=2)
1317       {
1318       t1=cal_buf3[mm*i+j  ]/ampmax;
1319       t2=cal_buf3[mm*i+j+1];
1320       fft1_filtercorr[mm*i+j  ]=t1*cos(t2);
1321       fft1_filtercorr[mm*i+j+1]=t1*sin(t2);
1322       }
1323     }
1324   write_filcorr(0);
1325   free(llsq_derivatives);
1326   free(llsq_errors);
1327 // Restore filtercorr and fft1 desired.
1328   if(fft2_size != fft1_size)
1329     {
1330     fft1_hz_per_point/=(float)(fft2_size)/fft1_size;
1331     fft1_size=fft2_size;
1332     fft1_n=fft2_n;
1333     init_fft1_filtercorr();
1334     }
1335   return 1;
1336 
1337   case 'X':
1338   goto err_ex2;
1339 
1340   case '+':
1341   cal_ygain*=2;
1342   break;
1343 
1344   case '-':
1345   cal_ygain/=2;
1346   break;
1347 
1348   case 'C':
1349   xgain/=2;
1350   break;
1351 
1352   case 'E':
1353   xgain*=2;
1354   break;
1355 
1356   case 'R':
1357   xmid-=0.25*screen_width/xgain;
1358   break;
1359 
1360   case 'L':
1361   xmid+=0.25*screen_width/xgain;
1362   break;
1363 
1364 
1365   case F1_KEY:
1366   case '!':
1367   help_message(309);
1368   break;
1369   }
1370 goto show;
1371 }
1372 
1373