Merged r7478:7608 from michaelld/t186 into trunk. Adds ability to compile GNU Radio...
[debian/gnuradio] / usrp / host / apps-inband / test_usrp_inband_underrun.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007 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
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <mb_mblock.h>
27 #include <mb_runtime.h>
28 #include <mb_protocol_class.h>
29 #include <mb_exception.h>
30 #include <mb_msg_queue.h>
31 #include <mb_message.h>
32 #include <mb_msg_accepter.h>
33 #include <mb_class_registry.h>
34 #include <pmt.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <sys/time.h>
38 #include <iostream>
39 #include <ui_nco.h>
40
41 // Include the symbols needed for communication with USRP server
42 #include <symbols_usrp_server_cs.h>
43 #include <symbols_usrp_channel.h>
44 #include <symbols_usrp_low_level_cs.h>
45 #include <symbols_usrp_tx.h>
46 #include <symbols_usrp_rx.h>
47
48 static bool verbose = true;
49
50 class test_usrp_inband_underrun : public mb_mblock
51 {
52
53   mb_port_sptr  d_tx;   // Ports connected to the USRP server
54   mb_port_sptr  d_rx;
55   mb_port_sptr  d_cs;
56
57   pmt_t   d_tx_chan;    // Returned channel from TX allocation
58   pmt_t   d_rx_chan;    // Returned channel from RX allocation
59
60   pmt_t   d_which_usrp; // The USRP to use for the test
61
62   long    d_warm_msgs;  // The number of messages to 'warm' the USRP
63   long    d_warm_recvd; // The number of msgs received in the 'warm' state
64
65   // Keep track of current state
66   enum state_t {
67     INIT,
68     OPENING_USRP,
69     ALLOCATING_CHANNELS,
70     WRITE_REGISTER,
71     READ_REGISTER,
72     TRANSMITTING,
73     CLOSING_CHANNELS,
74     CLOSING_USRP,
75   };
76   state_t d_state;
77   
78   long          d_nsamples_to_send;
79   long          d_nsamples_xmitted;
80   long          d_nframes_xmitted;
81   long          d_samples_per_frame;
82   bool          d_done_sending;
83
84   // for generating sine wave output
85   ui_nco<float,float>   d_nco;
86   double                d_amplitude;
87
88   long d_n_underruns;
89
90  public:
91   test_usrp_inband_underrun(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg);
92   ~test_usrp_inband_underrun();
93   void initial_transition();
94   void handle_message(mb_message_sptr msg);
95
96  protected:
97   void opening_usrp();
98   void allocating_channels();
99   void write_register();
100   void read_register();
101   void closing_channels();
102   void closing_usrp();
103   void enter_receiving();
104   void enter_transmitting();
105   void build_and_send_ping();
106   void build_and_send_next_frame();
107   void handle_xmit_response(pmt_t handle);
108   void handle_recv_response(pmt_t dict);
109 };
110
111
112 int
113 main (int argc, char **argv)
114 {
115   // handle any command line args here
116
117   mb_runtime_sptr rt = mb_make_runtime();
118   pmt_t result = PMT_NIL;
119
120   rt->run("top", "test_usrp_inband_underrun", PMT_F, &result);
121 }
122
123
124 test_usrp_inband_underrun::test_usrp_inband_underrun(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg)
125   : mb_mblock(runtime, instance_name, user_arg),
126   d_tx_chan(PMT_NIL),
127   d_rx_chan(PMT_NIL),
128   d_which_usrp(pmt_from_long(0)),
129   d_state(INIT),
130   d_nsamples_to_send((long) 20e6),
131   d_nsamples_xmitted(0),
132   d_nframes_xmitted(0),
133   d_samples_per_frame(d_nsamples_to_send),      // full packet
134
135   d_done_sending(false),
136   d_amplitude(16384),
137   d_n_underruns(0)
138 {
139   
140   // A dictionary is used to pass parameters to the USRP
141   pmt_t usrp_dict = pmt_make_dict();
142
143   // Specify the RBF to use
144   pmt_dict_set(usrp_dict,
145                pmt_intern("rbf"),
146                pmt_intern("nanocell9.rbf"));
147
148   // Set TX and RX interpolations
149   pmt_dict_set(usrp_dict,
150                pmt_intern("interp-tx"),
151                pmt_from_long(8));
152
153   pmt_dict_set(usrp_dict,
154                pmt_intern("decim-rx"),
155                pmt_from_long(128));
156   
157   d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL);
158   d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL);
159   d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL);
160
161   // Create an instance of USRP server and connect ports
162   define_component("server", "usrp_server", usrp_dict);
163   connect("self", "tx0", "server", "tx0");
164   connect("self", "rx0", "server", "rx0");
165   connect("self", "cs", "server", "cs");
166
167   // initialize NCO
168   double freq = 100e3;
169   int interp = 32;                          // 32 -> 4MS/s
170   double sample_rate = 128e6 / interp;  
171   d_nco.set_freq(2*M_PI * freq/sample_rate);
172 }
173
174 test_usrp_inband_underrun::~test_usrp_inband_underrun()
175 {
176 }
177
178 void
179 test_usrp_inband_underrun::initial_transition()
180 {
181   opening_usrp();
182 }
183
184 // Handle message reads all incoming messages from USRP server which will be
185 // initialization and ping responses.  We perform actions based on the current
186 // state and the event (ie, ping response)
187 void
188 test_usrp_inband_underrun::handle_message(mb_message_sptr msg)
189 {
190   pmt_t event = msg->signal();
191   pmt_t data = msg->data();
192   pmt_t port_id = msg->port_id();
193
194   pmt_t handle = PMT_F;
195   pmt_t status = PMT_F;
196   pmt_t dict = PMT_NIL;
197   std::string error_msg;
198       
199   // Check the recv sample responses for underruns and count
200   if(pmt_eq(event, s_response_recv_raw_samples)) {
201     handle = pmt_nth(0, data);
202     status = pmt_nth(1, data);
203     dict   = pmt_nth(4, data);
204
205     if(pmt_eq(status, PMT_T)) {
206       handle_recv_response(dict);
207       return;
208     }
209     else {
210       error_msg = "error while receiving samples:";
211       goto bail;
212     }
213   }
214
215
216   // Dispatch based on state
217   switch(d_state) {
218
219     //----------------------------- OPENING_USRP ----------------------------//
220     // We only expect a response from opening the USRP which should be succesful
221     // or failed.
222     case OPENING_USRP:
223       
224       if(pmt_eq(event, s_response_open)) {
225
226         status = pmt_nth(1, data);          // failed/succes
227         
228         if(pmt_eq(status, PMT_T)) {
229           allocating_channels();
230           return;
231         }
232         else {
233           error_msg = "failed to open usrp:";
234           goto bail;
235         }
236
237       }
238
239       goto unhandled;   // all other messages not handled in this state
240       
241     
242     //----------------------- ALLOCATING CHANNELS --------------------//
243     // When allocating channels, we need to wait for 2 responses from
244     // USRP server: one for TX and one for RX.  Both are initialized to
245     // NIL so we know to continue to the next state once both are set.
246     case ALLOCATING_CHANNELS:
247
248       // A TX allocation response
249       if(pmt_eq(event, s_response_allocate_channel)
250           && pmt_eq(d_tx->port_symbol(), port_id)) 
251       {
252         status = pmt_nth(1, data);
253         
254         // If successful response, extract the channel
255         if(pmt_eq(status, PMT_T)) {
256           
257           d_tx_chan = pmt_nth(2, data);
258
259           if(verbose)
260             std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received TX allocation"
261                       << " on channel " << d_tx_chan << std::endl;
262
263           // If the RX has also been allocated already, we can continue
264           if(!pmt_eqv(d_rx_chan, PMT_NIL)) {
265             enter_receiving();
266             enter_transmitting();
267           }
268
269           return;
270         }
271         else {  // TX allocation failed
272           error_msg = "failed to allocate TX channel:";
273           goto bail;
274         }
275       }
276       
277       // A RX allocation response
278       if(pmt_eq(event, s_response_allocate_channel)
279           && pmt_eq(d_rx->port_symbol(), port_id)) 
280       {
281         status = pmt_nth(1, data);
282         
283         // If successful response, extract the channel
284         if(pmt_eq(status, PMT_T)) {
285           
286           d_rx_chan = pmt_nth(2, data);
287
288           if(verbose)
289             std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received RX allocation"
290                       << " on channel " << d_rx_chan << std::endl;
291
292           // If the TX has also been allocated already, we can continue
293           if(!pmt_eqv(d_tx_chan, PMT_NIL)) {
294             enter_receiving();
295             enter_transmitting();
296           }
297
298           return;
299         }
300         else {  // RX allocation failed
301           error_msg = "failed to allocate RX channel:";
302           goto bail;
303         }
304       }
305
306       goto unhandled;
307
308     case WRITE_REGISTER:
309       goto unhandled;
310
311     case READ_REGISTER:
312       goto unhandled;
313
314     //-------------------------- TRANSMITTING ----------------------------//
315     // In the transmit state we count the number of underruns received and
316     // ballpark the number with an expected count (something >1 for starters)
317     case TRANSMITTING:
318       
319       // Check that the transmits are OK
320       if (pmt_eq(event, s_response_xmit_raw_frame)){
321         handle = pmt_nth(0, data);
322         status = pmt_nth(1, data);
323
324         if (pmt_eq(status, PMT_T)){
325           handle_xmit_response(handle);
326           return;
327         }
328         else {
329           error_msg = "bad response-xmit-raw-frame:";
330           goto bail;
331         }
332       }
333
334       goto unhandled;
335
336     //------------------------- CLOSING CHANNELS ----------------------------//
337     // Check deallocation responses, once the TX and RX channels are both
338     // deallocated then we close the USRP.
339     case CLOSING_CHANNELS:
340       
341       if (pmt_eq(event, s_response_deallocate_channel)
342           && pmt_eq(d_tx->port_symbol(), port_id))
343       {
344         status = pmt_nth(1, data);
345
346         // If successful, set the port to NIL
347         if(pmt_eq(status, PMT_T)) {
348           d_tx_chan = PMT_NIL;
349
350           if(verbose)
351             std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received TX deallocation\n";
352
353           // If the RX is also deallocated, we can close the USRP
354           if(pmt_eq(d_rx_chan, PMT_NIL)) 
355             closing_usrp();
356
357           return;
358
359         } else {
360
361           error_msg = "failed to deallocate TX channel:";
362           goto bail;
363
364         }
365       }
366
367       if (pmt_eq(event, s_response_deallocate_channel)
368           && pmt_eq(d_rx->port_symbol(), port_id))
369       {
370         status = pmt_nth(1, data);
371
372         // If successful, set the port to NIL
373         if(pmt_eq(status, PMT_T)) {
374           d_rx_chan = PMT_NIL;
375
376           if(verbose)
377             std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received RX deallocation\n";
378
379           // If the TX is also deallocated, we can close the USRP
380           if(pmt_eq(d_tx_chan, PMT_NIL)) 
381             closing_usrp();
382
383           return;
384
385         } else {
386           
387           error_msg = "failed to deallocate RX channel:";
388           goto bail;
389
390         }
391       }
392
393       goto unhandled;
394
395     //--------------------------- CLOSING USRP ------------------------------//
396     // Once we have received a successful USRP close response, we shutdown all
397     // mblocks and exit.
398     case CLOSING_USRP:
399       
400       if (pmt_eq(event, s_response_close)) {
401         
402         status = pmt_nth(1, data);
403
404         if(pmt_eq(status, PMT_T)) {
405
406           if(verbose)
407             std::cout << "[TEST_USRP_INBAND_UNDERRUN] Successfully closed USRP\n";
408
409           std::cout << "\nUnderruns: " << d_n_underruns << std::endl;
410           fflush(stdout);
411
412           shutdown_all(PMT_T);
413           return;
414
415         } else {
416
417           error_msg = "failed to close USRP:";
418           goto bail;
419         }
420       }
421
422       goto unhandled;
423
424     case INIT:
425       goto unhandled;
426
427   }
428  
429  // An error occured, print it, and shutdown all m-blocks
430  bail:
431   std::cerr << error_msg << data
432             << "status = " << status << std::endl;
433   shutdown_all(PMT_F);
434   return;
435
436  // Received an unhandled message for a specific state
437  unhandled:
438   if(verbose && !pmt_eq(event, pmt_intern("%shutdown")))
439     std::cout << "test_usrp_inband_tx: unhandled msg: " << msg
440               << "in state "<< d_state << std::endl;
441
442 }
443
444
445 // Sends a command to USRP server to open up a connection to the
446 // specified USRP, which is defaulted to USRP 0 on the system
447 void
448 test_usrp_inband_underrun::opening_usrp()
449 {
450
451   if(verbose)
452     std::cout << "[TEST_USRP_INBAND_UNDERRUN] Opening USRP " 
453               << d_which_usrp << std::endl;
454
455   d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, d_which_usrp));
456   d_state = OPENING_USRP;
457 }
458
459 // RX and TX channels must be allocated so that the USRP server can
460 // properly share bandwidth across multiple USRPs.  No commands will be
461 // successful to the USRP through the USRP server on the TX or RX channels until
462 // a bandwidth allocation has been received.
463 void
464 test_usrp_inband_underrun::allocating_channels()
465 {
466   d_state = ALLOCATING_CHANNELS;
467
468   long capacity = (long) 16e6;
469   d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity)));
470   d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity)));
471 }
472
473 // After allocating the channels, a write register command will be sent to the
474 // USRP.
475 void
476 test_usrp_inband_underrun::write_register()
477 {
478   d_state = WRITE_REGISTER;
479
480   long reg = 0;
481
482   d_tx->send(s_cmd_to_control_channel,    // C/S packet
483              pmt_list2(PMT_NIL,           // invoc handle
484                        pmt_list1(
485                             pmt_list2(s_op_write_reg, 
486                                       pmt_list2(
487                                       pmt_from_long(reg), 
488                                       pmt_from_long(0xbeef))))));
489
490   if(verbose)
491     std::cout << "[TEST_USRP_INBAND_REGISTERS] Writing 0xbeef to " 
492               << reg << std::endl;
493
494   read_register();  // immediately transition to read the register
495 }
496
497 // Temporary: for testing pings
498 void
499 test_usrp_inband_underrun::build_and_send_ping()
500 {
501   
502   d_tx->send(s_cmd_to_control_channel,
503              pmt_list2(PMT_NIL, pmt_list1(pmt_list2(s_op_ping_fixed,
504                                                     pmt_list2(pmt_from_long(0),
505                                                               pmt_from_long(0))))));
506
507   std::cout << "[TEST_USRP_INBAND_UNDERRUN] Ping sent" << std::endl;
508 }
509
510 // After writing to the register, we want to read the value back and ensure that
511 // it is the same value that we wrote.
512 void
513 test_usrp_inband_underrun::read_register()
514 {
515   d_state = READ_REGISTER;
516
517   long reg = 9;
518
519   d_tx->send(s_cmd_to_control_channel,    // C/S packet
520              pmt_list2(PMT_NIL,           // invoc handle
521                        pmt_list1(
522                             pmt_list2(s_op_read_reg, 
523                                       pmt_list2(
524                                       pmt_from_long(0),   // rid 
525                                       pmt_from_long(reg))))));
526   if(verbose)
527     std::cout << "[TEST_USRP_INBAND_UNDERRUN] Reading from register " 
528               << reg << std::endl;
529 }
530
531 // Used to enter the receiving state
532 void
533 test_usrp_inband_underrun::enter_receiving()
534 {
535   d_rx->send(s_cmd_start_recv_raw_samples,
536              pmt_list2(PMT_F,
537                        d_rx_chan));
538
539   if(verbose)
540     std::cout << "[TEST_USRP_INBAND_UNDERRUN] Started RX sample stream\n";
541 }
542
543 void
544 test_usrp_inband_underrun::enter_transmitting()
545 {
546   d_state = TRANSMITTING;
547   d_nsamples_xmitted = 0;
548
549   if(verbose)
550     std::cout << "[TEST_USRP_INBAND_UNDERRUN] Entering transmit state...\n";
551   
552   build_and_send_next_frame();  // fire off 4 to start pipeline
553   build_and_send_next_frame();
554   build_and_send_next_frame();
555   build_and_send_next_frame();
556 }
557
558 void
559 test_usrp_inband_underrun::build_and_send_next_frame()
560 {
561
562   long nsamples_this_frame =
563     std::min(d_nsamples_to_send - d_nsamples_xmitted,
564              d_samples_per_frame);
565
566   if (nsamples_this_frame == 0){
567     d_done_sending = true;
568     return;
569   }
570     
571   size_t nshorts = 2 * nsamples_this_frame;     // 16-bit I & Q
572   pmt_t uvec = pmt_make_s16vector(nshorts, 0);
573   size_t ignore;
574   int16_t *samples = pmt_s16vector_writeable_elements(uvec, ignore);
575
576   // fill in the complex sinusoid
577
578   for (int i = 0; i < nsamples_this_frame; i++){
579
580     if (1){
581       gr_complex s;
582       d_nco.sincos(&s, 1, d_amplitude);
583       // write 16-bit i & q
584       samples[2*i] =   (int16_t) s.real();
585       samples[2*i+1] = (int16_t) s.imag();
586     }
587     else {
588       gr_complex s(d_amplitude, d_amplitude);
589
590       // write 16-bit i & q
591       samples[2*i] =   (int16_t) s.real();
592       samples[2*i+1] = (int16_t) s.imag();
593     }
594   }
595
596   if(verbose)
597     std::cout << "[TEST_USRP_INBAND_TX] Transmitting frame...\n";
598
599   pmt_t timestamp = pmt_from_long(0xffffffff);  // NOW
600   d_tx->send(s_cmd_xmit_raw_frame,
601              pmt_list4(pmt_from_long(d_nframes_xmitted),  // invocation-handle
602                        d_tx_chan,                         // channel
603                        uvec,                              // the samples
604                        timestamp));
605
606   d_nsamples_xmitted += nsamples_this_frame;
607   d_nframes_xmitted++;
608
609   if(verbose)
610     std::cout << "[TEST_USRP_INBAND_TX] Transmitted frame\n";
611
612 }
613
614 void
615 test_usrp_inband_underrun::handle_xmit_response(pmt_t handle)
616 {
617   if (d_done_sending &&
618     pmt_to_long(handle) == (d_nframes_xmitted - 1)){
619     // We're done sending and have received all responses
620     closing_channels();
621     return;
622   }
623
624   build_and_send_next_frame();
625 }
626
627 void
628 test_usrp_inband_underrun::handle_recv_response(pmt_t dict)
629 {
630   if(!pmt_is_dict(dict)) {
631     std::cout << "[TEST_USRP_INBAND_UNDERRUN] Recv samples dictionary is improper\n";
632     return;
633   }
634
635   // Read the TX interpolations
636   if(pmt_t underrun = pmt_dict_ref(dict, 
637                                   pmt_intern("underrun"), 
638                                   PMT_NIL)) {
639     if(pmt_eqv(underrun, PMT_T)) {
640       d_n_underruns++;
641
642       if(verbose && 0)
643         std::cout << "[TEST_USRP_INBAND_UNDERRUN] Underrun\n";
644     }
645     else {
646     if(verbose && 0)
647       std::cout << "[TEST_USRP_INBAND_UNDERRUN] No underrun\n" << underrun <<std::endl;
648     }
649   } else {
650
651     if(verbose && 0)
652       std::cout << "[TEST_USRP_INBAND_UNDERRUN] No underrun\n";
653   }
654   
655 }
656
657 void
658 test_usrp_inband_underrun::closing_channels()
659 {
660   d_state = CLOSING_CHANNELS;
661
662   d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_tx_chan));
663   d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_rx_chan));
664 }
665
666 void
667 test_usrp_inband_underrun::closing_usrp()
668 {
669   d_state = CLOSING_USRP;
670
671   sleep(2);
672
673   d_cs->send(s_cmd_close, pmt_list1(PMT_NIL));
674 }
675
676 REGISTER_MBLOCK_CLASS(test_usrp_inband_underrun);