1 /////////////////////////////////////////////////////////////////////////////
2 //
3 //  Linrad driver code for the Funcube Dongle Pro Plus
4 //
5 //  Copyright (c) <2013> <Mike. J. Keehan>
6 //
7 // Permission is hereby granted, free of charge, to any person
8 // obtaining a copy of this software and associated documentation
9 // files (the "Software"), to deal in the Software without restriction,
10 // including without limitation the rights to use, copy, modify,
11 // merge, publish, distribute, sublicense, and/or sell copies of
12 // the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
25 // OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27 //
28 //
29 //
30 //
31 //  This code uses HIDAPI from http://www.signal11.us/oss/hidapi/
32 //  and the Linux kernel hidraw interface.
33 //
34 //  M. J. Keehan, 25th Feb 2013  (mike@keehan.net)
35 //
36 //
37 // NOTES:-
38 //
39 // Frequency setting
40 //   The user graphic window shows the FCD's current frequency setting.  It
41 //   is not an input field.
42 //
43 //   Linrad's frequency control box is configured for input in MHz, and
44 //   allows for 7 digits of input plus a decimal point, and uses a
45 //   floating point value to hold this input (floats are limited to approx
46 //   7 digits of precision).
47 //   The FCD supports setting input by individual Hz upto the 240MHz point,
48 //   and 7 digit precision above that.
49 //   Hence, it is possible to enter 1.234567MHz (or 12.34567, or 123.4567)
50 //   in Linrad and all digits will be used to set the FCD.  But above 240MHz
51 //   only 6 digits of frequency input are used (the code actually checks at
52 //   300MHz as the FCD has a break in coverage between approx 240MHz and 420MHz)
53 //
54 // Frequency increment
55 //   The value to be used in Linrad's frequency control box when up/down
56 //   buttons are used.  Defaults to 100kHz, range 1kHz-100MHz.
57 //
58 // Rx Gain control
59 //   The lower control in Linrad's Frequency control box has been configured
60 //   to adjust the FCD Pro Plus's IF Gain settings.
61 //
62 // PPM Correction
63 //   A ppm correction value can be set in the lowest line of the graphic box.
64 //   Either enter a value by clicking on the current value and using the
65 //   keyboard, or use the up/down arrow keys to increment/decrement the
66 //   current value.
67 //
68 //
69 //
70 //   Note: The usergraphic position, the ppm setting, frequency increment
71 //         and other values are saved in a parameterfile which is read
72 //         when Linrad is started.
73 //         The file name is "par_xxx_hwd_fcdgr" (with xxx = rx_mode) and its
74 //         content can be displayed (and changed - carefully) with an editor.
75 //
76 //
77 //////////////////////////////////////////////////////////////////////////////
78 
79 #if LIBUDEV_PRESENT==1
80 
81 #include <string.h>
82 
83 #include "fcdpp.h"
84 #include "hidapi.h"
85 
86 #include "globdef.h"
87 #include "uidef.h"
88 #include "screendef.h"
89 #include "fft1def.h"
90 #include "rusage.h"
91 #include "thrdef.h"
92 
93 
94 // UserGraphic Button defs.
95 #define UG_TOP 0
96 #define UG_BOTTOM 1
97 #define UG_LEFT 2
98 #define UG_RIGHT 3
99 #define UG_INCREASE_PPM_CORR 4
100 #define UG_DECREASE_PPM_CORR 5
101 #define UG_LNA_ON_OFF 6
102 #define UG_MIXER_GAIN_ON_OFF 7
103 #define UG_BIAS_T_ON_OFF 8
104 #define MAX_FCDGRBUTT 9
105 
106 //Size of messages in user graphic box
107 #define MAX_MSGSIZE 30
108 
109 
110 // FCDPro+ Global variables.
111 // =========================
112 int FCDstate;
113 #define FCD_NOTFOUND 0
114 #define FCD_RUNNING  2
115 #define FCD_BOOTLOAD 1
116 
117 #define MAX_HWAREDRIVER_PARM 12
118 char *fcdprodriver_parm_text[MAX_HWAREDRIVER_PARM]={
119         "fcdgr_xleft          ",
120         "fcdgr_ytop           ",
121         "center_freq          ",
122         "freq_increment       ",
123         "lna_on_off           ",
124         "mixer_gain_on_off    ",
125 	"rf_filter_val        ",
126         "if_filter_val        ",
127         "ppm_correction       ",
128         "if_gain              ",
129         "bias_t_on_off        ",
130         "dummy_entry_to_end   "
131         };
132 
133 typedef struct {
134         int fcdgr_xleft;
135         int fcdgr_ytop;
136 	unsigned int center_freq;
137         int freq_incr;
138 	int lna_on_off;
139 	int mixer_gain_on_off;
140 	int rf_filter_val;
141 	int if_filter_val;
142 	int ppm_correction;
143         int if_gain;
144 	int biasT_on_off;
145 	int dummy;
146 } HWAREDRIVER_PARMS;
147 HWAREDRIVER_PARMS fcdprodriver;
148 
149 typedef struct {
150         int ytop;
151         int ybottom;
152         int xleft;
153         int xright;
154         int yborder;
155 } UG_PARMS;
156 UG_PARMS fcdgr;
157 
158 BUTTONS fcdgrbutt[MAX_FCDGRBUTT];
159 
160 int fcdgr_old_y1;
161 int fcdgr_old_y2;
162 int fcdgr_old_x1;
163 int fcdgr_old_x2;
164 int fcdproplus_graph_scro;
165 
166 int fcdgr_oldx;
167 int fcdgr_oldy;
168 int fcdgr_yborder;
169 int fcdgr_hsiz;
170 int fcdgr_vsiz;
171 
172 int fcdgr_msg_color;          // switch for message-color in usergraph
173 int LinFreqColor = 7;
174 char fcdgr_msg0[MAX_MSGSIZE]; // messages in usergraph
175 char fcdgr_msg1[MAX_MSGSIZE];
176 char fcdgr_msg2[MAX_MSGSIZE];
177 
178 // Prototypes
179 int fcdppGetBiasT(unsigned char *);
180 int fcdppGetIFGain(unsigned char *);
181 int fcdppGetLNA(unsigned char *);
182 int fcdppGetMixerGain(unsigned char *);
183 int fcdppSetBiasT(unsigned char);
184 int fcdppSetFreq(unsigned int, unsigned int *);
185 int fcdppSetIFGain(unsigned char);
186 int fcdppSetLNA(unsigned char);
187 int fcdppSetMixerGain(unsigned char);
188 void fcd_append_freq(char *, double);
189 void init_fcdproplus_control_window(void);
190 void readDevice(int);
191 int read_value_from_fcd(unsigned char, unsigned char *);
192 void show_fcdpp_parms(void);
193 int write_value_to_fcd(unsigned char, unsigned int, unsigned int *);
194 
195 //////////////
196 // Code
197 //////////////
198 
fcdproplus_setup(void)199 void fcdproplus_setup(void)
200 {
201   // Routine to set up FCDProPlus from the soundcard setup menu.
202   clear_screen();
203   lir_text(3,3,"FCDProPlus selected. (Nothing to do)");
204   lir_text(3,6,press_any_key);
205   await_processed_keyboard();
206 }
207 
208 // This is the data entered in the rx gain control box.
209 // int fg.gain_increment = The increment in dB for clicking
210 // the arrows in the gain control box.
211 // The global fg_new_band can be used to allow for different
212 // settings on different bands.
213 //
214 // Uses global data:
215 //   int fg.gain
216 //************************************************************
fcdproplus_rx_amp_control(void)217 void fcdproplus_rx_amp_control(void)
218 {
219   fg.gain_increment = 1;	// FCD's IF GAIN, step by 1dB.
220 
221   // Limit gain to 0-59dB.  Be aware that values
222   // above about 30dB are likely to overload.
223   if ( fg.gain < 0 )
224     fg.gain = 0;
225   if ( fg.gain > 59 )
226     fg.gain = 59;
227 
228   FCDstate = fcdppSetIFGain(fg.gain);
229   FCDstate = fcdppGetIFGain((unsigned char *)&fcdprodriver.if_gain);
230   fg.gain = fcdprodriver.if_gain;
231 }
232 
fcdproplus_rx_freq_control(void)233 void fcdproplus_rx_freq_control(void)
234 { unsigned int fcdFreq, askFreq, centerfreq;
235   int correction;
236 
237   fg.passband_direction = 1;
238   fft1_direction = fg.passband_direction;
239   if ( fcdprodriver.dummy == 0 )	// not yet loaded user parameters
240     return;
241 
242   fg.passband_increment = ((double)fcdprodriver.freq_incr) / 1000.0;
243 
244   // Limit the requested frequency to what is possible on FCDproplus.
245   LinFreqColor = 10;			// normal GREEN
246   if ( fg.passband_center < .15 )     // set min freq to 150kHz.
247   { fg.passband_center = .15;
248     LinFreqColor = 12;		// show frequency in RED
249   }
250   if ( fg.passband_center > 2050 )    // set max freq to 2.05GHz.
251   { fg.passband_center = 2050;
252     LinFreqColor = 12;		// show frequency in RED
253   }
254 
255   // Following horrible expression uses floating point to maintain precision till result.
256   correction = (int)(((fg.passband_center*1000000.0*fcdprodriver.ppm_correction)+0.5)/1000000);
257 
258   // Round() request frequency to 7 digits of precision
259   centerfreq = (unsigned int)rint(fg.passband_center*1000000);
260   if ( centerfreq > 300000000 )
261     centerfreq = ((centerfreq)/1000)*1000;
262   if ( centerfreq > 99999999 )
263     centerfreq = ((centerfreq+4)/100)*100;
264   if ( centerfreq > 9999999 )
265     centerfreq = ((centerfreq+4)/10)*10;
266   fcdprodriver.center_freq = centerfreq;
267 
268   askFreq = centerfreq + correction;
269   if ( askFreq > 300000000 )
270     askFreq = ((askFreq)/1000)*1000;
271 
272   FCDstate = fcdppSetFreq(askFreq, &fcdFreq);
273 
274   if ( fcdFreq != askFreq )
275   { if ( FCDstate != FCD_RUNNING )
276       fprintf( stderr,"fcdpp: FAILED TO SET FCD FREQ! mode: %s, ask:%d, got:%d\n",
277                 (FCDstate==FCD_RUNNING) ? "Running" :
278                 (FCDstate==FCD_BOOTLOAD) ? "BootLoad" : "Not Found", askFreq, fcdFreq);
279 
280     LinFreqColor = 12;		// show frequency in RED text!
281     fg.passband_center = (double) (fcdFreq - correction)/1000000.0;
282     fcdprodriver.center_freq = fcdFreq;
283   }
284 
285   show_fcdpp_parms();
286 //fprintf( stderr,"fcdpp_rx_freq: fg.pass %f, center %d, corr %d, askFreq %d, fcdFreq %d\n",
287 //        fg.passband_center, centerfreq, correction, askFreq, fcdFreq);
288 }
289 
290 // Append frequency formatted in Hz or kHz to the end of
291 // the already present text in the MAX_MSGSIZE buffer.
fcd_append_freq(char * buffer,double fq)292 void fcd_append_freq(char *buffer, double fq)
293 { int j, freq;
294   char *src, *dst, *tail = " Hz", stmp[MAX_MSGSIZE];
295   unsigned u,spaceleft;
296 
297   freq = (int)fq;                // need Hertz, and not fractions of
298   sprintf(stmp, "%d", freq);
299 
300   // Find out how much space is left in given buffer.
301   spaceleft = MAX_MSGSIZE - strlen(buffer) - 1;
302 
303   // Change freq to kHz if too many digits present as Hz
304   if ( (strlen(stmp) + strlen(" Hz")) > spaceleft )
305   {
306     freq = freq / 1000;          // kHz
307     sprintf(stmp, "%d", freq);
308     tail = " kHz";
309   }
310 
311   // space pad the rest of the buffer, and terminate with the tail.
312   for ( u=strlen(buffer) ; u<(MAX_MSGSIZE - strlen(tail) -1) ; ++u )
313     buffer[u] = ' ';
314   strcpy(&buffer[MAX_MSGSIZE - strlen(tail) -1], tail);
315 
316   // copy frequency tail first, adding separators every 3rd digit
317   src = &stmp[strlen(stmp)];
318   dst = &buffer[MAX_MSGSIZE - strlen(tail) -1];
319   for ( u=strlen(stmp), j=0 ; u ; --u )
320   {
321     *--dst = *--src;
322     if ( (++j % 3) == 0  &&  u > 1 ) // but no sep. at front of digits
323       *--dst = ',';
324   }
325 }
326 
check_fcdgr_borders(void)327 void check_fcdgr_borders(void)  // required for move graph
328 {
329   current_graph_minh=fcdgr_vsiz;
330   current_graph_minw=fcdgr_hsiz;
331   check_graph_placement((void*)(&fcdgr));
332   set_graph_minwidth((void*)(&fcdgr));
333 }
334 
make_fcdproplus_control_graph(void)335 void make_fcdproplus_control_graph(void)
336 {
337   pause_thread(THREAD_SCREEN);
338 
339   check_fcdgr_borders();        // required for move graph
340   scro[fcdproplus_graph_scro].no=FCDPROPLUS_GRAPH;
341 
342   // These are the coordinates of the border lines.
343   scro[fcdproplus_graph_scro].x1=fcdgr.xleft;
344   scro[fcdproplus_graph_scro].x2=fcdgr.xright;
345   scro[fcdproplus_graph_scro].y1=fcdgr.ytop;
346   scro[fcdproplus_graph_scro].y2=fcdgr.ybottom;
347 
348   // Each border line is treated as a button.
349   // That is for the mouse to get hold of them so the window can be moved.
350   fcdgrbutt[UG_LEFT].x1=fcdgr.xleft;
351   fcdgrbutt[UG_LEFT].x2=fcdgr.xleft+2;
352   fcdgrbutt[UG_LEFT].y1=fcdgr.ytop;
353   fcdgrbutt[UG_LEFT].y2=fcdgr.ybottom;
354   fcdgrbutt[UG_RIGHT].x1=fcdgr.xright;
355   fcdgrbutt[UG_RIGHT].x2=fcdgr.xright-2;
356   fcdgrbutt[UG_RIGHT].y1=fcdgr.ytop;
357   fcdgrbutt[UG_RIGHT].y2=fcdgr.ybottom;
358   fcdgrbutt[UG_TOP].x1=fcdgr.xleft;
359   fcdgrbutt[UG_TOP].x2=fcdgr.xright;
360   fcdgrbutt[UG_TOP].y1=fcdgr.ytop;
361   fcdgrbutt[UG_TOP].y2=fcdgr.ytop+2;
362   fcdgrbutt[UG_BOTTOM].x1=fcdgr.xleft;
363   fcdgrbutt[UG_BOTTOM].x2=fcdgr.xright;
364   fcdgrbutt[UG_BOTTOM].y1=fcdgr.ybottom;
365   fcdgrbutt[UG_BOTTOM].y2=fcdgr.ybottom-2;
366 
367   // Draw the border lines
368   graph_borders((void*)&fcdgr,7);
369   fcdgr_oldx=-10000;                           // from freq_control
370   settextcolor(7);
371   make_button(fcdgr.xleft+57.2*text_width+2,-2+fcdgr.ybottom-1*text_height/2-1,
372                                      fcdgrbutt,UG_DECREASE_PPM_CORR,25);
373   make_button(fcdgr.xleft+58.7*text_width+2,-2+fcdgr.ybottom-1*text_height/2-1,
374                                      fcdgrbutt,UG_INCREASE_PPM_CORR,24);
375 
376   // Draw separator lines in usergraph
377   // vertical
378   lir_line(fcdgr.xleft+30.5*text_width,fcdgr.ytop+1.3*text_height,
379                                     fcdgr.xleft+30.5*text_width,fcdgr.ybottom,7);
380   // horizontal
381   lir_line(fcdgr.xleft,fcdgr.ytop+1.3*text_height,fcdgr.xright,fcdgr.ytop+1.3*text_height,7);
382 
383   // Make button for LNA
384   fcdgrbutt[UG_LNA_ON_OFF].x1=fcdgr.xleft+16*text_width;
385   fcdgrbutt[UG_LNA_ON_OFF].x2=fcdgr.xleft+20*text_width;
386   fcdgrbutt[UG_LNA_ON_OFF].y1=fcdgr.ytop+1.5*text_height;
387   fcdgrbutt[UG_LNA_ON_OFF].y2=fcdgr.ytop+2.4*text_height;
388 
389   // Make button for Mixer Gain
390   fcdgrbutt[UG_MIXER_GAIN_ON_OFF].x1=fcdgr.xleft+16*text_width;
391   fcdgrbutt[UG_MIXER_GAIN_ON_OFF].x2=fcdgr.xleft+20*text_width;
392   fcdgrbutt[UG_MIXER_GAIN_ON_OFF].y1=fcdgr.ytop+2.5*text_height;
393   fcdgrbutt[UG_MIXER_GAIN_ON_OFF].y2=fcdgr.ytop+3.4*text_height;
394 
395   // Make button for BiasT
396   fcdgrbutt[UG_BIAS_T_ON_OFF].x1=fcdgr.xleft+16*text_width;
397   fcdgrbutt[UG_BIAS_T_ON_OFF].x2=fcdgr.xleft+20*text_width;
398   fcdgrbutt[UG_BIAS_T_ON_OFF].y1=fcdgr.ytop+3.5*text_height;
399   fcdgrbutt[UG_BIAS_T_ON_OFF].y2=fcdgr.ytop+4.4*text_height;
400 
401   fcdproplus_rx_freq_control();
402   show_fcdpp_parms();
403   resume_thread(THREAD_SCREEN);
404 }
405 
append_blanks_fcdgrmsg(char * msg)406 void append_blanks_fcdgrmsg(char *msg)
407 { int i;
408 
409   i=strlen(msg);
410   if(i>=MAX_MSGSIZE)
411     lirerr(3413400);
412   while(i<MAX_MSGSIZE-1)
413   {
414     msg[i]=' ';
415     i++;
416   }
417   msg[MAX_MSGSIZE-1]=0;
418 }
419 
420 // Show the user parameters on screen
show_fcdpp_parms(void)421 void show_fcdpp_parms(void)
422 { int i;
423   int *sdr_pi;
424   char s[80];
425   char *mode = "";
426   char fcdprodriver_parfil_name[20];
427   FILE *fcdprodriver_file;
428 
429   switch(rx_mode)
430   {
431     case MODE_WCW:       mode = "WCW";       break;
432     case MODE_HSMS:      mode = "HSMS";      break;
433     case MODE_QRSS:      mode = "QRSS";      break;
434     case MODE_NCW:       mode = "NCW";       break;
435     case MODE_SSB:       mode = "SSB";       break;
436     case MODE_FM:        mode = "FM ";       break;
437     case MODE_AM:        mode = "AM ";       break;
438   }
439 
440   if ( fcdprodriver.dummy == 0 )	// if control window does not yet exist, exit.
441     return;
442 
443   hide_mouse(fcdgr.xleft, fcdgr.xright,fcdgr.ytop,fcdgr.ybottom);
444   sprintf(s,"FunCubeDongle Pro+  %4s         LINRAD  %s mode",
445                   (FCDstate==FCD_RUNNING) ? "Running" : \
446                   (FCDstate==FCD_BOOTLOAD) ? "BootLoad" : "Not Found", mode);
447   settextcolor(14); // yellow
448   lir_pixwrite(fcdgr.xleft+1.5*text_width,fcdgr.ytop+0.35*text_height,s);
449   settextcolor(15); // white
450   settextcolor(7);  // grey
451 
452   sprintf(s,"PPM correction : %d ", fcdprodriver.ppm_correction);
453   s[26]=0;
454   lir_pixwrite(fcdgr.xleft+31*text_width,fcdgr.ytop+1+3.5*text_height,s);
455   sprintf(s,"VFO incr (Khz) : %d        ", fcdprodriver.freq_incr);
456   s[25]=0;
457   lir_pixwrite(fcdgr.xleft+31*text_width,fcdgr.ytop+2.5*text_height,s);
458 
459   //Display messages in message-area of usergraph
460   settextcolor(11); // light blue
461   sprintf(s,"Low Noise Amp: %3s", fcdprodriver.lna_on_off ? "On" : "Off");
462   append_blanks_fcdgrmsg(s);
463   lir_pixwrite(fcdgr.xleft+1.5*text_width,fcdgr.ytop+1.5*text_height,s);
464   sprintf(s,"Mixer Gain   : %3s", fcdprodriver.mixer_gain_on_off? "On":"Off");
465   append_blanks_fcdgrmsg(s);
466   lir_pixwrite(fcdgr.xleft+1.5*text_width,fcdgr.ytop+2.5*text_height,s);
467   sprintf(s,"Bias T       : %3s", fcdprodriver.biasT_on_off ? "On" : "Off");
468   append_blanks_fcdgrmsg(s);
469   lir_pixwrite(fcdgr.xleft+1.5*text_width,fcdgr.ytop+3.5*text_height,s);
470 
471   sprintf(fcdgr_msg0,"FCDPP Freq: ");
472   fcd_append_freq(fcdgr_msg0,fcdprodriver.center_freq);
473   append_blanks_fcdgrmsg(fcdgr_msg0);
474   settextcolor(LinFreqColor);
475   lir_pixwrite(fcdgr.xleft+31*text_width,fcdgr.ytop+1.5*text_height,fcdgr_msg0);
476 
477   // Save screenposition, and FCD settings to the parameterfile on disk
478   sprintf(fcdprodriver_parfil_name,"%s_hwd_fcdgr",rxpar_filenames[rx_mode]);
479   fcdprodriver_file=fopen(fcdprodriver_parfil_name,"w");
480   fcdprodriver.fcdgr_xleft=fcdgr.xleft;
481   fcdprodriver.fcdgr_ytop=fcdgr.ytop;
482   sdr_pi=(int*)(&fcdprodriver);
483   for(i=0; i<MAX_HWAREDRIVER_PARM; i++)
484     fprintf(fcdprodriver_file,"%s [%d]\n",fcdprodriver_parm_text[i],sdr_pi[i]);
485   parfile_end(fcdprodriver_file);
486 
487   settextcolor(7);
488 }
489 
new_fcd_lna_on_off(void)490 void new_fcd_lna_on_off(void)
491 { int fme;
492 
493   fcdprodriver.lna_on_off ^= 1;
494   fme = fcdppSetLNA(fcdprodriver.lna_on_off);
495   if ( fme == 0 )
496     fprintf( stderr,"new_fcd_lna_on_off: Failed to change LNA setting\n");
497 }
498 
new_fcd_biasT(void)499 void new_fcd_biasT(void)
500 { int fme;
501 
502   fcdprodriver.biasT_on_off ^= 1;
503   fme = fcdppSetBiasT(fcdprodriver.biasT_on_off);
504   if ( fme == 0 )
505     fprintf( stderr,"new_fcd_biasT: Failed to change BIAS T setting\n");
506 }
507 
new_fcd_mixer_gain(void)508 void new_fcd_mixer_gain(void)
509 { int fme;
510 
511   fcdprodriver.mixer_gain_on_off ^= 1;
512   fme = fcdppSetMixerGain(fcdprodriver.mixer_gain_on_off);
513   if ( fme == 0 )
514     fprintf( stderr,"new_fcd_mixer_gain: Failed to change MixerGain setting\n");
515 }
516 
mouse_continue_fcdproplus_graph(void)517 void mouse_continue_fcdproplus_graph(void)
518 { int i, j;      // required for move graph
519 
520   // Move border lines immediately.
521   // Other functions must wait until button is released.
522   // Move fixed size window  based on coding in freq_control.c
523   switch (mouse_active_flag-1)
524   {
525     case UG_TOP:
526       if (fcdgr.ytop!=mouse_y)
527       break;
528 
529     case UG_BOTTOM:
530       if (fcdgr.ybottom!=mouse_y)
531       break;
532 
533     case UG_LEFT:
534       if (fcdgr.xleft!=mouse_x)
535       break;
536 
537     case UG_RIGHT:
538       if (fcdgr.xright==mouse_x)
539         break;
540       break;
541 
542     default:
543       goto await_release;
544   }
545 
546   pause_screen_and_hide_mouse();
547   graph_borders((void*)&fcdgr,0);
548   if (fcdgr_oldx==-10000)
549   {
550     fcdgr_oldx=mouse_x;
551     fcdgr_oldy=mouse_y;
552   }
553   else
554   {
555     i=mouse_x-fcdgr_oldx;
556     j=mouse_y-fcdgr_oldy;
557     fcdgr_oldx=mouse_x;
558     fcdgr_oldy=mouse_y;
559     fcdgr.ytop+=j;
560     fcdgr.ybottom+=j;
561     fcdgr.xleft+=i;
562     fcdgr.xright+=i;
563     check_fcdgr_borders();
564     fcdgr.yborder=(fcdgr.ytop+fcdgr.ybottom)>>1;
565   }
566   graph_borders((void*)&fcdgr,15);
567   resume_thread(THREAD_SCREEN);
568 
569   if (leftpressed == BUTTON_RELEASED)
570     goto finish;
571   return;
572 
573 await_release:;
574   if (leftpressed != BUTTON_RELEASED)
575     return;
576 
577   // Assuming the user wants to control hardware, allow
578   // commands only when data is not over the network.
579   if((ui.network_flag&NET_RX_INPUT) == 0) //for linrad02.23
580   {
581     switch (mouse_active_flag-1)
582     {
583       case UG_INCREASE_PPM_CORR:
584         if ( fcdprodriver.ppm_correction < 255 )
585         {
586           fcdprodriver.ppm_correction++;
587           fcdproplus_rx_freq_control();
588         }
589         break;
590 
591       case UG_DECREASE_PPM_CORR:
592         if ( fcdprodriver.ppm_correction > -255 )
593         {
594           fcdprodriver.ppm_correction--;
595           fcdproplus_rx_freq_control();
596         }
597         break;
598 
599       case UG_LNA_ON_OFF:
600         new_fcd_lna_on_off();
601         break;
602 
603       case UG_MIXER_GAIN_ON_OFF:
604         new_fcd_mixer_gain();
605         break;
606 
607       case UG_BIAS_T_ON_OFF:
608         new_fcd_biasT();
609         break;
610 
611       default:        // This should never happen.
612         lirerr(211053);
613         break;
614     }
615   }
616 
617 finish:;
618   hide_mouse(fcdgr_old_x1,fcdgr_old_x2,fcdgr_old_y1,fcdgr_old_y2);
619   leftpressed=BUTTON_IDLE;
620   mouse_active_flag=0;
621   graph_borders((void*)&fcdgr,0);
622   lir_fillbox(fcdgr_old_x1,fcdgr_old_y1,fcdgr_old_x2-fcdgr_old_x1,fcdgr_old_y2-fcdgr_old_y1,0);
623   make_fcdproplus_control_graph();
624   fcdgr_oldx=-10000;
625 }
626 
new_fcd_ppm_correction(void)627 void new_fcd_ppm_correction(void)
628 {
629   if ( numinput_int_data >= -255 && numinput_int_data <= 255 )
630   {
631     fcdprodriver.ppm_correction=numinput_int_data;
632     pause_thread(THREAD_SCREEN);
633     show_fcdpp_parms();
634     resume_thread(THREAD_SCREEN);
635     fcdproplus_rx_freq_control();
636   }
637 }
638 
new_fcd_vfo_incr(void)639 void new_fcd_vfo_incr(void)
640 {
641   if ( numinput_int_data < 1 )
642     numinput_int_data = 1;
643   if ( numinput_int_data > 100000 )
644     numinput_int_data = 100000;
645 
646   fcdprodriver.freq_incr=numinput_int_data;
647   fg.passband_increment = ((double)fcdprodriver.freq_incr) / 1000.0;
648   pause_thread(THREAD_SCREEN);
649   show_fcdpp_parms();
650   resume_thread(THREAD_SCREEN);
651 }
652 
mouse_on_fcdproplus_graph(void)653 void mouse_on_fcdproplus_graph(void)
654 { int event_no;
655   // First find out if we are on a button or border line.
656   for(event_no=0; event_no<MAX_FCDGRBUTT; event_no++)
657   {
658     if( fcdgrbutt[event_no].x1 <= mouse_x &&
659         fcdgrbutt[event_no].x2 >= mouse_x &&
660         fcdgrbutt[event_no].y1 <= mouse_y &&
661         fcdgrbutt[event_no].y2 >= mouse_y)
662     {
663       fcdgr_old_y1=fcdgr.ytop;
664       fcdgr_old_y2=fcdgr.ybottom;
665       fcdgr_old_x1=fcdgr.xleft;
666       fcdgr_old_x2=fcdgr.xright;
667       mouse_active_flag=1+event_no;
668       current_mouse_activity=mouse_continue_fcdproplus_graph;
669       return;
670     }
671   }
672 
673   mouse_active_flag=1;
674 
675   if(mouse_x > fcdgr.xleft+48*text_width && mouse_x < fcdgr.xleft+53*text_width)
676   {
677     numinput_xpix=fcdgr.xleft+48*text_width;
678     if(mouse_y > (fcdgr.ytop+3.5*text_height))
679     {
680       numinput_ypix=fcdgr.ytop+3.6*text_height;
681       numinput_chars=4;
682       erase_numinput_txt();
683       numinput_flag=FIXED_INT_PARM;
684       par_from_keyboard_routine=new_fcd_ppm_correction;	// PPM value
685     }
686     else if(mouse_y > (fcdgr.ytop+2.5*text_height))
687     {
688       numinput_ypix=fcdgr.ytop+(2.4*text_height)+2;
689       numinput_chars=6;
690       erase_numinput_txt();
691       numinput_flag=FIXED_INT_PARM;
692       par_from_keyboard_routine=new_fcd_vfo_incr;	// VFO incr value
693     }
694   }
695   else
696   { // If we did not select a numeric input by setting numinput_flag
697     // we have to set mouse_active flag.
698     // Set the routine to mouse_nothing, we just want to
699     // set flags when the mouse button is released.
700     current_mouse_activity=mouse_nothing;
701     mouse_active_flag=1;
702   }
703 }
704 
init_fcdproplus_control_window(void)705 void init_fcdproplus_control_window(void)
706 { int i;
707   int errcod;
708   int *sdr_pi;
709   char fcdprodriver_parfil_name[20];
710   FILE *fcdprodriver_file;
711 
712   // Get values from parameterfile
713   sprintf(fcdprodriver_parfil_name,"%s_hwd_fcdgr",rxpar_filenames[rx_mode]);
714   errcod = read_sdrpar(fcdprodriver_parfil_name, MAX_HWAREDRIVER_PARM,
715                        fcdprodriver_parm_text, (int*)((void*)&fcdprodriver));
716   if (errcod != 0)
717   {
718     fcdprodriver_file = fopen(fcdprodriver_parfil_name,"w");
719     //set_default_parameter values
720     fcdprodriver.fcdgr_xleft = 60*text_width;
721     fcdprodriver.fcdgr_ytop = (screen_last_line-4)*text_height;
722     fcdprodriver.center_freq = 1000000;
723     fcdprodriver.freq_incr = 100;	// khz
724     fcdprodriver.lna_on_off = 1;	// on
725     fcdprodriver.mixer_gain_on_off = 1; // on
726     fcdprodriver.if_filter_val = 0;
727     fcdprodriver.if_gain = 0;		// dB
728     fcdprodriver.ppm_correction = 0;
729     fcdprodriver.biasT_on_off = 0;	// off
730     sdr_pi = (int*)(&fcdprodriver);
731     for( i=0; i<MAX_HWAREDRIVER_PARM; i++ )
732     {
733       fprintf(fcdprodriver_file,"%s [%d]\n",fcdprodriver_parm_text[i],sdr_pi[i]);
734     }
735     parfile_end(fcdprodriver_file);
736   }
737 
738   fcdgr.xleft = fcdprodriver.fcdgr_xleft;
739   fcdgr.ytop = fcdprodriver.fcdgr_ytop;
740   // Read state of FCD itself
741   FCDstate = fcdppGetLNA((unsigned char *)&fcdprodriver.lna_on_off);
742   FCDstate = fcdppGetMixerGain((unsigned char *)&fcdprodriver.mixer_gain_on_off);
743   FCDstate = fcdppGetBiasT((unsigned char *)&fcdprodriver.biasT_on_off);
744   FCDstate = fcdppGetIFGain((unsigned char *)&fcdprodriver.if_gain);
745   fg.gain = fcdprodriver.if_gain;
746   fg.passband_center = ((double)fcdprodriver.center_freq) / 1000000.0;
747   fg.passband_increment = ((double)fcdprodriver.freq_incr) / 1000.0;
748 
749   fcdgr_msg_color = 10;
750   fcdgr_msg1[0] = 0;
751   fcdgr_hsiz = (30+MAX_MSGSIZE)*text_width;  //  WIDTH OF USERGRAPH
752   fcdgr_vsiz = (4.8*text_height);            //  HEIGHT OF USERGRAPH
753   fcdgr.xright = fcdgr.xleft+fcdgr_hsiz;
754   fcdgr.ybottom = fcdgr.ytop+fcdgr_vsiz;
755   if(rx_mode < MODE_TXTEST)
756   {
757     fcdproplus_graph_scro = no_of_scro;
758     make_fcdproplus_control_graph();
759     no_of_scro++;
760     if(no_of_scro >= MAX_SCRO)
761       lirerr(89);
762   }
763   fcdprodriver.dummy = 1;		// indicates control window is setup OK.
764 }
765 
766 /////////////////////
767 // FCDPP I/O routines
768 /////////////////////
769 
fcdppGetBiasT(unsigned char * state)770 int fcdppGetBiasT(unsigned char *state)
771 { return read_value_from_fcd(FCD_HID_CMD_GET_BIAS_TEE, state);
772 }
773 
fcdppGetIFGain(unsigned char * value)774 int fcdppGetIFGain(unsigned char *value)
775 { return read_value_from_fcd(FCD_HID_CMD_GET_IF_GAIN, value);
776 }
777 
fcdppGetLNA(unsigned char * state)778 int fcdppGetLNA(unsigned char *state)
779 { return read_value_from_fcd(FCD_HID_CMD_GET_LNA_GAIN, state);
780 }
781 
fcdppGetMixerGain(unsigned char * state)782 int fcdppGetMixerGain(unsigned char *state)
783 { return read_value_from_fcd(FCD_HID_CMD_GET_MIXER_GAIN, state);
784 }
785 
fcdppSetBiasT(unsigned char state)786 int fcdppSetBiasT(unsigned char state)
787 { return write_value_to_fcd(FCD_HID_CMD_SET_BIAS_TEE, state, NULL);
788 }
789 
fcdppSetFreq(unsigned int nufreq,unsigned int * fcdfreq)790 int fcdppSetFreq(unsigned int nufreq, unsigned int *fcdfreq)
791 { return write_value_to_fcd(FCD_HID_CMD_SET_FREQUENCY_HZ, nufreq, fcdfreq);
792 }
793 
fcdppSetIFGain(unsigned char value)794 int fcdppSetIFGain(unsigned char value)
795 { return write_value_to_fcd(FCD_HID_CMD_SET_IF_GAIN, value, NULL);
796 }
797 
fcdppSetLNA(unsigned char state)798 int fcdppSetLNA(unsigned char state)
799 { return write_value_to_fcd(FCD_HID_CMD_SET_LNA_GAIN, state, NULL);
800 }
801 
fcdppSetMixerGain(unsigned char state)802 int fcdppSetMixerGain(unsigned char state)
803 { return write_value_to_fcd(FCD_HID_CMD_SET_MIXER_GAIN, state, NULL);
804 }
805 
806 //////////////////////////////////////////////////////////////////
807 //
808 //  I/O routines using HID for the FCDProPlus
809 //
810 //////////////////////////////////////////////////////////////////
811 //  identifiers for the FCDPP USB interface
812 #define USB_VEND 0x04d8		// USB vendor ID (Microchip Technology Inc.)
813 #define USB_PROD 0xfb31		// USB product ID
814 #define FCD_BUFF 65
815 
read_value_from_fcd(unsigned char cmd,unsigned char * fcdbyte)816 int read_value_from_fcd(unsigned char cmd, unsigned char *fcdbyte)
817 { hid_device *fcd_hid;
818   unsigned char buff[FCD_BUFF];
819   int result;
820 
821   fcd_hid = hid_open(USB_VEND, USB_PROD, NULL);
822   if ( fcd_hid == NULL )
823     return FCD_NOTFOUND;
824 
825   memset(buff, 0, FCD_BUFF);
826   buff[0] = 0;		// Report ID.  Ignored by FCDPP
827   buff[1] = cmd;
828   result = hid_write(fcd_hid, buff, FCD_BUFF);
829   if ( result < 0 )
830     fprintf( stderr,"read_value_from_fcd: hid_write=%d\n", result);
831 
832   memset(buff, 0, FCD_BUFF);
833   result = hid_read(fcd_hid, buff, FCD_BUFF);
834   if ( result < 0 )
835     fprintf( stderr,"read_value_from_fcd: hid_read=%d\n", result);
836 
837   hid_close(fcd_hid);
838 
839   if ( (buff[0] == cmd)  &&  (buff[1] == 1) )
840   { *fcdbyte = buff[2];
841     return FCD_RUNNING;
842   }
843   return FCD_BOOTLOAD;
844 }
845 
write_value_to_fcd(unsigned char cmd,unsigned int fcdvalue,unsigned int * fcdfreq)846 int write_value_to_fcd(unsigned char cmd, unsigned int fcdvalue, unsigned int *fcdfreq)
847 { hid_device *fcd_hid;
848   unsigned char buff[FCD_BUFF];		// buffer to be written to and read from FCDPP
849   int result;
850 
851   fcd_hid = hid_open(USB_VEND, USB_PROD, NULL);
852   if ( fcd_hid == NULL )
853     return FCD_NOTFOUND;
854 
855   memset(buff, 0, FCD_BUFF);
856   buff[0] = 0;		// Report ID.  Ignored by FCDPP
857   buff[1] = cmd;
858   buff[2] = (unsigned char) fcdvalue & 0xFF;          // most values are single char only
859   buff[3] = (unsigned char) (fcdvalue >> 8) & 0xFF;   // Freq needs these extra 3 bytes.
860   buff[4] = (unsigned char) (fcdvalue >> 16) & 0xFF;
861   buff[5] = (unsigned char) (fcdvalue >> 24) & 0xFF;
862   result = hid_write(fcd_hid, buff, FCD_BUFF);
863   if ( result < 0 )
864     fprintf( stderr,"write_value_to_fcd: hid_write=%d\n", result);
865 
866   memset(buff, 0, FCD_BUFF);
867   result = hid_read(fcd_hid, buff, FCD_BUFF);
868   if ( result < 0 )
869     fprintf( stderr,"write_value_to_fcd: hid_read=%d\n", result);
870 
871   hid_close(fcd_hid);
872 
873   if ( buff[0]==FCD_HID_CMD_SET_FREQUENCY_HZ && buff[1]==1 )
874   { if (fcdfreq != NULL)
875     { *fcdfreq = (unsigned int) buff[2];
876       *fcdfreq += (unsigned int) (buff[3] << 8);
877       *fcdfreq += (unsigned int) (buff[4] << 16);
878       *fcdfreq += (unsigned int) (buff[5] << 24);
879     }
880   }
881 
882   if ( buff[0] == cmd )
883     return FCD_RUNNING;
884 
885   return FCD_BOOTLOAD;
886 }
887 
888 #endif
889