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 
24 #include "globdef.h"
25 #include "uidef.h"
26 #include "sigdef.h"
27 #include "screendef.h"
28 #include "fft1def.h"
29 #include "fft3def.h"
30 #include "options.h"
31 #include "rusage.h"
32 #include "thrdef.h"
33 
34 void show_cw(char *s);
35 void check_cw(int num,int type);
36 void make_coherent_graph(int clear_old);
37 void manage_meter_graph(void);
38 int coherent_graph_scro;
39 
40 extern int meter_graph_scro;
41 
42 // float[2] baseb_out=data to send to loudspeaker
43 // float[2] baseb=complex amplitude of first level coherent data.
44 // float[2] baseb_raw=complex amplitude of baseband signal. Raw data, pol. adapted.
45 // float[2] baseb_raw_orthog=complex amplitude of pol. orthog signal. Raw data.
46 // float[2] baseb_carrier=phase of carrier. Complex data, cosines and sines.
47 // float[1] baseb_carrier_ampl=amplitude of carrier
48 // float[1] baseb_totpwr=total power of baseb_raw
49 // float[2] baseb_envelope=complex amplitude from fitted dots and dashes.
50 // float[1] baseb_upthreshold=forward peak detector for power
51 // float[1] baseb_threshold=combined forward and backward peak detector at -6dB
52 // float[2] baseb_fit=fitted dots and dashes.
53 // float[2] baseb_tmp=array for intermediate data in complex format
54 // float[1] baseb_agc_level=used only when AGC is enabled.
55 // short int[1] baseb_ramp=indicator for time of power above/below threshold.
56 // short_int[1] baseb_clock=CW code clock
57 // float[2] baseb_tmp=for debug during development
58 
59 // baseb_pa  baseb exists up to baseb_pa-1.
60 // baseb_pb  The point up to which thresholds exist.
61 // baseb_pc  The point up to which ramp is collected.
62 // baseb_pd  A key up region close before baseb_pc
63 // baseb_pe  The point up to which we have run first detect.
64 // baseb_pf
65 // baseb_px  The oldest point that contains valid data.
66 
67 #define COH_GRAPH_ACTIVE -1000
68 #define X_SIZE 5
69 
evaluate_keying_spectrum(void)70 float evaluate_keying_spectrum(void)
71 {
72 int ia, ib, ic, kk, nn;
73 float t1, t2;
74 // keying_spectrum contains the modulation of the Morse-coded signal
75 // computed in mix2.c as the power spectrum of the real part of
76 // baseb_wb.
77 // One of the strong components should be the frequency of
78 // a string of Morse code dots, the "clock frequency" of the
79 // Morse code.
80 // The user has to set the baseband filter to allow this frequency
81 // within the baseband filter but the filter must not be set
82 // more than 2.5 times wider than optimum because we do not search
83 // for the "Morse clock" lower than this.
84 // keying_spectrum is computed over twice the bandwidth compared to
85 // the bandwidth of the user selected filter.
86 // The full baseband bandwidth, bw, corresponds to half the
87 // keying_spectrum width. (keying_spectrum_size/2)
88 // Start at bw/3 and step upwards until a minimum is reached.
89 // Look for signal power in pairs of points.
90 ia=3+keying_spectrum_size/6;
91 t1=keying_spectrum[ia]+keying_spectrum[ia-1];
92 t2=keying_spectrum[ia]+keying_spectrum[ia+1];
93 while(t2<t1 && ia<keying_spectrum_size/2)
94   {
95   t1=t2;
96   ia++;
97   t2=keying_spectrum[ia]+keying_spectrum[ia+1];
98   }
99 // Now we should have stepped away from the peak associated
100 // with the "Morse clock" divided by three.
101 // Look for the strongest peak above ia.
102 ib=ia;
103 t1=keying_spectrum[ib];
104 kk=ib;
105 while(ib<keying_spectrum_size/2)
106   {
107   if(t1<keying_spectrum[ib])
108     {
109     t1=keying_spectrum[ib];
110     kk=ib;
111     }
112   ib++;
113   }
114 // kk may be the Morse code clock, but it could also be
115 // half the frequency. Search 1.5 to 2.5 times kk for another
116 // maximum. If it has at least 50% of the power, attribute the
117 // higher peak to the Morse clock.
118 ib=1.5*kk;
119 ic=2.5*kk+1;
120 if(ic > keying_spectrum_size/2)ic=keying_spectrum_size/2;
121 nn=0;
122 t2=0;
123 while(ib < ic)
124   {
125   if(t2<keying_spectrum[ib])
126     {
127     t2=keying_spectrum[ib];
128     nn=ib;
129     }
130   ib++;
131   }
132 if(2*t2 > t1)kk=nn;
133 // Get the peak posuition with some decimals. Not very accurate,
134 // but the first decimal should be close to correct.
135 if(kk < keying_spectrum_size-1)
136   {
137   parabolic_fit(&t2, &t1, sqrt(keying_spectrum[kk-1]),
138                            sqrt(keying_spectrum[kk]),sqrt(keying_spectrum[kk+1]));
139   t1+=kk;
140   }
141 else
142   {
143   t1=kk;
144   t2=sqrt(keying_spectrum[kk]);
145   }
146 return t1;
147 }
148 
collect_ramp(void)149 void collect_ramp(void)
150 {
151 int ia, ib;
152 // Make a first decision key up/key down based on signal power.
153 // This is the conventional approach, a matched filter (the operator
154 // has to select the optimum bandwidth) followed by a squarelaw detector.
155 // The threshold is conventionally kept at a fixed level but here it is
156 // very close to the noise when no signal is present. while it is at
157 // half the peak amplitude for strong signals.
158 // Store the result in baseb_ramp as ramps.
159 // Positive values indicate key down while negative values
160 // indicate key up (or signal too weak)
161 ib=(baseb_pc+baseband_mask)&baseband_mask;
162 ia=baseb_pc;
163 while( ia != baseb_pb )
164   {
165   if(baseb_totpwr[ia] > baseb_threshold[ia])
166     {
167     if(baseb_ramp[ib]>0)
168       {
169       baseb_ramp[ia]=baseb_ramp[ib]+1;
170       }
171     else
172       {
173       baseb_ramp[ia]=1;
174       }
175     }
176   else
177     {
178     if(baseb_ramp[ib]<0)
179       {
180       baseb_ramp[ia]=baseb_ramp[ib]-1;
181       }
182     else
183       {
184       baseb_ramp[ia]=-1;
185       }
186     }
187   ib=ia;
188   ia=(ia+1)&baseband_mask;
189   }
190 baseb_pc=ib;
191 // Step baseb_pc backwards until a long key up region is found.
192 ib=2.5*cwbit_pts;
193 while(abs(baseb_ramp[baseb_pc]) < ib || baseb_ramp[baseb_pc] > 0)
194   {
195   baseb_pc=(baseb_pc-abs(baseb_ramp[baseb_pc])+baseband_size)&baseband_mask;
196   if( ((baseb_pc-baseb_pe+baseband_size)&baseband_mask)>baseband_neg)
197     {
198     baseb_pc=baseb_pe;
199     }
200   }
201 // Now we know that baseb_pc points to the start of a long negative ramp.
202 }
203 
204 
make_ideal_waveform(void)205 void make_ideal_waveform(void)
206 {
207 unsigned int i, j, k, m, ia;
208 float t1;
209 // Set the size we use for averaging the waveform around dashes.
210 // Make it an even number.
211 cw_avg_points=15*cwbit_pts+1;
212 cw_avg_points&=0xfffffffe;
213 if(cw_avg_points >= (fftn_tmp_bytes/(2*sizeof(float))))
214   {
215   lirerr(853021);
216   return;
217   }
218 // Assuming the modulation spectrum has been correctly evaluated
219 // the length of a Morse code bit should be about cwbit_pts points.
220 // Construct a dash of the appropriate lengths, make it symmetric
221 // around mix2.size/2 and run it through the baseband filter.
222 // Store the result as the first guess for our CW waveforms.
223 for(i=0; i<2*mix2.size; i++) mix2_tmp[i]=0;
224 j=mix2.size;
225 t1=cwbit_pts*3.0;
226 k=t1;
227 m=k/2;
228 k=2*m;
229 mix2_tmp[j]=1;
230 for(i=0; i<m; i++)
231   {
232   mix2_tmp[j+2*i]=1;
233   mix2_tmp[j-2-2*i]=1;
234   }
235 t1=0.5*(t1-k);
236 // t1 is the amount by which the dash is longer than the
237 // amount we set to 1 (on both sides).
238 // With full level for a fractional (=t1) sample period,
239 // the power within the interval is t1.
240 // The amplitude we should set is therefore sqrt(t1)
241 mix2_tmp[j+2*m]=sqrt(t1);
242 mix2_tmp[j-2-2*m]=sqrt(t1);
243 fftforward(mix2.size, mix2.n, mix2_tmp,
244                                 mix2.table, mix2.permute, yieldflag_ndsp_mix2);
245 i=1;
246 j=fft3_size/2;
247 while(bg_filterfunc[j] != 0)
248   {
249   mix2_tmp[2*i  ]*=bg_filterfunc[j];
250   mix2_tmp[2*i+1]*=bg_filterfunc[j];
251   mix2_tmp[2*(mix2.size-i)  ]*=bg_filterfunc[j];
252   mix2_tmp[2*(mix2.size-i)+1]*=bg_filterfunc[j];
253   i++;
254   j++;
255   }
256 while(i <= mix2.size/2)
257   {
258   mix2_tmp[2*i  ]=0;
259   mix2_tmp[2*i+1]=0;
260   mix2_tmp[2*(mix2.size-i)  ]=0;
261   mix2_tmp[2*(mix2.size-i)+1]=0;
262   i++;
263   }
264 fftback(mix2.size, mix2.n, mix2_tmp,
265                                mix2.table, mix2.permute, yieldflag_ndsp_mix2);
266 ia=mix2.size-cw_avg_points;
267 for(j=0; j<cw_avg_points; j++)
268   {
269   fftn_tmp[2*j  ]=mix2_tmp[ia];
270   fftn_tmp[2*j+1]=0;
271   ia+=2;
272   }
273 store_symmetry_adapted_dash(TRUE);
274 }
275 
coherent_cw_detect(void)276 void coherent_cw_detect(void)
277 {
278 int i, j;
279 float t1, t2, t3;
280 switch (cw_detect_flag)
281   {
282   case CWDETECT_CLEARED:
283   if(keying_spectrum_cnt < keying_spectrum_max)return;
284   keying_spectrum_cnt=0;
285 // Initiate a new search for Morse code.
286 // We will glook for the "Morse code clock" in keying_spectrum.
287   keying_spectrum_pos[0]=evaluate_keying_spectrum();
288   keying_spectrum_ampl[0]=keying_spectrum[0];
289   cwbit_pts=0.5*mix2.size/keying_spectrum_pos[0];
290   collect_ramp();
291   return;//öööööööööö
292   make_ideal_waveform();
293   keying_spectrum_ptr=1;
294   detect_cw_speed();
295   if(cw_detect_flag == CWDETECT_ERROR)
296     {
297     cw_detect_flag=CWDETECT_SEARCH_SPEED;
298     }
299   break;
300 
301   case CWDETECT_SEARCH_SPEED:
302   if(keying_spectrum_cnt < keying_spectrum_max)return;
303   keying_spectrum_cnt=0;
304 // Collect more data from keying_spectrum.
305   keying_spectrum_pos[keying_spectrum_ptr]=evaluate_keying_spectrum();
306   keying_spectrum_ampl[keying_spectrum_ptr]=keying_spectrum[0];
307   cwbit_pts=0.5*mix2.size/keying_spectrum_pos[keying_spectrum_ptr];
308   collect_ramp();
309   make_ideal_waveform();
310   detect_cw_speed();
311   if(cw_detect_flag == CWDETECT_ERROR)
312     {
313     cw_detect_flag=CWDETECT_SEARCH_SPEED;
314     keying_spectrum_ptr++;
315 // When KEYING_SPECTRUM_MAX spectra are evaluated, skip initial
316 // ones until the carrier power of the first one is above
317 // one third of the average carrier power.
318     if(keying_spectrum_ptr >= KEYING_SPECTRUM_MAX)
319       {
320       t1=keying_spectrum_ampl[0];
321       for(i=1; i<KEYING_SPECTRUM_MAX; i++)
322         {
323         t1+=keying_spectrum_ampl[i];
324         }
325       t1/=3*KEYING_SPECTRUM_MAX;
326       i=0;
327       while(keying_spectrum_ampl[i] < t1)i++;
328       if(i != 0)
329         {
330         j=0;
331         while(i<KEYING_SPECTRUM_MAX)
332           {
333           keying_spectrum_ampl[j]=keying_spectrum_ampl[i];
334           keying_spectrum_pos[j]=keying_spectrum_pos[i];
335           i++;
336           j++;
337           }
338         keying_spectrum_ptr=j;
339         }
340       else
341         {
342 // We have KEYING_SPECTRUM_MAX peaks in keying_spectrum.
343 // Check if they are close together.
344         t1=0;
345         t2=0;
346         for(i=0; i<KEYING_SPECTRUM_MAX; i++)
347           {
348           t1+=keying_spectrum_ampl[i];
349           t2+=keying_spectrum_ampl[i]*keying_spectrum_pos[i];
350           }
351         t3=t2/t1;
352         t1=0;
353         t2=0;
354         for(i=0; i<KEYING_SPECTRUM_MAX; i++)
355           {
356           if(fabs(t3-keying_spectrum_pos[i]) < 0.1*keying_spectrum_size)
357             {
358             t1+=keying_spectrum_ampl[i];
359             t2+=keying_spectrum_ampl[i]*keying_spectrum_pos[i];
360             }
361           }
362         if(t1 == 0)goto fail;
363         t2/=t1;
364         if(fabs(t3-t2) > 0.1*keying_spectrum_size)goto fail;
365 // The most probable frequency for the fundamental of the
366 // "Morse code clock" seems to be t2.
367 // Convert from frequency to time (samples)
368         cwbit_pts=0.5*mix2.size/t2;
369         make_ideal_waveform();
370         detect_cw_speed();
371         if(cw_detect_flag == CWDETECT_ERROR)goto fail;
372         }
373       }
374     }
375   break;
376 
377   case CWDETECT_WAVEFORM_ESTABLISHED:
378   collect_ramp();
379   first_find_parts();
380   break;
381 
382   case CWDETECT_SOME_PARTS_FITTED:
383   collect_ramp();
384   second_find_parts();
385   break;
386 
387   case CWDETECT_LIMITS_FOUND:
388   collect_ramp();
389   init_cw_decode_region();
390   break;
391 
392   case CWDETECT_REGION_INITIATED:
393   collect_ramp();
394   cw_decode_region();
395   break;
396 
397   case CWDETECT_REGION_WAVEFORM_OK:
398   collect_ramp();
399   init_cw_decode();
400   break;
401 
402   case CWDETECT_SOME_ASCII_FITTED:
403   collect_ramp();
404   cw_decode();
405   break;
406 
407   case CWDETECT_DEBUG_STOP:
408   lir_fillbox(screen_width/2,0,50,screen_height-1,3);
409   DEB"\nno more!!");
410   cw_detect_flag=CWDETECT_DEBUG_IDLE;
411   break;
412 
413   case CWDETECT_DEBUG_IDLE:
414   break;
415 
416   case CWDETECT_ERROR:
417 // Detect failed. Remove old data so buffers will not overflow.
418 fail:;
419   cw_detect_flag=CWDETECT_CLEARED;
420 // Do not store more than 2 minutes of bad data
421 // and make sure we leave some space in the buffer.
422   i=(baseb_pa-baseb_px+baseband_size)&baseband_mask;
423   j=i;
424   if(i>baseband_neg)i=baseband_neg;
425   if(i>120*baseband_sampling_speed)i=120*baseband_sampling_speed;
426   if(i != j)
427     {
428     baseb_px=(baseb_pa-i+baseband_size)&baseband_mask;
429     }
430   no_of_cwdat=0;
431   baseb_pf=baseb_px;
432   baseb_pe=baseb_px;
433   baseb_pd=baseb_px;
434   baseb_pc=baseb_px;
435 XZ("\n\nERROR  error  fail!! ");
436   break;
437   }
438 }
439 
check_cg_borders(void)440 void check_cg_borders(void)
441 {
442 int xsiz, ysiz;
443 xsiz=3*cg_size+2;
444 ysiz=xsiz+5*text_height+4;
445 current_graph_minh=ysiz;
446 current_graph_minw=xsiz;
447 check_graph_placement((void*)(&cg));
448 set_graph_minwidth((void*)(&cg));
449 }
450 
451 
help_on_coherent_graph(void)452 void help_on_coherent_graph(void)
453 {
454 int msg_no;
455 int event_no;
456 msg_no=68;
457 for(event_no=0; event_no<MAX_CGBUTT; event_no++)
458   {
459   if( cgbutt[event_no].x1 <= mouse_x &&
460       cgbutt[event_no].x2 >= mouse_x &&
461       cgbutt[event_no].y1 <= mouse_y &&
462       cgbutt[event_no].y2 >= mouse_y)
463     {
464     switch (event_no)
465       {
466       case CG_TOP:
467       case CG_BOTTOM:
468       case CG_LEFT:
469       case CG_RIGHT:
470       msg_no=101;
471       break;
472 
473       case CG_OSCILLOSCOPE:
474       msg_no=69;
475       break;
476 
477       case CG_METER_GRAPH:
478       msg_no=99;
479       break;
480       }
481     }
482   }
483 help_message(msg_no);
484 }
485 
486 
487 
488 
mouse_continue_coh_graph(void)489 void mouse_continue_coh_graph(void)
490 {
491 int i, j;
492 switch (mouse_active_flag-1)
493   {
494   case CG_TOP:
495   if(cg.ytop!=mouse_y)goto cgm;
496   break;
497 
498   case CG_BOTTOM:
499   if(cg.ybottom!=mouse_y)goto cgm;
500   break;
501 
502   case CG_LEFT:
503   if(cg.xleft!=mouse_x)goto cgm;
504   break;
505 
506   case CG_RIGHT:
507   if(cg.xright==mouse_x)break;
508 cgm:;
509   pause_screen_and_hide_mouse();
510   graph_borders((void*)&cg,0);
511   if(cg_oldx==COH_GRAPH_ACTIVE)
512     {
513     cg_oldx=mouse_x;
514     cg_oldy=mouse_y;
515     }
516   else
517     {
518     i=mouse_x-cg_oldx;
519     j=mouse_y-cg_oldy;
520     cg_oldx=mouse_x;
521     cg_oldy=mouse_y;
522     cg.ytop+=j;
523     cg.ybottom+=j;
524     cg.xleft+=i;
525     cg.xright+=i;
526     check_cg_borders();
527     }
528   graph_borders((void*)&cg,15);
529   resume_thread(THREAD_SCREEN);
530   break;
531 
532   default:
533   goto await_release;
534   }
535 if(leftpressed == BUTTON_RELEASED)goto finish;
536 return;
537 await_release:;
538 if(leftpressed != BUTTON_RELEASED)return;
539 switch (mouse_active_flag-1)
540   {
541   case CG_OSCILLOSCOPE:
542   if(ui.operator_skil == OPERATOR_SKIL_EXPERT)
543     {
544     pause_thread(THREAD_SCREEN);
545     hide_mouse(0,screen_width>>1,0,screen_height);
546     clear_cg_traces();
547     resume_thread(THREAD_SCREEN);
548     cg.oscill_on^=1;
549     cg.oscill_on&=1;
550     cg_max_trlevel=0;
551     }
552   break;
553 
554   case CG_METER_GRAPH:
555   pause_thread(THREAD_SCREEN);
556   hide_mouse(0,screen_width>>1,0,screen_height);
557   cg.meter_graph_on^=1;
558   manage_meter_graph();
559   resume_thread(THREAD_SCREEN);
560   break;
561 
562   default:
563   lirerr(872);
564   break;
565   }
566 finish:;
567 leftpressed=BUTTON_IDLE;
568 mouse_active_flag=0;
569 make_coherent_graph(TRUE);
570 cg_oldx=COH_GRAPH_ACTIVE;
571 }
572 
573 
mouse_on_coh_graph(void)574 void mouse_on_coh_graph(void)
575 {
576 int event_no;
577 // First find out is we are on a button or border line.
578 for(event_no=0; event_no<MAX_CGBUTT; event_no++)
579   {
580   if( cgbutt[event_no].x1 <= mouse_x &&
581       cgbutt[event_no].x2 >= mouse_x &&
582       cgbutt[event_no].y1 <= mouse_y &&
583       cgbutt[event_no].y2 >= mouse_y)
584     {
585     mouse_active_flag=1+event_no;
586     current_mouse_activity=mouse_continue_coh_graph;
587     return;
588     }
589   }
590 // Not button or border.
591 // Do nothing.
592 current_mouse_activity=mouse_nothing;
593 mouse_active_flag=1;
594 }
595 
make_coherent_graph(int clear_old)596 void make_coherent_graph(int clear_old)
597 {
598 int len;
599 pause_thread(THREAD_SCREEN);
600 hide_mouse(0,screen_width>>1,0,screen_height);
601 clear_cg_traces();
602 if(clear_old)
603   {
604   hide_mouse(cg_old_x1,cg_old_x2,cg_old_y1,cg_old_y2);
605   lir_fillbox(cg_old_x1,cg_old_y1,cg_old_x2-cg_old_x1+1,
606                                                     cg_old_y2-cg_old_y1+1,0);
607   }
608 check_cg_borders();
609 clear_button(cgbutt, MAX_CGBUTT);
610 hide_mouse(cg.xleft,cg.xright,cg.ytop,cg.ybottom);
611 scro[coherent_graph_scro].no=COH_GRAPH;
612 scro[coherent_graph_scro].x1=cg.xleft;
613 scro[coherent_graph_scro].x2=cg.xright;
614 scro[coherent_graph_scro].y1=cg.ytop;
615 scro[coherent_graph_scro].y2=cg.ybottom;
616 cgbutt[CG_LEFT].x1=cg.xleft;
617 cgbutt[CG_LEFT].x2=cg.xleft+2;
618 cgbutt[CG_LEFT].y1=cg.ytop;
619 cgbutt[CG_LEFT].y2=cg.ybottom;
620 cgbutt[CG_RIGHT].x1=cg.xright-2;
621 cgbutt[CG_RIGHT].x2=cg.xright;
622 cgbutt[CG_RIGHT].y1=cg.ytop;
623 cgbutt[CG_RIGHT].y2=cg.ybottom;
624 cgbutt[CG_TOP].x1=cg.xleft;
625 cgbutt[CG_TOP].x2=cg.xright;
626 cgbutt[CG_TOP].y1=cg.ytop;
627 cgbutt[CG_TOP].y2=cg.ytop+2;
628 cgbutt[CG_BOTTOM].x1=cg.xleft;
629 cgbutt[CG_BOTTOM].x2=cg.xright;
630 cgbutt[CG_BOTTOM].y1=cg.ybottom-2;
631 cgbutt[CG_BOTTOM].y2=cg.ybottom;
632 // Draw the border lines
633 graph_borders((void*)&cg,7);
634 if(ui.operator_skil == OPERATOR_SKIL_EXPERT)
635   {
636   make_button(cg.xleft+text_width-1,cg.ybottom-text_height/2-1,
637                                                cgbutt,CG_OSCILLOSCOPE,'o');
638   }
639 make_button(cg.xleft+text_width-1,cg.ybottom-5*(text_height/2-1),
640                                                cgbutt,CG_METER_GRAPH,'s');
641 cg_oldx=COH_GRAPH_ACTIVE;
642 settextcolor(7);
643 cg_x0=(cg.xleft+cg.xright)>>1;
644 len=((cg.xright-cg.xleft)>>1);
645 cg_y0=cg.ytop+len+1;
646 cg_y1=cg_y0+len;
647 lir_hline(cg.xleft+1,cg_y1,cg.xright-1,7);
648 if(kill_all_flag) return;
649 if(clear_old)
650   {
651   clear_coherent();
652   }
653 cg_old_y1=cg.ytop;
654 cg_old_y2=cg.ybottom;
655 cg_old_x1=cg.xleft;
656 cg_old_x2=cg.xright;
657 manage_meter_graph();
658 resume_thread(THREAD_SCREEN);
659 make_modepar_file(GRAPHTYPE_CG);
660 update_meter_time=current_time();
661 }
662 
init_coherent_graph(void)663 void init_coherent_graph(void)
664 {
665 int xsiz, ysiz;
666 xsiz=3*cg_size+2;
667 ysiz=xsiz+5*text_height+4;
668 if (read_modepar_file(GRAPHTYPE_CG) == 0)
669   {
670 reinit:;
671   cg.xright=screen_width-1;
672   cg.xleft=cg.xright-xsiz;
673   cg.ybottom=bg.ybottom;
674   cg.ytop=cg.ybottom-ysiz;
675   cg.meter_graph_on=1;
676   cg.oscill_on=0;
677   }
678 if(cg.ybottom-cg.ytop != ysiz ||
679    cg.xright-cg.xleft != xsiz)goto reinit;
680 cg_flag=1;
681 mg_flag=0;
682 cg_osc_shift_flag=0;
683 cg_max_trlevel=0;
684 coherent_graph_scro=no_of_scro;
685 // We have to reserve space for the meter graph here.
686 // The meter graph can be enabled or disabled from the coherent
687 // window at run time.
688 meter_graph_scro=no_of_scro+1;
689 cg.oscill_on&=1;
690 if(no_of_scro+2 >= MAX_SCRO)lirerr(89);
691 if(ui.operator_skil != OPERATOR_SKIL_EXPERT)cg.oscill_on=0;
692 make_coherent_graph(FALSE);
693 no_of_scro+=2;
694 }
695 
696