/* -*- 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
#endif
static const int DEFAULT_RX_SCALE = 1024;
-static const int DEFAULT_TX_SCALE = 3000;
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);
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)
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";
// 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()
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)
{
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)
{
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)
{
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);
}
+ 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