Updated FSF address in all files. Fixes ticket:51
[debian/gnuradio] / ezdop / src / host / ezdop / ezdop.cc
1 /*
2  * Copyright 2006 Free Software Foundation, Inc.
3  * 
4  * This file is part of GNU Radio
5  * 
6  * GNU Radio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  * 
11  * GNU Radio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with GNU Radio; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 // Application specific includes
23 #include "ezdop.h"
24
25 // Boost includes
26 #include <boost/scoped_array.hpp>
27
28 // System includes (FIXME: autoconf these)
29 #include <cassert>
30 #include <cstdio>
31 #include <unistd.h>
32
33 ezdop::ezdop()
34 {
35     d_device = new struct ftdi_context;
36     if (ftdi_init(d_device))
37         fprintf(stderr, "ftdi_init: %s", d_device->error_str);
38     d_online = false;
39     d_rotating = false;
40     d_rate = EZDOP_DEFAULT_RATE;
41     d_state = ST_HI;
42     d_ant = 8; // FIXME: get from controller.h
43     d_seq = 0;
44     d_val = 0;
45     d_in_phase = 0;
46     d_quadrature = 0;
47     d_rotating = false;
48 }
49
50 ezdop::~ezdop()
51 {
52     assert(d_device);
53     delete d_device;
54 }
55
56 bool ezdop::init()
57 {
58     assert(d_device);
59
60     d_online = false;
61
62     // Attaches to first found device matching ID strings
63     if (ftdi_usb_open(d_device, EZDOP_VENDORID, EZDOP_PRODUCTID)) {
64         fprintf(stderr, "ftdi_usb_open: %s", d_device->error_str);
65         return false;
66     }
67
68     d_online = true;
69     reset();
70
71     return d_online;
72 }
73
74 bool ezdop::finish()
75 {
76     assert(d_device);
77
78     if (!d_online)
79         return true;
80
81     if (d_rotating)
82         stop_rotating();    
83
84     if (ftdi_usb_close(d_device)) {
85         fprintf(stderr, "ftdi_usb_close: %s", d_device->error_str);
86         return false;
87     }
88
89     d_online = false;
90     return true;
91 }
92
93 bool ezdop::reset()
94 {
95     assert(d_device);
96     assert(d_online);
97
98     // Reset FTDI chipset
99     if (ftdi_usb_reset(d_device)) {
100         fprintf(stderr, "ftdi_usb_reset: %s", d_device->error_str);
101         return false;
102     }
103
104     // Set FTDI chipset baudrate for bitbang
105     if (ftdi_set_baudrate(d_device, EZDOP_BAUDRATE)) {
106         fprintf(stderr, "ftdi_set_baudrate: %s", d_device->error_str);
107         return false;
108     }
109
110     // Toggle DTR (-->AVR RESET)
111     // Enable bitbang
112     if (ftdi_enable_bitbang(d_device, EZDOP_BBDIR)) {
113         fprintf(stderr, "ftdi_enable_bitbang: %s", d_device->error_str);
114         return false;
115     }
116
117     // Lower DTR by writing 0 to bitbang output
118     if (!send_byte(0x00)) // This actually lowers all outputs, not just DTR
119         return false;
120
121     // 10 ms sleep with RESET low
122     usleep(10000);
123
124     // Now raise DTR by writing 1 to bitbang output
125     if (!send_byte(0xFF)) // This actually raises all outputs, not just DTR
126         return false;
127
128     if (ftdi_disable_bitbang(d_device)) {
129         fprintf(stderr, "ftdi_disable_bitbang: %s", d_device->error_str);
130         return false;
131     }
132         
133     // Minimum chunk size for reads to reduce latency
134     if (ftdi_read_data_set_chunksize(d_device, 256)) {
135         fprintf(stderr, "ftdi_read_data_set_chunksize: %s", d_device->error_str);
136         return false;
137     }
138
139     // 100 ms after RESET cleared to let things warm up
140     usleep(100000);
141     
142     d_rate = EZDOP_DEFAULT_RATE;
143     return true;
144 }
145
146 bool ezdop::set_rate(int rate)
147 {
148     assert(d_device);
149     assert(d_online);
150
151     // Rate command is one byte, followed by rate as operand
152     int divisor = 2000/rate;
153     if (send_byte(EZDOP_CMD_RATE) && send_byte((unsigned char)divisor)) {
154         d_rate = divisor;
155         return true;
156     }
157
158     return false;
159 }
160
161 bool ezdop::rotate()
162 {
163     assert(d_online);
164     assert(d_device);
165
166     d_rotating = send_byte(EZDOP_CMD_ROTATE);
167     return d_rotating;
168 }
169
170 bool ezdop::stop_rotating()
171 {
172     assert(d_online);
173     assert(d_device);
174
175     // TODO: set to antenna #1, perhaps do this in firmware instead
176     d_rotating = send_byte(EZDOP_CMD_STOP);
177     return d_rotating;
178 }
179
180 bool ezdop::stream()
181 {
182     assert(d_online);
183     assert(d_device);
184
185     return (send_byte(EZDOP_CMD_STREAM));
186 }
187
188 bool ezdop::stop_streaming()
189 {
190     assert(d_online);
191     assert(d_device);
192
193     return (send_byte(EZDOP_CMD_STROFF));
194 }
195
196 bool ezdop::send_byte(unsigned char data)
197 {
198     assert(d_online);
199     assert(d_device);
200
201     if (ftdi_write_data(d_device, &data, 1) != 1) {
202         fprintf(stderr, "ftdi_write_data: %s", d_device->error_str);
203         return false;
204     }
205     
206     return true;        
207 }
208
209 int ezdop::read_raw(unsigned char *buffer, unsigned int length)
210 {
211     assert(d_online);
212     assert(d_device);
213     assert(buffer);
214         
215     // Read samples from USB port, 2 bytes per sample
216     int rd = ftdi_read_data(d_device, buffer, length);
217     if (rd < 0) {
218         fprintf(stderr, "ftdi_read_data: %s", d_device->error_str);
219         return -1;
220     }
221
222     return rd;
223 }
224
225 typedef boost::scoped_array<unsigned char> unsigned_char_scoped_array;
226
227 int ezdop::read_iq(complex<float> *buffer, unsigned int samples, float &volume)
228 {
229     assert(d_online);
230     assert(d_device);
231     assert(buffer);
232
233     // 4 phases, d_rate samples per phase, 2 bytes per sample
234     int raw_size = 8*d_rate*samples;
235     unsigned_char_scoped_array raw(new unsigned char[raw_size]);
236
237     // Read until required bytes are read. Will block until bytes arrive.
238     int rd = 0;
239     while (rd < raw_size)
240         rd += read_raw(&raw[rd], raw_size-rd);
241
242     // Iterate through read bytes and invoke state machine
243     int i = 0, j = 0;   // i index inputs, j indexes outputs
244
245     while (i < raw_size) {
246         unsigned char ch = raw[i++];
247         if (d_state == ST_LO) {
248             d_val = ch;                     // Save lo byte
249             d_state = ST_HI;                // Switch states
250             continue;                       // Done with this state
251         }
252         
253         if (d_state == ST_HI) {
254             unsigned char ant = ch >> 4;    // antenna is high nibble
255             if (ant != d_ant) {             // Didn't get expected antenna
256                 // Abort current sequence
257                 d_ant = 8;                  
258                 d_seq = 0;
259                 d_val = 0;
260                 d_in_phase = 0; d_quadrature = 0;
261                 d_val = ch;                 // Act as if this were a lo byte instead
262                 continue;                   // Stay in ST_HI
263             }
264         }
265
266         // Got correct antenna
267         d_val |= (ch & 0x03) << 8;  // Mask off and save audio high value
268
269         // This down-converts rotation frequency to exactly 0 Hz
270         // while integrating audio response over duration of one antenna phase
271         if (d_ant == 8)                 // +I
272             d_in_phase += d_val;
273         else if (d_ant == 4)            // +Q
274             d_quadrature += d_val;
275         else if (d_ant == 2)            // -I
276             d_in_phase -= d_val;
277         else if (d_ant == 1)            // -Q
278             d_quadrature -= d_val;
279
280         d_val = 0;
281     
282         // Update expected antenna and sequence
283         if (++d_seq == d_rate)  {
284             d_ant = d_ant >> 1;
285             d_seq = 0;
286             if (d_ant == 0) {           // fell off the end
287                 d_ant = 8;              // FIXME: grab from controller.h
288     
289                 // We've accumulated I and Q over a whole antenna rotation
290                 // Output complex<float> in range [-1.0, 1.0]
291                 buffer[j++] = complex<float>(d_in_phase/(1024.0*d_rate), 
292                                              d_quadrature/(1024.0*d_rate));
293                 d_in_phase = 0; d_quadrature = 0;
294             }
295         }
296
297         d_state = ST_LO;  // Switch states
298     };
299
300     volume = 0.0;
301     return j;
302 }