Adds ifc_name() to libusrp2 and gr-usrp2
[debian/gnuradio] / usrp2 / host / lib / usrp2_impl.cc
index 956f2561eef00562dff22aa74a2e7a92ac79c2bb..3a0cd918e02327315d0b3754f12512afa018dabe 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- c++ -*- */
 /*
- * Copyright 2008 Free Software Foundation, Inc.
+ * Copyright 2008,2009 Free Software Foundation, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -46,7 +46,6 @@
 #endif
 
 static const int DEFAULT_RX_SCALE = 1024;
-static const int DEFAULT_TX_SCALE = 3000;
 
 namespace usrp2 {
 
@@ -71,12 +70,16 @@ namespace usrp2 {
     case OP_CONFIG_MIMO: return "OP_CONFIG_MIMO";
     case OP_DBOARD_INFO: return "OP_DBOARD_INFO";
     case OP_DBOARD_INFO_REPLY: return "OP_DBOARD_INFO_REPLY";
-#if 0
-    case OP_WRITE_REG: return "OP_WRITE_REG";
-    case OP_WRITE_REG_MASKED: return "OP_WRITE_REG_MASKED";
-    case OP_READ_REG: return "OP_READ_REG";
-    case OP_READ_REG_REPLY: return "OP_READ_REG_REPLY";
-#endif
+    case OP_SYNC_TO_PPS: return "OP_SYNC_TO_PPS";
+    case OP_PEEK: return "OP_PEEK";
+    case OP_PEEK_REPLY: return "OP_PEEK_REPLY";
+    case OP_SET_TX_LO_OFFSET: return "OP_SET_TX_LO_OFFSET";
+    case OP_SET_TX_LO_OFFSET_REPLY: return "OP_SET_TX_LO_OFFSET_REPLY";
+    case OP_SET_RX_LO_OFFSET: return "OP_SET_RX_LO_OFFSET";
+    case OP_SET_RX_LO_OFFSET_REPLY: return "OP_SET_RX_LO_OFFSET_REPLY";
+    case OP_SYNC_EVERY_PPS: return "OP_SYNC_EVERY_PPS";
+    case OP_SYNC_EVERY_PPS_REPLY: return "OP_SYNC_EVERY_PPS_REPLY";
+
     default:
       char buf[64];
       snprintf(buf, sizeof(buf), "<unknown opcode: %d>", opcode);
@@ -126,8 +129,8 @@ namespace usrp2 {
 
 
   usrp2::impl::impl(const std::string &ifc, props *p)
-    : d_eth_buf(new eth_buffer()), d_pf(0), d_bg_thread(0), d_bg_running(false),
-      d_rx_seqno(-1), d_tx_seqno(0), d_next_rid(0),
+    : d_eth_buf(new eth_buffer()), d_ifc_name(ifc), d_pf(0), d_bg_thread(0),
+      d_bg_running(false), d_rx_seqno(-1), d_tx_seqno(0), d_next_rid(0),
       d_num_rx_frames(0), d_num_rx_missing(0), d_num_rx_overruns(0), d_num_rx_bytes(0), 
       d_num_enqueued(0), d_enqueued_mutex(), d_bg_pending_cond(&d_enqueued_mutex),
       d_channel_rings(NCHANS), d_tx_interp(0), d_rx_decim(0)
@@ -172,6 +175,10 @@ namespace usrp2 {
       fprintf(stderr, "  gain_db_per_step = %g\n", rx_gain_db_per_step());
     }
 
+    // Ensure any custom values in hardware are cleared
+    if (!reset_db())
+      std::cerr << "usrp2::ctor reset_db failed\n";
+
     // default gains to mid point
     if (!set_tx_gain((tx_gain_min() + tx_gain_max()) / 2))
       std::cerr << "usrp2::ctor set_tx_gain failed\n";
@@ -189,9 +196,6 @@ namespace usrp2 {
     // set workable defaults for scaling
     if (!set_rx_scale_iq(DEFAULT_RX_SCALE, DEFAULT_RX_SCALE))
       std::cerr << "usrp2::ctor set_rx_scale_iq failed\n";
-
-    if (!set_tx_scale_iq(DEFAULT_TX_SCALE, DEFAULT_TX_SCALE))
-      std::cerr << "usrp2::ctor set_tx_scale_iq failed\n";
   }
   
   usrp2::impl::~impl()
@@ -477,6 +481,33 @@ namespace usrp2 {
     return success;
   }
   
+  bool
+  usrp2::impl::set_rx_lo_offset(double frequency)
+  {
+    op_freq_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SET_RX_LO_OFFSET;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+
+    u2_fxpt_freq_t fxpt = u2_double_to_fxpt_freq(frequency);
+    cmd.op.freq_hi = htonl(u2_fxpt_freq_hi(fxpt));
+    cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
+
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
   bool
   usrp2::impl::set_rx_center_freq(double frequency, tune_result *result)
   {
@@ -716,6 +747,33 @@ namespace usrp2 {
     return success;
   }
   
+  bool
+  usrp2::impl::set_tx_lo_offset(double frequency)
+  {
+    op_freq_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SET_TX_LO_OFFSET;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+
+    u2_fxpt_freq_t fxpt = u2_double_to_fxpt_freq(frequency);
+    cmd.op.freq_hi = htonl(u2_fxpt_freq_hi(fxpt));
+    cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
+
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
   bool
   usrp2::impl::set_tx_center_freq(double frequency, tune_result *result)
   {
@@ -770,11 +828,42 @@ namespace usrp2 {
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
-    if (success)
+    if (success) {
       d_tx_interp = interpolation_factor;
+
+      // Auto-set TX scaling based on interpolation rate
+      int scale_i, scale_q;
+      default_tx_scale_iq(d_tx_interp, &scale_i, &scale_q);
+      return set_tx_scale_iq(scale_i, scale_q);
+    }
+
     return success;
   }
   
+  void
+  usrp2::impl::default_tx_scale_iq(int interpolation_factor, int *scale_i, int *scale_q)
+  {
+    // Calculate CIC interpolation (i.e., without halfband interpolators)
+    int i = interpolation_factor;
+    if (i > 128)
+      i = i >> 1;
+    if (i > 128)
+      i = i >> 1;
+
+    // Calculate dsp_core_tx gain absent scale multipliers
+    float gain = (1.65*i*i*i)/(4096*pow(2, ceil(log2(i*i*i))));
+    
+    // Calculate closest multiplier constant to reverse gain
+    int scale = (int)rint(1.0/gain);
+    // fprintf(stderr, "if=%i i=%i gain=%f scale=%i\n", interpolation_factor, i, gain, scale);
+
+    // Both I and Q are identical in this case
+    if (scale_i)
+      *scale_i = scale;
+    if (scale_q)
+      *scale_q = scale;
+  }
+
   bool
   usrp2::impl::set_tx_scale_iq(int scale_i, int scale_q)
   {
@@ -906,6 +995,7 @@ namespace usrp2 {
     cmd.op.opcode = OP_CONFIG_MIMO;
     cmd.op.len = sizeof(cmd.op);
     cmd.op.rid = d_next_rid++;
+    cmd.op.flags = flags;
     cmd.eop.opcode = OP_EOP;
     cmd.eop.len = sizeof(cmd.eop);
     
@@ -1020,4 +1110,177 @@ namespace usrp2 {
   }
 
 
+  bool
+  usrp2::impl::sync_to_pps()
+  {
+    op_generic_cmd cmd;
+    op_generic_t   reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SYNC_TO_PPS;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    return ntohx(reply.ok) == 1;
+  }
+
+  bool
+  usrp2::impl::sync_every_pps(bool enable)
+  {
+    op_generic_cmd cmd;
+    op_generic_t   reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SYNC_EVERY_PPS;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.ok = enable ? 1 : 0;
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    return ntohx(reply.ok) == 1;
+  }
+
+  std::vector<uint32_t>
+  usrp2::impl::peek32(uint32_t addr, uint32_t words)
+  {
+    std::vector<uint32_t> result; // zero sized on error return
+    // fprintf(stderr, "usrp2::peek: addr=%08X words=%u\n", addr, words);
+
+    if (addr % 4 != 0) {
+      fprintf(stderr, "usrp2::peek: addr (=%08X) must be 32-bit word aligned\n", addr); 
+      return result;
+    }
+
+    if (words == 0)
+      return result;
+
+    op_peek_cmd   cmd;
+    op_generic_t *reply;
+
+    int wlen = sizeof(uint32_t);
+    int rlen = sizeof(op_generic_t);
+    size_t bytes = words*wlen;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_PEEK;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+
+    cmd.op.addr = htonl(addr);
+    cmd.op.bytes = htonl(bytes);
+
+    reply = (op_generic_t *)malloc(rlen+bytes);
+    pending_reply p(cmd.op.rid, reply, rlen+bytes);
+    if (transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT)) {
+      uint32_t nwords = (reply->len-rlen)/sizeof(uint32_t);
+      uint32_t *data = (uint32_t *)(reply+rlen/wlen);
+      for (unsigned int i = 0; i < nwords; i++)
+       result.push_back(ntohl(data[i]));
+    }
+
+    free(reply);
+    return result;
+  }
+
+  bool
+  usrp2::impl::poke32(uint32_t addr, const std::vector<uint32_t> &data)
+  {
+    if (addr % 4 != 0) {
+      fprintf(stderr, "usrp2::poke32: addr (=%08X) must be 32-bit word aligned\n", addr); 
+      return false;
+    }
+
+    int plen = sizeof(op_poke_cmd);
+    int wlen = sizeof(uint32_t);
+    int max_words = (MAX_SUBPKT_LEN-plen)/wlen;
+    int words = data.size();
+
+    if (words > max_words) {
+      fprintf(stderr, "usrp2::poke32: write size (=%u) exceeds maximum of %u words\n",
+             words, max_words);
+      return false;
+    }
+
+    //fprintf(stderr, "usrp2::poke32: addr=%08X words=%u\n", addr, words);
+
+    if (words == 0)
+      return true; // NOP
+
+    op_poke_cmd  *cmd;
+    op_generic_t *eop;
+
+    // Allocate, clear, and initialize command packet
+    int bytes = words*wlen;
+    int l = plen+bytes+sizeof(*eop); // op_poke_cmd+data+eop
+    cmd = (op_poke_cmd *)malloc(l);
+    //fprintf(stderr, "cmd=%p l=%i\n", cmd, l);
+    memset(cmd, 0, l);
+    init_etf_hdrs(&cmd->h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd->op.opcode = OP_POKE;
+    cmd->op.len = sizeof(cmd->op)+bytes;
+    cmd->op.rid = d_next_rid++;
+    cmd->op.addr = htonl(addr);
+
+    // Copy data from vector into packet space
+    uint32_t *dest = (uint32_t *)((uint8_t *)cmd+plen);
+    for (int i = 0; i < words; i++) {
+      //fprintf(stderr, "%03i@%p\n", i, dest);
+      *dest++ = htonl(data[i]);
+    }
+
+    // Write end-of-packet subpacket
+    eop = (op_generic_t *)dest;
+    eop->opcode = OP_EOP;
+    eop->len = sizeof(*eop);
+    //fprintf(stderr, "eop=%p len=%i\n", eop, eop->len);
+
+    // Send command to device and retrieve reply
+    bool ok = false;
+    op_generic_t reply;
+    pending_reply p(cmd->op.rid, &reply, sizeof(reply));
+    if (transmit_cmd(cmd, l, &p, DEF_CMD_TIMEOUT))
+      ok = (ntohx(reply.ok) == 1);
+
+    free(cmd);
+    return ok;
+  }
+
+  bool
+  usrp2::impl::reset_db()
+  {
+    op_generic_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_RESET_DB;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
 } // namespace usrp2