a8bc3e525bdb5eea535c128d29b74c5b7b0d30ad
[debian/gnuradio] / vrt / lib / quadradio.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2009 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <vrt/quadradio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdexcept>
29 #include <math.h>
30
31 #define MIN_IP_LOCAL_PORT       32768
32 #define MAX_IP_LOCAL_PORT       61000
33
34 #define ALL_DBOARDS     0xf
35
36 static bool
37 send_and_check(int fd, void *buf, size_t len)
38 {
39   int r = send(fd, buf, len, 0);
40   if (r < 0){
41     perror("send");
42     return false;
43   }
44   if ((size_t) r != len){
45     fprintf(stderr, "send: short return value.  expected %zd, got %d\n", len, r);
46     return false;
47   }
48   return true;
49 }
50
51
52 vrt::quadradio::quadradio(const std::string &ip, size_t rx_bufsize)
53   : d_ctrl_fd(0), d_data_fd(0), d_data_port(0),
54   d_band_select(VRT_BAND_SEL_A), d_rx_antenna(0), d_attenuation0(0), d_attenuation1(0)//d_10dB_atten(true)
55 {
56   if (!open(ip.c_str()))
57     throw std::runtime_error("vrt::quadradio: failed to open " + ip + "\n");
58
59   d_rx = vrt::rx::make(data_socket_fd(), rx_bufsize);
60   set_test_signal(VRT_TEST_SIG_NORMAL);
61 }
62
63 vrt::quadradio::~quadradio()
64 {
65   ::close(d_ctrl_fd);
66 }
67
68 bool
69 vrt::quadradio::open(const char *ip)
70 {
71   return open_sockets(ip, control_port(),
72                       &d_ctrl_fd, &d_ctrl_port_inaddr,
73                       &d_data_fd, &d_data_port);
74 }
75
76 bool
77 vrt::quadradio::start_streaming(int samples_per_pkt)
78 {
79   int rxdspno = 0;      // FIXME make it the first param
80
81   return send_rx_command(d_ctrl_fd, rxdspno, true, d_ctrl_port_inaddr,
82                          d_data_port, samples_per_pkt);
83 }
84
85 bool
86 vrt::quadradio::stop_streaming()
87 {
88   int rxdspno = 0;      // FIXME make it the first param
89
90   return send_stop_rx_command(d_ctrl_fd, rxdspno);
91 }
92
93 bool
94 vrt::quadradio::set_center_freq(double target_freq){
95     if (target_freq < 700e6) return false;
96     if (target_freq <= 1.0e9) return set_band_select(VRT_BAND_SEL_A);
97     if (target_freq <= 1.5e9) return set_band_select(VRT_BAND_SEL_B);
98     if (target_freq <= 2.2e9) return set_band_select(VRT_BAND_SEL_C);
99     if (target_freq <= 3.0e9) return set_band_select(VRT_BAND_SEL_D);
100     return false;
101 }
102
103 bool
104 vrt::quadradio::set_band_select(vrt_band_sel_t band){
105     d_band_select = band;
106     update_dboard_pins();
107     return true;
108 }
109
110 //void
111 //vrt::quadradio::set_10dB_atten(bool on){
112 //    d_10dB_atten = on;
113 //    update_dboard_pins();
114 //}
115
116 bool
117 vrt::quadradio::select_rx_antenna(const std::string &ant){
118     if (ant == "rf") d_rx_antenna = 0;
119     else if (ant == "cal") d_rx_antenna = 1;
120     else return true;
121     update_dboard_pins();
122     return true;
123 }
124
125 bool
126 vrt::quadradio::set_attenuation0(int attenuation){
127     if (attenuation < 0 || attenuation > 31) return false;
128     d_attenuation0 = attenuation;
129     update_dboard_pins();
130     return true;
131 }
132
133 bool
134 vrt::quadradio::set_attenuation1(int attenuation){
135     if (attenuation < 0 || attenuation > 31) return false;
136     d_attenuation1 = attenuation;
137     update_dboard_pins();
138     return true;
139 }
140
141 //bit reversal, length in bits
142 static int reverse_bits(int input, int len){
143     int reversed = 0;
144     for (int i = 0; i < len; i++){
145         reversed += (input & (1<<i))?(1 << (len-i-1)):0;
146     }
147     return reversed;
148 }
149
150 void
151 vrt::quadradio::update_dboard_pins(void){
152     //convert the band ID to bits
153     int band_select;
154     switch (d_band_select){
155         case VRT_BAND_SEL_A: band_select = 3; break;
156         case VRT_BAND_SEL_B: band_select = 2; break;
157         case VRT_BAND_SEL_C: band_select = 1; break;
158         case VRT_BAND_SEL_D: band_select = 0; break;
159         default: band_select = 0;
160     }
161     //calculate the control bits
162     int db_ctrl = \
163         ((reverse_bits(d_attenuation0, 5)  & 0x1f) << 10) | \
164         ((reverse_bits(~d_attenuation1, 5) & 0x1f) << 03) | \
165         ((band_select                      & 0x03) << 01) | \
166         ((d_rx_antenna                     & 0x01) << 00);
167     set_dboard_pins(ALL_DBOARDS, db_ctrl);  // FIXME sets them all
168 }
169
170 void
171 vrt::quadradio::set_adc_gain(bool on){
172   set_hsadc_conf(ALL_DBOARDS, 0x14, on ? 0x90 : 0x80);
173 }
174
175 void
176 vrt::quadradio::set_dc_offset_comp(bool on){
177     if (on) {
178         set_hsadc_conf(ALL_DBOARDS, 0x1B, 0x80);
179         set_hsadc_conf(ALL_DBOARDS, 0x1A, 0x00); //bits 6:4 set time constant
180     }
181     else set_hsadc_conf(ALL_DBOARDS, 0x1B, 0x00);
182 }
183
184 void
185 vrt::quadradio::set_digital_gain(float gain){
186     int gain_q1 = static_cast<int>(round(gain*2.0));
187     set_hsadc_conf(ALL_DBOARDS, 0x17, gain_q1);
188 }
189
190 void
191 vrt::quadradio::set_test_signal(vrt_test_sig_t type){
192     set_hsadc_conf(ALL_DBOARDS, 0x16, type);
193 }
194
195 bool
196 vrt::quadradio::open_sockets(const char *quad_radio_ip, int quad_radio_ctrl_port,
197                              int *ctrl_fd_ptr, struct in_addr *ctrl_port_inaddr,
198                              int *data_fd_ptr, int *data_port_ptr)
199 {
200   int   ctrl_fd;        // socket for control
201   int   data_fd;        // socket fd for data
202   int   data_port;      // our port number
203
204   //
205   // create a udp socket and connect it to the quad radio control port
206   //
207
208   ctrl_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
209   if (ctrl_fd == -1){
210     perror("socket: ctrl_fd");
211     return false;
212   }
213
214   struct sockaddr_in si_other;
215   memset(&si_other, 0, sizeof(si_other));
216   si_other.sin_family = AF_INET;
217   si_other.sin_port = htons(quad_radio_ctrl_port);
218   if (inet_pton(AF_INET, quad_radio_ip, &si_other.sin_addr) <= 0){
219     perror("inet_pton");
220     return false;
221   }
222
223   if (connect(ctrl_fd, (struct sockaddr *) &si_other, sizeof(si_other)) != 0){
224     perror("connect");
225     return false;
226   }
227
228   // get our ip address associated with the interface connected to the control port
229
230   struct sockaddr_in si_me;
231   memset(&si_me, 0, sizeof(si_me));
232   socklen_t sockname_len = sizeof(si_me);
233   if (getsockname(ctrl_fd, (struct sockaddr *) &si_me, &sockname_len) != 0){
234     perror("getsockname");
235   }
236   
237   *ctrl_port_inaddr = si_me.sin_addr;
238
239   if (1){
240     char buf[128];
241     const char *s = inet_ntop(si_me.sin_family, &si_me.sin_addr, buf, sizeof(buf));
242     if (s == 0){
243       perror("inet_ntop");
244       return false;
245     }
246     // printf("our ip addr associated with ctrl port: %s\n", s);
247   }
248   
249   //
250   // create a udp socket to use to receive data
251   //
252
253   data_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
254   if (data_fd == -1){
255     perror("socket: data_fd");
256     return false;
257   }
258
259   // bind it to a local port on the interface that connects to the ctrl port.
260   // FIXME this assumes that interface connected to the control port and the
261   //   interface connected to the data port are the same.  If we're using
262   //   both ethernet ports on the quad radio, this may not be the case.
263
264   data_port = -1;
265   for (int port = MIN_IP_LOCAL_PORT; port <= MAX_IP_LOCAL_PORT; port++){
266     struct sockaddr_in si_me;
267     memset(&si_me, 0, sizeof(si_me));
268     si_me.sin_family = AF_INET;
269     si_me.sin_port = htons(port);
270     si_me.sin_addr.s_addr = htonl(INADDR_ANY);
271
272     if (bind(data_fd, (struct sockaddr *) &si_me, sizeof(si_me)) == 0){ // found one!
273       data_port = port;
274       break;
275     }
276   }
277
278   if (data_port == -1){
279     fprintf(stderr, "failed to bind to a local port\n");
280     return false;
281   }
282
283   // printf("our data port = %d\n", data_port);
284
285   *ctrl_fd_ptr = ctrl_fd;
286   *data_fd_ptr = data_fd;
287   *data_port_ptr = data_port;
288
289   return true;
290 }
291
292 // ------------------------------------------------------------------------
293
294 bool
295 vrt::quadradio::send_rx_command(int ctrl_fd, int rxdspno, bool start,
296                                 struct in_addr addr, int data_port,
297                                 int samples_per_pkt)
298 {
299   uint32_t cmd[7];
300   cmd[0] = htonl(0);               // verb: set
301   cmd[1] = htonl(0);               // id: rx_streaming
302   cmd[2] = htonl(start ? 1: 0);    // start or stop?
303   cmd[3] = addr.s_addr;            // ip address to send data to (already network endian)
304   cmd[4] = htonl(data_port);       // port to send data to
305   cmd[5] = htonl(samples_per_pkt);
306   cmd[6] = htonl(rxdspno);         // the DSP pipeline to configure
307
308   return send_and_check(ctrl_fd, cmd, sizeof(cmd));
309 }
310
311 bool
312 vrt::quadradio::send_stop_rx_command(int ctrl_fd, int rxdspno)
313 {
314   struct in_addr in_addr;
315   in_addr.s_addr = 0;
316   return send_rx_command(ctrl_fd, rxdspno, false, in_addr, 0, 0);
317 }
318
319 bool
320 vrt::quadradio::set_dboard_pins(int dboard_bitmask, int v)
321 {
322   uint32_t cmd[4];
323   cmd[0] = htonl(0);               // verb: set
324   cmd[1] = htonl(1);               // id: dboard_pins
325   cmd[2] = htonl(dboard_bitmask);
326   cmd[3] = htonl(v);               // value
327
328   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
329 }
330   
331 bool
332 vrt::quadradio::set_setting_reg(int regno, int value)
333 {
334   uint32_t cmd[4];
335   cmd[0] = htonl(0);               // verb: set
336   cmd[1] = htonl(2);               // id: SR
337   cmd[2] = htonl(regno);
338   cmd[3] = htonl(value);
339
340   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
341 }
342
343 bool
344 vrt::quadradio::set_hsadc_conf(int dboard_bitmask, int regno, int value)
345 {
346   uint32_t cmd[5];
347   cmd[0] = htonl(0);               // verb: set
348   cmd[1] = htonl(3);               // id: HSADC_CONF
349   cmd[2] = htonl(dboard_bitmask);
350   cmd[3] = htonl(regno);
351   cmd[4] = htonl(value);
352
353   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
354 }
355
356 bool
357 vrt::quadradio::set_lsdac(int dboard_bitmask, int which_dac, int value)
358 {
359   uint32_t cmd[5];
360   cmd[0] = htonl(0);               // verb: set
361   cmd[1] = htonl(4);               // id: LSDAC
362   cmd[2] = htonl(dboard_bitmask);
363   cmd[3] = htonl(which_dac);
364   cmd[4] = htonl(value);
365
366   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
367 }
368
369 bool
370 vrt::quadradio::set_mem32(int addr, int value)
371 {
372   uint32_t cmd[4];
373   cmd[0] = htonl(0);               // verb: set
374   cmd[1] = htonl(5);               // id: MEM32
375   cmd[2] = htonl(addr);
376   cmd[3] = htonl(value);
377
378   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
379 }
380
381 bool
382 vrt::quadradio::set_lo_freq(double freq)
383 {
384   uint64_t lo_freq = uint64_t(freq * (uint64_t(1)<<20)); //q20 format
385   uint32_t cmd[4];
386   cmd[0] = htonl(0);               // verb: set
387   cmd[1] = htonl(6);               // id: lo freq
388   cmd[2] = htonl((lo_freq >> 32) & 0xffffffff);
389   cmd[3] = htonl((lo_freq >> 0) & 0xffffffff);
390
391   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
392 }
393
394 bool
395 vrt::quadradio::set_cal_freq(double freq)
396 {
397   uint64_t cal_freq = uint64_t(freq * (uint64_t(1)<<20)); //q20 format
398   uint32_t cmd[4];
399   cmd[0] = htonl(0);               // verb: set
400   cmd[1] = htonl(7);               // id: cal freq
401   cmd[2] = htonl((cal_freq >> 32) & 0xffffffff);
402   cmd[3] = htonl((cal_freq >> 0) & 0xffffffff);
403
404   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
405 }
406
407 bool
408 vrt::quadradio::set_beamforming(int32_t gains[8]){
409   uint32_t cmd[2+8];
410   cmd[0] = htonl(0);               // verb: set
411   cmd[1] = htonl(8);               // id: beamformin
412   for (int i = 0; i < 8; i++){
413     //printf("%d\n", gains[i]);
414     cmd[i+2] = htonl(gains[i]); 
415   }
416   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
417 }
418
419 bool
420 vrt::quadradio::set_cal_enb(bool enb)
421 {
422   uint32_t cmd[3];
423   cmd[0] = htonl(0);               // verb: set
424   cmd[1] = htonl(9);               // id: cal enb
425   cmd[2] = htonl(enb);
426
427   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
428 }