Merged VRT work-in-progress from eb/vrt2 (11518:11598) into trunk.
[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(0), 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   return send_rx_command(d_ctrl_fd, true, d_ctrl_port_inaddr,
80                          d_data_port, samples_per_pkt, 0);
81 }
82
83 bool
84 vrt::quadradio::stop_streaming()
85 {
86   return send_stop_rx_command(d_ctrl_fd);
87 }
88
89 bool
90 vrt::quadradio::set_center_freq(double target_freq){
91     if (target_freq < 700e6) return false;
92     if (target_freq <= 1.0e9) return set_band_select("A");
93     if (target_freq <= 1.5e9) return set_band_select("B");
94     if (target_freq <= 2.2e9) return set_band_select("C");
95     if (target_freq <= 3.0e9) return set_band_select("D");
96     return false;
97 }
98
99 bool
100 vrt::quadradio::set_band_select(const std::string &band){
101     if (band == "A") d_band_select = 3;
102     else if (band == "B") d_band_select = 2;
103     else if (band == "C") d_band_select = 1;
104     else if (band == "D") d_band_select = 0;
105     else return false;
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     int db_ctrl = \
153         ((reverse_bits(d_attenuation0, 5) & 0x1f) << 10) | \
154         ((reverse_bits(~d_attenuation1, 5) & 0x1f) << 03) | \
155         ((d_band_select                   & 0x03) << 01) | \
156         ((d_rx_antenna                    & 0x01) << 00);
157     set_dboard_pins(ALL_DBOARDS, db_ctrl);  // FIXME sets them all
158 }
159
160 void
161 vrt::quadradio::set_adc_gain(bool on){
162   set_hsadc_conf(ALL_DBOARDS, 0x14, on ? 0x90 : 0x80);
163 }
164
165 void
166 vrt::quadradio::set_dc_offset_comp(bool on){
167     if (on) {
168         set_hsadc_conf(ALL_DBOARDS, 0x1B, 0x80);
169         set_hsadc_conf(ALL_DBOARDS, 0x1A, 0x00); //bits 6:4 set time constant
170     }
171     else set_hsadc_conf(ALL_DBOARDS, 0x1B, 0x00);
172 }
173
174 void
175 vrt::quadradio::set_digital_gain(float gain){
176     int gain_q1 = static_cast<int>(round(gain*2.0));
177     set_hsadc_conf(ALL_DBOARDS, 0x17, gain_q1);
178 }
179
180 void
181 vrt::quadradio::set_test_signal(vrt_test_sig_t type){
182     set_hsadc_conf(ALL_DBOARDS, 0x16, type);
183 }
184
185 bool
186 vrt::quadradio::open_sockets(const char *quad_radio_ip, int quad_radio_ctrl_port,
187                              int *ctrl_fd_ptr, struct in_addr *ctrl_port_inaddr,
188                              int *data_fd_ptr, int *data_port_ptr)
189 {
190   int   ctrl_fd;        // socket for control
191   int   data_fd;        // socket fd for data
192   int   data_port;      // our port number
193
194   //
195   // create a udp socket and connect it to the quad radio control port
196   //
197
198   ctrl_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
199   if (ctrl_fd == -1){
200     perror("socket: ctrl_fd");
201     return false;
202   }
203
204   struct sockaddr_in si_other;
205   memset(&si_other, 0, sizeof(si_other));
206   si_other.sin_family = AF_INET;
207   si_other.sin_port = htons(quad_radio_ctrl_port);
208   if (inet_pton(AF_INET, quad_radio_ip, &si_other.sin_addr) <= 0){
209     perror("inet_pton");
210     return false;
211   }
212
213   if (connect(ctrl_fd, (struct sockaddr *) &si_other, sizeof(si_other)) != 0){
214     perror("connect");
215     return false;
216   }
217
218   // get our ip address associated with the interface connected to the control port
219
220   struct sockaddr_in si_me;
221   memset(&si_me, 0, sizeof(si_me));
222   socklen_t sockname_len = sizeof(si_me);
223   if (getsockname(ctrl_fd, (struct sockaddr *) &si_me, &sockname_len) != 0){
224     perror("getsockname");
225   }
226   
227   *ctrl_port_inaddr = si_me.sin_addr;
228
229   if (1){
230     char buf[128];
231     const char *s = inet_ntop(si_me.sin_family, &si_me.sin_addr, buf, sizeof(buf));
232     if (s == 0){
233       perror("inet_ntop");
234       return false;
235     }
236     // printf("our ip addr associated with ctrl port: %s\n", s);
237   }
238   
239   //
240   // create a udp socket to use to receive data
241   //
242
243   data_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
244   if (data_fd == -1){
245     perror("socket: data_fd");
246     return false;
247   }
248
249   // bind it to a local port on the interface that connects to the ctrl port.
250   // FIXME this assumes that interface connected to the control port and the
251   //   interface connected to the data port are the same.  If we're using
252   //   both ethernet ports on the quad radio, this may not be the case.
253
254   data_port = -1;
255   for (int port = MIN_IP_LOCAL_PORT; port <= MAX_IP_LOCAL_PORT; port++){
256     struct sockaddr_in si_me;
257     memset(&si_me, 0, sizeof(si_me));
258     si_me.sin_family = AF_INET;
259     si_me.sin_port = htons(port);
260     si_me.sin_addr.s_addr = htonl(INADDR_ANY);
261
262     if (bind(data_fd, (struct sockaddr *) &si_me, sizeof(si_me)) == 0){ // found one!
263       data_port = port;
264       break;
265     }
266   }
267
268   if (data_port == -1){
269     fprintf(stderr, "failed to bind to a local port\n");
270     return false;
271   }
272
273   // printf("our data port = %d\n", data_port);
274
275   *ctrl_fd_ptr = ctrl_fd;
276   *data_fd_ptr = data_fd;
277   *data_port_ptr = data_port;
278
279   return true;
280 }
281
282 // ------------------------------------------------------------------------
283
284 bool
285 vrt::quadradio::send_rx_command(int ctrl_fd, bool start,
286                                 struct in_addr addr, int data_port,
287                                 int samples_per_pkt, int siggen_param)
288 {
289   uint32_t cmd[7];
290   cmd[0] = htonl(0);               // verb: set
291   cmd[1] = htonl(0);               // id: rx_streaming
292   cmd[2] = htonl(start ? 1: 0);    // start or stop?
293   cmd[3] = addr.s_addr;            // ip address to send data to (already network endian)
294   cmd[4] = htonl(data_port);       // port to send data to
295   cmd[5] = htonl(samples_per_pkt);
296   cmd[6] = htonl(siggen_param);
297
298   return send_and_check(ctrl_fd, cmd, sizeof(cmd));
299 }
300
301 bool
302 vrt::quadradio::send_stop_rx_command(int ctrl_fd)
303 {
304   struct in_addr in_addr;
305   in_addr.s_addr = 0;
306   return send_rx_command(ctrl_fd, false, in_addr, 0, 0, 0);
307 }
308
309 bool
310 vrt::quadradio::set_dboard_pins(int dboard_bitmask, int v)
311 {
312   uint32_t cmd[4];
313   cmd[0] = htonl(0);               // verb: set
314   cmd[1] = htonl(1);               // id: dboard_pins
315   cmd[2] = htonl(dboard_bitmask);
316   cmd[3] = htonl(v);               // value
317
318   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
319 }
320   
321 bool
322 vrt::quadradio::set_setting_reg(int regno, int value)
323 {
324   uint32_t cmd[4];
325   cmd[0] = htonl(0);               // verb: set
326   cmd[1] = htonl(2);               // id: SR
327   cmd[2] = htonl(regno);
328   cmd[3] = htonl(value);
329
330   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
331 }
332
333 bool
334 vrt::quadradio::set_hsadc_conf(int dboard_bitmask, int regno, int value)
335 {
336   uint32_t cmd[5];
337   cmd[0] = htonl(0);               // verb: set
338   cmd[1] = htonl(3);               // id: HSADC_CONF
339   cmd[2] = htonl(dboard_bitmask);
340   cmd[3] = htonl(regno);
341   cmd[4] = htonl(value);
342
343   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
344 }
345
346 bool
347 vrt::quadradio::set_lsdac(int dboard_bitmask, int which_dac, int value)
348 {
349   uint32_t cmd[5];
350   cmd[0] = htonl(0);               // verb: set
351   cmd[1] = htonl(4);               // id: LSDAC
352   cmd[2] = htonl(dboard_bitmask);
353   cmd[3] = htonl(which_dac);
354   cmd[4] = htonl(value);
355
356   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
357 }
358
359 bool
360 vrt::quadradio::set_mem32(int addr, int value)
361 {
362   uint32_t cmd[4];
363   cmd[0] = htonl(0);               // verb: set
364   cmd[1] = htonl(5);               // id: MEM32
365   cmd[2] = htonl(addr);
366   cmd[3] = htonl(value);
367
368   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
369 }
370
371 bool
372 vrt::quadradio::set_lo_freq(double freq)
373 {
374   uint64_t lo_freq = uint64_t(freq * (uint64_t(1)<<20)); //q20 format
375   uint32_t cmd[4];
376   cmd[0] = htonl(0);               // verb: set
377   cmd[1] = htonl(6);               // id: lo freq
378   cmd[2] = htonl((lo_freq >> 32) & 0xffffffff);
379   cmd[3] = htonl((lo_freq >> 0) & 0xffffffff);
380
381   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
382 }
383
384 bool
385 vrt::quadradio::set_cal_freq(double freq)
386 {
387   uint64_t cal_freq = uint64_t(freq * (uint64_t(1)<<20)); //q20 format
388   uint32_t cmd[4];
389   cmd[0] = htonl(0);               // verb: set
390   cmd[1] = htonl(7);               // id: cal freq
391   cmd[2] = htonl((cal_freq >> 32) & 0xffffffff);
392   cmd[3] = htonl((cal_freq >> 0) & 0xffffffff);
393
394   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
395 }
396
397 bool
398 vrt::quadradio::set_beamforming(int32_t gains[8]){
399   uint32_t cmd[2+8];
400   cmd[0] = htonl(0);               // verb: set
401   cmd[1] = htonl(8);               // id: beamformin
402   for (int i = 0; i < 8; i++){
403     //printf("%d\n", gains[i]);
404     cmd[i+2] = htonl(gains[i]); 
405   }
406   return send_and_check(d_ctrl_fd, cmd, sizeof(cmd));
407 }
408